diff --git a/Cargo.lock b/Cargo.lock index 0778faa5b1..76aea16d08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1733,6 +1733,7 @@ dependencies = [ "serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", + "shellexpand 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", "surf 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2646,6 +2647,11 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "shellexpand" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "slab" version = "0.4.2" @@ -3568,6 +3574,7 @@ dependencies = [ "checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582" "checksum shell-words 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "39acde55a154c4cd3ae048ac78cc21c25f3a0145e44111b523279113dce0d94a" "checksum shell32-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ee04b46101f57121c9da2b151988283b6beb79b34f5bb29a58ee48cb695122c" +"checksum shellexpand 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de7a5b5a9142fd278a10e0209b021a1b85849352e6951f4f914735c976737564" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum sluice 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ec70d7c3b17c262d4a18f7291c6ce62bf47170915f3b795434d3c5c49a4e59b7" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" diff --git a/Cargo.toml b/Cargo.toml index 4b30972a44..906e8e2b21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -78,6 +78,7 @@ battery = "0.7.4" textwrap = {version = "0.11.0", features = ["term_size"]} rawkey = {version = "0.1.2", optional = true } clipboard = {version = "0.5", optional = true } +shellexpand = "1.0.0" [dev-dependencies] pretty_assertions = "0.6.1" diff --git a/src/cli.rs b/src/cli.rs index 90bdc435b5..c3a2995744 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -58,12 +58,17 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel let mut input = String::new(); match reader.read_line(&mut input) { - Ok(_) => { + Ok(count) => { + trace!("processing response ({} bytes)", count); + let response = serde_json::from_str::>>(&input); match response { Ok(jrpc) => match jrpc.params { Ok(params) => { let fname = path.to_string_lossy(); + + trace!("processing {:?}", params); + if params.is_filter { let fname = fname.to_string(); let name = params.name.clone(); @@ -93,14 +98,18 @@ fn load_plugins_in_dir(path: &std::path::PathBuf, context: &mut Context) -> Resu let re_bin = Regex::new(r"^nu_plugin_[A-Za-z_]+$")?; let re_exe = Regex::new(r"^nu_plugin_[A-Za-z_]+\.(exe|bat)$")?; + trace!("Looking for plugins in {:?}", path); + match std::fs::read_dir(path) { Ok(p) => { for entry in p { let entry = entry?; let filename = entry.file_name(); let f_name = filename.to_string_lossy(); + if re_bin.is_match(&f_name) || re_exe.is_match(&f_name) { let mut load_path = path.clone(); + trace!("Found {:?}", f_name); load_path.push(f_name.to_string()); load_plugin(&load_path, context)?; } @@ -513,7 +522,9 @@ fn classify_command( trace!(target: "nu::build_pipeline", "classifying {:?}", config); - let args: hir::Call = config.parse_args(call, context.registry(), source)?; + let args: hir::Call = config.parse_args(call, &context, source)?; + + trace!(target: "nu::build_pipeline", "args :: {}", args.debug(source)); Ok(ClassifiedCommand::Internal(InternalCommand { command, diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 28005ceb7a..926b08355f 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -122,10 +122,11 @@ impl InternalCommand { self.name_span.clone(), context.source_map.clone(), self.args, - source, + &source, objects, ); + let result = trace_out_stream!(target: "nu::trace_stream::internal", source: &source, "output" = result); let mut result = result.values; let mut stream = VecDeque::new(); diff --git a/src/commands/command.rs b/src/commands/command.rs index b498b5e66a..b11c0a5beb 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -422,6 +422,25 @@ pub enum CommandAction { LeaveShell, } +impl ToDebug for CommandAction { + fn fmt_debug(&self, f: &mut fmt::Formatter, _source: &str) -> fmt::Result { + match self { + CommandAction::ChangePath(s) => write!(f, "action:change-path={}", s), + CommandAction::AddSpanSource(u, source) => { + write!(f, "action:add-span-source={}@{:?}", u, source) + } + CommandAction::Exit => write!(f, "action:exit"), + CommandAction::EnterShell(s) => write!(f, "action:enter-shell={}", s), + CommandAction::EnterValueShell(t) => { + write!(f, "action:enter-value-shell={:?}", t.debug()) + } + CommandAction::PreviousShell => write!(f, "action:previous-shell"), + CommandAction::NextShell => write!(f, "action:next-shell"), + CommandAction::LeaveShell => write!(f, "action:leave-shell"), + } + } +} + #[derive(Debug, Serialize, Deserialize)] pub enum ReturnSuccess { Value(Tagged), @@ -430,6 +449,16 @@ pub enum ReturnSuccess { pub type ReturnValue = Result; +impl ToDebug for ReturnValue { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + match self { + Err(err) => write!(f, "{}", err.debug(source)), + Ok(ReturnSuccess::Value(v)) => write!(f, "{:?}", v.debug()), + Ok(ReturnSuccess::Action(a)) => write!(f, "{}", a.debug(source)), + } + } +} + impl From> for ReturnValue { fn from(input: Tagged) -> ReturnValue { Ok(ReturnSuccess::Value(input)) @@ -469,7 +498,7 @@ pub trait WholeStreamCommand: Send + Sync { Signature { name: self.name().to_string(), positional: vec![], - rest_positional: true, + rest_positional: None, named: indexmap::IndexMap::new(), is_filter: true, } @@ -491,7 +520,7 @@ pub trait PerItemCommand: Send + Sync { Signature { name: self.name().to_string(), positional: vec![], - rest_positional: true, + rest_positional: None, named: indexmap::IndexMap::new(), is_filter: true, } diff --git a/src/commands/get.rs b/src/commands/get.rs index 37dba702bb..cd8db615a7 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -22,33 +22,41 @@ impl WholeStreamCommand for Get { ) -> Result { args.process(registry, get)?.run() } + fn signature(&self) -> Signature { - Signature::build("get").rest() + Signature::build("get").rest(SyntaxType::Member) } } fn get_member(path: &Tagged, obj: &Tagged) -> Result, ShellError> { - let mut current = obj; + let mut current = Some(obj); for p in path.split(".") { - match current.get_data_by_key(p) { - Some(v) => current = v, - None => { + if let Some(obj) = current { + current = match obj.get_data_by_key(p) { + Some(v) => Some(v), + None => // Before we give up, see if they gave us a path that matches a field name by itself - match obj.get_data_by_key(&path.item) { - Some(v) => return Ok(v.clone()), - None => { - return Err(ShellError::labeled_error( - "Unknown column", - "table missing column", - path.span(), - )); + { + match obj.get_data_by_key(&path.item) { + Some(v) => return Ok(v.clone()), + None => { + return Err(ShellError::labeled_error( + "Unknown column", + "table missing column", + path.span(), + )); + } } } } } } - Ok(current.clone()) + match current { + Some(v) => Ok(v.clone()), + None => Ok(Value::nothing().tagged(obj.tag)), + } + // Ok(current.clone()) } pub fn get( diff --git a/src/commands/mkdir.rs b/src/commands/mkdir.rs index 895b46b320..5f92f1177b 100644 --- a/src/commands/mkdir.rs +++ b/src/commands/mkdir.rs @@ -27,7 +27,7 @@ impl PerItemCommand for Mkdir { } fn signature(&self) -> Signature { - Signature::build("mkdir").rest() + Signature::build("mkdir").rest(SyntaxType::Path) } } diff --git a/src/commands/open.rs b/src/commands/open.rs index 3f40a94020..4282da8eab 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -44,7 +44,8 @@ fn run(call_info: &CallInfo, shell_manager: &ShellManager) -> Result file, }; - let path_str = path.as_string()?; + let path_buf = path.as_path()?; + let path_str = path_buf.display().to_string(); let path_span = path.span(); let name_span = call_info.name_span; let has_raw = call_info.args.has("raw"); diff --git a/src/commands/pick.rs b/src/commands/pick.rs index bee63ecdf9..6c7fca8f25 100644 --- a/src/commands/pick.rs +++ b/src/commands/pick.rs @@ -17,7 +17,7 @@ impl WholeStreamCommand for Pick { } fn signature(&self) -> Signature { - Signature::build("pick").rest() + Signature::build("pick").rest(SyntaxType::Any) } fn run( diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 0b02b40f75..09eadc9734 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -3,6 +3,7 @@ use crate::errors::ShellError; use crate::parser::registry; use crate::prelude::*; use derive_new::new; +use log::trace; use serde::{self, Deserialize, Serialize}; use std::io::prelude::*; use std::io::BufReader; @@ -64,6 +65,8 @@ pub fn filter_plugin( args: CommandArgs, registry: &CommandRegistry, ) -> Result { + trace!("filter_plugin :: {}", path); + let args = args.evaluate_once(registry)?; let mut child = std::process::Command::new(path) @@ -80,6 +83,8 @@ pub fn filter_plugin( let call_info = args.call_info.clone(); + trace!("filtering :: {:?}", call_info); + let stream = bos .chain(args.input.values) .chain(eos) @@ -95,7 +100,14 @@ pub fn filter_plugin( let request = JsonRpc::new("begin_filter", call_info.clone()); let request_raw = serde_json::to_string(&request).unwrap(); - let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error + match stdin.write(format!("{}\n", request_raw).as_bytes()) { + Ok(_) => {} + Err(err) => { + let mut result = VecDeque::new(); + result.push_back(Err(ShellError::unexpected(format!("{}", err)))); + return result; + } + } let mut input = String::new(); match reader.read_line(&mut input) { @@ -140,7 +152,15 @@ pub fn filter_plugin( let mut reader = BufReader::new(stdout); let request: JsonRpc> = JsonRpc::new("end_filter", vec![]); - let request_raw = serde_json::to_string(&request).unwrap(); + let request_raw = match serde_json::to_string(&request) { + Ok(req) => req, + Err(err) => { + let mut result = VecDeque::new(); + result.push_back(Err(ShellError::unexpected(format!("{}", err)))); + return result; + } + }; + let _ = stdin.write(format!("{}\n", request_raw).as_bytes()); // TODO: Handle error let mut input = String::new(); diff --git a/src/commands/reject.rs b/src/commands/reject.rs index 4fc60fecf8..92aff1cb2e 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -24,7 +24,7 @@ impl WholeStreamCommand for Reject { } fn signature(&self) -> Signature { - Signature::build("reject").rest() + Signature::build("reject").rest(SyntaxType::Member) } } diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index 1a8d74b03c..c726dbedc0 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -4,13 +4,19 @@ use crate::prelude::*; pub struct SortBy; +#[derive(Deserialize)] +pub struct SortByArgs { + rest: Vec>, + reverse: bool, +} + impl WholeStreamCommand for SortBy { fn run( &self, args: CommandArgs, registry: &CommandRegistry, ) -> Result { - sort_by(args, registry) + args.process(registry, sort_by)?.run() } fn name(&self) -> &str { @@ -18,30 +24,21 @@ impl WholeStreamCommand for SortBy { } fn signature(&self) -> Signature { - Signature::build("sort-by").switch("reverse") + Signature::build("sort-by") + .rest(SyntaxType::String) + .switch("reverse") } } -fn sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result { - let args = args.evaluate_once(registry)?; - let (input, args) = args.parts(); +fn sort_by( + SortByArgs { reverse, rest }: SortByArgs, + mut context: RunnableContext, +) -> Result { + Ok(OutputStream::new(async_stream_block! { + let mut vec = context.input.drain_vec().await; - let fields: Result, _> = args - .positional - .iter() - .flatten() - .map(|a| a.as_string()) - .collect(); - - let fields = fields?; - - let output = input.values.collect::>(); - - let reverse = args.has("reverse"); - let output = output.map(move |mut vec| { let calc_key = |item: &Tagged| { - fields - .iter() + rest.iter() .map(|f| item.get_data_by_key(f).map(|i| i.clone())) .collect::>>>() }; @@ -49,10 +46,10 @@ fn sort_by(args: CommandArgs, registry: &CommandRegistry) -> Result>() - }); - - Ok(output.flatten_stream().from_input_stream()) + })) } diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index fbccc89c6d..612d58ef64 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -28,7 +28,7 @@ impl WholeStreamCommand for SplitColumn { fn signature(&self) -> Signature { Signature::build("split-column") .required("separator", SyntaxType::Any) - .rest() + .rest(SyntaxType::Member) } } diff --git a/src/context.rs b/src/context.rs index 48cc44be5c..0a69979d00 100644 --- a/src/context.rs +++ b/src/context.rs @@ -121,7 +121,7 @@ impl Context { name_span: Span, source_map: SourceMap, args: hir::Call, - source: Text, + source: &Text, input: InputStream, ) -> OutputStream { let command_args = self.command_args(args, input, source, source_map, name_span); @@ -131,13 +131,13 @@ impl Context { fn call_info( &self, args: hir::Call, - source: Text, + source: &Text, source_map: SourceMap, name_span: Span, ) -> UnevaluatedCallInfo { UnevaluatedCallInfo { args, - source, + source: source.clone(), source_map, name_span, } @@ -147,7 +147,7 @@ impl Context { &self, args: hir::Call, input: InputStream, - source: Text, + source: &Text, source_map: SourceMap, name_span: Span, ) -> CommandArgs { diff --git a/src/errors.rs b/src/errors.rs index cd1120dadc..84bb96f037 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -5,6 +5,7 @@ use ansi_term::Color; use derive_new::new; use language_reporting::{Diagnostic, Label, Severity}; use serde::{Deserialize, Serialize}; +use std::fmt; #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] pub enum Description { @@ -56,6 +57,12 @@ pub struct ShellError { cause: Option>, } +impl ToDebug for ShellError { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + self.error.fmt_debug(f, source) + } +} + impl serde::de::Error for ShellError { fn custom(msg: T) -> Self where @@ -335,6 +342,7 @@ pub enum ProximateShellError { right: Tagged, }, } + impl ProximateShellError { fn start(self) -> ShellError { ShellError { @@ -344,6 +352,13 @@ impl ProximateShellError { } } +impl ToDebug for ProximateShellError { + fn fmt_debug(&self, f: &mut fmt::Formatter, _source: &str) -> fmt::Result { + // TODO: Custom debug for inner spans + write!(f, "{:?}", self) + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ShellDiagnostic { crate diagnostic: Diagnostic, diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index b58eac2202..9d768a46d9 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -39,6 +39,7 @@ crate fn evaluate_baseline_expr( ) -> Result, ShellError> { match &expr.item { RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(*literal), source)), + RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.span())), RawExpression::Synthetic(hir::Synthetic::String(s)) => Ok(Value::string(s).tagged_unknown()), RawExpression::Variable(var) => evaluate_reference(var, scope, source), RawExpression::ExternalCommand(external) => evaluate_external(external, scope, source), diff --git a/src/format/table.rs b/src/format/table.rs index eb6babeae7..78a2708971 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -79,7 +79,7 @@ impl TableView { let mut current_col_max = 0; for row in 0..values.len() { let value_length = entries[row][head].0.len(); - if value_length > current_col_max { + if head > entries[row].len() && value_length > current_col_max { current_col_max = value_length; } } diff --git a/src/lib.rs b/src/lib.rs index cead5e2f83..74ad22a68e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::context::{SourceMap, SpanSource}; pub use crate::env::host::BasicHost; pub use crate::object::base::OF64; +pub use crate::parser::hir::SyntaxType; pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::utils::{AbsolutePath, RelativePath}; pub use cli::cli; diff --git a/src/object/base.rs b/src/object/base.rs index 5b136b3d4c..b7088a59fd 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -335,6 +335,7 @@ impl Value { } } + // TODO: This is basically a legacy construct, I think pub fn data_descriptors(&self) -> Vec { match self { Value::Primitive(_) => vec![], @@ -599,6 +600,10 @@ impl Value { Value::Primitive(Primitive::String(s.into())) } + pub fn path(s: impl Into) -> Value { + Value::Primitive(Primitive::Path(s.into())) + } + pub fn bytes(s: impl Into) -> Value { Value::Primitive(Primitive::Bytes(s.into())) } @@ -634,6 +639,18 @@ impl Value { } } +impl Tagged { + crate fn as_path(&self) -> Result { + match self.item() { + Value::Primitive(Primitive::Path(path)) => Ok(path.clone()), + other => Err(ShellError::type_error( + "Path", + other.type_name().tagged(self.span()), + )), + } + } +} + crate fn select_fields(obj: &Value, fields: &[String], tag: impl Into) -> Tagged { let mut out = TaggedDictBuilder::new(tag); diff --git a/src/object/types.rs b/src/object/types.rs index 719a990ad0..9a29284cbf 100644 --- a/src/object/types.rs +++ b/src/object/types.rs @@ -2,7 +2,6 @@ use crate::object::base as value; use crate::parser::hir; use crate::prelude::*; use log::trace; -use std::path::PathBuf; pub trait ExtractType: Sized { fn extract(value: &Tagged) -> Result; @@ -196,9 +195,9 @@ impl ExtractType for std::path::PathBuf { match &value { Tagged { - item: Value::Primitive(Primitive::String(p)), + item: Value::Primitive(Primitive::Path(p)), .. - } => Ok(PathBuf::from(p)), + } => Ok(p.clone()), other => Err(ShellError::type_error("Path", other.tagged_type_name())), } } diff --git a/src/parser/hir.rs b/src/parser/hir.rs index e323812d17..2148a179f4 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -11,16 +11,22 @@ use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; use std::fmt; +use std::path::PathBuf; use crate::evaluate::Scope; -crate use self::baseline_parse::{baseline_parse_single_token, baseline_parse_token_as_string}; -crate use self::baseline_parse_tokens::{baseline_parse_next_expr, SyntaxType, TokensIterator}; +crate use self::baseline_parse::{ + baseline_parse_single_token, baseline_parse_token_as_number, baseline_parse_token_as_path, + baseline_parse_token_as_string, +}; +crate use self::baseline_parse_tokens::{baseline_parse_next_expr, TokensIterator}; crate use self::binary::Binary; crate use self::external_command::ExternalCommand; crate use self::named::NamedArguments; crate use self::path::Path; +pub use self::baseline_parse_tokens::SyntaxType; + pub fn path(head: impl Into, tail: Vec>>) -> Path { Path::new( head.into(), @@ -68,6 +74,8 @@ impl ToDebug for Call { write!(f, "{}", named.debug(source))?; } + write!(f, ")")?; + Ok(()) } } @@ -81,6 +89,7 @@ pub enum RawExpression { Block(Vec), List(Vec), Path(Box), + FilePath(PathBuf), ExternalCommand(ExternalCommand), #[allow(unused)] @@ -105,6 +114,7 @@ impl RawExpression { match self { RawExpression::Literal(literal) => literal.type_name(), RawExpression::Synthetic(synthetic) => synthetic.type_name(), + RawExpression::FilePath(..) => "filepath", RawExpression::Variable(..) => "variable", RawExpression::List(..) => "list", RawExpression::Binary(..) => "binary", @@ -141,6 +151,10 @@ impl Expression { ) } + crate fn file_path(path: impl Into, outer: impl Into) -> Expression { + Tagged::from_simple_spanned_item(RawExpression::FilePath(path.into()), outer.into()) + } + crate fn bare(span: impl Into) -> Expression { Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into()) } @@ -170,7 +184,8 @@ impl Expression { impl ToDebug for Expression { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { match self.item() { - RawExpression::Literal(l) => write!(f, "{:?}", l), + RawExpression::Literal(l) => l.tagged(self.span()).fmt_debug(f, source), + RawExpression::FilePath(p) => write!(f, "{}", p.display()), RawExpression::Synthetic(Synthetic::String(s)) => write!(f, "{:?}", s), RawExpression::Variable(Variable::It(_)) => write!(f, "$it"), RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)), diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index 6813470640..a894f2bd7c 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -1,5 +1,7 @@ +use crate::context::Context; use crate::parser::{hir, RawToken, Token}; use crate::Text; +use std::path::PathBuf; pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Expression { match *token.item() { @@ -15,6 +17,20 @@ pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Express } } +pub fn baseline_parse_token_as_number(token: &Token, source: &Text) -> hir::Expression { + match *token.item() { + RawToken::Variable(span) if span.slice(source) == "it" => { + hir::Expression::it_variable(span, token.span()) + } + RawToken::External(span) => hir::Expression::external_command(span, token.span()), + RawToken::Variable(span) => hir::Expression::variable(span, token.span()), + RawToken::Integer(int) => hir::Expression::int(int, token.span()), + RawToken::Size(int, unit) => hir::Expression::size(int, unit, token.span()), + RawToken::Bare => hir::Expression::bare(token.span()), + RawToken::String(span) => hir::Expression::string(span, token.span()), + } +} + pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expression { match *token.item() { RawToken::Variable(span) if span.slice(source) == "it" => { @@ -28,3 +44,32 @@ pub fn baseline_parse_token_as_string(token: &Token, source: &Text) -> hir::Expr RawToken::String(span) => hir::Expression::string(span, token.span()), } } + +pub fn baseline_parse_token_as_path( + token: &Token, + context: &Context, + source: &Text, +) -> hir::Expression { + match *token.item() { + RawToken::Variable(span) if span.slice(source) == "it" => { + hir::Expression::it_variable(span, token.span()) + } + RawToken::External(span) => hir::Expression::external_command(span, token.span()), + RawToken::Variable(span) => hir::Expression::variable(span, token.span()), + RawToken::Integer(_) => hir::Expression::bare(token.span()), + RawToken::Size(_, _) => hir::Expression::bare(token.span()), + RawToken::Bare => hir::Expression::file_path( + expand_path(token.span().slice(source), context), + token.span(), + ), + RawToken::String(span) => { + hir::Expression::file_path(expand_path(span.slice(source), context), token.span()) + } + } +} + +pub fn expand_path(string: &str, context: &Context) -> PathBuf { + let expanded = shellexpand::tilde_with_context(string, || context.shell_manager.homedir()); + + PathBuf::from(expanded.as_ref()) +} diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index d9891b2fca..248169bbb8 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -1,8 +1,11 @@ +use crate::context::Context; use crate::errors::ShellError; -use crate::parser::registry::CommandRegistry; use crate::parser::{ hir, - hir::{baseline_parse_single_token, baseline_parse_token_as_string}, + hir::{ + baseline_parse_single_token, baseline_parse_token_as_number, baseline_parse_token_as_path, + baseline_parse_token_as_string, + }, DelimitedNode, Delimiter, PathNode, RawToken, TokenNode, }; use crate::{Span, Tag, Tagged, TaggedItem, Text}; @@ -12,8 +15,9 @@ use serde::{Deserialize, Serialize}; pub fn baseline_parse_tokens( token_nodes: &mut TokensIterator<'_>, - registry: &CommandRegistry, + context: &Context, source: &Text, + syntax_type: SyntaxType, ) -> Result, ShellError> { let mut exprs: Vec = vec![]; @@ -22,7 +26,7 @@ pub fn baseline_parse_tokens( break; } - let expr = baseline_parse_next_expr(token_nodes, registry, source, SyntaxType::Any)?; + let expr = baseline_parse_next_expr(token_nodes, context, source, syntax_type)?; exprs.push(expr); } @@ -35,7 +39,10 @@ pub enum SyntaxType { Any, List, Literal, + String, + Member, Variable, + Number, Path, Binary, Block, @@ -44,7 +51,7 @@ pub enum SyntaxType { pub fn baseline_parse_next_expr( tokens: &mut TokensIterator, - registry: &CommandRegistry, + context: &Context, source: &Text, syntax_type: SyntaxType, ) -> Result { @@ -56,7 +63,7 @@ pub fn baseline_parse_next_expr( match (syntax_type, next) { (SyntaxType::Path, TokenNode::Token(token)) => { - return Ok(baseline_parse_token_as_string(token, source)) + return Ok(baseline_parse_token_as_path(token, context, source)) } (SyntaxType::Path, token) => { @@ -66,10 +73,50 @@ pub fn baseline_parse_next_expr( )) } - _ => {} + (SyntaxType::String, TokenNode::Token(token)) => { + return Ok(baseline_parse_token_as_string(token, source)); + } + + (SyntaxType::String, token) => { + return Err(ShellError::type_error( + "String", + token.type_name().simple_spanned(token.span()), + )) + } + + (SyntaxType::Number, TokenNode::Token(token)) => { + return Ok(baseline_parse_token_as_number(token, source)); + } + + (SyntaxType::Number, token) => { + return Err(ShellError::type_error( + "Numeric", + token.type_name().simple_spanned(token.span()), + )) + } + + // TODO: More legit member processing + (SyntaxType::Member, TokenNode::Token(token)) => { + return Ok(baseline_parse_token_as_string(token, source)); + } + + (SyntaxType::Member, token) => { + return Err(ShellError::type_error( + "member", + token.type_name().simple_spanned(token.span()), + )) + } + + (SyntaxType::Any, _) => {} + (SyntaxType::List, _) => {} + (SyntaxType::Literal, _) => {} + (SyntaxType::Variable, _) => {} + (SyntaxType::Binary, _) => {} + (SyntaxType::Block, _) => {} + (SyntaxType::Boolean, _) => {} }; - let first = baseline_parse_semantic_token(next, registry, source)?; + let first = baseline_parse_semantic_token(next, context, source)?; let possible_op = tokens.peek(); @@ -88,7 +135,7 @@ pub fn baseline_parse_next_expr( op.span(), )) } - Some(token) => baseline_parse_semantic_token(token, registry, source)?, + Some(token) => baseline_parse_semantic_token(token, context, source)?, }; // We definitely have a binary expression here -- let's see if we should coerce it into a block @@ -176,13 +223,13 @@ pub fn baseline_parse_next_expr( pub fn baseline_parse_semantic_token( token: &TokenNode, - registry: &CommandRegistry, + context: &Context, source: &Text, ) -> Result { match token { TokenNode::Token(token) => Ok(baseline_parse_single_token(token, source)), TokenNode::Call(_call) => unimplemented!(), - TokenNode::Delimited(delimited) => baseline_parse_delimited(delimited, registry, source), + TokenNode::Delimited(delimited) => baseline_parse_delimited(delimited, context, source), TokenNode::Pipeline(_pipeline) => unimplemented!(), TokenNode::Operator(_op) => unreachable!(), TokenNode::Flag(_flag) => Err(ShellError::unimplemented( @@ -191,20 +238,24 @@ pub fn baseline_parse_semantic_token( TokenNode::Member(_span) => unreachable!(), TokenNode::Whitespace(_span) => unreachable!(), TokenNode::Error(error) => Err(*error.item.clone()), - TokenNode::Path(path) => baseline_parse_path(path, registry, source), + TokenNode::Path(path) => baseline_parse_path(path, context, source), } } pub fn baseline_parse_delimited( token: &Tagged, - registry: &CommandRegistry, + context: &Context, source: &Text, ) -> Result { match token.delimiter() { Delimiter::Brace => { let children = token.children(); - let exprs = - baseline_parse_tokens(&mut TokensIterator::new(children), registry, source)?; + let exprs = baseline_parse_tokens( + &mut TokensIterator::new(children), + context, + source, + SyntaxType::Any, + )?; let expr = hir::RawExpression::Block(exprs); Ok(Tagged::from_simple_spanned_item(expr, token.span())) @@ -212,8 +263,12 @@ pub fn baseline_parse_delimited( Delimiter::Paren => unimplemented!(), Delimiter::Square => { let children = token.children(); - let exprs = - baseline_parse_tokens(&mut TokensIterator::new(children), registry, source)?; + let exprs = baseline_parse_tokens( + &mut TokensIterator::new(children), + context, + source, + SyntaxType::Any, + )?; let expr = hir::RawExpression::List(exprs); Ok(expr.tagged(Tag::unknown_origin(token.span()))) @@ -223,10 +278,10 @@ pub fn baseline_parse_delimited( pub fn baseline_parse_path( token: &Tagged, - registry: &CommandRegistry, + context: &Context, source: &Text, ) -> Result { - let head = baseline_parse_semantic_token(token.head(), registry, source)?; + let head = baseline_parse_semantic_token(token.head(), context, source)?; let mut tail = vec![]; diff --git a/src/parser/hir/named.rs b/src/parser/hir/named.rs index 5ce06fe6f5..f95896cbfb 100644 --- a/src/parser/hir/named.rs +++ b/src/parser/hir/named.rs @@ -27,7 +27,7 @@ impl ToDebug for NamedArguments { for (name, value) in &self.named { match value { NamedValue::AbsentSwitch => continue, - NamedValue::PresentSwitch(span) => write!(f, " {}", span.slice(source))?, + NamedValue::PresentSwitch(span) => write!(f, " --{}", span.slice(source))?, NamedValue::AbsentValue => continue, NamedValue::Value(expr) => write!(f, " --{} {}", name, expr.debug(source))?, } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 2f060ca29b..a5afac756d 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -540,6 +540,8 @@ fn is_start_bare_char(c: char) -> bool { '@' => true, '*' => true, '?' => true, + '~' => true, + '+' => true, _ => false, } } @@ -557,6 +559,8 @@ fn is_bare_char(c: char) -> bool { '*' => true, '?' => true, '=' => true, + '~' => true, + '+' => true, _ => false, } } diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index 9ae920d2c2..4d8204f16b 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -1,5 +1,6 @@ +use crate::context::Context; use crate::errors::{ArgumentError, ShellError}; -use crate::parser::registry::{CommandRegistry, NamedType, PositionalType, Signature}; +use crate::parser::registry::{NamedType, PositionalType, Signature}; use crate::parser::{baseline_parse_tokens, CallNode}; use crate::parser::{ hir::{self, NamedArguments}, @@ -10,7 +11,7 @@ use log::trace; pub fn parse_command( config: &Signature, - registry: &CommandRegistry, + context: &Context, call: &Tagged, source: &Text, ) -> Result { @@ -31,7 +32,7 @@ pub fn parse_command( .collect() }); - match parse_command_tail(&config, registry, children, source, call.span())? { + match parse_command_tail(&config, context, children, source, call.span())? { None => Ok(hir::Call::new(Box::new(head), None, None)), Some((positional, named)) => Ok(hir::Call::new(Box::new(head), positional, named)), } @@ -63,7 +64,7 @@ fn parse_command_head(head: &TokenNode) -> Result { fn parse_command_tail( config: &Signature, - registry: &CommandRegistry, + context: &Context, tail: Option>, source: &Text, command_span: Span, @@ -101,7 +102,7 @@ fn parse_command_tail( } let expr = - hir::baseline_parse_next_expr(tail, registry, source, *syntax_type)?; + hir::baseline_parse_next_expr(tail, context, source, *syntax_type)?; tail.restart(); named.insert_mandatory(name, expr); @@ -121,7 +122,7 @@ fn parse_command_tail( )); } - let expr = hir::baseline_parse_next_expr(tail, registry, source, *syntax_type)?; + let expr = hir::baseline_parse_next_expr(tail, context, source, *syntax_type)?; tail.restart(); named.insert_optional(name, Some(expr)); @@ -160,16 +161,17 @@ fn parse_command_tail( } } - let result = hir::baseline_parse_next_expr(tail, registry, source, arg.syntax_type())?; + let result = hir::baseline_parse_next_expr(tail, context, source, arg.syntax_type())?; positional.push(result); } trace_remaining("after positional", tail.clone(), source); - // TODO: Only do this if rest params are specified - let remainder = baseline_parse_tokens(tail, registry, source)?; - positional.extend(remainder); + if let Some(syntax_type) = config.rest_positional { + let remainder = baseline_parse_tokens(tail, context, source, syntax_type)?; + positional.extend(remainder); + } trace_remaining("after rest", tail.clone(), source); @@ -180,6 +182,8 @@ fn parse_command_tail( positional => Some(positional), }; + // TODO: Error if extra unconsumed positional arguments + let named = match named { named if named.named.is_empty() => None, named => Some(named), diff --git a/src/parser/registry.rs b/src/parser/registry.rs index d7f41d4360..3d24e99214 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -74,8 +74,8 @@ pub struct Signature { pub name: String, #[new(default)] pub positional: Vec, - #[new(value = "false")] - pub rest_positional: bool, + #[new(value = "None")] + pub rest_positional: Option, #[new(default)] pub named: IndexMap, #[new(value = "false")] @@ -130,8 +130,8 @@ impl Signature { self } - pub fn rest(mut self) -> Signature { - self.rest_positional = true; + pub fn rest(mut self, ty: SyntaxType) -> Signature { + self.rest_positional = Some(ty); self } } @@ -279,10 +279,10 @@ impl Signature { crate fn parse_args( &self, call: &Tagged, - registry: &CommandRegistry, + context: &Context, source: &Text, ) -> Result { - let args = parse_command(self, registry, call, source)?; + let args = parse_command(self, context, call, source)?; trace!("parsed args: {:?}", args); diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 2634c14970..93c519212e 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -1,7 +1,6 @@ -use indexmap::IndexMap; use nu::{ - serve_plugin, CallInfo, Plugin, PositionalType, Primitive, ReturnSuccess, ReturnValue, - ShellError, Signature, Tagged, Value, + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, + SyntaxType, Tagged, Value, }; struct Add { @@ -43,17 +42,12 @@ impl Add { impl Plugin for Add { fn config(&mut self) -> Result { - Ok(Signature { - name: "add".to_string(), - positional: vec![ - PositionalType::mandatory_any("Field"), - PositionalType::mandatory_any("Value"), - ], - is_filter: true, - named: IndexMap::new(), - rest_positional: true, - }) + Ok(Signature::build("add") + .required("Field", SyntaxType::String) + .required("Value", SyntaxType::String) + .rest(SyntaxType::String).filter()) } + fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { if let Some(args) = call_info.args.positional { match &args[0] { diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index 58ae9756a8..fb9c2213a2 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -1,8 +1,5 @@ use crossterm::{cursor, terminal, Attribute, RawScreen}; -use indexmap::IndexMap; -use nu::{ - serve_plugin, CallInfo, NamedType, Plugin, ShellError, Signature, SpanSource, Tagged, Value, -}; +use nu::{serve_plugin, CallInfo, Plugin, ShellError, Signature, SpanSource, Tagged, Value}; use pretty_hex::*; struct BinaryView; @@ -15,15 +12,7 @@ impl BinaryView { impl Plugin for BinaryView { fn config(&mut self) -> Result { - let mut named = IndexMap::new(); - named.insert("lores".to_string(), NamedType::Switch); - Ok(Signature { - name: "binaryview".to_string(), - positional: vec![], - is_filter: false, - named, - rest_positional: false, - }) + Ok(Signature::build("binaryview").switch("lores")) } fn sink(&mut self, call_info: CallInfo, input: Vec>) { diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index 6e94c1c83f..902c038bc1 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -1,7 +1,6 @@ -use indexmap::IndexMap; use nu::{ - serve_plugin, CallInfo, Plugin, PositionalType, Primitive, ReturnSuccess, ReturnValue, - ShellError, Signature, Tagged, Value, + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, + SyntaxType, Tagged, Value, }; struct Edit { @@ -42,17 +41,12 @@ impl Edit { impl Plugin for Edit { fn config(&mut self) -> Result { - Ok(Signature { - name: "edit".to_string(), - positional: vec![ - PositionalType::mandatory_any("Field"), - PositionalType::mandatory_any("Value"), - ], - is_filter: true, - named: IndexMap::new(), - rest_positional: true, - }) + Ok(Signature::build("edit") + .required("Field", SyntaxType::String) + .required("Value", SyntaxType::String) + .filter()) } + fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { if let Some(args) = call_info.args.positional { match &args[0] { diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 173ced2fe1..ece83b7f21 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -1,7 +1,6 @@ -use indexmap::IndexMap; use nu::{ - serve_plugin, CallInfo, NamedType, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, - Signature, Tagged, TaggedItem, Value, + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, + SyntaxType, Tagged, TaggedItem, Value, }; enum Action { @@ -116,19 +115,14 @@ impl Inc { impl Plugin for Inc { fn config(&mut self) -> Result { - let mut named = IndexMap::new(); - named.insert("major".to_string(), NamedType::Switch); - named.insert("minor".to_string(), NamedType::Switch); - named.insert("patch".to_string(), NamedType::Switch); - - Ok(Signature { - name: "inc".to_string(), - positional: vec![], - is_filter: true, - named, - rest_positional: true, - }) + Ok(Signature::build("inc") + .switch("major") + .switch("minor") + .switch("patch") + .rest(SyntaxType::String) + .filter()) } + fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { if call_info.args.has("major") { self.for_semver(SemVerAction::Major); diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs index 8633c5e5db..6f78351fee 100644 --- a/src/plugins/skip.rs +++ b/src/plugins/skip.rs @@ -1,7 +1,7 @@ use indexmap::IndexMap; use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - Tagged, Value, + SyntaxType, Tagged, Value, }; struct Skip { @@ -20,7 +20,7 @@ impl Plugin for Skip { positional: vec![], is_filter: true, named: IndexMap::new(), - rest_positional: true, + rest_positional: Some(SyntaxType::Number), }) } fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 679ede80a6..0656d5f780 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -1,7 +1,6 @@ -use indexmap::IndexMap; use nu::{ - serve_plugin, CallInfo, NamedType, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, - Signature, Tagged, Value, + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, + SyntaxType, Tagged, Value, }; use regex::Regex; @@ -40,9 +39,11 @@ impl Str { let applied = match self.action.as_ref() { Some(Action::Downcase) => Value::string(input.to_ascii_lowercase()), Some(Action::Upcase) => Value::string(input.to_ascii_uppercase()), - Some(Action::ToInteger) => match input.trim().parse::() { - Ok(v) => Value::int(v), - Err(_) => Value::string(input), + Some(Action::ToInteger) => match input.trim() { + other => match other.parse::() { + Ok(v) => Value::int(v), + Err(_) => Value::string(input), + }, }, Some(Action::Replace(ref mode)) => match mode { ReplaceAction::Direct => Value::string(self.first_param()), @@ -138,9 +139,7 @@ impl Str { Some(ref f) => { let replacement = match value.item.get_data_by_path(value.tag(), f) { Some(result) => self.strutils(result.map(|x| x.clone()))?, - None => { - return Err(ShellError::string("str could not find field to replace")) - } + None => return Ok(Tagged::from_item(Value::nothing(), value.tag)), }; match value .item @@ -168,20 +167,14 @@ impl Str { impl Plugin for Str { fn config(&mut self) -> Result { - let mut named = IndexMap::new(); - named.insert("downcase".to_string(), NamedType::Switch); - named.insert("upcase".to_string(), NamedType::Switch); - named.insert("to-int".to_string(), NamedType::Switch); - named.insert("replace".to_string(), NamedType::Switch); - named.insert("find-replace".to_string(), NamedType::Switch); - - Ok(Signature { - name: "str".to_string(), - positional: vec![], - is_filter: true, - named, - rest_positional: true, - }) + Ok(Signature::build("str") + .switch("downcase") + .switch("upcase") + .switch("to-int") + .switch("replace") + .switch("find-replace") + .rest(SyntaxType::Member) + .filter()) } fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs index 34cc59ec7b..1c983b1ad8 100644 --- a/src/plugins/sum.rs +++ b/src/plugins/sum.rs @@ -1,4 +1,3 @@ -use indexmap::IndexMap; use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, Tag, Tagged, Value, @@ -14,6 +13,7 @@ impl Sum { fn sum(&mut self, value: Tagged) -> Result<(), ShellError> { match value.item { + Value::Primitive(Primitive::Nothing) => Ok(()), Value::Primitive(Primitive::Int(i)) => { match self.total { Some(Tagged { @@ -64,14 +64,9 @@ impl Sum { impl Plugin for Sum { fn config(&mut self) -> Result { - Ok(Signature { - name: "sum".to_string(), - positional: vec![], - is_filter: true, - named: IndexMap::new(), - rest_positional: true, - }) + Ok(Signature::build("sum").filter()) } + fn begin_filter(&mut self, _: CallInfo) -> Result, ShellError> { Ok(vec![]) } diff --git a/src/plugins/sys.rs b/src/plugins/sys.rs index c442f39f9d..00e4ca47d2 100644 --- a/src/plugins/sys.rs +++ b/src/plugins/sys.rs @@ -1,10 +1,9 @@ use futures::executor::block_on; use futures::stream::StreamExt; use heim::{disk, memory, net}; -use indexmap::IndexMap; use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - Tag, Tagged, TaggedDictBuilder, Value, + SyntaxType, Tag, Tagged, TaggedDictBuilder, Value, }; use std::ffi::OsStr; @@ -251,14 +250,9 @@ async fn sysinfo(tag: Tag) -> Vec> { impl Plugin for Sys { fn config(&mut self) -> Result { - Ok(Signature { - name: "sys".to_string(), - positional: vec![], - is_filter: true, - named: IndexMap::new(), - rest_positional: true, - }) + Ok(Signature::build("sys").rest(SyntaxType::Any)) } + fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { Ok(block_on(sysinfo(Tag::unknown_origin(callinfo.name_span))) .into_iter() diff --git a/src/plugins/textview.rs b/src/plugins/textview.rs index 238144cbe2..aefb09992f 100644 --- a/src/plugins/textview.rs +++ b/src/plugins/textview.rs @@ -1,6 +1,5 @@ use crossterm::{cursor, terminal, RawScreen}; use crossterm::{InputEvent, KeyEvent}; -use indexmap::IndexMap; use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ShellError, Signature, SourceMap, SpanSource, Tagged, Value, @@ -27,13 +26,7 @@ impl TextView { impl Plugin for TextView { fn config(&mut self) -> Result { - Ok(Signature { - name: "textview".to_string(), - positional: vec![], - is_filter: false, - named: IndexMap::new(), - rest_positional: false, - }) + Ok(Signature::build("textview")) } fn sink(&mut self, call_info: CallInfo, input: Vec>) { diff --git a/src/plugins/tree.rs b/src/plugins/tree.rs index dcffa48eb8..9784e854c3 100644 --- a/src/plugins/tree.rs +++ b/src/plugins/tree.rs @@ -1,5 +1,4 @@ use derive_new::new; -use indexmap::IndexMap; use nu::{serve_plugin, CallInfo, Plugin, ShellError, Signature, Tagged, Value}; use ptree::item::StringItem; use ptree::output::print_tree_with; @@ -81,13 +80,7 @@ struct TreeViewer; impl Plugin for TreeViewer { fn config(&mut self) -> Result { - Ok(Signature { - name: "tree".to_string(), - positional: vec![], - is_filter: false, - named: IndexMap::new(), - rest_positional: true, - }) + Ok(Signature::build("tree")) } fn sink(&mut self, _call_info: CallInfo, input: Vec>) { diff --git a/src/prelude.rs b/src/prelude.rs index 9e1f7322f8..481d897576 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -32,6 +32,25 @@ macro_rules! trace_stream { }}; } +#[macro_export] +macro_rules! trace_out_stream { + (target: $target:tt, source: $source:expr, $desc:tt = $expr:expr) => {{ + if log::log_enabled!(target: $target, log::Level::Trace) { + use futures::stream::StreamExt; + + let source = $source.clone(); + + let objects = $expr.values.inspect(move |o| { + trace!(target: $target, "{} = {}", $desc, o.debug(&source)); + }); + + $crate::stream::OutputStream::new(objects) + } else { + $expr + } + }}; +} + crate use crate::cli::MaybeOwned; crate use crate::commands::command::{ CallInfo, CommandAction, CommandArgs, ReturnSuccess, ReturnValue, RunnableContext, diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index adeb551faf..1f2f945510 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -72,12 +72,16 @@ impl Shell for FilesystemShell { "filesystem".to_string() } + fn homedir(&self) -> Option { + dirs::home_dir() + } + fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let cwd = self.path(); let mut full_path = PathBuf::from(self.path()); match &args.nth(0) { - Some(Tagged { item: value, .. }) => full_path.push(Path::new(&value.as_string()?)), + Some(value) => full_path.push(Path::new(&value.as_path()?)), _ => {} } @@ -176,7 +180,7 @@ impl Shell for FilesystemShell { } }, Some(v) => { - let target = v.as_string()?; + let target = v.as_path()?; let path = PathBuf::from(self.path()); match dunce::canonicalize(path.join(target).as_path()) { Ok(p) => p, diff --git a/src/shell/shell.rs b/src/shell/shell.rs index 1db76d699c..4157cacb68 100644 --- a/src/shell/shell.rs +++ b/src/shell/shell.rs @@ -7,9 +7,11 @@ use crate::context::SourceMap; use crate::errors::ShellError; use crate::prelude::*; use crate::stream::OutputStream; +use std::path::PathBuf; pub trait Shell: std::fmt::Debug { fn name(&self, source_map: &SourceMap) -> String; + fn homedir(&self) -> Option; fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn cp(&self, args: CopyArgs, name: Span, path: &str) -> Result; diff --git a/src/shell/shell_manager.rs b/src/shell/shell_manager.rs index 49e8484ee2..1d58738fee 100644 --- a/src/shell/shell_manager.rs +++ b/src/shell/shell_manager.rs @@ -9,6 +9,7 @@ use crate::shell::filesystem_shell::FilesystemShell; use crate::shell::shell::Shell; use crate::stream::OutputStream; use std::error::Error; +use std::path::PathBuf; use std::sync::{Arc, Mutex}; #[derive(Clone, Debug)] @@ -102,16 +103,24 @@ impl ShellManager { self.set_path(self.path()); } + pub fn homedir(&self) -> Option { + let env = self.shells.lock().unwrap(); + + env[self.current_shell].homedir() + } + pub fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let env = self.shells.lock().unwrap(); env[self.current_shell].ls(args) } + pub fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let env = self.shells.lock().unwrap(); env[self.current_shell].cd(args) } + pub fn cp( &self, args: CopyArgs, diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index c65a8833c1..3c4e78c8bf 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -69,6 +69,10 @@ impl Shell for ValueShell { ) } + fn homedir(&self) -> Option { + dirs::home_dir() + } + fn ls(&self, _args: EvaluatedWholeStreamCommandArgs) -> Result { Ok(self .members() diff --git a/src/utils.rs b/src/utils.rs index 7a0527565e..6b4fc33f37 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -158,6 +158,7 @@ impl FileStructure { #[cfg(test)] mod tests { + use pretty_assertions::assert_eq; use super::{FileStructure, Res}; use std::path::PathBuf; diff --git a/tests/command_ls_tests.rs b/tests/command_ls_tests.rs index 0cda5c93d8..6223709805 100644 --- a/tests/command_ls_tests.rs +++ b/tests/command_ls_tests.rs @@ -18,7 +18,7 @@ fn ls_lists_regular_files() { nu!( output, cwd(&full_path), - "ls | get name | lines| split-column \".\" | get Column2 | str Column2 --to-int | sum | echo $it" + r#"ls | get name | lines | split-column "." | get Column2 | str Column2 --to-int | sum | echo $it"# ); assert_eq!(output, "30");