diff --git a/src/cli.rs b/src/cli.rs index 03025ebec3..6dc62aa182 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -6,6 +6,7 @@ use crate::commands::classified::{ }; use crate::commands::command::sink; use crate::commands::plugin::JsonRpc; +use crate::commands::plugin::{PluginCommand, PluginSink}; use crate::context::Context; crate use crate::errors::ShellError; use crate::evaluate::Scope; @@ -43,8 +44,6 @@ impl MaybeOwned<'a, T> { } fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), ShellError> { - use crate::commands::{command, plugin}; - let mut child = std::process::Command::new(path) .stdin(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped()) @@ -70,20 +69,17 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel Ok(jrpc) => match jrpc.params { Ok(params) => { let fname = path.to_string_lossy(); - //println!("Loaded: {} from {}", params.name, fname); if params.is_filter { let fname = fname.to_string(); - context.add_commands(vec![command( - ¶ms.name, - Box::new(move |x| plugin::filter_plugin(fname.clone(), x)), - )]); + let name = params.name.clone(); + context.add_commands(vec![Arc::new(PluginCommand::new( + name, fname, params, + ))]); Ok(()) } else if params.is_sink { let fname = fname.to_string(); - context.add_sinks(vec![sink( - ¶ms.name, - Box::new(move |x| plugin::sink_plugin(fname.clone(), x)), - )]); + let name = params.name.clone(); + context.add_sinks(vec![Arc::new(PluginSink::new(name, fname, params))]); Ok(()) } else { Ok(()) diff --git a/src/commands/macros.rs b/src/commands/macros.rs index 97818df542..44a88c189f 100644 --- a/src/commands/macros.rs +++ b/src/commands/macros.rs @@ -1,4 +1,5 @@ #[doc(hidden)] +#[allow(unused)] macro_rules! named_type { ($name:ident) => { $crate::parser::registry::NamedType::$($name)* diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 3d9ecb5c54..a27f1944af 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -1,6 +1,7 @@ -use crate::commands::command::SinkCommandArgs; use crate::errors::ShellError; +use crate::parser::registry; use crate::prelude::*; +use derive_new::new; use serde::{self, Deserialize, Serialize}; use std::io::prelude::*; use std::io::BufReader; @@ -32,6 +33,44 @@ pub enum NuResult { }, } +#[derive(new)] +pub struct PluginCommand { + name: String, + path: String, + config: registry::CommandConfig, +} + +impl Command for PluginCommand { + fn run(&self, args: CommandArgs) -> Result { + filter_plugin(self.path.clone(), args) + } + fn name(&self) -> &str { + &self.name + } + fn config(&self) -> registry::CommandConfig { + self.config.clone() + } +} + +#[derive(new)] +pub struct PluginSink { + name: String, + path: String, + config: registry::CommandConfig, +} + +impl Sink for PluginSink { + fn run(&self, args: SinkCommandArgs) -> Result<(), ShellError> { + sink_plugin(self.path.clone(), args) + } + fn name(&self) -> &str { + &self.name + } + fn config(&self) -> registry::CommandConfig { + self.config.clone() + } +} + pub fn filter_plugin(path: String, args: CommandArgs) -> Result { let mut child = std::process::Command::new(path) .stdin(std::process::Stdio::piped()) diff --git a/src/object/types.rs b/src/object/types.rs index abc4b7e8fa..0264a33769 100644 --- a/src/object/types.rs +++ b/src/object/types.rs @@ -54,9 +54,6 @@ impl ExtractType for Spanned { } } -#[derive(Debug)] -pub struct FilePath; - impl ExtractType for std::path::PathBuf { fn syntax_type() -> hir::SyntaxType { hir::SyntaxType::Path @@ -66,7 +63,7 @@ impl ExtractType for std::path::PathBuf { match &value { Spanned { item: Value::Primitive(Primitive::String(p)), - span, + .. } => Ok(PathBuf::from(p)), other => Err(ShellError::type_error("Path", other.spanned_type_name())), } diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index b4dc38458e..02fa63f54d 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -21,7 +21,7 @@ pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expr } RawToken::Variable(span) => hir::Expression::variable(span, token.span), RawToken::Integer(_) => hir::Expression::bare(token.span), - RawToken::Size(int, unit) => hir::Expression::bare(token.span), + RawToken::Size(_, _) => hir::Expression::bare(token.span), RawToken::Bare => hir::Expression::bare(token.span), RawToken::String(span) => hir::Expression::string(span, token.span), } diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index 573b04c5ad..b12bc3e338 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -102,15 +102,15 @@ impl TokenNode { pub fn type_name(&self) -> String { match self { TokenNode::Token(t) => t.type_name(), - TokenNode::Call(s) => "command", + TokenNode::Call(_) => "command", TokenNode::Delimited(d) => d.type_name(), - TokenNode::Pipeline(s) => "pipeline", - TokenNode::Operator(s) => "operator", - TokenNode::Flag(s) => "flag", - TokenNode::Member(s) => "member", - TokenNode::Whitespace(s) => "whitespace", - TokenNode::Error(s) => "error", - TokenNode::Path(s) => "path", + TokenNode::Pipeline(_) => "pipeline", + TokenNode::Operator(_) => "operator", + TokenNode::Flag(_) => "flag", + TokenNode::Member(_) => "member", + TokenNode::Whitespace(_) => "whitespace", + TokenNode::Error(_) => "error", + TokenNode::Path(_) => "path", } .to_string() } diff --git a/src/parser/registry.rs b/src/parser/registry.rs index 0d54f0429e..2e910ae961 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use std::fmt; #[allow(unused)] -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub enum NamedType { Switch, Mandatory(SyntaxType), @@ -32,10 +32,19 @@ impl PositionalType { PositionalType::Mandatory(name.to_string(), SyntaxType::Any) } + pub fn optional(name: &str, ty: SyntaxType) -> PositionalType { + PositionalType::Optional(name.to_string(), ty) + } + + pub fn optional_any(name: &str) -> PositionalType { + PositionalType::Optional(name.to_string(), SyntaxType::Any) + } + pub fn mandatory_block(name: &str) -> PositionalType { PositionalType::Mandatory(name.to_string(), SyntaxType::Block) } + #[allow(unused)] crate fn to_coerce_hint(&self) -> Option { match self { PositionalType::Mandatory(_, SyntaxType::Block) @@ -59,7 +68,7 @@ impl PositionalType { } } -#[derive(Debug, Getters, Serialize, Deserialize)] +#[derive(Debug, Getters, Serialize, Deserialize, Clone)] #[get = "crate"] pub struct CommandConfig { pub name: String, diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index d56037a02d..8c525f3886 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -1,6 +1,6 @@ use crossterm::{cursor, terminal, Attribute, RawScreen}; use indexmap::IndexMap; -use nu::{serve_plugin, Args, CommandConfig, Plugin, ShellError, Spanned, Value}; +use nu::{serve_plugin, Args, CommandConfig, NamedType, Plugin, ShellError, Spanned, Value}; use pretty_hex::*; struct BinaryView; @@ -13,24 +13,26 @@ impl BinaryView { impl Plugin for BinaryView { fn config(&mut self) -> Result { + let mut named = IndexMap::new(); + named.insert("lores".to_string(), NamedType::Switch); Ok(CommandConfig { name: "binaryview".to_string(), positional: vec![], is_filter: false, is_sink: true, - named: IndexMap::new(), - rest_positional: true, + named, + rest_positional: false, }) } - fn sink(&mut self, _args: Args, input: Vec>) { + fn sink(&mut self, args: Args, input: Vec>) { for v in input { match v { Spanned { item: Value::Binary(b), .. } => { - let _ = view_binary(&b); + let _ = view_binary(&b, args.has("lores")); } _ => {} } @@ -38,62 +40,65 @@ impl Plugin for BinaryView { } } -fn view_binary(b: &[u8]) -> Result<(), Box> { +fn view_binary(b: &[u8], lores_mode: bool) -> Result<(), Box> { if b.len() > 3 { match (b[0], b[1], b[2]) { (0x4e, 0x45, 0x53) => { - view_contents_interactive(b)?; + view_contents_interactive(b, lores_mode)?; return Ok(()); } _ => {} } } - view_contents(b)?; + view_contents(b, lores_mode)?; Ok(()) } -pub struct Context { +pub struct RenderContext { pub width: usize, pub height: usize, - pub frame_buffer: Vec<(char, (u8, u8, u8))>, + pub frame_buffer: Vec<(u8, u8, u8)>, pub since_last_button: Vec, + pub lores_mode: bool, } -impl Context { - pub fn blank() -> Context { - Context { +impl RenderContext { + pub fn blank(lores_mode: bool) -> RenderContext { + RenderContext { width: 0, height: 0, frame_buffer: vec![], since_last_button: vec![0; 8], + lores_mode, } } pub fn clear(&mut self) { - self.frame_buffer = vec![(' ', (0, 0, 0)); self.width * self.height as usize]; + self.frame_buffer = vec![(0, 0, 0); self.width * self.height as usize]; } - pub fn flush(&self) -> Result<(), Box> { - let cursor = cursor(); - cursor.goto(0, 0)?; + fn render_to_screen_lores(&mut self) -> Result<(), Box> { let mut prev_color: Option<(u8, u8, u8)> = None; let mut prev_count = 1; + let cursor = cursor(); + cursor.goto(0, 0)?; + for pixel in &self.frame_buffer { match prev_color { - Some(c) if c == pixel.1 => { + Some(c) if c == *pixel => { prev_count += 1; } Some(c) => { print!( "{}", ansi_term::Colour::RGB(c.0, c.1, c.2) - .paint((0..prev_count).map(|_| pixel.0).collect::()) + .paint((0..prev_count).map(|_| "█").collect::()) ); - prev_color = Some(pixel.1); + prev_color = Some(*pixel); prev_count = 1; } _ => { - prev_color = Some(pixel.1); + prev_color = Some(*pixel); prev_count = 1; } } @@ -104,15 +109,77 @@ impl Context { print!( "{}", ansi_term::Colour::RGB(color.0, color.1, color.2) - .paint((0..prev_count).map(|_| "@").collect::()) + .paint((0..prev_count).map(|_| "█").collect::()) ); } } - println!("{}", Attribute::Reset); - Ok(()) } + fn render_to_screen_hires(&mut self) -> Result<(), Box> { + let mut prev_fg: Option<(u8, u8, u8)> = None; + let mut prev_bg: Option<(u8, u8, u8)> = None; + let mut prev_count = 1; + + let mut pos = 0; + let fb_len = self.frame_buffer.len(); + + let cursor = cursor(); + cursor.goto(0, 0)?; + + while pos < (fb_len - self.width) { + let top_pixel = self.frame_buffer[pos]; + let bottom_pixel = self.frame_buffer[pos + self.width]; + + match (prev_fg, prev_bg) { + (Some(c), Some(d)) if c == top_pixel && d == bottom_pixel => { + prev_count += 1; + } + (Some(c), Some(d)) => { + print!( + "{}", + ansi_term::Colour::RGB(c.0, c.1, c.2) + .on(ansi_term::Colour::RGB(d.0, d.1, d.2,)) + .paint((0..prev_count).map(|_| "▀").collect::()) + ); + prev_fg = Some(top_pixel); + prev_bg = Some(bottom_pixel); + prev_count = 1; + } + _ => { + prev_fg = Some(top_pixel); + prev_bg = Some(bottom_pixel); + prev_count = 1; + } + } + pos += 1; + if pos % self.width == 0 { + pos += self.width; + } + } + if prev_count > 0 { + match (prev_fg, prev_bg) { + (Some(c), Some(d)) => { + print!( + "{}", + ansi_term::Colour::RGB(c.0, c.1, c.2) + .on(ansi_term::Colour::RGB(d.0, d.1, d.2,)) + .paint((0..prev_count).map(|_| "▀").collect::()) + ); + } + _ => {} + } + } + println!("{}", Attribute::Reset); + Ok(()) + } + pub fn flush(&mut self) -> Result<(), Box> { + if self.lores_mode { + self.render_to_screen_lores() + } else { + self.render_to_screen_hires() + } + } pub fn update(&mut self) -> Result<(), Box> { let terminal = terminal(); let terminal_size = terminal.terminal_size(); @@ -122,7 +189,11 @@ impl Context { cursor.hide()?; self.width = terminal_size.0 as usize + 1; - self.height = terminal_size.1 as usize; + self.height = if self.lores_mode { + terminal_size.1 as usize + } else { + terminal_size.1 as usize * 2 + }; } Ok(()) @@ -176,7 +247,7 @@ fn load_from_jpg_buffer(buffer: &[u8]) -> Option<(RawImageBuffer)> { }) } -pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { +pub fn view_contents(buffer: &[u8], lores_mode: bool) -> Result<(), Box> { let mut raw_image_buffer = load_from_png_buffer(buffer); if raw_image_buffer.is_none() { @@ -190,9 +261,9 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { } let raw_image_buffer = raw_image_buffer.unwrap(); - let mut context: Context = Context::blank(); - let _ = context.update(); - context.clear(); + let mut render_context: RenderContext = RenderContext::blank(lores_mode); + let _ = render_context.update(); + render_context.clear(); match raw_image_buffer.colortype { image::ColorType::RGBA(8) => { @@ -205,8 +276,8 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { let resized_img = image::imageops::resize( &img, - context.width as u32, - context.height as u32, + render_context.width as u32, + render_context.height as u32, image::FilterType::Lanczos3, ); @@ -214,8 +285,7 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { for pixel in resized_img.pixels() { use image::Pixel; let rgb = pixel.to_rgb(); - //print!("{}", rgb[0]); - context.frame_buffer[count] = ('@', (rgb[0], rgb[1], rgb[2])); + render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]); count += 1; } } @@ -229,8 +299,8 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { let resized_img = image::imageops::resize( &img, - context.width as u32, - context.height as u32, + render_context.width as u32, + render_context.height as u32, image::FilterType::Lanczos3, ); @@ -238,8 +308,7 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { for pixel in resized_img.pixels() { use image::Pixel; let rgb = pixel.to_rgb(); - //print!("{}", rgb[0]); - context.frame_buffer[count] = ('@', (rgb[0], rgb[1], rgb[2])); + render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]); count += 1; } } @@ -250,7 +319,7 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { } } - context.flush()?; + render_context.flush()?; let cursor = cursor(); let _ = cursor.show(); @@ -261,7 +330,10 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { Ok(()) } -pub fn view_contents_interactive(buffer: &[u8]) -> Result<(), Box> { +pub fn view_contents_interactive( + buffer: &[u8], + lores_mode: bool, +) -> Result<(), Box> { use rawkey::{KeyCode, RawKey}; let mut nes = neso::Nes::new(48000.0); @@ -271,7 +343,7 @@ pub fn view_contents_interactive(buffer: &[u8]) -> Result<(), Box Result<(), Box Result<(), Box, &[u8]>::from_raw(256, 240, slice).unwrap(); let resized_img = image::imageops::resize( &img, - context.width as u32, - context.height as u32, + render_context.width as u32, + render_context.height as u32, image::FilterType::Lanczos3, ); - context.clear(); + render_context.clear(); let mut count = 0; for pixel in resized_img.pixels() { use image::Pixel; let rgb = pixel.to_rgb(); - context.frame_buffer[count] = ('@', (rgb[0], rgb[1], rgb[2])); + render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]); count += 1; } - context.flush()?; + render_context.flush()?; if rawkey.is_pressed(rawkey::KeyCode::Escape) { break 'gameloop; diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 0e41d585b8..faf5ccc2b5 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -17,7 +17,7 @@ impl Plugin for Inc { fn config(&mut self) -> Result { Ok(CommandConfig { name: "inc".to_string(), - positional: vec![PositionalType::mandatory_any("Increment")], + positional: vec![PositionalType::optional_any("Increment")], is_filter: true, is_sink: false, named: IndexMap::new(), diff --git a/src/prelude.rs b/src/prelude.rs index 7316efada3..509a0866ac 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -34,7 +34,7 @@ macro_rules! trace_stream { crate use crate::cli::MaybeOwned; crate use crate::commands::command::{ - Command, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, + Command, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, Sink, SinkCommandArgs, }; crate use crate::context::Context; crate use crate::env::host::handle_unexpected;