pager: fix filter invocation and ansi parsing
parent
bee1baedb2
commit
049175e743
|
@ -98,7 +98,7 @@ index_style = "Compact"
|
||||||
identity="username@hostname.local"
|
identity="username@hostname.local"
|
||||||
|
|
||||||
[pager]
|
[pager]
|
||||||
filter = "/usr/bin/pygmentize"
|
filter = "COLUMNS=72 /usr/local/bin/pygmentize -l email"
|
||||||
html_filter = "w3m -I utf-8 -T text/html"
|
html_filter = "w3m -I utf-8 -T text/html"
|
||||||
|
|
||||||
[notifications]
|
[notifications]
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
# "Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
|
# "Sent" = { query="from:username@server.tld from:username2@server.tld", subscribe = true }
|
||||||
#
|
#
|
||||||
#[pager]
|
#[pager]
|
||||||
#filter = "/usr/bin/pygmentize"
|
#filter = "COLUMNS=72 /usr/local/bin/pygmentize -l email"
|
||||||
#pager_context = 0 # default, optional
|
#pager_context = 0 # default, optional
|
||||||
#headers_sticky = true # default, optional
|
#headers_sticky = true # default, optional
|
||||||
|
|
||||||
|
|
|
@ -383,10 +383,11 @@ impl Pager {
|
||||||
width = Some(pager_minimum_width);
|
width = Some(pager_minimum_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(bin) = pager_filter {
|
if let Some(content) = pager_filter.and_then(|bin| {
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
let mut filter_child = Command::new(bin)
|
let mut filter_child = Command::new("sh")
|
||||||
|
.args(&["-c", bin])
|
||||||
.stdin(Stdio::piped())
|
.stdin(Stdio::piped())
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
.spawn()
|
.spawn()
|
||||||
|
@ -405,8 +406,14 @@ impl Pager {
|
||||||
.stdout,
|
.stdout,
|
||||||
)
|
)
|
||||||
.to_string();
|
.to_string();
|
||||||
|
if text.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
crate::terminal::ansi::ansi_to_cellbuffer(&text)
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
return Pager::from_buf(content, cursor_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
let content = {
|
let content = {
|
||||||
let lines: Vec<String> = if let Some(width) = width {
|
let lines: Vec<String> = if let Some(width) = width {
|
||||||
text.split_lines_reflow(reflow, Some(width.saturating_sub(2)))
|
text.split_lines_reflow(reflow, Some(width.saturating_sub(2)))
|
||||||
|
@ -482,6 +489,7 @@ impl Pager {
|
||||||
width,
|
width,
|
||||||
dirty: true,
|
dirty: true,
|
||||||
content,
|
content,
|
||||||
|
initialised: true,
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
@ -524,7 +532,7 @@ impl Component for Pager {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.initialised {
|
if !self.initialised && !self.text.is_empty() {
|
||||||
let mut width = width!(area);
|
let mut width = width!(area);
|
||||||
if width < self.minimum_width {
|
if width < self.minimum_width {
|
||||||
width = self.minimum_width;
|
width = self.minimum_width;
|
||||||
|
|
|
@ -1842,7 +1842,8 @@ pub mod ansi {
|
||||||
use super::{Attr, Cell, CellBuffer, Color};
|
use super::{Attr, Cell, CellBuffer, Color};
|
||||||
/// Create a `CellBuffer` from a string slice containing ANSI escape codes.
|
/// Create a `CellBuffer` from a string slice containing ANSI escape codes.
|
||||||
pub fn ansi_to_cellbuffer(s: &str) -> Option<CellBuffer> {
|
pub fn ansi_to_cellbuffer(s: &str) -> Option<CellBuffer> {
|
||||||
let mut buf: Vec<Cell> = Vec::with_capacity(2048);
|
let mut bufs: Vec<Vec<Cell>> = Vec::with_capacity(2048);
|
||||||
|
let mut row: Vec<Cell> = Vec::with_capacity(2048);
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
Start,
|
Start,
|
||||||
|
@ -1853,7 +1854,7 @@ pub mod ansi {
|
||||||
use State::*;
|
use State::*;
|
||||||
|
|
||||||
let mut rows = 0;
|
let mut rows = 0;
|
||||||
let mut cols = 0;
|
let mut max_cols = 0;
|
||||||
let mut current_fg = Color::Default;
|
let mut current_fg = Color::Default;
|
||||||
let mut current_bg = Color::Default;
|
let mut current_bg = Color::Default;
|
||||||
let mut current_attrs = Attr::DEFAULT;
|
let mut current_attrs = Attr::DEFAULT;
|
||||||
|
@ -1863,7 +1864,11 @@ pub mod ansi {
|
||||||
cur_cell = Cell::default();
|
cur_cell = Cell::default();
|
||||||
state = State::Start;
|
state = State::Start;
|
||||||
let mut chars = l.chars().peekable();
|
let mut chars = l.chars().peekable();
|
||||||
cols = 0;
|
if rows > 0 {
|
||||||
|
max_cols = std::cmp::max(row.len(), max_cols);
|
||||||
|
bufs.push(row);
|
||||||
|
row = Vec::with_capacity(2048);
|
||||||
|
}
|
||||||
rows += 1;
|
rows += 1;
|
||||||
'line_loop: loop {
|
'line_loop: loop {
|
||||||
let c = chars.next();
|
let c = chars.next();
|
||||||
|
@ -1882,10 +1887,8 @@ pub mod ansi {
|
||||||
cur_cell.set_fg(current_fg);
|
cur_cell.set_fg(current_fg);
|
||||||
cur_cell.set_bg(current_bg);
|
cur_cell.set_bg(current_bg);
|
||||||
cur_cell.set_attrs(current_attrs);
|
cur_cell.set_attrs(current_attrs);
|
||||||
buf.push(cur_cell);
|
row.push(cur_cell);
|
||||||
cur_cell = Cell::default();
|
cur_cell = Cell::default();
|
||||||
|
|
||||||
cols += 1;
|
|
||||||
}
|
}
|
||||||
(Csi, 'm') => {
|
(Csi, 'm') => {
|
||||||
/* Reset styles */
|
/* Reset styles */
|
||||||
|
@ -1894,39 +1897,53 @@ pub mod ansi {
|
||||||
current_attrs = Attr::DEFAULT;
|
current_attrs = Attr::DEFAULT;
|
||||||
state = Start;
|
state = Start;
|
||||||
}
|
}
|
||||||
(Csi, '0'..='8') if chars.peek() == Some(&'m') => {
|
(Csi, '0') if chars.peek() == Some(&'0') => {
|
||||||
match chars.next() {
|
current_attrs = Attr::DEFAULT;
|
||||||
Some('0') => {
|
chars.next();
|
||||||
|
let next = chars.next();
|
||||||
|
if next == Some('m') {
|
||||||
|
state = Start;
|
||||||
|
} else if next != Some(';') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Csi, c @ '0'..='8') if chars.peek() == Some(&'m') => {
|
||||||
|
chars.next();
|
||||||
|
state = Start;
|
||||||
|
match c {
|
||||||
|
'0' => {
|
||||||
//Reset all attributes
|
//Reset all attributes
|
||||||
current_fg = Color::Default;
|
current_fg = Color::Default;
|
||||||
current_bg = Color::Default;
|
current_bg = Color::Default;
|
||||||
current_attrs = Attr::DEFAULT;
|
current_attrs = Attr::DEFAULT;
|
||||||
state = Start;
|
|
||||||
}
|
}
|
||||||
Some('1') => {
|
'1' => {
|
||||||
current_attrs.set(Attr::BOLD, true);
|
current_attrs.set(Attr::BOLD, true);
|
||||||
}
|
}
|
||||||
Some('2') => {
|
'2' => {
|
||||||
current_attrs.set(Attr::DIM, true);
|
current_attrs.set(Attr::DIM, true);
|
||||||
}
|
}
|
||||||
Some('3') => {
|
'3' => {
|
||||||
current_attrs.set(Attr::ITALICS, true);
|
current_attrs.set(Attr::ITALICS, true);
|
||||||
}
|
}
|
||||||
Some('4') => {
|
'4' => {
|
||||||
current_attrs.set(Attr::UNDERLINE, true);
|
current_attrs.set(Attr::UNDERLINE, true);
|
||||||
}
|
}
|
||||||
Some('5') => {
|
'5' => {
|
||||||
current_attrs.set(Attr::BLINK, true);
|
current_attrs.set(Attr::BLINK, true);
|
||||||
}
|
}
|
||||||
Some('7') => {
|
'7' => {
|
||||||
current_attrs.set(Attr::REVERSE, true);
|
current_attrs.set(Attr::REVERSE, true);
|
||||||
}
|
}
|
||||||
Some('8') => {
|
'8' => {
|
||||||
current_attrs.set(Attr::HIDDEN, true);
|
current_attrs.set(Attr::HIDDEN, true);
|
||||||
}
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(Csi, '0') => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
(Csi, '2') => {
|
(Csi, '2') => {
|
||||||
match (chars.next(), chars.next()) {
|
match (chars.next(), chars.next()) {
|
||||||
(Some('2'), Some('m')) => {
|
(Some('2'), Some('m')) => {
|
||||||
|
@ -1961,8 +1978,20 @@ pub mod ansi {
|
||||||
/* Next arguments are 5;n or 2;r;g;b */
|
/* Next arguments are 5;n or 2;r;g;b */
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
chars.next();
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
Some('9') => {
|
||||||
|
current_fg = Color::Default;
|
||||||
|
/* default foreground color */
|
||||||
|
let next = chars.next();
|
||||||
|
if next == Some('m') {
|
||||||
|
state = Start;
|
||||||
|
} else if next != Some(';') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Some(c) if c >= '0' && c < '8' => {
|
Some(c) if c >= '0' && c < '8' => {
|
||||||
current_fg = Color::from_byte(c as u8 - 0x30);
|
current_fg = Color::from_byte(c as u8 - 0x30);
|
||||||
if chars.next() != Some('m') {
|
if chars.next() != Some('m') {
|
||||||
|
@ -1984,6 +2013,17 @@ pub mod ansi {
|
||||||
}
|
}
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
Some('9') => {
|
||||||
|
/* default background color */
|
||||||
|
current_bg = Color::Default;
|
||||||
|
let next = chars.next();
|
||||||
|
if next == Some('m') {
|
||||||
|
state = Start;
|
||||||
|
} else if next != Some(';') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
Some(c) if c >= '0' && c < '8' => {
|
Some(c) if c >= '0' && c < '8' => {
|
||||||
current_bg = Color::from_byte(c as u8 - 0x30);
|
current_bg = Color::from_byte(c as u8 - 0x30);
|
||||||
if chars.next() != Some('m') {
|
if chars.next() != Some('m') {
|
||||||
|
@ -1994,6 +2034,44 @@ pub mod ansi {
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
(Csi, '9') => {
|
||||||
|
match chars.next() {
|
||||||
|
Some('0') => current_fg = Color::Black,
|
||||||
|
Some('1') => current_fg = Color::Red,
|
||||||
|
Some('2') => current_fg = Color::Green,
|
||||||
|
Some('3') => current_fg = Color::Yellow,
|
||||||
|
Some('4') => current_fg = Color::Blue,
|
||||||
|
Some('5') => current_fg = Color::Magenta,
|
||||||
|
Some('6') => current_fg = Color::Cyan,
|
||||||
|
Some('7') => current_fg = Color::White,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
let next = chars.next();
|
||||||
|
if next != Some('m') {
|
||||||
|
//debug!(next);
|
||||||
|
}
|
||||||
|
state = Start;
|
||||||
|
}
|
||||||
|
(Csi, '1') if chars.peek() == Some(&'0') => {
|
||||||
|
chars.next();
|
||||||
|
match chars.next() {
|
||||||
|
Some('0') => current_bg = Color::Black,
|
||||||
|
Some('1') => current_bg = Color::Red,
|
||||||
|
Some('2') => current_bg = Color::Green,
|
||||||
|
Some('3') => current_bg = Color::Yellow,
|
||||||
|
Some('4') => current_bg = Color::Blue,
|
||||||
|
Some('5') => current_bg = Color::Magenta,
|
||||||
|
Some('6') => current_bg = Color::Cyan,
|
||||||
|
Some('7') => current_bg = Color::White,
|
||||||
|
_ => {},
|
||||||
|
}
|
||||||
|
let next = chars.next();
|
||||||
|
if next != Some('m') {
|
||||||
|
//debug!(next);
|
||||||
|
}
|
||||||
|
|
||||||
|
state = Start;
|
||||||
|
}
|
||||||
(SetFg, '5') => {
|
(SetFg, '5') => {
|
||||||
if chars.next() != Some(';') {
|
if chars.next() != Some(';') {
|
||||||
return None;
|
return None;
|
||||||
|
@ -2098,13 +2176,32 @@ pub mod ansi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if buf.len() != rows * cols {
|
max_cols = std::cmp::max(row.len(), max_cols);
|
||||||
debug!("rows: {} cols: {}, buf.len() = {}", rows, cols, buf.len());
|
bufs.push(row);
|
||||||
|
let mut buf: Vec<Cell> = Vec::with_capacity(max_cols * bufs.len());
|
||||||
|
for l in bufs {
|
||||||
|
let row_len = l.len();
|
||||||
|
buf.extend(l.into_iter());
|
||||||
|
if row_len < max_cols {
|
||||||
|
for _ in row_len..max_cols {
|
||||||
|
buf.push(Cell::default());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if buf.len() != rows * max_cols {
|
||||||
|
debug!(
|
||||||
|
"BUG: rows: {} cols: {} = {}, but buf.len() = {}",
|
||||||
|
rows,
|
||||||
|
max_cols,
|
||||||
|
rows * max_cols,
|
||||||
|
buf.len()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Some(CellBuffer {
|
Some(CellBuffer {
|
||||||
buf,
|
buf,
|
||||||
rows,
|
rows,
|
||||||
cols,
|
cols: max_cols,
|
||||||
growable: false,
|
growable: false,
|
||||||
ascii_drawing: false,
|
ascii_drawing: false,
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue