From 06f9047be45934c430a683b4142205a52909e7bd Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Fri, 18 Feb 2022 13:43:34 -0500 Subject: [PATCH] Add an explicit 'print' command (#4535) --- crates/nu-cli/src/lib.rs | 4 ++ crates/nu-cli/src/print.rs | 57 ++++++++++++++++++++ crates/nu-cli/src/util.rs | 73 ++++++++++++++++++++++++++ crates/nu-protocol/src/value/stream.rs | 14 ++++- src/main.rs | 1 + src/utils.rs | 73 ++------------------------ 6 files changed, 151 insertions(+), 71 deletions(-) create mode 100644 crates/nu-cli/src/print.rs create mode 100644 crates/nu-cli/src/util.rs diff --git a/crates/nu-cli/src/lib.rs b/crates/nu-cli/src/lib.rs index c00d104f2c..47fa86a853 100644 --- a/crates/nu-cli/src/lib.rs +++ b/crates/nu-cli/src/lib.rs @@ -1,13 +1,17 @@ mod completions; mod errors; mod nu_highlight; +mod print; mod prompt; mod syntax_highlight; +mod util; mod validation; pub use completions::NuCompleter; pub use errors::CliError; pub use nu_highlight::NuHighlight; +pub use print::Print; pub use prompt::NushellPrompt; pub use syntax_highlight::NuHighlighter; +pub use util::print_pipeline_data; pub use validation::NuValidator; diff --git a/crates/nu-cli/src/print.rs b/crates/nu-cli/src/print.rs new file mode 100644 index 0000000000..134a998ff1 --- /dev/null +++ b/crates/nu-cli/src/print.rs @@ -0,0 +1,57 @@ +use nu_engine::CallExt; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value, +}; + +#[derive(Clone)] +pub struct Print; + +impl Command for Print { + fn name(&self) -> &str { + "print" + } + + fn signature(&self) -> Signature { + Signature::build("print") + .rest("rest", SyntaxShape::Any, "the values to print") + .category(Category::Strings) + } + + fn usage(&self) -> &str { + "Prints the values given" + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + let args: Vec = call.rest(engine_state, stack, 0)?; + let head = call.head; + + for arg in args { + crate::util::print_pipeline_data(arg.into_pipeline_data(), engine_state, stack)?; + } + + Ok(PipelineData::new(head)) + } + + fn examples(&self) -> Vec { + vec![ + Example { + description: "Print 'hello world'", + example: r#"print "hello world""#, + result: None, + }, + Example { + description: "Print the sum of 2 and 3", + example: r#"print (2 + 3)"#, + result: None, + }, + ] + } +} diff --git a/crates/nu-cli/src/util.rs b/crates/nu-cli/src/util.rs new file mode 100644 index 0000000000..ee8cf8f14d --- /dev/null +++ b/crates/nu-cli/src/util.rs @@ -0,0 +1,73 @@ +use std::io::Write; + +use nu_protocol::{ + ast::Call, + engine::{EngineState, Stack}, + PipelineData, ShellError, Span, Value, +}; + +pub fn print_pipeline_data( + input: PipelineData, + engine_state: &EngineState, + stack: &mut Stack, +) -> Result<(), ShellError> { + // If the table function is in the declarations, then we can use it + // to create the table value that will be printed in the terminal + + let config = stack.get_config().unwrap_or_default(); + + let stdout = std::io::stdout(); + + if let PipelineData::RawStream(stream, _, _) = input { + for s in stream { + let _ = stdout.lock().write_all(s?.as_binary()?); + } + return Ok(()); + } + + match engine_state.find_decl("table".as_bytes()) { + Some(decl_id) => { + let table = engine_state.get_decl(decl_id).run( + engine_state, + stack, + &Call::new(Span::new(0, 0)), + input, + )?; + + for item in table { + let stdout = std::io::stdout(); + + if let Value::Error { error } = item { + return Err(error); + } + + let mut out = item.into_string("\n", &config); + out.push('\n'); + + match stdout.lock().write_all(out.as_bytes()) { + Ok(_) => (), + Err(err) => eprintln!("{}", err), + }; + } + } + None => { + for item in input { + let stdout = std::io::stdout(); + + if let Value::Error { error } = item { + return Err(error); + } + + let mut out = item.into_string("\n", &config); + out.push('\n'); + + match stdout.lock().write_all(out.as_bytes()) { + Ok(_) => (), + Err(err) => eprintln!("{}", err), + }; + } + } + }; + + Ok(()) +} diff --git a/crates/nu-protocol/src/value/stream.rs b/crates/nu-protocol/src/value/stream.rs index 6f00299eb7..f6c26c3547 100644 --- a/crates/nu-protocol/src/value/stream.rs +++ b/crates/nu-protocol/src/value/stream.rs @@ -146,7 +146,19 @@ impl Iterator for RawStream { } Err(e) => Some(Err(e)), }, - None => None, + None => { + if !self.leftover.is_empty() { + let output = Ok(Value::Binary { + val: self.leftover.clone(), + span: self.span, + }); + self.leftover.clear(); + + Some(output) + } else { + None + } + } } } } diff --git a/src/main.rs b/src/main.rs index 1646d8ed5f..27f7d73bd5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -51,6 +51,7 @@ fn main() -> Result<()> { let delta = { let mut working_set = nu_protocol::engine::StateWorkingSet::new(&engine_state); working_set.add_decl(Box::new(nu_cli::NuHighlight)); + working_set.add_decl(Box::new(nu_cli::Print)); working_set.render() }; diff --git a/src/utils.rs b/src/utils.rs index 51c6edc4f0..8537aac346 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,13 +1,12 @@ use log::trace; -use nu_cli::CliError; +use nu_cli::{print_pipeline_data, CliError}; use nu_engine::eval_block; use nu_parser::{lex, parse, trim_quotes, Token, TokenContents}; use nu_protocol::{ - ast::Call, engine::{EngineState, Stack, StateWorkingSet}, - PipelineData, ShellError, Span, Value, + PipelineData, ShellError, Value, }; -use std::{io::Write, path::PathBuf}; +use std::path::PathBuf; // This will collect environment variables from std::env and adds them to a stack. // @@ -188,72 +187,6 @@ pub(crate) fn gather_parent_env_vars(engine_state: &mut EngineState) { } } -fn print_pipeline_data( - input: PipelineData, - engine_state: &EngineState, - stack: &mut Stack, -) -> Result<(), ShellError> { - // If the table function is in the declarations, then we can use it - // to create the table value that will be printed in the terminal - - let config = stack.get_config().unwrap_or_default(); - - let stdout = std::io::stdout(); - - if let PipelineData::RawStream(stream, _, _) = input { - for s in stream { - let _ = stdout.lock().write_all(s?.as_binary()?); - } - return Ok(()); - } - - match engine_state.find_decl("table".as_bytes()) { - Some(decl_id) => { - let table = engine_state.get_decl(decl_id).run( - engine_state, - stack, - &Call::new(Span::new(0, 0)), - input, - )?; - - for item in table { - let stdout = std::io::stdout(); - - if let Value::Error { error } = item { - return Err(error); - } - - let mut out = item.into_string("\n", &config); - out.push('\n'); - - match stdout.lock().write_all(out.as_bytes()) { - Ok(_) => (), - Err(err) => eprintln!("{}", err), - }; - } - } - None => { - for item in input { - let stdout = std::io::stdout(); - - if let Value::Error { error } = item { - return Err(error); - } - - let mut out = item.into_string("\n", &config); - out.push('\n'); - - match stdout.lock().write_all(out.as_bytes()) { - Ok(_) => (), - Err(err) => eprintln!("{}", err), - }; - } - } - }; - - Ok(()) -} - pub(crate) fn eval_source( engine_state: &mut EngineState, stack: &mut Stack,