diff --git a/Cargo.toml b/Cargo.toml index 69123a2f8..12b64cfed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -111,6 +111,10 @@ path = "src/plugins/tree.rs" name = "nu_plugin_binaryview" path = "src/plugins/binaryview.rs" +[[bin]] +name = "nu_plugin_textview" +path = "src/plugins/textview.rs" + [[bin]] name = "nu" path = "src/main.rs" diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index c68d75c76..2fb65b171 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -1,9 +1,7 @@ use crate::commands::command::SinkCommandArgs; -use crate::context::{SourceMap, SpanSource}; use crate::errors::ShellError; use crate::format::GenericView; use crate::prelude::*; -use std::path::Path; pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { if args.input.len() > 0 { @@ -14,7 +12,7 @@ pub fn autoview(args: SinkCommandArgs) -> Result<(), ShellError> { { args.ctx.get_sink("binaryview").run(args)?; } else if is_single_text_value(&args.input) { - view_text_value(&args.input[0], &args.call_info.source_map); + args.ctx.get_sink("textview").run(args)?; } else if equal_shapes(&args.input) { args.ctx.get_sink("table").run(args)?; } else { @@ -63,169 +61,3 @@ fn is_single_text_value(input: &Vec>) -> bool { false } } - -fn scroll_view_lines(lines: Vec) { - use crossterm::{cursor, input, terminal, InputEvent, KeyEvent, RawScreen}; - use std::io::Write; - - let mut starting_row = 0; - - let terminal = terminal(); - - if let Ok(_raw) = RawScreen::into_raw_mode() { - let input = input(); - let cursor = cursor(); - - let _ = cursor.hide(); - - let mut sync_stdin = input.read_sync(); - - loop { - let size = terminal.terminal_size(); - let _ = terminal.clear(crossterm::ClearType::All); - let _ = cursor.goto(0, 0); - - let mut total_max_num_lines = 0; - for line in lines.iter().skip(starting_row).take(size.1 as usize) { - //let pos = cursor.pos(); - let stripped_line = strip_ansi_escapes::strip(&line.as_bytes()).unwrap(); - let line_length = stripped_line.len(); - - let max_num_lines = line_length as u16 / size.0 - + if (line_length as u16 % size.0) > 0 { - 1 - } else { - 0 - }; - total_max_num_lines += max_num_lines; - - if total_max_num_lines < size.1 { - print!("{}\r\n", line); - } else { - break; - } - } - - let _ = cursor.goto(0, size.1); - print!( - "{}", - ansi_term::Colour::Blue.paint("[ESC to quit, arrow keys to move]") - ); - let _ = std::io::stdout().flush(); - - let event = sync_stdin.next(); - - if let Some(key_event) = event { - match key_event { - InputEvent::Keyboard(k) => match k { - KeyEvent::Esc => { - break; - } - KeyEvent::Up => { - if starting_row > 0 { - starting_row -= 1; - } - } - KeyEvent::Down => { - if starting_row - < (std::cmp::max(size.1 as usize, lines.len()) - size.1 as usize) - { - starting_row += 1; - } - } - KeyEvent::PageUp => { - starting_row -= std::cmp::min(starting_row, size.1 as usize); - } - KeyEvent::Char(c) if c == ' ' => { - if starting_row - < (std::cmp::max(size.1 as usize, lines.len()) - size.1 as usize) - { - starting_row += size.1 as usize; - } - } - KeyEvent::PageDown => { - if starting_row - < (std::cmp::max(size.1 as usize, lines.len()) - size.1 as usize) - { - starting_row += size.1 as usize; - } - } - _ => {} - }, - - _ => {} - } - } - } - - let _ = cursor.show(); - } -} - -fn scroll_view(s: &str) { - let lines: Vec<_> = s.lines().map(|x| x.to_string()).collect(); - - scroll_view_lines(lines); -} - -fn view_text_value(value: &Spanned, source_map: &SourceMap) { - match value { - Spanned { - item: Value::Primitive(Primitive::String(s)), - span, - } => { - let source = span.source.map(|x| source_map.get(&x)).flatten(); - - if let Some(source) = source { - match source { - SpanSource::File(file) => { - let path = Path::new(file); - match path.extension() { - Some(extension) => { - use syntect::easy::HighlightLines; - use syntect::highlighting::{Style, ThemeSet}; - use syntect::parsing::SyntaxSet; - use syntect::util::as_24_bit_terminal_escaped; - - // Load these once at the start of your program - let ps: SyntaxSet = syntect::dumps::from_binary(include_bytes!( - "../../assets/syntaxes.bin" - )); - - if let Some(syntax) = - ps.find_syntax_by_extension(extension.to_str().unwrap()) - { - let ts: ThemeSet = syntect::dumps::from_binary(include_bytes!( - "../../assets/themes.bin" - )); - let mut h = - HighlightLines::new(syntax, &ts.themes["OneHalfDark"]); - - let mut v = vec![]; - for line in s.lines() { - let ranges: Vec<(Style, &str)> = h.highlight(line, &ps); - let escaped = - as_24_bit_terminal_escaped(&ranges[..], false); - v.push(format!("{}", escaped)); - } - scroll_view_lines(v); - } else { - scroll_view(s); - } - } - _ => { - scroll_view(s); - } - } - } - _ => { - scroll_view(s); - } - } - } else { - scroll_view(s); - } - } - _ => {} - } -} diff --git a/src/lib.rs b/src/lib.rs index f5f7f90f7..9fef9eab1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,7 +25,7 @@ mod shell; mod stream; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; -pub use crate::context::SpanSource; +pub use crate::context::{SourceMap, SpanSource}; pub use crate::env::host::BasicHost; pub use crate::parser::parse::span::SpannedItem; pub use crate::parser::Spanned; diff --git a/src/plugins/textview.rs b/src/plugins/textview.rs new file mode 100644 index 000000000..79c792cba --- /dev/null +++ b/src/plugins/textview.rs @@ -0,0 +1,203 @@ +#![feature(option_flattening)] + +use crossterm::{cursor, input, terminal, InputEvent, KeyEvent, RawScreen}; +use indexmap::IndexMap; +use nu::{ + serve_plugin, CallInfo, CommandConfig, Plugin, Primitive, ShellError, SourceMap, SpanSource, + Spanned, Value, +}; +use std::io::Write; + +use std::path::Path; + +struct TextView; + +impl TextView { + fn new() -> TextView { + TextView + } +} + +impl Plugin for TextView { + fn config(&mut self) -> Result { + Ok(CommandConfig { + name: "textview".to_string(), + positional: vec![], + is_filter: false, + is_sink: true, + named: IndexMap::new(), + rest_positional: false, + }) + } + + fn sink(&mut self, call_info: CallInfo, input: Vec>) { + view_text_value(&input[0], &call_info.source_map); + } +} + +fn scroll_view_lines(lines: Vec) { + let mut starting_row = 0; + + let terminal = terminal(); + + if let Ok(_raw) = RawScreen::into_raw_mode() { + let input = input(); + let cursor = cursor(); + + let _ = cursor.hide(); + + let mut sync_stdin = input.read_sync(); + + loop { + let size = terminal.terminal_size(); + let _ = terminal.clear(crossterm::ClearType::All); + let _ = cursor.goto(0, 0); + + let mut total_max_num_lines = 0; + for line in lines.iter().skip(starting_row).take(size.1 as usize) { + //let pos = cursor.pos(); + let stripped_line = strip_ansi_escapes::strip(&line.as_bytes()).unwrap(); + let line_length = stripped_line.len(); + + let max_num_lines = line_length as u16 / size.0 + + if (line_length as u16 % size.0) > 0 { + 1 + } else { + 0 + }; + total_max_num_lines += max_num_lines; + + if total_max_num_lines < size.1 { + print!("{}\r\n", line); + } else { + break; + } + } + + let _ = cursor.goto(0, size.1); + print!( + "{}", + ansi_term::Colour::Blue.paint("[ESC to quit, arrow keys to move]") + ); + let _ = std::io::stdout().flush(); + + let event = sync_stdin.next(); + + if let Some(key_event) = event { + match key_event { + InputEvent::Keyboard(k) => match k { + KeyEvent::Esc => { + break; + } + KeyEvent::Up => { + if starting_row > 0 { + starting_row -= 1; + } + } + KeyEvent::Down => { + if starting_row + < (std::cmp::max(size.1 as usize, lines.len()) - size.1 as usize) + { + starting_row += 1; + } + } + KeyEvent::PageUp => { + starting_row -= std::cmp::min(starting_row, size.1 as usize); + } + KeyEvent::Char(c) if c == ' ' => { + if starting_row + < (std::cmp::max(size.1 as usize, lines.len()) - size.1 as usize) + { + starting_row += size.1 as usize; + } + } + KeyEvent::PageDown => { + if starting_row + < (std::cmp::max(size.1 as usize, lines.len()) - size.1 as usize) + { + starting_row += size.1 as usize; + } + } + _ => {} + }, + + _ => {} + } + } + } + + let _ = cursor.show(); + } +} + +fn scroll_view(s: &str) { + let lines: Vec<_> = s.lines().map(|x| x.to_string()).collect(); + + scroll_view_lines(lines); +} + +fn view_text_value(value: &Spanned, source_map: &SourceMap) { + match value { + Spanned { + item: Value::Primitive(Primitive::String(s)), + span, + } => { + let source = span.source.map(|x| source_map.get(&x)).flatten(); + + if let Some(source) = source { + match source { + SpanSource::File(file) => { + let path = Path::new(file); + match path.extension() { + Some(extension) => { + use syntect::easy::HighlightLines; + use syntect::highlighting::{Style, ThemeSet}; + use syntect::parsing::SyntaxSet; + use syntect::util::as_24_bit_terminal_escaped; + + // Load these once at the start of your program + let ps: SyntaxSet = syntect::dumps::from_binary(include_bytes!( + "../../assets/syntaxes.bin" + )); + + if let Some(syntax) = + ps.find_syntax_by_extension(extension.to_str().unwrap()) + { + let ts: ThemeSet = syntect::dumps::from_binary(include_bytes!( + "../../assets/themes.bin" + )); + let mut h = + HighlightLines::new(syntax, &ts.themes["OneHalfDark"]); + + let mut v = vec![]; + for line in s.lines() { + let ranges: Vec<(Style, &str)> = h.highlight(line, &ps); + let escaped = + as_24_bit_terminal_escaped(&ranges[..], false); + v.push(format!("{}", escaped)); + } + scroll_view_lines(v); + } else { + scroll_view(s); + } + } + _ => { + scroll_view(s); + } + } + } + _ => { + scroll_view(s); + } + } + } else { + scroll_view(s); + } + } + _ => {} + } +} + +fn main() { + serve_plugin(&mut TextView::new()); +}