diff --git a/README.md b/README.md index 622b5ca539..1ad054c2e4 100644 --- a/README.md +++ b/README.md @@ -6,19 +6,21 @@ Like having a playground for a shell. This project is currently in its early stages, though it already works well enough for contributors to dogfood it as their daily driver. Its design is subject to change as it matures. -Nu has a list of built-in commands (listed belowed). If a command is unknown, the command will shell-out and execute it (using cmd on Windows or bash on Linux and MacOS), correctly passing through stdin, stdout and stderr, so things like your daily git workflows and even `vim` will work just fine. +Nu has a list of built-in commands (listed below). If a command is unknown, the command will shell-out and execute it (using cmd on Windows or bash on Linux and MacOS), correctly passing through stdin, stdout and stderr, so things like your daily git workflows and even `vim` will work just fine. # Philosophy -Nu draws heavy inspiration from projects like PowerShell. Rather than thinking of you filesystem and services as raw streams of text, Nu looks at each input as something with structure. When you list the contents of a directory, what you get back in a list of objects, where each object represents an item in that directory. +Nu draws heavy inspiration from projects like PowerShell. Rather than thinking of you filesystem and services as raw streams of text, Nu looks at each input as something with structure. For example, when you list the contents of a directory, what you get back in a list of objects, where each object represents an item in that directory. ## Pipelines -Nu takes this a step further and builds heavily on the idea of _pipelines_. Just as the Unix philosophy, Nu allows commands to output from stdout and ready from stdin. Additionally, commands can output structured data (you can think of this as a kind of third stream). Commands that work in the pipeline fit into one of three categories +Nu takes this a step further and builds heavily on the idea of _pipelines_. Just as the Unix philosophy, Nu allows commands to output from stdout and read from stdin. Additionally, commands can output structured data (you can think of this as a third kind of stream). Commands that work in the pipeline fit into one of three categories -* Commands that produce a stream (eg, "ls") -* Commands that filter a stream (eg, "where size > 10") -* Commands that consumes the output of the pipeline (eg, "autoview") +* Commands that produce a stream (eg, `ls`) +* Commands that filter a stream (eg, `where "file type" == "Directory"`) +* Commands that consumes the output of the pipeline (eg, `autoview`) + +Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right. ``` /home/jonathan/Source/nushell(master)> ls | where "file type" == "Directory" | autoview @@ -35,7 +37,7 @@ Nu takes this a step further and builds heavily on the idea of _pipelines_. Just -----------+-----------+----------+--------+--------------+---------------- ``` -Because most of the time you'll want to see the output of a pipeline, `autoview` is assumed, so we could have also written the above: +Because most of the time you'll want to see the output of a pipeline, `autoview` is assumed. We could have also written the above: ``` /home/jonathan/Source/nushell(master)> ls | where "file type" == "Directory" @@ -97,7 +99,7 @@ By default, Nu opens up into your filesystem and the current working directory. | ------ | ---- | | Filesystem | /home/jonathan/Source/nushell | -Using the `cd` command allows you to change the path from the current path to a new path, just as you might expect. Using 'ls' allows you to view the contents of the filesystem at the current path (or at the path of your choosing). +Using the `cd` command allows you to change the path from the current path to a new path, just as you might expect. Using `ls` allows you to view the contents of the filesystem at the current path (or at the path of your choosing). In addition to `cd` and `ls`, we can `enter` an object. Entering an object makes it the current object to navigate (similar to the concept of mounting a filesystem in Unix systems). @@ -193,7 +195,7 @@ These goals are all critical, project-defining priorities. Priority #1 is "direc | command | description | | ------------- | ------------- | | autoview | View the contents of the pipeline as a table or list | -| clip | Copy the contents of the pipeline fo the copy/paste buffer | +| clip | Copy the contents of the pipeline to the copy/paste buffer | | save filename | Save the contents of the pipeline to a file | | tree | View the contents of the pipeline as a tree | diff --git a/src/cli.rs b/src/cli.rs index 24e654da34..aad01ad193 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -15,6 +15,7 @@ use crate::evaluate::Scope; use crate::git::current_branch; use crate::object::Value; use crate::parser::ast::{Expression, Leaf, RawExpression}; +use crate::parser::lexer::Spanned; use crate::parser::{Args, Pipeline}; use log::debug; @@ -416,13 +417,17 @@ fn classify_command( })) } false => { - let arg_list_strings: Vec = match args { - Some(args) => args.iter().map(|i| i.as_external_arg()).collect(), + let arg_list_strings: Vec> = match args { + Some(args) => args + .iter() + .map(|i| Spanned::from_item(i.as_external_arg(), i.span)) + .collect(), None => vec![], }; Ok(ClassifiedCommand::External(ExternalCommand { name: name.to_string(), + name_span: Some(span.clone()), args: arg_list_strings, })) } diff --git a/src/commands/classified.rs b/src/commands/classified.rs index fcd9586f2d..90cd8b36c4 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,6 +1,6 @@ use crate::commands::command::Sink; use crate::parser::ast::Expression; -use crate::parser::lexer::Span; +use crate::parser::lexer::{Span, Spanned}; use crate::parser::registry::Args; use crate::prelude::*; use bytes::{BufMut, BytesMut}; @@ -153,7 +153,8 @@ impl InternalCommand { crate struct ExternalCommand { crate name: String, - crate args: Vec, + crate name_span: Option, + crate args: Vec>, } crate enum StreamNext { @@ -174,7 +175,7 @@ impl ExternalCommand { let mut arg_string = format!("{}", self.name); for arg in &self.args { arg_string.push_str(" "); - arg_string.push_str(&arg); + arg_string.push_str(&arg.item); } let mut process; @@ -185,6 +186,23 @@ impl ExternalCommand { if arg_string.contains("$it") { let mut first = true; for i in &inputs { + if i.as_string().is_err() { + let mut span = None; + for arg in &self.args { + if arg.item.contains("$it") { + span = Some(arg.span); + } + } + if let Some(span) = span { + return Err(ShellError::labeled_error( + "External $it needs string data", + "given object instead of string data", + span, + )); + } else { + return Err(ShellError::string("Error: $it needs string data")); + } + } if !first { process = process.arg("&&"); process = process.arg(&self.name); @@ -209,6 +227,23 @@ impl ExternalCommand { if arg_string.contains("$it") { let mut first = true; for i in &inputs { + if i.as_string().is_err() { + let mut span = None; + for arg in &self.args { + if arg.item.contains("$it") { + span = Some(arg.span); + } + } + if let Some(span) = span { + return Err(ShellError::labeled_error( + "External $it needs string data", + "given object instead of string data", + span, + )); + } else { + return Err(ShellError::string("Error: $it needs string data")); + } + } if !first { new_arg_string.push_str("&&"); new_arg_string.push_str(&self.name);