From 8a9cdcab173c77914e776480bdada4388f5492ae Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 3 Sep 2019 18:04:46 +1200 Subject: [PATCH 001/342] Split fetch command away from open --- README.md | 4 +- src/cli.rs | 1 + src/commands.rs | 2 + src/commands/fetch.rs | 302 +++++++++++++++++++++++++++++++++ src/commands/open.rs | 379 ++++++++++++------------------------------ 5 files changed, 413 insertions(+), 275 deletions(-) create mode 100644 src/commands/fetch.rs diff --git a/README.md b/README.md index d2243e1193..a809c39619 100644 --- a/README.md +++ b/README.md @@ -213,7 +213,8 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | ps | View current processes | | sys | View information about the current system | | which filename | Finds a program file. | -| open {filename or url} | Load a file into a cell, convert to table if possible (avoid by appending '--raw') | +| open filename | Load a file into a cell, convert to table if possible (avoid by appending '--raw') | +| fetch url | Fetch contents from a url and retrieve data as a table if possible | | post url body (--user ) (--password ) | Post content to a url and retrieve data as a table if possible | | rm {file or directory} | Remove a file, (for removing directory append '--recursive') | | exit (--now) | Exit the current shell (or all shells) | @@ -255,7 +256,6 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | to-bson | Convert table into .bson binary data | | to-tsv | Convert table into .tsv text | | to-sqlite | Convert table to sqlite .db binary data | -| reverse | Reverse the rows of a table | ## Filters on text (unstructured data) | command | description | diff --git a/src/cli.rs b/src/cli.rs index a5a2aebdf7..7cd64531fe 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -208,6 +208,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Pick), whole_stream_command(Get), per_item_command(Remove), + per_item_command(Fetch), per_item_command(Open), per_item_command(Post), per_item_command(Where), diff --git a/src/commands.rs b/src/commands.rs index 0da8cadbd4..bc6452090f 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -13,6 +13,7 @@ pub(crate) mod date; pub(crate) mod debug; pub(crate) mod enter; pub(crate) mod exit; +pub(crate) mod fetch; pub(crate) mod first; pub(crate) mod from_array; pub(crate) mod from_bson; @@ -78,6 +79,7 @@ pub(crate) use date::Date; pub(crate) use debug::Debug; pub(crate) use enter::Enter; pub(crate) use exit::Exit; +pub(crate) use fetch::Fetch; pub(crate) use first::First; pub(crate) use from_array::FromArray; pub(crate) use from_bson::FromBSON; diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs new file mode 100644 index 0000000000..8f69929a19 --- /dev/null +++ b/src/commands/fetch.rs @@ -0,0 +1,302 @@ +use crate::commands::UnevaluatedCallInfo; +use crate::context::SpanSource; +use crate::errors::ShellError; +use crate::object::Value; +use crate::parser::hir::SyntaxType; +use crate::parser::registry::Signature; +use crate::prelude::*; +use mime::Mime; +use std::path::PathBuf; +use std::str::FromStr; +use surf::mime; +use uuid::Uuid; +pub struct Fetch; + +impl PerItemCommand for Fetch { + fn name(&self) -> &str { + "fetch" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) + .required("path", SyntaxType::Path) + .switch("raw") + } + + fn usage(&self) -> &str { + "Load from a URL into a cell, convert to table if possible (avoid by appending '--raw')" + } + + fn run( + &self, + call_info: &CallInfo, + registry: &CommandRegistry, + raw_args: &RawCommandArgs, + _input: Tagged, + ) -> Result { + run(call_info, registry, raw_args) + } +} + +fn run( + call_info: &CallInfo, + registry: &CommandRegistry, + raw_args: &RawCommandArgs, +) -> Result { + let path = match call_info + .args + .nth(0) + .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? + { + file => file, + }; + let path_buf = path.as_path()?; + let path_str = path_buf.display().to_string(); + let path_span = path.span(); + let has_raw = call_info.args.has("raw"); + let registry = registry.clone(); + let raw_args = raw_args.clone(); + + let stream = async_stream_block! { + + let result = fetch(&path_str, path_span).await; + + if let Err(e) = result { + yield Err(e); + return; + } + let (file_extension, contents, contents_tag, span_source) = result.unwrap(); + + let file_extension = if has_raw { + None + } else { + // If the extension could not be determined via mimetype, try to use the path + // extension. Some file types do not declare their mimetypes (such as bson files). + file_extension.or(path_str.split('.').last().map(String::from)) + }; + + if let Some(uuid) = contents_tag.origin { + // If we have loaded something, track its source + yield ReturnSuccess::action(CommandAction::AddSpanSource( + uuid, + span_source, + )); + } + + let tagged_contents = contents.tagged(contents_tag); + + if let Some(extension) = file_extension { + let command_name = format!("from-{}", extension); + if let Some(converter) = registry.get_command(&command_name) { + let new_args = RawCommandArgs { + host: raw_args.host, + shell_manager: raw_args.shell_manager, + call_info: UnevaluatedCallInfo { + args: crate::parser::hir::Call { + head: raw_args.call_info.args.head, + positional: None, + named: None + }, + source: raw_args.call_info.source, + source_map: raw_args.call_info.source_map, + name_span: raw_args.call_info.name_span, + } + }; + let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); + let result_vec: Vec> = result.drain_vec().await; + for res in result_vec { + match res { + Ok(ReturnSuccess::Value(Tagged { item: Value::List(list), ..})) => { + for l in list { + yield Ok(ReturnSuccess::Value(l)); + } + } + Ok(ReturnSuccess::Value(Tagged { item, .. })) => { + yield Ok(ReturnSuccess::Value(Tagged { item, tag: contents_tag })); + } + x => yield x, + } + } + } else { + yield ReturnSuccess::value(tagged_contents); + } + } else { + yield ReturnSuccess::value(tagged_contents); + } + }; + + Ok(stream.to_output_stream()) +} + +pub async fn fetch( + location: &str, + span: Span, +) -> Result<(Option, Value, Tag, SpanSource), ShellError> { + if let Err(_) = url::Url::parse(location) { + return Err(ShellError::labeled_error( + "Incomplete or incorrect url", + "expected a full url", + span, + )); + } + + let response = surf::get(location).await; + match response { + Ok(mut r) => match r.headers().get("content-type") { + Some(content_type) => { + let content_type = Mime::from_str(content_type).unwrap(); + match (content_type.type_(), content_type.subtype()) { + (mime::APPLICATION, mime::XML) => Ok(( + Some("xml".to_string()), + Value::string(r.body_string().await.map_err(|_| { + ShellError::labeled_error( + "Could not load text from remote url", + "could not load", + span, + ) + })?), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::Url(location.to_string()), + )), + (mime::APPLICATION, mime::JSON) => Ok(( + Some("json".to_string()), + Value::string(r.body_string().await.map_err(|_| { + ShellError::labeled_error( + "Could not load text from remote url", + "could not load", + span, + ) + })?), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::Url(location.to_string()), + )), + (mime::APPLICATION, mime::OCTET_STREAM) => { + let buf: Vec = r.body_bytes().await.map_err(|_| { + ShellError::labeled_error( + "Could not load binary file", + "could not load", + span, + ) + })?; + Ok(( + None, + Value::Binary(buf), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::Url(location.to_string()), + )) + } + (mime::IMAGE, mime::SVG) => Ok(( + Some("svg".to_string()), + Value::string(r.body_string().await.map_err(|_| { + ShellError::labeled_error( + "Could not load svg from remote url", + "could not load", + span, + ) + })?), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::Url(location.to_string()), + )), + (mime::IMAGE, image_ty) => { + let buf: Vec = r.body_bytes().await.map_err(|_| { + ShellError::labeled_error( + "Could not load image file", + "could not load", + span, + ) + })?; + Ok(( + Some(image_ty.to_string()), + Value::Binary(buf), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::Url(location.to_string()), + )) + } + (mime::TEXT, mime::HTML) => Ok(( + Some("html".to_string()), + Value::string(r.body_string().await.map_err(|_| { + ShellError::labeled_error( + "Could not load text from remote url", + "could not load", + span, + ) + })?), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::Url(location.to_string()), + )), + (mime::TEXT, mime::PLAIN) => { + let path_extension = url::Url::parse(location) + .unwrap() + .path_segments() + .and_then(|segments| segments.last()) + .and_then(|name| if name.is_empty() { None } else { Some(name) }) + .and_then(|name| { + PathBuf::from(name) + .extension() + .map(|name| name.to_string_lossy().to_string()) + }); + + Ok(( + path_extension, + Value::string(r.body_string().await.map_err(|_| { + ShellError::labeled_error( + "Could not load text from remote url", + "could not load", + span, + ) + })?), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::Url(location.to_string()), + )) + } + (ty, sub_ty) => Ok(( + None, + Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::Url(location.to_string()), + )), + } + } + None => Ok(( + None, + Value::string(format!("No content type found")), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::Url(location.to_string()), + )), + }, + Err(_) => { + return Err(ShellError::labeled_error( + "URL could not be opened", + "url not found", + span, + )); + } + } +} diff --git a/src/commands/open.rs b/src/commands/open.rs index 0faebdef77..ea960d8380 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -5,10 +5,7 @@ use crate::object::Value; use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; -use mime::Mime; use std::path::{Path, PathBuf}; -use std::str::FromStr; -use surf::mime; use uuid::Uuid; pub struct Open; @@ -138,290 +135,126 @@ pub async fn fetch( span: Span, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { let mut cwd = cwd.clone(); - if location.starts_with("http:") || location.starts_with("https:") { - let response = surf::get(location).await; - match response { - Ok(mut r) => match r.headers().get("content-type") { - Some(content_type) => { - let content_type = Mime::from_str(content_type).unwrap(); - match (content_type.type_(), content_type.subtype()) { - (mime::APPLICATION, mime::XML) => Ok(( - Some("xml".to_string()), - Value::string(r.body_string().await.map_err(|_| { - ShellError::labeled_error( - "Could not load text from remote url", - "could not load", - span, - ) - })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::Url(location.to_string()), - )), - (mime::APPLICATION, mime::JSON) => Ok(( - Some("json".to_string()), - Value::string(r.body_string().await.map_err(|_| { - ShellError::labeled_error( - "Could not load text from remote url", - "could not load", - span, - ) - })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::Url(location.to_string()), - )), - (mime::APPLICATION, mime::OCTET_STREAM) => { - let buf: Vec = r.body_bytes().await.map_err(|_| { - ShellError::labeled_error( - "Could not load binary file", - "could not load", - span, - ) - })?; - Ok(( - None, - Value::Binary(buf), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::Url(location.to_string()), - )) - } - (mime::IMAGE, mime::SVG) => Ok(( - Some("svg".to_string()), - Value::string(r.body_string().await.map_err(|_| { - ShellError::labeled_error( - "Could not load svg from remote url", - "could not load", - span, - ) - })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::Url(location.to_string()), - )), - (mime::IMAGE, image_ty) => { - let buf: Vec = r.body_bytes().await.map_err(|_| { - ShellError::labeled_error( - "Could not load image file", - "could not load", - span, - ) - })?; - Ok(( - Some(image_ty.to_string()), - Value::Binary(buf), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::Url(location.to_string()), - )) - } - (mime::TEXT, mime::HTML) => Ok(( - Some("html".to_string()), - Value::string(r.body_string().await.map_err(|_| { - ShellError::labeled_error( - "Could not load text from remote url", - "could not load", - span, - ) - })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::Url(location.to_string()), - )), - (mime::TEXT, mime::PLAIN) => { - let path_extension = url::Url::parse(location) - .unwrap() - .path_segments() - .and_then(|segments| segments.last()) - .and_then(|name| if name.is_empty() { None } else { Some(name) }) - .and_then(|name| { - PathBuf::from(name) - .extension() - .map(|name| name.to_string_lossy().to_string()) - }); - Ok(( - path_extension, - Value::string(r.body_string().await.map_err(|_| { - ShellError::labeled_error( - "Could not load text from remote url", - "could not load", - span, - ) - })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::Url(location.to_string()), - )) - } - (ty, sub_ty) => Ok(( - None, - Value::string(format!( - "Not yet supported MIME type: {} {}", - ty, sub_ty - )), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::Url(location.to_string()), - )), - } - } - None => Ok(( - None, - Value::string(format!("No content type found")), + cwd.push(Path::new(location)); + if let Ok(cwd) = dunce::canonicalize(cwd) { + match std::fs::read(&cwd) { + Ok(bytes) => match std::str::from_utf8(&bytes) { + Ok(s) => Ok(( + cwd.extension() + .map(|name| name.to_string_lossy().to_string()), + Value::string(s), Tag { span, origin: Some(Uuid::new_v4()), }, - SpanSource::Url(location.to_string()), + SpanSource::File(cwd.to_string_lossy().to_string()), )), + Err(_) => { + //Non utf8 data. + match (bytes.get(0), bytes.get(1)) { + (Some(x), Some(y)) if *x == 0xff && *y == 0xfe => { + // Possibly UTF-16 little endian + let utf16 = read_le_u16(&bytes[2..]); + + if let Some(utf16) = utf16 { + match std::string::String::from_utf16(&utf16) { + Ok(s) => Ok(( + cwd.extension() + .map(|name| name.to_string_lossy().to_string()), + Value::string(s), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::File(cwd.to_string_lossy().to_string()), + )), + Err(_) => Ok(( + None, + Value::Binary(bytes), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::File(cwd.to_string_lossy().to_string()), + )), + } + } else { + Ok(( + None, + Value::Binary(bytes), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::File(cwd.to_string_lossy().to_string()), + )) + } + } + (Some(x), Some(y)) if *x == 0xfe && *y == 0xff => { + // Possibly UTF-16 big endian + let utf16 = read_be_u16(&bytes[2..]); + + if let Some(utf16) = utf16 { + match std::string::String::from_utf16(&utf16) { + Ok(s) => Ok(( + cwd.extension() + .map(|name| name.to_string_lossy().to_string()), + Value::string(s), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::File(cwd.to_string_lossy().to_string()), + )), + Err(_) => Ok(( + None, + Value::Binary(bytes), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::File(cwd.to_string_lossy().to_string()), + )), + } + } else { + Ok(( + None, + Value::Binary(bytes), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::File(cwd.to_string_lossy().to_string()), + )) + } + } + _ => Ok(( + None, + Value::Binary(bytes), + Tag { + span, + origin: Some(Uuid::new_v4()), + }, + SpanSource::File(cwd.to_string_lossy().to_string()), + )), + } + } }, Err(_) => { return Err(ShellError::labeled_error( - "URL could not be opened", - "url not found", + "File could not be opened", + "file not found", span, )); } } } else { - cwd.push(Path::new(location)); - if let Ok(cwd) = dunce::canonicalize(cwd) { - match std::fs::read(&cwd) { - Ok(bytes) => match std::str::from_utf8(&bytes) { - Ok(s) => Ok(( - cwd.extension() - .map(|name| name.to_string_lossy().to_string()), - Value::string(s), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::File(cwd.to_string_lossy().to_string()), - )), - Err(_) => { - //Non utf8 data. - match (bytes.get(0), bytes.get(1)) { - (Some(x), Some(y)) if *x == 0xff && *y == 0xfe => { - // Possibly UTF-16 little endian - let utf16 = read_le_u16(&bytes[2..]); - - if let Some(utf16) = utf16 { - match std::string::String::from_utf16(&utf16) { - Ok(s) => Ok(( - cwd.extension() - .map(|name| name.to_string_lossy().to_string()), - Value::string(s), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::File(cwd.to_string_lossy().to_string()), - )), - Err(_) => Ok(( - None, - Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::File(cwd.to_string_lossy().to_string()), - )), - } - } else { - Ok(( - None, - Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::File(cwd.to_string_lossy().to_string()), - )) - } - } - (Some(x), Some(y)) if *x == 0xfe && *y == 0xff => { - // Possibly UTF-16 big endian - let utf16 = read_be_u16(&bytes[2..]); - - if let Some(utf16) = utf16 { - match std::string::String::from_utf16(&utf16) { - Ok(s) => Ok(( - cwd.extension() - .map(|name| name.to_string_lossy().to_string()), - Value::string(s), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::File(cwd.to_string_lossy().to_string()), - )), - Err(_) => Ok(( - None, - Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::File(cwd.to_string_lossy().to_string()), - )), - } - } else { - Ok(( - None, - Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::File(cwd.to_string_lossy().to_string()), - )) - } - } - _ => Ok(( - None, - Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, - SpanSource::File(cwd.to_string_lossy().to_string()), - )), - } - } - }, - Err(_) => { - return Err(ShellError::labeled_error( - "File could not be opened", - "file not found", - span, - )); - } - } - } else { - return Err(ShellError::labeled_error( - "File could not be opened", - "file not found", - span, - )); - } + return Err(ShellError::labeled_error( + "File could not be opened", + "file not found", + span, + )); } } From b0a02518f97a7e59b9dd6f815169bda372c57a20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 3 Sep 2019 02:43:37 -0500 Subject: [PATCH 002/342] cd can be awared inside a value entered. --- src/object/dict.rs | 2 +- src/shell/value_shell.rs | 12 +++ src/utils.rs | 218 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 217 insertions(+), 15 deletions(-) diff --git a/src/object/dict.rs b/src/object/dict.rs index 1f53d2ade5..e52ab5882a 100644 --- a/src/object/dict.rs +++ b/src/object/dict.rs @@ -102,7 +102,7 @@ impl Dictionary { #[derive(Debug)] pub struct TaggedListBuilder { - pub tag: Tag, + tag: Tag, list: Vec>, } diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 4c9cab5a93..76be024dd4 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -6,6 +6,7 @@ use crate::commands::rm::RemoveArgs; use crate::context::SourceMap; use crate::prelude::*; use crate::shell::shell::Shell; +use crate::utils::ValueStructure; use std::ffi::OsStr; use std::path::PathBuf; @@ -103,6 +104,17 @@ impl Shell for ValueShell { } }; + let mut value_system = ValueStructure::new(); + value_system.walk_decorate(&self.value)?; + + if !value_system.exists(&PathBuf::from(&path)) { + return Err(ShellError::labeled_error( + "Can not change to path inside", + "No such path exists", + args.call_info.name_span, + )); + } + let mut stream = VecDeque::new(); stream.push_back(ReturnSuccess::change_cwd(path)); Ok(stream.into()) diff --git a/src/utils.rs b/src/utils.rs index 4ed9be2540..060b72d7a2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,9 @@ use crate::errors::ShellError; +use crate::object::meta::Tagged; +use crate::object::Value; use std::fmt; use std::ops::Div; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; pub struct AbsoluteFile { inner: PathBuf, @@ -129,23 +131,131 @@ impl fmt::Display for RelativePath { } } +pub enum TaggedValueIter<'a> { + Empty, + List(indexmap::map::Iter<'a, String, Tagged>), +} + +impl<'a> Iterator for TaggedValueIter<'a> { + type Item = (&'a String, &'a Tagged); + + fn next(&mut self) -> Option { + match self { + TaggedValueIter::Empty => None, + TaggedValueIter::List(iter) => iter.next(), + } + } +} + +impl Tagged { + fn is_dir(&self) -> bool { + match self.item() { + Value::Object(_) | Value::List(_) => true, + _ => false, + } + } + + fn entries(&self) -> TaggedValueIter<'_> { + match self.item() { + Value::Object(o) => { + let iter = o.entries.iter(); + TaggedValueIter::List(iter) + } + _ => TaggedValueIter::Empty, + } + } +} + +#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct ValueResource { + pub at: usize, + pub loc: PathBuf, +} + +impl ValueResource {} + +pub struct ValueStructure { + pub resources: Vec, +} + +impl ValueStructure { + pub fn new() -> ValueStructure { + ValueStructure { + resources: Vec::::new(), + } + } + + pub fn exists(&self, path: &Path) -> bool { + if path == Path::new("/") { + return true; + } + + let path = if path.starts_with("/") { + match path.strip_prefix("/") { + Ok(p) => p, + Err(_) => path, + } + } else { + path + }; + + let comps: Vec<_> = path.components().map(Component::as_os_str).collect(); + + let mut is_there = true; + + for (at, fragment) in comps.iter().enumerate() { + is_there = is_there + && self + .resources + .iter() + .any(|resource| at == resource.at && *fragment == resource.loc.as_os_str()); + } + + is_there + } + + pub fn walk_decorate(&mut self, start: &Tagged) -> Result<(), ShellError> { + self.resources = Vec::::new(); + self.build(start, 0)?; + self.resources.sort(); + + Ok(()) + } + + fn build(&mut self, src: &Tagged, lvl: usize) -> Result<(), ShellError> { + for entry in src.entries() { + let value = entry.1; + let path = entry.0; + + self.resources.push(ValueResource { + at: lvl, + loc: PathBuf::from(path), + }); + + if value.is_dir() { + self.build(value, lvl + 1)?; + } + } + + Ok(()) + } +} + #[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct Res { - pub loc: PathBuf, pub at: usize, + pub loc: PathBuf, } impl Res {} pub struct FileStructure { - root: PathBuf, pub resources: Vec, } impl FileStructure { pub fn new() -> FileStructure { FileStructure { - root: PathBuf::new(), resources: Vec::::new(), } } @@ -158,10 +268,6 @@ impl FileStructure { self.resources.len() > 0 } - pub fn set_root(&mut self, path: &Path) { - self.root = path.to_path_buf(); - } - pub fn paths_applying_with( &mut self, to: F, @@ -177,7 +283,6 @@ impl FileStructure { } pub fn walk_decorate(&mut self, start_path: &Path) -> Result<(), ShellError> { - self.set_root(&dunce::canonicalize(start_path)?); self.resources = Vec::::new(); self.build(start_path, 0)?; self.resources.sort(); @@ -189,7 +294,7 @@ impl FileStructure { let source = dunce::canonicalize(src)?; if source.is_dir() { - for entry in std::fs::read_dir(&source)? { + for entry in std::fs::read_dir(src)? { let entry = entry?; let path = entry.path(); @@ -215,9 +320,10 @@ impl FileStructure { #[cfg(test)] mod tests { + use super::{FileStructure, Res, ValueResource, ValueStructure}; + use crate::object::meta::{Tag, Tagged}; + use crate::object::{TaggedDictBuilder, Value}; use pretty_assertions::assert_eq; - - use super::{FileStructure, Res}; use std::path::PathBuf; fn fixtures() -> PathBuf { @@ -232,11 +338,95 @@ mod tests { } } + fn structured_sample_record(key: &str, value: &str) -> Tagged { + let mut record = TaggedDictBuilder::new(Tag::unknown()); + record.insert(key.clone(), Value::string(value)); + record.into_tagged_value() + } + + fn sample_nushell_source_code() -> Tagged { + /* + src + commands + plugins => "sys.rs" + tests + helpers => "mod.rs" + */ + + let mut src = TaggedDictBuilder::new(Tag::unknown()); + let mut record = TaggedDictBuilder::new(Tag::unknown()); + + record.insert_tagged("commands", structured_sample_record("plugins", "sys.rs")); + record.insert_tagged("tests", structured_sample_record("helpers", "mod.rs")); + src.insert_tagged("src", record.into_tagged_value()); + + src.into_tagged_value() + } + #[test] - fn prepares_and_decorates_source_files_for_copying() { + fn prepares_and_decorates_value_filesystemlike_sources() { + let mut res = ValueStructure::new(); + + res.walk_decorate(&sample_nushell_source_code()) + .expect("Can not decorate values traversal."); + + assert_eq!( + res.resources, + vec![ + ValueResource { + loc: PathBuf::from("src"), + at: 0, + }, + ValueResource { + loc: PathBuf::from("commands"), + at: 1, + }, + ValueResource { + loc: PathBuf::from("tests"), + at: 1, + }, + ValueResource { + loc: PathBuf::from("helpers"), + at: 2, + }, + ValueResource { + loc: PathBuf::from("plugins"), + at: 2, + }, + ] + ); + } + + #[test] + fn recognizes_if_path_exists_in_value_filesystemlike_sources() { + let mut res = ValueStructure::new(); + + res.walk_decorate(&sample_nushell_source_code()) + .expect("Can not decorate values traversal."); + + assert!(res.exists(&PathBuf::from("/"))); + + assert!(res.exists(&PathBuf::from("src/commands/plugins"))); + assert!(res.exists(&PathBuf::from("src/commands"))); + assert!(res.exists(&PathBuf::from("src/tests"))); + assert!(res.exists(&PathBuf::from("src/tests/helpers"))); + assert!(res.exists(&PathBuf::from("src"))); + + assert!(res.exists(&PathBuf::from("/src/commands/plugins"))); + assert!(res.exists(&PathBuf::from("/src/commands"))); + assert!(res.exists(&PathBuf::from("/src/tests"))); + assert!(res.exists(&PathBuf::from("/src/tests/helpers"))); + assert!(res.exists(&PathBuf::from("/src"))); + + assert!(!res.exists(&PathBuf::from("/not_valid"))); + assert!(!res.exists(&PathBuf::from("/src/not_valid"))); + } + + #[test] + fn prepares_and_decorates_filesystem_source_files() { let mut res = FileStructure::new(); - res.walk_decorate(fixtures().as_path()) + res.walk_decorate(&fixtures()) .expect("Can not decorate files traversal."); assert_eq!( From a449d2c005727693728d984de1ff1c70877dd5b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 3 Sep 2019 03:49:20 -0500 Subject: [PATCH 003/342] If path to cd given. Report the error with the path given. --- src/shell/value_shell.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 76be024dd4..1f6d6c7963 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -82,7 +82,9 @@ impl Shell for ValueShell { } fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { - let path = match args.nth(0) { + let destination = args.nth(0); + + let path = match destination { None => "/".to_string(), Some(v) => { let target = v.as_path()?; @@ -108,6 +110,14 @@ impl Shell for ValueShell { value_system.walk_decorate(&self.value)?; if !value_system.exists(&PathBuf::from(&path)) { + if let Some(destination) = destination { + return Err(ShellError::labeled_error( + "Can not change to path inside", + "No such path exists", + destination.span(), + )); + } + return Err(ShellError::labeled_error( "Can not change to path inside", "No such path exists", From 030d73147ec96e55be200a83d66d9123d9d059c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 3 Sep 2019 04:05:52 -0500 Subject: [PATCH 004/342] can view help for a given command by entering a command. --- src/commands/enter.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/commands/enter.rs b/src/commands/enter.rs index dda96ab2e9..7b42793e12 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -38,11 +38,22 @@ impl PerItemCommand for Enter { let location = location.to_string(); let location_clone = location.to_string(); - if registry.has(&location) { - Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( - Value::string(location_clone).tagged(Tag::unknown()), - )))] - .into()) + if location.starts_with("help") { + let spec = location.split(":").collect::>(); + + let (_, command) = (spec[0], spec[1]); + + if registry.has(command) { + Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( + Value::string(command).tagged(Tag::unknown()), + )))] + .into()) + } else { + Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( + Value::nothing().tagged(Tag::unknown()), + )))] + .into()) + } } else if PathBuf::from(location).is_dir() { Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterShell( location_clone, From b031d4cd779e70535d0dd44d83f6adfbca19544e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 3 Sep 2019 04:36:23 -0500 Subject: [PATCH 005/342] can view list of commands for details. --- src/commands/help.rs | 47 ++++++++++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/commands/help.rs b/src/commands/help.rs index def2f33385..bfdd61173f 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -1,6 +1,7 @@ use crate::commands::command::CommandAction; use crate::commands::PerItemCommand; use crate::errors::ShellError; +use crate::object::{command_dict, TaggedDictBuilder}; use crate::parser::registry; use crate::prelude::*; @@ -22,34 +23,50 @@ impl PerItemCommand for Help { fn run( &self, call_info: &CallInfo, - _registry: &CommandRegistry, + registry: &CommandRegistry, _raw_args: &RawCommandArgs, _input: Tagged, ) -> Result { let span = call_info.name_span; if call_info.args.len() == 0 { - return Ok( - vec![ - Ok(ReturnSuccess::Action( - CommandAction::EnterHelpShell( - Tagged::from_simple_spanned_item(Value::nothing(), span) - )))].into() - ) + return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( + Tagged::from_simple_spanned_item(Value::nothing(), span), + )))] + .into()); } match call_info.args.expect_nth(0)? { Tagged { item: Value::Primitive(Primitive::String(document)), - .. - } => Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( - Tagged::from_simple_spanned_item(Value::string(document), span) + tag, + } => { + if document == "commands" { + let mut specs = VecDeque::new(); + + for cmd in registry.names() { + let mut spec = TaggedDictBuilder::new(tag.clone()); + let value = command_dict(registry.get_command(&cmd).unwrap(), tag.clone()); + + spec.insert("name", cmd); + spec.insert( + "description", + value.get_data_by_key("usage").unwrap().as_string().unwrap(), + ); + spec.insert_tagged("details", value); + + specs.push_back(ReturnSuccess::value(spec.into_tagged_value())); + } + + return Ok(specs.to_output_stream()); + } + + Ok(OutputStream::empty()) + } + x => Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( + x.clone(), )))] .into()), - x => Ok( - vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(x.clone())))] - .into(), - ), } } } From 1d0ed7e95760cf70f4851d9b16e98d773b23b530 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 3 Sep 2019 05:17:44 -0500 Subject: [PATCH 006/342] ls lists contents of value entered with or without path given. --- src/shell/value_shell.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 1f6d6c7963..8d6c7b6fd5 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -8,7 +8,7 @@ use crate::prelude::*; use crate::shell::shell::Shell; use crate::utils::ValueStructure; use std::ffi::OsStr; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; #[derive(Clone, Debug)] pub struct ValueShell { @@ -23,9 +23,10 @@ impl ValueShell { value, } } - fn members(&self) -> VecDeque> { + + fn members_under(&self, path: &Path) -> VecDeque> { let mut shell_entries = VecDeque::new(); - let full_path = PathBuf::from(&self.path); + let full_path = path.to_path_buf(); let mut viewed = self.value.clone(); let sep_string = std::path::MAIN_SEPARATOR.to_string(); let sep = OsStr::new(&sep_string); @@ -54,7 +55,11 @@ impl ValueShell { } } - shell_entries + shell_entries + } + + fn members(&self) -> VecDeque> { + self.members_under(Path::new(".")) } } @@ -74,9 +79,16 @@ impl Shell for ValueShell { dirs::home_dir() } - fn ls(&self, _args: EvaluatedWholeStreamCommandArgs) -> Result { + fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + let mut full_path = PathBuf::from(self.path()); + + match &args.nth(0) { + Some(value) => full_path.push(Path::new(&value.as_path()?)), + _ => {} + } + Ok(self - .members() + .members_under(full_path.as_path()) .map(|x| ReturnSuccess::value(x)) .to_output_stream()) } From 3256b7adb3c2786afdaa19f313330f2602b8233c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 3 Sep 2019 05:24:04 -0500 Subject: [PATCH 007/342] if path to ls given that does not exist, report the error. --- src/shell/value_shell.rs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 8d6c7b6fd5..26dab3583f 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -82,11 +82,32 @@ impl Shell for ValueShell { fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let mut full_path = PathBuf::from(self.path()); - match &args.nth(0) { + let target = args.nth(0); + + match target { Some(value) => full_path.push(Path::new(&value.as_path()?)), _ => {} } + let mut value_system = ValueStructure::new(); + value_system.walk_decorate(&self.value)?; + + if !value_system.exists(&full_path) { + if let Some(target) = target { + return Err(ShellError::labeled_error( + "Can not list entries inside", + "No such path exists", + target.span(), + )); + } + + return Err(ShellError::labeled_error( + "Can not list entries inside", + "No such path exists", + args.call_info.name_span, + )); + } + Ok(self .members_under(full_path.as_path()) .map(|x| ReturnSuccess::value(x)) From 5b706599e9dde7e2707e2cf9d0a073da40064e02 Mon Sep 17 00:00:00 2001 From: Jan Koprowski Date: Tue, 3 Sep 2019 20:26:18 +0200 Subject: [PATCH 008/342] Mention pkg-config package in installation instruction Ubuntu distributions does not came with pkg-config by default. OpenSSL package's documentation mention pkg-config as requirement: https://docs.rs/openssl/0.10.24/openssl/#automatic --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a809c39619..cbe91059ed 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,8 @@ To build Nu, you will need to use the **nightly** version of the compiler. Required dependencies: -* libssl (only needed on Linux) - * on Debian/Ubuntu: `apt install libssl-dev` +* pkg-config and libssl (only needed on Linux) + * on Debian/Ubuntu: `apt install pkg-config libssl-dev` Optional dependencies: From ab97459d0edde60d06cabc175090c980be8e3c58 Mon Sep 17 00:00:00 2001 From: Jan Koprowski Date: Tue, 3 Sep 2019 21:40:42 +0200 Subject: [PATCH 009/342] Stop printing CTRL-D on EOF --- src/cli.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 7cd64531fe..f70fa42d54 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -451,10 +451,7 @@ async fn process_line(readline: Result, ctx: &mut Context LineResult::Success(line.clone()) } Err(ReadlineError::Interrupted) => LineResult::CtrlC, - Err(ReadlineError::Eof) => { - println!("CTRL-D"); - LineResult::Break - } + Err(ReadlineError::Eof) => LineResult::Break, Err(err) => { println!("Error: {:?}", err); LineResult::Break From ab48d3a3f256145bc2e699b9dc052e32c1e53b8b Mon Sep 17 00:00:00 2001 From: Patrick Meredith Date: Tue, 3 Sep 2019 21:50:23 -0400 Subject: [PATCH 010/342] Support binary save --- src/commands/command.rs | 15 ++++++++++ src/commands/save.rs | 59 ++++++++++++++++++++++++++------------- src/commands/to_bson.rs | 4 +++ src/commands/to_sqlite.rs | 8 ++++++ 4 files changed, 67 insertions(+), 19 deletions(-) diff --git a/src/commands/command.rs b/src/commands/command.rs index 67286e98a1..a79e797fd7 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -496,6 +496,10 @@ pub trait WholeStreamCommand: Send + Sync { args: CommandArgs, registry: ®istry::CommandRegistry, ) -> Result; + + fn is_binary(&self) -> bool { + false + } } pub trait PerItemCommand: Send + Sync { @@ -521,6 +525,10 @@ pub trait PerItemCommand: Send + Sync { raw_args: &RawCommandArgs, input: Tagged, ) -> Result; + + fn is_binary(&self) -> bool { + false + } } pub enum Command { @@ -608,6 +616,13 @@ impl Command { } } } + + pub fn is_binary(&self) -> bool { + match self { + Command::WholeStream(command) => command.is_binary(), + Command::PerItem(command) => command.is_binary(), + } + } } pub struct FnFilterCommand { diff --git a/src/commands/save.rs b/src/commands/save.rs index c32c016f4b..cdf41df6e7 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -96,7 +96,7 @@ fn save( } } - let content = if !save_raw { + let content : Result, ShellError> = if !save_raw { if let Some(extension) = full_path.extension() { let command_name = format!("to-{}", extension.to_str().unwrap()); if let Some(converter) = registry.get_command(&command_name) { @@ -116,22 +116,43 @@ fn save( }; let mut result = converter.run(new_args.with_input(input), ®istry); let result_vec: Vec> = result.drain_vec().await; - let mut result_string = String::new(); - for res in result_vec { - match res { - Ok(ReturnSuccess::Value(Tagged { item: Value::Primitive(Primitive::String(s)), .. })) => { - result_string.push_str(&s); + if converter.is_binary() { + let mut result_binary : Vec = Vec::new(); + for res in result_vec { + match res { + Ok(ReturnSuccess::Value(Tagged { item: Value::Binary(b), .. })) => { + for u in b.into_iter() { + result_binary.push(u); + } + } + _ => { + yield Err(ShellError::labeled_error( + "Save could not successfully save", + "unexpected data during binary save", + name_span, + )); + }, } - _ => { - yield Err(ShellError::labeled_error( - "Save could not successfully save", - "unexpected data during save", - name_span, - )); - }, } + Ok(result_binary) + } else { + let mut result_string = String::new(); + for res in result_vec { + match res { + Ok(ReturnSuccess::Value(Tagged { item: Value::Primitive(Primitive::String(s)), .. })) => { + result_string.push_str(&s); + } + _ => { + yield Err(ShellError::labeled_error( + "Save could not successfully save", + "unexpected data during text save", + name_span, + )); + }, + } + } + Ok(result_string.into_bytes()) } - Ok(result_string) } else { let mut result_string = String::new(); for res in input { @@ -148,7 +169,7 @@ fn save( }, } } - Ok(result_string) + Ok(result_string.into_bytes()) } } else { let mut result_string = String::new(); @@ -166,10 +187,10 @@ fn save( }, } } - Ok(result_string) + Ok(result_string.into_bytes()) } } else { - string_from(&input) + Ok(string_from(&input).into_bytes()) }; match content { @@ -185,7 +206,7 @@ fn save( Ok(OutputStream::new(stream)) } -fn string_from(input: &Vec>) -> Result { +fn string_from(input: &Vec>) -> String { let mut save_data = String::new(); if input.len() > 0 { @@ -202,5 +223,5 @@ fn string_from(input: &Vec>) -> Result { } } - Ok(save_data) + save_data } diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index 35fc9839d4..73232e75b0 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -26,6 +26,10 @@ impl WholeStreamCommand for ToBSON { ) -> Result { to_bson(args, registry) } + + fn is_binary(&self) -> bool { + true + } } pub fn value_to_bson_value(v: &Tagged) -> Result { diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index dca953c235..e229057637 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -27,6 +27,10 @@ impl WholeStreamCommand for ToSQLite { ) -> Result { to_sqlite(args, registry) } + + fn is_binary(&self) -> bool { + true + } } pub struct ToDB; @@ -51,6 +55,10 @@ impl WholeStreamCommand for ToDB { ) -> Result { to_sqlite(args, registry) } + + fn is_binary(&self) -> bool { + true + } } fn comma_concat(acc: String, current: String) -> String { From 1f05e98965cb4c09dcc3098fdda3a97b11d5b07e Mon Sep 17 00:00:00 2001 From: Patrick Meredith Date: Tue, 3 Sep 2019 22:21:37 -0400 Subject: [PATCH 011/342] Refactor to make save.rs readable --- src/commands/save.rs | 144 +++++++++++++++++++++++-------------------- 1 file changed, 78 insertions(+), 66 deletions(-) diff --git a/src/commands/save.rs b/src/commands/save.rs index cdf41df6e7..e998329c8f 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -6,6 +6,80 @@ use std::path::{Path, PathBuf}; pub struct Save; +macro_rules! process_string { + ($input:ident, $name_span:ident) => {{ + let mut result_string = String::new(); + for res in $input { + match res { + Tagged { + item: Value::Primitive(Primitive::String(s)), + .. + } => { + result_string.push_str(&s); + } + _ => { + yield core::task::Poll::Ready(Err(ShellError::labeled_error( + "Save could not successfully save", + "unexpected data during save", + $name_span, + ))); + } + } + } + Ok(result_string.into_bytes()) + }}; +} + +macro_rules! process_string_return_success { + ($result_vec:ident, $name_span:ident) => {{ + let mut result_string = String::new(); + for res in $result_vec { + match res { + Ok(ReturnSuccess::Value(Tagged { + item: Value::Primitive(Primitive::String(s)), + .. + })) => { + result_string.push_str(&s); + } + _ => { + yield core::task::Poll::Ready(Err(ShellError::labeled_error( + "Save could not successfully save", + "unexpected data during text save", + $name_span, + ))); + } + } + } + Ok(result_string.into_bytes()) + }}; +} + +macro_rules! process_binary_return_success { + ($result_vec:ident, $name_span:ident) => {{ + let mut result_binary: Vec = Vec::new(); + for res in $result_vec { + match res { + Ok(ReturnSuccess::Value(Tagged { + item: Value::Binary(b), + .. + })) => { + for u in b.into_iter() { + result_binary.push(u); + } + } + _ => { + yield core::task::Poll::Ready(Err(ShellError::labeled_error( + "Save could not successfully save", + "unexpected data during binary save", + $name_span, + ))); + } + } + } + Ok(result_binary) + }}; +} + #[derive(Deserialize)] pub struct SaveArgs { path: Option>, @@ -117,77 +191,15 @@ fn save( let mut result = converter.run(new_args.with_input(input), ®istry); let result_vec: Vec> = result.drain_vec().await; if converter.is_binary() { - let mut result_binary : Vec = Vec::new(); - for res in result_vec { - match res { - Ok(ReturnSuccess::Value(Tagged { item: Value::Binary(b), .. })) => { - for u in b.into_iter() { - result_binary.push(u); - } - } - _ => { - yield Err(ShellError::labeled_error( - "Save could not successfully save", - "unexpected data during binary save", - name_span, - )); - }, - } - } - Ok(result_binary) + process_binary_return_success!(result_vec, name_span) } else { - let mut result_string = String::new(); - for res in result_vec { - match res { - Ok(ReturnSuccess::Value(Tagged { item: Value::Primitive(Primitive::String(s)), .. })) => { - result_string.push_str(&s); - } - _ => { - yield Err(ShellError::labeled_error( - "Save could not successfully save", - "unexpected data during text save", - name_span, - )); - }, - } - } - Ok(result_string.into_bytes()) + process_string_return_success!(result_vec, name_span) } } else { - let mut result_string = String::new(); - for res in input { - match res { - Tagged { item: Value::Primitive(Primitive::String(s)), .. } => { - result_string.push_str(&s); - } - _ => { - yield Err(ShellError::labeled_error( - "Save could not successfully save", - "unexpected data during save", - name_span, - )); - }, - } - } - Ok(result_string.into_bytes()) + process_string!(input, name_span) } } else { - let mut result_string = String::new(); - for res in input { - match res { - Tagged { item: Value::Primitive(Primitive::String(s)), .. } => { - result_string.push_str(&s); - } - _ => { - yield Err(ShellError::labeled_error( - "Save could not successfully save", - "unexpected data during save", - name_span, - )); - }, - } - } - Ok(result_string.into_bytes()) + process_string!(input, name_span) } } else { Ok(string_from(&input).into_bytes()) From 05e858fa944421262fef83730e1b634d61491cf0 Mon Sep 17 00:00:00 2001 From: Patrick Meredith Date: Tue, 3 Sep 2019 22:37:26 -0400 Subject: [PATCH 012/342] Add test --- tests/commands_test.rs | 33 +++++++++++++++++++++++++++++++-- tests/helpers/mod.rs | 8 ++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 4d3c9d0086..33e440f937 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -36,8 +36,8 @@ fn save_figures_out_intelligently_where_to_write_out_with_metadata() { description = "A shell for the GitHub era" license = "ISC" edition = "2018" - "#) - ]); + "#, + )]); let subject_file = dirs.test().join("cargo_sample.toml"); @@ -66,3 +66,32 @@ fn save_can_write_out_csv() { assert!(actual.contains("[list list],A shell for the GitHub era,2018,ISC,nu,0.2.0")); }) } + +#[test] +fn save_can_write_out_bson() { + Playground::setup("save_test_3", |dirs, _| { + let expected_file = dirs.test().join("cargo_sample.bson"); + + nu!( + cwd: dirs.root(), + "open {}/cargo_sample.toml | inc package.version --minor | get package | save save_test_3/cargo_sample.bson", + dirs.formats() + ); + + let actual = h::file_contents_binary(expected_file); + assert!( + actual + == vec![ + 168, 0, 0, 0, 4, 97, 117, 116, 104, 111, 114, 115, 0, 43, 0, 0, 0, 2, 48, 0, + 31, 0, 0, 0, 89, 101, 104, 117, 100, 97, 32, 75, 97, 116, 122, 32, 60, 119, + 121, 99, 97, 116, 115, 64, 103, 109, 97, 105, 108, 46, 99, 111, 109, 62, 0, 0, + 2, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 0, 27, 0, 0, 0, 65, + 32, 115, 104, 101, 108, 108, 32, 102, 111, 114, 32, 116, 104, 101, 32, 71, 105, + 116, 72, 117, 98, 32, 101, 114, 97, 0, 2, 101, 100, 105, 116, 105, 111, 110, 0, + 5, 0, 0, 0, 50, 48, 49, 56, 0, 2, 108, 105, 99, 101, 110, 115, 101, 0, 4, 0, 0, + 0, 73, 83, 67, 0, 2, 110, 97, 109, 101, 0, 3, 0, 0, 0, 110, 117, 0, 2, 118, + 101, 114, 115, 105, 111, 110, 0, 6, 0, 0, 0, 48, 46, 50, 46, 48, 0, 0 + ] + ); + }) +} diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 538959e9ea..c27738c79a 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -307,6 +307,14 @@ pub fn file_contents(full_path: impl AsRef) -> String { contents } +pub fn file_contents_binary(full_path: impl AsRef) -> Vec { + let mut file = std::fs::File::open(full_path.as_ref()).expect("can not open file"); + let mut contents = Vec::new(); + file.read_to_end(&mut contents) + .expect("can not read file"); + contents +} + pub fn line_ending() -> String { #[cfg(windows)] { From 479f0a566e132ab47ba931c663fec2177209f48f Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 4 Sep 2019 18:48:40 +1200 Subject: [PATCH 013/342] Covert to_* commands to work on whole table --- src/cli.rs | 3 - src/commands.rs | 4 -- src/commands/from_array.rs | 43 -------------- src/commands/to_array.rs | 38 ------------- src/commands/to_bson.rs | 50 +++++++++++----- src/commands/to_csv.rs | 113 +++++++++++++++++++++++++++---------- src/commands/to_json.rs | 50 ++++++++++------ src/commands/to_sqlite.rs | 20 +++---- src/commands/to_toml.rs | 52 +++++++++++------ src/commands/to_tsv.rs | 112 ++++++++++++++++++++++++++---------- src/commands/to_yaml.rs | 51 +++++++++++------ tests/filters_test.rs | 2 +- 12 files changed, 317 insertions(+), 221 deletions(-) delete mode 100644 src/commands/from_array.rs delete mode 100644 src/commands/to_array.rs diff --git a/src/cli.rs b/src/cli.rs index f70fa42d54..9b41c9dd21 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -179,7 +179,6 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Reject), whole_stream_command(Reverse), whole_stream_command(Trim), - whole_stream_command(ToArray), whole_stream_command(ToBSON), whole_stream_command(ToCSV), whole_stream_command(ToJSON), @@ -192,8 +191,6 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Tags), whole_stream_command(First), whole_stream_command(Last), - whole_stream_command(FromArray), - whole_stream_command(FromArray), whole_stream_command(FromCSV), whole_stream_command(FromTSV), whole_stream_command(FromINI), diff --git a/src/commands.rs b/src/commands.rs index bc6452090f..cd44fbb09b 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -15,7 +15,6 @@ pub(crate) mod enter; pub(crate) mod exit; pub(crate) mod fetch; pub(crate) mod first; -pub(crate) mod from_array; pub(crate) mod from_bson; pub(crate) mod from_csv; pub(crate) mod from_ini; @@ -52,7 +51,6 @@ pub(crate) mod split_column; pub(crate) mod split_row; pub(crate) mod table; pub(crate) mod tags; -pub(crate) mod to_array; pub(crate) mod to_bson; pub(crate) mod to_csv; pub(crate) mod to_json; @@ -81,7 +79,6 @@ pub(crate) use enter::Enter; pub(crate) use exit::Exit; pub(crate) use fetch::Fetch; pub(crate) use first::First; -pub(crate) use from_array::FromArray; pub(crate) use from_bson::FromBSON; pub(crate) use from_csv::FromCSV; pub(crate) use from_ini::FromINI; @@ -119,7 +116,6 @@ pub(crate) use split_column::SplitColumn; pub(crate) use split_row::SplitRow; pub(crate) use table::Table; pub(crate) use tags::Tags; -pub(crate) use to_array::ToArray; pub(crate) use to_bson::ToBSON; pub(crate) use to_csv::ToCSV; pub(crate) use to_json::ToJSON; diff --git a/src/commands/from_array.rs b/src/commands/from_array.rs deleted file mode 100644 index 93ba87ecea..0000000000 --- a/src/commands/from_array.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::commands::WholeStreamCommand; -use crate::object::Value; -use crate::prelude::*; - -pub struct FromArray; - -impl WholeStreamCommand for FromArray { - fn name(&self) -> &str { - "from-array" - } - - fn signature(&self) -> Signature { - Signature::build("from-array") - } - - fn usage(&self) -> &str { - "Expand an array/list into rows" - } - - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - from_array(args, registry) - } -} - -fn from_array(args: CommandArgs, _registry: &CommandRegistry) -> Result { - let stream = args - .input - .values - .map(|item| match item { - Tagged { - item: Value::List(vec), - .. - } => VecDeque::from(vec), - x => VecDeque::from(vec![x]), - }) - .flatten(); - - Ok(stream.to_output_stream()) -} diff --git a/src/commands/to_array.rs b/src/commands/to_array.rs deleted file mode 100644 index 04c429e1b4..0000000000 --- a/src/commands/to_array.rs +++ /dev/null @@ -1,38 +0,0 @@ -use crate::commands::WholeStreamCommand; -use crate::object::Value; -use crate::prelude::*; - -pub struct ToArray; - -impl WholeStreamCommand for ToArray { - fn name(&self) -> &str { - "to-array" - } - - fn signature(&self) -> Signature { - Signature::build("to-array") - } - - fn usage(&self) -> &str { - "Collapse rows into a single list." - } - - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - to_array(args, registry) - } -} - -fn to_array(args: CommandArgs, registry: &CommandRegistry) -> Result { - let args = args.evaluate_once(registry)?; - let span = args.call_info.name_span; - let out = args.input.values.collect(); - - Ok(out - .map(move |vec: Vec<_>| stream![Value::List(vec).simple_spanned(span)]) - .flatten_stream() - .from_input_stream()) -} diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index 73232e75b0..8da3cf2a6d 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -236,21 +236,41 @@ fn bson_value_to_bytes(bson: Bson, span: Span) -> Result, ShellError> { fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let name_span = args.name_span(); - let out = args.input; + let stream = async_stream_block! { + let input: Vec> = args.input.values.collect().await; - Ok(out - .values - .map( - move |a| match bson_value_to_bytes(value_to_bson_value(&a)?, name_span) { - Ok(x) => ReturnSuccess::value(Value::Binary(x).simple_spanned(name_span)), - _ => Err(ShellError::labeled_error_with_secondary( + let to_process_input = if input.len() > 1 { + let tag = input[0].tag; + vec![Tagged { item: Value::List(input), tag } ] + } else if input.len() == 1 { + input + } else { + vec![] + }; + + for value in to_process_input { + match value_to_bson_value(&value) { + Ok(bson_value) => { + match bson_value_to_bytes(bson_value, name_span) { + Ok(x) => yield ReturnSuccess::value( + Value::Binary(x).simple_spanned(name_span), + ), + _ => yield Err(ShellError::labeled_error_with_secondary( + "Expected an object with BSON-compatible structure.span() from pipeline", + "requires BSON-compatible input", + name_span, + "originates from here".to_string(), + value.span(), + )), + } + } + _ => yield Err(ShellError::labeled_error( "Expected an object with BSON-compatible structure from pipeline", - "requires BSON-compatible input: Must be Array or Object", - name_span, - format!("{} originates from here", a.item.type_name()), - a.span(), - )), - }, - ) - .to_output_stream()) + "requires BSON-compatible input", + name_span)) + } + } + }; + + Ok(stream.to_output_stream()) } diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 58ad208192..058ad585de 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -16,8 +16,7 @@ impl WholeStreamCommand for ToCSV { } fn signature(&self) -> Signature { - Signature::build("to-csv") - .switch("headerless") + Signature::build("to-csv").switch("headerless") } fn usage(&self) -> &str { @@ -47,7 +46,7 @@ pub fn value_to_csv_value(v: &Value) -> Value { } } -fn to_string_helper(v: &Value) -> Result> { +fn to_string_helper(v: &Value) -> Result { match v { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), @@ -55,11 +54,23 @@ fn to_string_helper(v: &Value) -> Result> { Value::List(_) => return Ok(String::from("[list list]")), Value::Object(_) => return Ok(String::from("[object]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), - _ => return Err("Bad input".into()), + _ => return Err(ShellError::string("Unexpected value")), } } -pub fn to_string(v: &Value) -> Result> { +fn merge_descriptors(values: &[Tagged]) -> Vec { + let mut ret = vec![]; + for value in values { + for desc in value.data_descriptors() { + if !ret.contains(&desc) { + ret.push(desc); + } + } + } + ret +} + +pub fn to_string(v: &Value) -> Result { match v { Value::Object(o) => { let mut wtr = WriterBuilder::new().from_writer(vec![]); @@ -68,13 +79,46 @@ pub fn to_string(v: &Value) -> Result> { for (k, v) in o.entries.iter() { fields.push_back(k.clone()); + values.push_back(to_string_helper(&v)?); } wtr.write_record(fields).expect("can not write."); wtr.write_record(values).expect("can not write."); - return Ok(String::from_utf8(wtr.into_inner()?)?); + return Ok(String::from_utf8( + wtr.into_inner() + .map_err(|_| ShellError::string("Could not convert record"))?, + ) + .map_err(|_| ShellError::string("Could not convert record"))?); + } + Value::List(list) => { + let mut wtr = WriterBuilder::new().from_writer(vec![]); + + let merged_descriptors = merge_descriptors(&list); + wtr.write_record(&merged_descriptors) + .expect("can not write."); + + for l in list { + let mut row = vec![]; + for desc in &merged_descriptors { + match l.item.get_data_by_key(&desc) { + Some(s) => { + row.push(to_string_helper(s)?); + } + None => { + row.push(String::new()); + } + } + } + wtr.write_record(&row).expect("can not write"); + } + + return Ok(String::from_utf8( + wtr.into_inner() + .map_err(|_| ShellError::string("Could not convert record"))?, + ) + .map_err(|_| ShellError::string("Could not convert record"))?); } _ => return to_string_helper(&v), } @@ -85,29 +129,40 @@ fn to_csv( RunnableContext { input, name, .. }: RunnableContext, ) -> Result { let name_span = name; - let out = input; + let stream = async_stream_block! { + let input: Vec> = input.values.collect().await; - Ok(out - .values - .map(move |a| match to_string(&value_to_csv_value(&a.item)) { - Ok(x) => { - let converted = if headerless { - x.lines().skip(1).collect() - } else { - x - }; + let to_process_input = if input.len() > 1 { + let tag = input[0].tag; + vec![Tagged { item: Value::List(input), tag } ] + } else if input.len() == 1 { + input + } else { + vec![] + }; - ReturnSuccess::value( - Value::Primitive(Primitive::String(converted)).simple_spanned(name_span), - ) - } - _ => Err(ShellError::labeled_error_with_secondary( - "Expected an object with CSV-compatible structure from pipeline", - "requires CSV-compatible input", - name_span, - format!("{} originates from here", a.item.type_name()), - a.span(), - )), - }) - .to_output_stream()) + for value in to_process_input { + match to_string(&value_to_csv_value(&value.item)) { + Ok(x) => { + let converted = if headerless { + x.lines().skip(1).collect() + } else { + x + }; + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span)) + } + _ => { + yield Err(ShellError::labeled_error_with_secondary( + "Expected an object with CSV-compatible structure.span() from pipeline", + "requires CSV-compatible input", + name_span, + "originates from here".to_string(), + value.span(), + )) + } + } + } + }; + + Ok(stream.to_output_stream()) } diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 9849e691e3..9af5985cb1 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -80,23 +80,41 @@ fn json_list(input: &Vec>) -> Result, Shell fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let name_span = args.name_span(); - let out = args.input; + let stream = async_stream_block! { + let input: Vec> = args.input.values.collect().await; - Ok(out - .values - .map( - move |a| match serde_json::to_string(&value_to_json_value(&a)?) { - Ok(x) => ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).simple_spanned(name_span), - ), - _ => Err(ShellError::labeled_error_with_secondary( + let to_process_input = if input.len() > 1 { + let tag = input[0].tag; + vec![Tagged { item: Value::List(input), tag } ] + } else if input.len() == 1 { + input + } else { + vec![] + }; + + for value in to_process_input { + match value_to_json_value(&value) { + Ok(json_value) => { + match serde_json::to_string(&json_value) { + Ok(x) => yield ReturnSuccess::value( + Value::Primitive(Primitive::String(x)).simple_spanned(name_span), + ), + _ => yield Err(ShellError::labeled_error_with_secondary( + "Expected an object with JSON-compatible structure.span() from pipeline", + "requires JSON-compatible input", + name_span, + "originates from here".to_string(), + value.span(), + )), + } + } + _ => yield Err(ShellError::labeled_error( "Expected an object with JSON-compatible structure from pipeline", "requires JSON-compatible input", - name_span, - format!("{} originates from here", a.item.type_name()), - a.span(), - )), - }, - ) - .to_output_stream()) + name_span)) + } + } + }; + + Ok(stream.to_output_stream()) } diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index e229057637..25c8acdc84 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -201,19 +201,19 @@ fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result = args.input.into_vec().await; - match sqlite_input_stream_to_bytes(values) { - Ok(out) => { - yield ReturnSuccess::value(out) - } - Err(_) => { + let input: Vec> = args.input.values.collect().await; + + match sqlite_input_stream_to_bytes(input) { + Ok(out) => yield ReturnSuccess::value(out), + _ => { yield Err(ShellError::labeled_error( - "Expected an object with SQLite-compatible structure from pipeline", + "Expected an object with SQLite-compatible structure.span() from pipeline", "requires SQLite-compatible input", name_span, - )) - } - }; + )) + }, + } }; + Ok(stream.to_output_stream()) } diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 8c44e21b2c..3960368662 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -75,23 +75,41 @@ fn collect_values(input: &Vec>) -> Result, ShellE fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let name_span = args.name_span(); - let out = args.input; + let stream = async_stream_block! { + let input: Vec> = args.input.values.collect().await; - Ok(out - .values - .map(move |a| match toml::to_string(&value_to_toml_value(&a)?) { - Ok(val) => { - return ReturnSuccess::value( - Value::Primitive(Primitive::String(val)).simple_spanned(name_span), - ) + let to_process_input = if input.len() > 1 { + let tag = input[0].tag; + vec![Tagged { item: Value::List(input), tag } ] + } else if input.len() == 1 { + input + } else { + vec![] + }; + + for value in to_process_input { + match value_to_toml_value(&value) { + Ok(toml_value) => { + match toml::to_string(&toml_value) { + Ok(x) => yield ReturnSuccess::value( + Value::Primitive(Primitive::String(x)).simple_spanned(name_span), + ), + _ => yield Err(ShellError::labeled_error_with_secondary( + "Expected an object with TOML-compatible structure.span() from pipeline", + "requires TOML-compatible input", + name_span, + "originates from here".to_string(), + value.span(), + )), + } + } + _ => yield Err(ShellError::labeled_error( + "Expected an object with TOML-compatible structure from pipeline", + "requires TOML-compatible input", + name_span)) } - _ => Err(ShellError::labeled_error_with_secondary( - "Expected an object with TOML-compatible structure from pipeline", - "requires TOML-compatible input", - name_span, - format!("{} originates from here", a.item.type_name()), - a.span(), - )), - }) - .to_output_stream()) + } + }; + + Ok(stream.to_output_stream()) } diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index 1a229d768e..d4958b773b 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -16,8 +16,7 @@ impl WholeStreamCommand for ToTSV { } fn signature(&self) -> Signature { - Signature::build("to-tsv") - .switch("headerless") + Signature::build("to-tsv").switch("headerless") } fn usage(&self) -> &str { @@ -47,7 +46,7 @@ pub fn value_to_tsv_value(v: &Value) -> Value { } } -fn to_string_helper(v: &Value) -> Result> { +fn to_string_helper(v: &Value) -> Result { match v { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), @@ -55,11 +54,23 @@ fn to_string_helper(v: &Value) -> Result> { Value::List(_) => return Ok(String::from("[list list]")), Value::Object(_) => return Ok(String::from("[object]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), - _ => return Err("Bad input".into()), + _ => Err(ShellError::string("Unexpected value")), } } -pub fn to_string(v: &Value) -> Result> { +fn merge_descriptors(values: &[Tagged]) -> Vec { + let mut ret = vec![]; + for value in values { + for desc in value.data_descriptors() { + if !ret.contains(&desc) { + ret.push(desc); + } + } + } + ret +} + +pub fn to_string(v: &Value) -> Result { match v { Value::Object(o) => { let mut wtr = WriterBuilder::new().delimiter(b'\t').from_writer(vec![]); @@ -74,7 +85,39 @@ pub fn to_string(v: &Value) -> Result> { wtr.write_record(fields).expect("can not write."); wtr.write_record(values).expect("can not write."); - return Ok(String::from_utf8(wtr.into_inner()?)?); + return Ok(String::from_utf8( + wtr.into_inner() + .map_err(|_| ShellError::string("Could not convert record"))?, + ) + .map_err(|_| ShellError::string("Could not convert record"))?); + } + Value::List(list) => { + let mut wtr = WriterBuilder::new().delimiter(b'\t').from_writer(vec![]); + + let merged_descriptors = merge_descriptors(&list); + wtr.write_record(&merged_descriptors) + .expect("can not write."); + + for l in list { + let mut row = vec![]; + for desc in &merged_descriptors { + match l.item.get_data_by_key(&desc) { + Some(s) => { + row.push(to_string_helper(s)?); + } + None => { + row.push(String::new()); + } + } + } + wtr.write_record(&row).expect("can not write"); + } + + return Ok(String::from_utf8( + wtr.into_inner() + .map_err(|_| ShellError::string("Could not convert record"))?, + ) + .map_err(|_| ShellError::string("Could not convert record"))?); } _ => return to_string_helper(&v), } @@ -85,29 +128,40 @@ fn to_tsv( RunnableContext { input, name, .. }: RunnableContext, ) -> Result { let name_span = name; - let out = input; + let stream = async_stream_block! { + let input: Vec> = input.values.collect().await; - Ok(out - .values - .map(move |a| match to_string(&value_to_tsv_value(&a.item)) { - Ok(x) => { - let converted = if headerless { - x.lines().skip(1).collect() - } else { - x - }; + let to_process_input = if input.len() > 1 { + let tag = input[0].tag; + vec![Tagged { item: Value::List(input), tag } ] + } else if input.len() == 1 { + input + } else { + vec![] + }; - ReturnSuccess::value( - Value::Primitive(Primitive::String(converted)).simple_spanned(name_span), - ) - } - _ => Err(ShellError::labeled_error_with_secondary( - "Expected an object with TSV-compatible structure from pipeline", - "requires TSV-compatible input", - name_span, - format!("{} originates from here", a.item.type_name()), - a.span(), - )), - }) - .to_output_stream()) + for value in to_process_input { + match to_string(&value_to_tsv_value(&value.item)) { + Ok(x) => { + let converted = if headerless { + x.lines().skip(1).collect() + } else { + x + }; + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span)) + } + _ => { + yield Err(ShellError::labeled_error_with_secondary( + "Expected an object with TSV-compatible structure.span() from pipeline", + "requires TSV-compatible input", + name_span, + "originates from here".to_string(), + value.span(), + )) + } + } + } + }; + + Ok(stream.to_output_stream()) } diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index 8ef6e90da2..86995a74a8 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -76,22 +76,41 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result Result { let args = args.evaluate_once(registry)?; let name_span = args.name_span(); - let out = args.input; - Ok(out - .values - .map( - move |a| match serde_yaml::to_string(&value_to_yaml_value(&a)?) { - Ok(x) => ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).simple_spanned(name_span), - ), - _ => Err(ShellError::labeled_error_with_secondary( + let stream = async_stream_block! { + let input: Vec> = args.input.values.collect().await; + + let to_process_input = if input.len() > 1 { + let tag = input[0].tag; + vec![Tagged { item: Value::List(input), tag } ] + } else if input.len() == 1 { + input + } else { + vec![] + }; + + for value in to_process_input { + match value_to_yaml_value(&value) { + Ok(yaml_value) => { + match serde_yaml::to_string(&yaml_value) { + Ok(x) => yield ReturnSuccess::value( + Value::Primitive(Primitive::String(x)).simple_spanned(name_span), + ), + _ => yield Err(ShellError::labeled_error_with_secondary( + "Expected an object with YAML-compatible structure.span() from pipeline", + "requires YAML-compatible input", + name_span, + "originates from here".to_string(), + value.span(), + )), + } + } + _ => yield Err(ShellError::labeled_error( "Expected an object with YAML-compatible structure from pipeline", "requires YAML-compatible input", - name_span, - format!("{} originates from here", a.item.type_name()), - a.span(), - )), - }, - ) - .to_output_stream()) + name_span)) + } + } + }; + + Ok(stream.to_output_stream()) } diff --git a/tests/filters_test.rs b/tests/filters_test.rs index e3ebcd1a6d..b44ac5c47d 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -218,8 +218,8 @@ fn converts_structured_table_to_json_text() { | split-column "," name luck | pick name | to-json - | nth 0 | from-json + | nth 0 | get name | echo $it "# From 4591397fa3c682c4cfd1a12d6675df834c3835cb Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 4 Sep 2019 19:20:14 +1200 Subject: [PATCH 014/342] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index cbe91059ed..bf80590b0b 100644 --- a/README.md +++ b/README.md @@ -246,8 +246,6 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | nth row-number | Return only the selected row | | str (field) | Apply string function. Optional use the field of a table | | tags | Read the tags (metadata) for values | -| from-array | Expand an array/list into rows | -| to-array | Collapse rows into a single list | | to-json | Convert table into .json text | | to-toml | Convert table into .toml text | | to-yaml | Convert table into .yaml text | From 6034de641a15dc6b2ca219199e045e51a3836179 Mon Sep 17 00:00:00 2001 From: George Pollard Date: Thu, 5 Sep 2019 03:30:51 +1200 Subject: [PATCH 015/342] Improve parsing of pipelines, require pipes At the moment the pipeline parser does not enforce that there must be a pipe between each part of the pipeline, which can lead to confusing behaviour or misleading errors. --- src/parser/parse/parser.rs | 18 +++++++++--------- src/parser/parse/pipeline.rs | 2 +- src/parser/parse/token_tree_builder.rs | 13 ++++++------- src/shell/helper.rs | 8 ++++---- 4 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 238a144d5a..8e4d995a2d 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -552,11 +552,11 @@ pub fn node(input: NomSpan) -> IResult { pub fn pipeline(input: NomSpan) -> IResult { trace_step(input, "pipeline", |input| { let start = input.offset; - let (input, head) = opt(tuple((raw_call, opt(space1), opt(tag("|")))))(input)?; + let (input, head) = opt(tuple((raw_call, opt(space1))))(input)?; let (input, items) = trace_step( input, "many0", - many0(tuple((opt(space1), raw_call, opt(space1), opt(tag("|"))))), + many0(tuple((tag("|"), opt(space1), raw_call, opt(space1)))), )?; let (input, tail) = opt(space1)(input)?; @@ -582,28 +582,28 @@ pub fn pipeline(input: NomSpan) -> IResult { } fn make_call_list( - head: Option<(Tagged, Option, Option)>, + head: Option<(Tagged, Option)>, items: Vec<( + NomSpan, Option, Tagged, Option, - Option, )>, ) -> Vec { let mut out = vec![]; if let Some(head) = head { - let el = PipelineElement::new(None, head.0, head.1.map(Span::from), head.2.map(Span::from)); + let el = PipelineElement::new(None, None, head.0, head.1.map(Span::from)); out.push(el); } - for (ws1, call, ws2, pipe) in items { + for (pipe, ws1, call, ws2) in items { let el = PipelineElement::new( + Some(pipe).map(Span::from), ws1.map(Span::from), call, - ws2.map(Span::from), - pipe.map(Span::from), - ); + ws2.map(Span::from)); + out.push(el); } diff --git a/src/parser/parse/pipeline.rs b/src/parser/parse/pipeline.rs index 20365bfbc7..75155d143a 100644 --- a/src/parser/parse/pipeline.rs +++ b/src/parser/parse/pipeline.rs @@ -11,9 +11,9 @@ pub struct Pipeline { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] pub struct PipelineElement { + pub pipe: Option, pub pre_ws: Option, #[get = "pub(crate)"] call: Tagged, pub post_ws: Option, - pub post_pipe: Option, } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index ad4eebdc8a..9dd1ebc16c 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -47,32 +47,31 @@ impl TokenTreeBuilder { .next() .expect("A pipeline must contain at least one element"); + let pipe = None; let pre_span = pre.map(|pre| b.consume(&pre)); let call = call(b); let post_span = post.map(|post| b.consume(&post)); - let pipe = input.peek().map(|_| Span::from(b.consume("|"))); + out.push(PipelineElement::new( + pipe, pre_span.map(Span::from), call, - post_span.map(Span::from), - pipe, - )); + post_span.map(Span::from))); loop { match input.next() { None => break, Some((pre, call, post)) => { + let pipe = Some(Span::from(b.consume("|"))); let pre_span = pre.map(|pre| b.consume(&pre)); let call = call(b); let post_span = post.map(|post| b.consume(&post)); - let pipe = input.peek().map(|_| Span::from(b.consume("|"))); - out.push(PipelineElement::new( + pipe, pre_span.map(Span::from), call, post_span.map(Span::from), - pipe, )); } } diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 9feffcb4ce..8e21c50ec8 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -147,6 +147,10 @@ fn paint_token_node(token_node: &TokenNode, line: &str) -> String { fn paint_pipeline_element(pipeline_element: &PipelineElement, line: &str) -> String { let mut styled = String::new(); + if let Some(_) = pipeline_element.pipe { + styled.push_str(&Color::Purple.paint("|")); + } + if let Some(ws) = pipeline_element.pre_ws { styled.push_str(&Color::White.normal().paint(ws.slice(line))); } @@ -168,10 +172,6 @@ fn paint_pipeline_element(pipeline_element: &PipelineElement, line: &str) -> Str styled.push_str(&Color::White.normal().paint(ws.slice(line))); } - if let Some(_) = pipeline_element.post_pipe { - styled.push_str(&Color::Purple.paint("|")); - } - styled.to_string() } From 60212611e504178dd5ca8eee34f60c89166902f3 Mon Sep 17 00:00:00 2001 From: George Pollard Date: Thu, 5 Sep 2019 04:13:07 +1200 Subject: [PATCH 016/342] Allow leading space before head of pipeline --- src/parser/parse/parser.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 8e4d995a2d..f230c36c22 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -552,7 +552,7 @@ pub fn node(input: NomSpan) -> IResult { pub fn pipeline(input: NomSpan) -> IResult { trace_step(input, "pipeline", |input| { let start = input.offset; - let (input, head) = opt(tuple((raw_call, opt(space1))))(input)?; + let (input, head) = opt(tuple((opt(space1), raw_call, opt(space1))))(input)?; let (input, items) = trace_step( input, "many0", @@ -582,7 +582,11 @@ pub fn pipeline(input: NomSpan) -> IResult { } fn make_call_list( - head: Option<(Tagged, Option)>, + head: Option<( + Option, + Tagged, + Option + )>, items: Vec<( NomSpan, Option, @@ -593,7 +597,12 @@ fn make_call_list( let mut out = vec![]; if let Some(head) = head { - let el = PipelineElement::new(None, None, head.0, head.1.map(Span::from)); + let el = PipelineElement::new( + None, + head.0.map(Span::from), + head.1, + head.2.map(Span::from)); + out.push(el); } From 0a9897c5ca14e5c98f160f33b484bb26a08564bb Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 5 Sep 2019 04:29:49 +1200 Subject: [PATCH 017/342] Move us away from mixing OOP and spreadsheet to just spreadsheet --- README.md | 80 +++++++++++++++++++------------------- src/commands/classified.rs | 4 +- src/commands/to_bson.rs | 4 +- src/commands/to_csv.rs | 2 +- src/commands/to_json.rs | 4 +- src/commands/to_sqlite.rs | 4 +- src/commands/to_toml.rs | 4 +- src/commands/to_tsv.rs | 6 +-- src/commands/to_yaml.rs | 4 +- src/object/base.rs | 6 +-- src/plugins/add.rs | 5 ++- src/plugins/edit.rs | 6 +-- src/plugins/inc.rs | 4 +- src/plugins/str.rs | 2 +- 14 files changed, 69 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index bf80590b0b..7d38fcdc82 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ The second container is a bit smaller, if size is important to you. # Philosophy -Nu draws inspiration from projects like PowerShell, functional programming languages, and modern cli tools. Rather than thinking of files 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 is a list of objects, where each object represents an item in that directory. These values can be piped through a series of steps, in a series of commands called a 'pipeline'. +Nu draws inspiration from projects like PowerShell, functional programming languages, and modern cli tools. Rather than thinking of files 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 is a table of rows, where each row represents an item in that directory. These values can be piped through a series of steps, in a series of commands called a 'pipeline'. ## Pipelines @@ -104,17 +104,18 @@ Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing lef ``` /home/jonathan/Source/nushell(master)> ls | where type == "Directory" | autoview ---------+-----------+----------+--------+--------------+---------------- - name | type | readonly | size | accessed | modified ---------+-----------+----------+--------+--------------+---------------- - target | Directory | | 4.1 KB | 19 hours ago | 19 hours ago - images | Directory | | 4.1 KB | 2 weeks ago | a week ago - tests | Directory | | 4.1 KB | 2 weeks ago | 18 minutes ago - docs | Directory | | 4.1 KB | a week ago | a week ago - .git | Directory | | 4.1 KB | 2 weeks ago | 25 minutes ago - src | Directory | | 4.1 KB | 2 weeks ago | 25 minutes ago - .cargo | Directory | | 4.1 KB | 2 weeks ago | 2 weeks ago ---------+-----------+----------+--------+--------------+---------------- +━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +────┼───────────┼───────────┼──────────┼────────┼──────────────┼──────────────── + 0 │ .azure │ Directory │ │ 4.1 KB │ 2 months ago │ a day ago + 1 │ target │ Directory │ │ 4.1 KB │ 3 days ago │ 3 days ago + 2 │ images │ Directory │ │ 4.1 KB │ 2 months ago │ 2 weeks ago + 3 │ tests │ Directory │ │ 4.1 KB │ 2 months ago │ 37 minutes ago + 4 │ tmp │ Directory │ │ 4.1 KB │ 2 weeks ago │ 2 weeks ago + 5 │ src │ Directory │ │ 4.1 KB │ 2 months ago │ 37 minutes ago + 6 │ assets │ Directory │ │ 4.1 KB │ a month ago │ a month ago + 7 │ docs │ Directory │ │ 4.1 KB │ 2 months ago │ 2 months ago +━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ ``` 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: @@ -126,15 +127,16 @@ Because most of the time you'll want to see the output of a pipeline, `autoview` Being able to use the same commands and compose them differently is an important philosophy in Nu. For example, we could use the built-in `ps` command as well to get a list of the running processes, using the same `where` as above. ```text -C:\Code\nushell(master)> ps | where cpu > 0 ------------------- +-----+-------+-------+---------- - name | cmd | cpu | pid | status ------------------- +-----+-------+-------+---------- - msedge.exe | - | 0.77 | 26472 | Runnable - nu.exe | - | 7.83 | 15473 | Runnable - SearchIndexer.exe | - | 82.17 | 23476 | Runnable - BlueJeans.exe | - | 4.54 | 10000 | Runnable --------------------+-----+-------+-------+---------- +/home/jonathan/Source/nushell(master)> ps | where cpu > 0 +━━━┯━━━━━━━┯━━━━━━━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━ + # │ pid │ name │ status │ cpu +───┼───────┼─────────────────┼──────────┼────────── + 0 │ 992 │ chrome │ Sleeping │ 6.988768 + 1 │ 4240 │ chrome │ Sleeping │ 5.645982 + 2 │ 13973 │ qemu-system-x86 │ Sleeping │ 4.996551 + 3 │ 15746 │ nu │ Sleeping │ 84.59905 +━━━┷━━━━━━━┷━━━━━━━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━ + ``` ## Opening files @@ -143,22 +145,22 @@ Nu can load file and URL contents as raw text or as structured data (if it recog ``` /home/jonathan/Source/nushell(master)> open Cargo.toml ------------------+------------------+----------------- - dependencies | dev-dependencies | package ------------------+------------------+----------------- - [object Object] | [object Object] | [object Object] ------------------+------------------+----------------- +━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━ + bin │ dependencies │ dev-dependencies +──────────────────┼────────────────┼────────────────── + [table: 12 rows] │ [table: 1 row] │ [table: 1 row] +━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━ ``` We can pipeline this into a command that gets the contents of one of the columns: ``` /home/jonathan/Source/nushell(master)> open Cargo.toml | get package --------------+----------------------------+---------+---------+------+--------- - authors | description | edition | license | name | version --------------+----------------------------+---------+---------+------+--------- - [list List] | A shell for the GitHub era | 2018 | MIT | nu | 0.2.0 --------------+----------------------------+---------+---------+------+--------- +━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━┯━━━━━━┯━━━━━━━━━ + authors │ description │ edition │ license │ name │ version +─────────────────┼────────────────────────────┼─────────┼─────────┼──────┼───────── + [table: 3 rows] │ A shell for the GitHub era │ 2018 │ ISC │ nu │ 0.2.0 +━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━┷━━━━━━┷━━━━━━━━━ ``` Finally, we can use commands outside of Nu once we have the data we want: @@ -180,7 +182,7 @@ Finally, to get a list of all the current shells, you can use the `shells` comma ## Plugins -Nu supports plugins that offer additional functionality to the shell and follow the same object model that built-in commands use. This allows you to extend nu for your needs. +Nu supports plugins that offer additional functionality to the shell and follow the same structured data model that built-in commands use. This allows you to extend nu for your needs. There are a few examples in the `plugins` directory. @@ -196,7 +198,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat * Nu's workflow and tools should have the usability in day-to-day experience of using a shell in 2019 (and beyond). -* Nu views data as both structured and unstructured. It is an object shell like PowerShell. +* Nu views data as both structured and unstructured. It is a structured shell like PowerShell. * Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state. @@ -233,18 +235,18 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | get column-or-column-path | Open column and get data from the corresponding cells | | sort-by ...columns | Sort by the given columns | | where condition | Filter table to match the condition | -| inc (field) | Increment a value or version. Optional use the field of a table | -| add field value | Add a new field to the table | -| embed field | Embeds a new field to the table | +| inc (column-or-column-path) | Increment a value or version. Optionally use the column of a table | +| add column-or-column-path value | Add a new column to the table | +| embed column | Creates a new table of one column with the given name, and places the current table inside of it | | sum | Sum a column of values | -| edit field value | Edit an existing field to have a new value | +| edit column-or-column-path value | Edit an existing column to have a new value | | reverse | Reverses the table. | | skip amount | Skip a number of rows | | skip-while condition | Skips rows while the condition matches. | | first amount | Show only the first number of rows | | last amount | Show only the last number of rows | | nth row-number | Return only the selected row | -| str (field) | Apply string function. Optional use the field of a table | +| str (column) | Apply string function. Optionally use the column of a table | | tags | Read the tags (metadata) for values | | to-json | Convert table into .json text | | to-toml | Convert table into .toml text | @@ -269,7 +271,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | from-yaml | Parse text as a .yaml/.yml and create a table | | lines | Split single string into rows, one per line | | size | Gather word count statistics on the text | -| split-column sep ...fields | Split row contents across multiple columns via the separator | +| split-column sep ...column-names | Split row contents across multiple columns via the separator, optionally give the columns names | | split-row sep | Split row contents over multiple rows via the separator | | trim | Trim leading and following whitespace from text data | | {external-command} $it | Run external command with given arguments, replacing $it with each row text | diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 622d92e77f..94e5656001 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -238,7 +238,7 @@ impl ExternalCommand { if let Some(span) = span { return Err(ShellError::labeled_error( "External $it needs string data", - "given object instead of string data", + "given row instead of string data", span, )); } else { @@ -293,7 +293,7 @@ impl ExternalCommand { } return Err(ShellError::labeled_error( "External $it needs string data", - "given object instead of string data", + "given row instead of string data", span, )); } diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index 8da3cf2a6d..8f7e9c3624 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -256,7 +256,7 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( - "Expected an object with BSON-compatible structure.span() from pipeline", + "Expected a table with BSON-compatible structure.span() from pipeline", "requires BSON-compatible input", name_span, "originates from here".to_string(), @@ -265,7 +265,7 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error( - "Expected an object with BSON-compatible structure from pipeline", + "Expected a table with BSON-compatible structure from pipeline", "requires BSON-compatible input", name_span)) } diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 058ad585de..8f788e6484 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -153,7 +153,7 @@ fn to_csv( } _ => { yield Err(ShellError::labeled_error_with_secondary( - "Expected an object with CSV-compatible structure.span() from pipeline", + "Expected a table with CSV-compatible structure.span() from pipeline", "requires CSV-compatible input", name_span, "originates from here".to_string(), diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 9af5985cb1..a6123528dc 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -100,7 +100,7 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( - "Expected an object with JSON-compatible structure.span() from pipeline", + "Expected a table with JSON-compatible structure.span() from pipeline", "requires JSON-compatible input", name_span, "originates from here".to_string(), @@ -109,7 +109,7 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error( - "Expected an object with JSON-compatible structure from pipeline", + "Expected a table with JSON-compatible structure from pipeline", "requires JSON-compatible input", name_span)) } diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index 25c8acdc84..6506ac335d 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -187,7 +187,7 @@ fn sqlite_input_stream_to_bytes( other => { return Err(std::io::Error::new( std::io::ErrorKind::Other, - format!("Expected object, found {:?}", other), + format!("Expected row, found {:?}", other), )) } } @@ -207,7 +207,7 @@ fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result yield ReturnSuccess::value(out), _ => { yield Err(ShellError::labeled_error( - "Expected an object with SQLite-compatible structure.span() from pipeline", + "Expected a table with SQLite-compatible structure.span() from pipeline", "requires SQLite-compatible input", name_span, )) diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 3960368662..419a50ac77 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -95,7 +95,7 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( - "Expected an object with TOML-compatible structure.span() from pipeline", + "Expected a table with TOML-compatible structure.span() from pipeline", "requires TOML-compatible input", name_span, "originates from here".to_string(), @@ -104,7 +104,7 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error( - "Expected an object with TOML-compatible structure from pipeline", + "Expected a table with TOML-compatible structure from pipeline", "requires TOML-compatible input", name_span)) } diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index d4958b773b..e787d197ec 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -51,8 +51,8 @@ fn to_string_helper(v: &Value) -> Result { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), Value::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?), - Value::List(_) => return Ok(String::from("[list list]")), - Value::Object(_) => return Ok(String::from("[object]")), + Value::List(_) => return Ok(String::from("[table]")), + Value::Object(_) => return Ok(String::from("[row]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), _ => Err(ShellError::string("Unexpected value")), } @@ -152,7 +152,7 @@ fn to_tsv( } _ => { yield Err(ShellError::labeled_error_with_secondary( - "Expected an object with TSV-compatible structure.span() from pipeline", + "Expected a table with TSV-compatible structure.span() from pipeline", "requires TSV-compatible input", name_span, "originates from here".to_string(), diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index 86995a74a8..5655567ef0 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -96,7 +96,7 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( - "Expected an object with YAML-compatible structure.span() from pipeline", + "Expected a table with YAML-compatible structure.span() from pipeline", "requires YAML-compatible input", name_span, "originates from here".to_string(), @@ -105,7 +105,7 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error( - "Expected an object with YAML-compatible structure from pipeline", + "Expected a table with YAML-compatible structure from pipeline", "requires YAML-compatible input", name_span)) } diff --git a/src/object/base.rs b/src/object/base.rs index 552d2c86de..402d212a86 100644 --- a/src/object/base.rs +++ b/src/object/base.rs @@ -504,11 +504,11 @@ impl Value { .map(|e| e.source(&b.source).to_string()), "; ", ), - Value::Object(_) => format!("[{}]", self.type_name()), + Value::Object(_) => format!("[table: 1 row]"), Value::List(l) => format!( - "[{} {}]", + "[table: {} {}]", l.len(), - if l.len() == 1 { "item" } else { "items" } + if l.len() == 1 { "row" } else { "rows" } ), Value::Binary(_) => format!(""), } diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 27a12e677e..3915cc4566 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -29,7 +29,7 @@ impl Add { } }, None => Err(ShellError::string( - "add needs a field when adding a value to an object", + "add needs a column name when adding a value to a table", )), }, x => Err(ShellError::string(format!( @@ -46,7 +46,8 @@ impl Plugin for Add { .desc("Add a new field to the table.") .required("Field", SyntaxType::String) .required("Value", SyntaxType::String) - .rest(SyntaxType::String).filter()) + .rest(SyntaxType::String) + .filter()) } fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index ab8a25aa68..15e87d996e 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -23,12 +23,12 @@ impl Edit { Some(v) => return Ok(v), None => { return Err(ShellError::string( - "edit could not find place to insert field", + "edit could not find place to insert column", )) } }, None => Err(ShellError::string( - "edit needs a field when adding a value to an object", + "edit needs a column when changing a value in a table", )), }, x => Err(ShellError::string(format!( @@ -42,7 +42,7 @@ impl Edit { impl Plugin for Edit { fn config(&mut self) -> Result { Ok(Signature::build("edit") - .desc("Edit an existing field to have a new value.") + .desc("Edit an existing column to have a new value.") .required("Field", SyntaxType::String) .required("Value", SyntaxType::String) .filter()) diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index fecba04e7d..ec78dbb622 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -102,7 +102,7 @@ impl Inc { } } None => Err(ShellError::string( - "inc needs a field when incrementing a value in an object", + "inc needs a field when incrementing a column in a table", )), }, x => Err(ShellError::string(format!( @@ -116,7 +116,7 @@ impl Inc { impl Plugin for Inc { fn config(&mut self) -> Result { Ok(Signature::build("inc") - .desc("Increment a value or version. Optional use the field of a table.") + .desc("Increment a value or version. Optionally use the column of a table.") .switch("major") .switch("minor") .switch("patch") diff --git a/src/plugins/str.rs b/src/plugins/str.rs index d47fec0f22..9b2f73c553 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -153,7 +153,7 @@ impl Str { } None => Err(ShellError::string(format!( "{}: {}", - "str needs a field when applying it to a value in an object", + "str needs a column when applied to a value in a row", Str::usage() ))), }, From e8764911cb8c3368a639bbf9e85975759335d58b Mon Sep 17 00:00:00 2001 From: Patrick Meredith Date: Wed, 4 Sep 2019 13:36:12 -0400 Subject: [PATCH 018/342] Add comments for sample.{bson,db} --- tests/command_open_tests.rs | 74 +++++++++++++++++++++++++++++++++++++ tests/commands_test.rs | 3 ++ 2 files changed, 77 insertions(+) diff --git a/tests/command_open_tests.rs b/tests/command_open_tests.rs index 968bc7531b..54dc7ad54d 100644 --- a/tests/command_open_tests.rs +++ b/tests/command_open_tests.rs @@ -30,6 +30,31 @@ fn recognizes_csv() { }) } +// sample.bson has the following format: +// ━━━━━━━━━━┯━━━━━━━━━━━ +// _id │ root +// ──────────┼─────────── +// [object] │ [9 items] +// ━━━━━━━━━━┷━━━━━━━━━━━ +// +// the root value is: +// ━━━┯━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━ +// # │ _id │ a │ b │ c +// ───┼───────────────────┼─────────────────────────┼──────────┼────────── +// 0 │ [object] │ 1.000000000000000 │ hello │ [2 items] +// 1 │ [object] │ 42.00000000000000 │ whel │ hello +// 2 │ [object] │ [object] │ │ +// 3 │ [object] │ │ [object] │ +// 4 │ [object] │ │ │ [object] +// 5 │ [object] │ │ │ [object] +// 6 │ [object] │ [object] │ [object] │ +// 7 │ [object] │ │ [object] │ +// 8 │ 1.000000 │ │ [object] │ +// +// The decimal value is supposed to be π, but is currently wrong due to +// what appears to be an issue in the bson library that is under investigation. +// + #[test] fn open_can_parse_bson_1() { let actual = nu!( @@ -57,6 +82,55 @@ fn open_can_parse_bson_2() { assert_eq!(actual, "function"); } +// sample.db has the following format: +// +// ━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━ +// # │ table_name │ table_values +// ───┼────────────┼────────────── +// 0 │ strings │ [6 items] +// 1 │ ints │ [5 items] +// 2 │ floats │ [4 items] +// ━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━ +// +// In this case, this represents a sqlite database +// with three tables named `strings`, `ints`, and `floats`. +// The table_values represent the values for the tables: +// +// ━━━━┯━━━━━━━┯━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// # │ x │ y │ z │ f +// ────┼───────┼──────────┼──────┼────────────────────────────────────────────────────────────────────── +// 0 │ hello │ │ │ +// 1 │ hello │ │ │ +// 2 │ hello │ │ │ +// 3 │ hello │ │ │ +// 4 │ world │ │ │ +// 5 │ world │ │ │ +// 6 │ │ │ 1 │ +// 7 │ │ │ 42 │ +// 8 │ │ │ 425 │ +// 9 │ │ │ 4253 │ +// 10 │ │ │ │ +// 11 │ │ │ │ 3.400000000000000 +// 12 │ │ │ │ 3.141592650000000 +// 13 │ │ │ │ 23.00000000000000 +// 14 │ │ │ │ this string that doesn't really belong here but sqlite is what it is +// ━━━━┷━━━━━━━┷━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// +// We can see here that each table has different columns. `strings` has `x` and `y`, while +// `ints` has just `z`, and `floats` has only the column `f`. This means, in general, when working +// with sqlite, one will want to select a single table, e.g.: +// +// open sample.db | nth 1 | get table_values +// ━━━┯━━━━━━ +// # │ z +// ───┼────── +// 0 │ 1 +// 1 │ 42 +// 2 │ 425 +// 3 │ 4253 +// 4 │ +// ━━━┷━━━━━━ + #[test] fn open_can_parse_sqlite() { let actual = nu!( diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 33e440f937..0b0e848723 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -67,6 +67,9 @@ fn save_can_write_out_csv() { }) } +// This text is more tricky since we are checking for binary output. The output rendered in ASCII is (roughly): +// �authors+0Yehuda Katz descriptionA shell for the GitHub eraedition2018licenseISCnamenuversion0.2.0 +// It is not valid utf-8, so this is just an approximation. #[test] fn save_can_write_out_bson() { Playground::setup("save_test_3", |dirs, _| { From 39fce1191f655542a920461afae5aa4208af1878 Mon Sep 17 00:00:00 2001 From: Patrick Meredith Date: Wed, 4 Sep 2019 13:38:17 -0400 Subject: [PATCH 019/342] Fix typo --- tests/commands_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 0b0e848723..4e66aa7ab3 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -67,7 +67,7 @@ fn save_can_write_out_csv() { }) } -// This text is more tricky since we are checking for binary output. The output rendered in ASCII is (roughly): +// This test is more tricky since we are checking for binary output. The output rendered in ASCII is (roughly): // �authors+0Yehuda Katz descriptionA shell for the GitHub eraedition2018licenseISCnamenuversion0.2.0 // It is not valid utf-8, so this is just an approximation. #[test] From dcd97b6346db4be7397aeee2b36e7ae8540b3510 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 6 Sep 2019 04:23:42 +1200 Subject: [PATCH 020/342] Move internal terminology to tables/rows --- src/cli.rs | 4 +-- src/commands/args.rs | 2 +- src/commands/command.rs | 2 +- src/commands/config.rs | 12 ++++---- src/commands/date.rs | 8 ++---- src/commands/fetch.rs | 4 +-- src/commands/from_bson.rs | 4 +-- src/commands/from_csv.rs | 9 +++--- src/commands/from_ini.rs | 4 +-- src/commands/from_json.rs | 9 +++--- src/commands/from_sqlite.rs | 8 +++--- src/commands/from_toml.rs | 6 ++-- src/commands/from_tsv.rs | 6 ++-- src/commands/from_xml.rs | 6 ++-- src/commands/from_yaml.rs | 6 ++-- src/commands/get.rs | 4 +-- src/commands/help.rs | 2 +- src/commands/lines.rs | 2 +- src/commands/macros.rs | 4 +-- src/commands/open.rs | 4 +-- src/commands/pick.rs | 2 +- src/commands/post.rs | 4 +-- src/commands/ps.rs | 2 +- src/commands/reject.rs | 2 +- src/commands/save.rs | 2 +- src/commands/shells.rs | 2 +- src/commands/size.rs | 2 +- src/commands/split_column.rs | 2 +- src/commands/split_row.rs | 2 +- src/commands/tags.rs | 2 +- src/commands/to_bson.rs | 8 +++--- src/commands/to_csv.rs | 16 +++++------ src/commands/to_json.rs | 8 +++--- src/commands/to_sqlite.rs | 10 +++---- src/commands/to_toml.rs | 8 +++--- src/commands/to_tsv.rs | 16 +++++------ src/commands/to_yaml.rs | 8 +++--- src/commands/trim.rs | 2 +- src/commands/version.rs | 4 +-- src/commands/which_.rs | 2 +- src/{object.rs => data.rs} | 0 src/{object => data}/base.rs | 48 +++++++++++++++---------------- src/{object => data}/command.rs | 11 ++++--- src/{object => data}/config.rs | 6 ++-- src/{object => data}/dict.rs | 8 +++--- src/{object => data}/files.rs | 2 +- src/{object => data}/into.rs | 2 +- src/{object => data}/meta.rs | 0 src/{object => data}/operators.rs | 0 src/{object => data}/process.rs | 2 +- src/{object => data}/types.rs | 0 src/evaluate/evaluator.rs | 4 +-- src/format/generic.rs | 6 ++-- src/format/table.rs | 4 +-- src/format/vtable.rs | 2 +- src/lib.rs | 8 +++--- src/parser/deserializer.rs | 39 ++++++++++++------------- src/parser/parse/unit.rs | 2 +- src/plugins/add.rs | 2 +- src/plugins/edit.rs | 2 +- src/plugins/embed.rs | 2 +- src/plugins/inc.rs | 8 +++--- src/plugins/str.rs | 12 ++++---- src/plugins/sys.rs | 10 +++---- src/plugins/tree.rs | 4 +-- src/prelude.rs | 10 +++---- src/shell/filesystem_shell.rs | 2 +- src/shell/help_shell.rs | 17 +++++++---- src/shell/value_shell.rs | 4 +-- src/utils.rs | 12 ++++---- 70 files changed, 220 insertions(+), 219 deletions(-) rename src/{object.rs => data.rs} (100%) rename src/{object => data}/base.rs (94%) rename src/{object => data}/command.rs (87%) rename src/{object => data}/config.rs (92%) rename src/{object => data}/dict.rs (96%) rename src/{object => data}/files.rs (96%) rename src/{object => data}/into.rs (92%) rename src/{object => data}/meta.rs (100%) rename src/{object => data}/operators.rs (100%) rename src/{object => data}/process.rs (94%) rename src/{object => data}/types.rs (100%) diff --git a/src/cli.rs b/src/cli.rs index 9b41c9dd21..66572d3d8e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,9 +7,9 @@ use crate::commands::plugin::JsonRpc; use crate::commands::plugin::{PluginCommand, PluginSink}; use crate::commands::whole_stream_command; use crate::context::Context; +use crate::data::Value; pub(crate) use crate::errors::ShellError; use crate::git::current_branch; -use crate::object::Value; use crate::parser::registry::Signature; use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode}; use crate::prelude::*; @@ -265,7 +265,7 @@ pub async fn cli() -> Result<(), Box> { context.shell_manager.clone(), ))); - let edit_mode = crate::object::config::config(Span::unknown())? + let edit_mode = crate::data::config::config(Span::unknown())? .get("edit_mode") .map(|s| match s.as_string().unwrap().as_ref() { "vi" => EditMode::Vi, diff --git a/src/commands/args.rs b/src/commands/args.rs index c08a075dd2..85329af5a1 100644 --- a/src/commands/args.rs +++ b/src/commands/args.rs @@ -1,4 +1,4 @@ -use crate::object::Value; +use crate::data::Value; #[derive(Debug)] pub enum LogLevel {} diff --git a/src/commands/command.rs b/src/commands/command.rs index a79e797fd7..9f4d256c2b 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,7 +1,7 @@ use crate::context::{SourceMap, SpanSource}; use crate::errors::ShellError; use crate::evaluate::Scope; -use crate::object::Value; +use crate::data::Value; use crate::parser::hir; use crate::parser::{registry, ConfigDeserializer}; use crate::prelude::*; diff --git a/src/commands/config.rs b/src/commands/config.rs index 56cce62270..c1f000d04d 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::{config, Value}; +use crate::data::{config, Value}; use crate::parser::hir::SyntaxType; use crate::parser::registry::{self}; use std::iter::FromIterator; @@ -55,7 +55,7 @@ pub fn config( }: ConfigArgs, RunnableContext { name, .. }: RunnableContext, ) -> Result { - let mut result = crate::object::config::config(name)?; + let mut result = crate::data::config::config(name)?; if let Some(v) = get { let key = v.to_string(); @@ -74,7 +74,7 @@ pub fn config( config::write_config(&result)?; return Ok(stream![Tagged::from_simple_spanned_item( - Value::Object(result.into()), + Value::Row(result.into()), value.span() )] .from_input_stream()); @@ -90,7 +90,7 @@ pub fn config( config::write_config(&result)?; return Ok(stream![Tagged::from_simple_spanned_item( - Value::Object(result.into()), + Value::Row(result.into()), span )] .from_input_stream()); @@ -123,9 +123,9 @@ pub fn config( ))); } - let obj = VecDeque::from_iter(vec![Value::Object(result.into()).simple_spanned(v.span())]); + let obj = VecDeque::from_iter(vec![Value::Row(result.into()).simple_spanned(v.span())]); return Ok(obj.from_input_stream()); } - return Ok(vec![Value::Object(result.into()).simple_spanned(name)].into()); + return Ok(vec![Value::Row(result.into()).simple_spanned(name)].into()); } diff --git a/src/commands/date.rs b/src/commands/date.rs index 0e2fee563a..7d3307fe58 100644 --- a/src/commands/date.rs +++ b/src/commands/date.rs @@ -1,5 +1,5 @@ use crate::errors::ShellError; -use crate::object::{Dictionary, Value}; +use crate::data::{Dictionary, Value}; use crate::prelude::*; use chrono::{DateTime, Local, Utc}; @@ -17,9 +17,7 @@ impl WholeStreamCommand for Date { } fn signature(&self) -> Signature { - Signature::build("date") - .switch("utc") - .switch("local") + Signature::build("date").switch("utc").switch("local") } fn usage(&self) -> &str { @@ -72,7 +70,7 @@ where Tagged::from_simple_spanned_item(Value::string(format!("{}", tz)), span), ); - Tagged::from_simple_spanned_item(Value::Object(Dictionary::from(indexmap)), span) + Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span) } pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index 8f69929a19..e87ddf8347 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -1,7 +1,7 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; +use crate::data::Value; use crate::errors::ShellError; -use crate::object::Value; use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; @@ -106,7 +106,7 @@ fn run( let result_vec: Vec> = result.drain_vec().await; for res in result_vec { match res { - Ok(ReturnSuccess::Value(Tagged { item: Value::List(list), ..})) => { + Ok(ReturnSuccess::Value(Tagged { item: Value::Table(list), ..})) => { for l in list { yield Ok(ReturnSuccess::Value(l)); } diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index 492553e9d9..e2f5421bd9 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ExpectedRange; -use crate::object::{Primitive, TaggedDictBuilder, Value}; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use bson::{decode_document, spec::BinarySubtype, Bson}; use std::str::FromStr; @@ -48,7 +48,7 @@ fn convert_bson_value_to_nu_value( Ok(match v { Bson::FloatingPoint(n) => Value::Primitive(Primitive::from(*n)).tagged(tag), Bson::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag), - Bson::Array(a) => Value::List(bson_array(a, tag)?).tagged(tag), + Bson::Array(a) => Value::Table(bson_array(a, tag)?).tagged(tag), Bson::Document(doc) => { let mut collected = TaggedDictBuilder::new(tag); for (k, v) in doc.iter() { diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index 9f4a96da3e..c872e77360 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, TaggedDictBuilder, Value}; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use csv::ReaderBuilder; @@ -16,8 +16,7 @@ impl WholeStreamCommand for FromCSV { } fn signature(&self) -> Signature { - Signature::build("from-csv") - .switch("headerless") + Signature::build("from-csv").switch("headerless") } fn usage(&self) -> &str { @@ -78,7 +77,7 @@ pub fn from_csv_string_to_value( } } - Ok(Tagged::from_item(Value::List(rows), tag)) + Ok(Tagged::from_item(Value::Table(rows), tag)) } fn from_csv( @@ -116,7 +115,7 @@ fn from_csv( match from_csv_string_to_value(concat_string, skip_headers, name_span) { Ok(x) => match x { - Tagged { item: Value::List(list), .. } => { + Tagged { item: Value::Table(list), .. } => { for l in list { yield ReturnSuccess::value(l); } diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index 8495c4b221..0e128a22c4 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, TaggedDictBuilder, Value}; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use std::collections::HashMap; @@ -94,7 +94,7 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result match x { - Tagged { item: Value::List(list), .. } => { + Tagged { item: Value::Table(list), .. } => { for l in list { yield ReturnSuccess::value(l); } diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index f932e35793..6864391af8 100644 --- a/src/commands/from_json.rs +++ b/src/commands/from_json.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, TaggedDictBuilder, Value}; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; pub struct FromJSON; @@ -15,8 +15,7 @@ impl WholeStreamCommand for FromJSON { } fn signature(&self) -> Signature { - Signature::build("from-json") - .switch("objects") + Signature::build("from-json").switch("objects") } fn usage(&self) -> &str { @@ -44,7 +43,7 @@ fn convert_json_value_to_nu_value(v: &serde_hjson::Value, tag: impl Into) - serde_hjson::Value::String(s) => { Value::Primitive(Primitive::String(String::from(s))).tagged(tag) } - serde_hjson::Value::Array(a) => Value::List( + serde_hjson::Value::Array(a) => Value::Table( a.iter() .map(|x| convert_json_value_to_nu_value(x, tag)) .collect(), @@ -126,7 +125,7 @@ fn from_json( match from_json_string_to_value(concat_string, name_span) { Ok(x) => match x { - Tagged { item: Value::List(list), .. } => { + Tagged { item: Value::Table(list), .. } => { for l in list { yield ReturnSuccess::value(l); } diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index 9aca8a222e..c19c4fef10 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::errors::ShellError; -use crate::object::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use rusqlite::{types::ValueRef, Connection, Row, NO_PARAMS}; use std::io::Write; @@ -76,11 +76,11 @@ pub fn convert_sqlite_file_to_nu_value( "table_name".to_string(), Value::Primitive(Primitive::String(table_name)).tagged(tag.clone()), ); - meta_dict.insert_tagged("table_values", Value::List(out).tagged(tag.clone())); + meta_dict.insert_tagged("table_values", Value::Table(out).tagged(tag.clone())); meta_out.push(meta_dict.into_tagged_value()); } let tag = tag.into(); - Ok(Value::List(meta_out).tagged(tag)) + Ok(Value::Table(meta_out).tagged(tag)) } fn convert_sqlite_row_to_nu_value( @@ -140,7 +140,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result match from_sqlite_bytes_to_value(vb, span) { Ok(x) => match x { - Tagged { item: Value::List(list), .. } => { + Tagged { item: Value::Table(list), .. } => { for l in list { yield ReturnSuccess::value(l); } diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index 3d1d92fb67..c1338cb01f 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, TaggedDictBuilder, Value}; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; pub struct FromTOML; @@ -34,7 +34,7 @@ pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into) -> T toml::Value::Integer(n) => Value::number(n).tagged(tag), toml::Value::Float(n) => Value::number(n).tagged(tag), toml::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag), - toml::Value::Array(a) => Value::List( + toml::Value::Array(a) => Value::Table( a.iter() .map(|x| convert_toml_value_to_nu_value(x, tag)) .collect(), @@ -98,7 +98,7 @@ pub fn from_toml( match from_toml_string_to_value(concat_string, span) { Ok(x) => match x { - Tagged { item: Value::List(list), .. } => { + Tagged { item: Value::Table(list), .. } => { for l in list { yield ReturnSuccess::value(l); } diff --git a/src/commands/from_tsv.rs b/src/commands/from_tsv.rs index 366621d435..92696c1aaf 100644 --- a/src/commands/from_tsv.rs +++ b/src/commands/from_tsv.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, TaggedDictBuilder, Value}; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use csv::ReaderBuilder; @@ -78,7 +78,7 @@ pub fn from_tsv_string_to_value( } } - Ok(Tagged::from_item(Value::List(rows), tag)) + Ok(Tagged::from_item(Value::Table(rows), tag)) } fn from_tsv( @@ -116,7 +116,7 @@ fn from_tsv( match from_tsv_string_to_value(concat_string, skip_headers, name_span) { Ok(x) => match x { - Tagged { item: Value::List(list), .. } => { + Tagged { item: Value::Table(list), .. } => { for l in list { yield ReturnSuccess::value(l); } diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index 4e4be72bc2..2c3f94cddc 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, TaggedDictBuilder, Value}; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; pub struct FromXML; @@ -55,7 +55,7 @@ fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>, tag: impl Into) .collect(); let mut collected = TaggedDictBuilder::new(tag); - collected.insert(name.clone(), Value::List(children_values)); + collected.insert(name.clone(), Value::Table(children_values)); collected.into_tagged_value() } else if n.is_comment() { @@ -113,7 +113,7 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result match x { - Tagged { item: Value::List(list), .. } => { + Tagged { item: Value::Table(list), .. } => { for l in list { yield ReturnSuccess::value(l); } diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index 3bac9d3f81..430ac765b9 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, TaggedDictBuilder, Value}; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; pub struct FromYAML; @@ -62,7 +62,7 @@ fn convert_yaml_value_to_nu_value(v: &serde_yaml::Value, tag: impl Into) -> Value::Primitive(Primitive::from(n.as_f64().unwrap())).tagged(tag) } serde_yaml::Value::String(s) => Value::string(s).tagged(tag), - serde_yaml::Value::Sequence(a) => Value::List( + serde_yaml::Value::Sequence(a) => Value::Table( a.iter() .map(|x| convert_yaml_value_to_nu_value(x, tag)) .collect(), @@ -127,7 +127,7 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result match x { - Tagged { item: Value::List(list), .. } => { + Tagged { item: Value::Table(list), .. } => { for l in list { yield ReturnSuccess::value(l); } diff --git a/src/commands/get.rs b/src/commands/get.rs index 44e3259fd8..2f9ed8a32a 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::Value; +use crate::data::Value; use crate::prelude::*; pub struct Get; @@ -73,7 +73,7 @@ pub fn get( for field in &fields { match get_member(field, &item) { Ok(Tagged { - item: Value::List(l), + item: Value::Table(l), .. }) => { for item in l { diff --git a/src/commands/help.rs b/src/commands/help.rs index bfdd61173f..571868a02a 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -1,7 +1,7 @@ use crate::commands::command::CommandAction; use crate::commands::PerItemCommand; use crate::errors::ShellError; -use crate::object::{command_dict, TaggedDictBuilder}; +use crate::data::{command_dict, TaggedDictBuilder}; use crate::parser::registry; use crate::prelude::*; diff --git a/src/commands/lines.rs b/src/commands/lines.rs index 0195177542..15a467224f 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::{Primitive, Value}; +use crate::data::{Primitive, Value}; use crate::prelude::*; use log::trace; diff --git a/src/commands/macros.rs b/src/commands/macros.rs index adac8d9c89..4a83f5e069 100644 --- a/src/commands/macros.rs +++ b/src/commands/macros.rs @@ -266,7 +266,7 @@ macro_rules! command { Extract { $($extract:tt)* { - use $crate::object::types::ExtractType; + use $crate::data::types::ExtractType; let value = $args.expect_nth($($positional_count)*)?; Block::extract(value)? } @@ -321,7 +321,7 @@ macro_rules! command { Extract { $($extract:tt)* { - use $crate::object::types::ExtractType; + use $crate::data::types::ExtractType; let value = $args.expect_nth($($positional_count)*)?; <$param_kind>::extract(&value)? } diff --git a/src/commands/open.rs b/src/commands/open.rs index ea960d8380..32a99c6f17 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,7 +1,7 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; +use crate::data::Value; use crate::errors::ShellError; -use crate::object::Value; use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; @@ -107,7 +107,7 @@ fn run( let result_vec: Vec> = result.drain_vec().await; for res in result_vec { match res { - Ok(ReturnSuccess::Value(Tagged { item: Value::List(list), ..})) => { + Ok(ReturnSuccess::Value(Tagged { item: Value::Table(list), ..})) => { for l in list { yield Ok(ReturnSuccess::Value(l)); } diff --git a/src/commands/pick.rs b/src/commands/pick.rs index 927edf8b3d..bc5f8df401 100644 --- a/src/commands/pick.rs +++ b/src/commands/pick.rs @@ -1,7 +1,7 @@ use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; use crate::errors::ShellError; -use crate::object::base::select_fields; +use crate::data::base::select_fields; use crate::prelude::*; #[derive(Deserialize)] diff --git a/src/commands/post.rs b/src/commands/post.rs index c1fc5d42fe..8790c929a7 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -1,7 +1,7 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; use crate::errors::ShellError; -use crate::object::Value; +use crate::data::Value; use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; @@ -116,7 +116,7 @@ fn run( let result_vec: Vec> = result.drain_vec().await; for res in result_vec { match res { - Ok(ReturnSuccess::Value(Tagged { item: Value::List(list), ..})) => { + Ok(ReturnSuccess::Value(Tagged { item: Value::Table(list), ..})) => { for l in list { yield Ok(ReturnSuccess::Value(l)); } diff --git a/src/commands/ps.rs b/src/commands/ps.rs index b11e797961..cdacd7113c 100644 --- a/src/commands/ps.rs +++ b/src/commands/ps.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::TaggedDictBuilder; +use crate::data::TaggedDictBuilder; use crate::prelude::*; use std::time::Duration; use std::usize; diff --git a/src/commands/reject.rs b/src/commands/reject.rs index b519f8ea64..49dbc4dd36 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::base::reject_fields; +use crate::data::base::reject_fields; use crate::prelude::*; #[derive(Deserialize)] diff --git a/src/commands/save.rs b/src/commands/save.rs index e998329c8f..26bf8cb16d 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -1,6 +1,6 @@ use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand}; use crate::errors::ShellError; -use crate::object::Value; +use crate::data::Value; use crate::prelude::*; use std::path::{Path, PathBuf}; diff --git a/src/commands/shells.rs b/src/commands/shells.rs index 5e0159e147..856c23e7be 100644 --- a/src/commands/shells.rs +++ b/src/commands/shells.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::TaggedDictBuilder; +use crate::data::TaggedDictBuilder; use crate::prelude::*; pub struct Shells; diff --git a/src/commands/size.rs b/src/commands/size.rs index 02da0460cb..d8383efcf8 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::{TaggedDictBuilder, Value}; +use crate::data::{TaggedDictBuilder, Value}; use crate::prelude::*; pub struct Size; diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index 4bde5e25b0..2df02cac16 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::{Primitive, TaggedDictBuilder, Value}; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use log::trace; diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index fa95225f4f..d4f3824a80 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::{Primitive, Value}; +use crate::data::{Primitive, Value}; use crate::prelude::*; use log::trace; diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 973105709c..3f112482ca 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::{TaggedDictBuilder, Value}; +use crate::data::{TaggedDictBuilder, Value}; use crate::prelude::*; pub struct Tags; diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index 8f7e9c3624..bb0355a5e9 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Dictionary, Primitive, Value}; +use crate::data::{Dictionary, Primitive, Value}; use crate::prelude::*; use bson::{encode_document, oid::ObjectId, spec::BinarySubtype, Bson, Document}; use std::convert::TryInto; @@ -51,14 +51,14 @@ pub fn value_to_bson_value(v: &Tagged) -> Result { Value::Primitive(Primitive::Nothing) => Bson::Null, Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()), Value::Primitive(Primitive::Path(s)) => Bson::String(s.display().to_string()), - Value::List(l) => Bson::Array( + Value::Table(l) => Bson::Array( l.iter() .map(|x| value_to_bson_value(x)) .collect::>()?, ), Value::Block(_) => Bson::Null, Value::Binary(b) => Bson::Binary(BinarySubtype::Generic, b.clone()), - Value::Object(o) => object_value_to_bson(o)?, + Value::Row(o) => object_value_to_bson(o)?, }) } @@ -241,7 +241,7 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result 1 { let tag = input[0].tag; - vec![Tagged { item: Value::List(input), tag } ] + vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input } else { diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 8f788e6484..0d13cab8fc 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, Value}; +use crate::data::{Primitive, Value}; use crate::prelude::*; use csv::WriterBuilder; @@ -39,8 +39,8 @@ pub fn value_to_csv_value(v: &Value) -> Value { Value::Primitive(Primitive::Boolean(b)) => Value::Primitive(Primitive::Boolean(b.clone())), Value::Primitive(Primitive::Bytes(b)) => Value::Primitive(Primitive::Bytes(b.clone())), Value::Primitive(Primitive::Date(d)) => Value::Primitive(Primitive::Date(d.clone())), - Value::Object(o) => Value::Object(o.clone()), - Value::List(l) => Value::List(l.clone()), + Value::Row(o) => Value::Row(o.clone()), + Value::Table(l) => Value::Table(l.clone()), Value::Block(_) => Value::Primitive(Primitive::Nothing), _ => Value::Primitive(Primitive::Nothing), } @@ -51,8 +51,8 @@ fn to_string_helper(v: &Value) -> Result { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), Value::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?), - Value::List(_) => return Ok(String::from("[list list]")), - Value::Object(_) => return Ok(String::from("[object]")), + Value::Table(_) => return Ok(String::from("[list list]")), + Value::Row(_) => return Ok(String::from("[object]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), _ => return Err(ShellError::string("Unexpected value")), } @@ -72,7 +72,7 @@ fn merge_descriptors(values: &[Tagged]) -> Vec { pub fn to_string(v: &Value) -> Result { match v { - Value::Object(o) => { + Value::Row(o) => { let mut wtr = WriterBuilder::new().from_writer(vec![]); let mut fields: VecDeque = VecDeque::new(); let mut values: VecDeque = VecDeque::new(); @@ -92,7 +92,7 @@ pub fn to_string(v: &Value) -> Result { ) .map_err(|_| ShellError::string("Could not convert record"))?); } - Value::List(list) => { + Value::Table(list) => { let mut wtr = WriterBuilder::new().from_writer(vec![]); let merged_descriptors = merge_descriptors(&list); @@ -134,7 +134,7 @@ fn to_csv( let to_process_input = if input.len() > 1 { let tag = input[0].tag; - vec![Tagged { item: Value::List(input), tag } ] + vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input } else { diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index a6123528dc..f53fbd8d28 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, Value}; +use crate::data::{Primitive, Value}; use crate::prelude::*; pub struct ToJSON; @@ -48,7 +48,7 @@ pub fn value_to_json_value(v: &Tagged) -> Result serde_json::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => serde_json::Value::String(s.display().to_string()), - Value::List(l) => serde_json::Value::Array(json_list(l)?), + Value::Table(l) => serde_json::Value::Array(json_list(l)?), Value::Block(_) => serde_json::Value::Null, Value::Binary(b) => serde_json::Value::Array( b.iter() @@ -57,7 +57,7 @@ pub fn value_to_json_value(v: &Tagged) -> Result { + Value::Row(o) => { let mut m = serde_json::Map::new(); for (k, v) in o.entries.iter() { m.insert(k.clone(), value_to_json_value(v)?); @@ -85,7 +85,7 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result 1 { let tag = input[0].tag; - vec![Tagged { item: Value::List(input), tag } ] + vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input } else { diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index 6506ac335d..7580c3f4b7 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Dictionary, Primitive, Value}; +use crate::data::{Dictionary, Primitive, Value}; use crate::prelude::*; use hex::encode; use rusqlite::{Connection, NO_PARAMS}; @@ -71,7 +71,7 @@ fn comma_concat(acc: String, current: String) -> String { fn get_columns(rows: &Vec>) -> Result { match &rows[0].item { - Value::Object(d) => Ok(d + Value::Row(d) => Ok(d .entries .iter() .map(|(k, _v)| k.clone()) @@ -107,7 +107,7 @@ fn get_insert_values(rows: Vec>) -> Result let values: Result, _> = rows .into_iter() .map(|value| match value.item { - Value::Object(d) => Ok(format!( + Value::Row(d) => Ok(format!( "({})", d.entries .iter() @@ -139,7 +139,7 @@ fn generate_statements(table: Dictionary) -> Result<(String, String), std::io::E }; let (columns, insert_values) = match table.entries.get("table_values") { Some(Tagged { - item: Value::List(l), + item: Value::Table(l), .. }) => (get_columns(l), get_insert_values(l.to_vec())), _ => { @@ -169,7 +169,7 @@ fn sqlite_input_stream_to_bytes( let tag = values[0].tag.clone(); for value in values.into_iter() { match value.item() { - Value::Object(d) => { + Value::Row(d) => { let (create, insert) = generate_statements(d.to_owned())?; match conn .execute(&create, NO_PARAMS) diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 419a50ac77..7bca9840e9 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, Value}; +use crate::data::{Primitive, Value}; use crate::prelude::*; pub struct ToTOML; @@ -47,12 +47,12 @@ pub fn value_to_toml_value(v: &Tagged) -> Result Value::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => toml::Value::String(s.display().to_string()), - Value::List(l) => toml::Value::Array(collect_values(l)?), + Value::Table(l) => toml::Value::Array(collect_values(l)?), Value::Block(_) => toml::Value::String("".to_string()), Value::Binary(b) => { toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect()) } - Value::Object(o) => { + Value::Row(o) => { let mut m = toml::map::Map::new(); for (k, v) in o.entries.iter() { m.insert(k.clone(), value_to_toml_value(v)?); @@ -80,7 +80,7 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result 1 { let tag = input[0].tag; - vec![Tagged { item: Value::List(input), tag } ] + vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input } else { diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index e787d197ec..2842db2315 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, Value}; +use crate::data::{Primitive, Value}; use crate::prelude::*; use csv::WriterBuilder; @@ -39,8 +39,8 @@ pub fn value_to_tsv_value(v: &Value) -> Value { Value::Primitive(Primitive::Boolean(b)) => Value::Primitive(Primitive::Boolean(b.clone())), Value::Primitive(Primitive::Bytes(b)) => Value::Primitive(Primitive::Bytes(b.clone())), Value::Primitive(Primitive::Date(d)) => Value::Primitive(Primitive::Date(d.clone())), - Value::Object(o) => Value::Object(o.clone()), - Value::List(l) => Value::List(l.clone()), + Value::Row(o) => Value::Row(o.clone()), + Value::Table(l) => Value::Table(l.clone()), Value::Block(_) => Value::Primitive(Primitive::Nothing), _ => Value::Primitive(Primitive::Nothing), } @@ -51,8 +51,8 @@ fn to_string_helper(v: &Value) -> Result { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), Value::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?), - Value::List(_) => return Ok(String::from("[table]")), - Value::Object(_) => return Ok(String::from("[row]")), + Value::Table(_) => return Ok(String::from("[table]")), + Value::Row(_) => return Ok(String::from("[row]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), _ => Err(ShellError::string("Unexpected value")), } @@ -72,7 +72,7 @@ fn merge_descriptors(values: &[Tagged]) -> Vec { pub fn to_string(v: &Value) -> Result { match v { - Value::Object(o) => { + Value::Row(o) => { let mut wtr = WriterBuilder::new().delimiter(b'\t').from_writer(vec![]); let mut fields: VecDeque = VecDeque::new(); let mut values: VecDeque = VecDeque::new(); @@ -91,7 +91,7 @@ pub fn to_string(v: &Value) -> Result { ) .map_err(|_| ShellError::string("Could not convert record"))?); } - Value::List(list) => { + Value::Table(list) => { let mut wtr = WriterBuilder::new().delimiter(b'\t').from_writer(vec![]); let merged_descriptors = merge_descriptors(&list); @@ -133,7 +133,7 @@ fn to_tsv( let to_process_input = if input.len() > 1 { let tag = input[0].tag; - vec![Tagged { item: Value::List(input), tag } ] + vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input } else { diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index 5655567ef0..129deebdf6 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -1,5 +1,5 @@ use crate::commands::WholeStreamCommand; -use crate::object::{Primitive, Value}; +use crate::data::{Primitive, Value}; use crate::prelude::*; pub struct ToYAML; @@ -45,7 +45,7 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result serde_yaml::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => serde_yaml::Value::String(s.display().to_string()), - Value::List(l) => { + Value::Table(l) => { let mut out = vec![]; for value in l { @@ -60,7 +60,7 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result { + Value::Row(o) => { let mut m = serde_yaml::Mapping::new(); for (k, v) in o.entries.iter() { m.insert( @@ -81,7 +81,7 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result 1 { let tag = input[0].tag; - vec![Tagged { item: Value::List(input), tag } ] + vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input } else { diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 66152843f0..865f6b50bb 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::Value; +use crate::data::Value; use crate::prelude::*; pub struct Trim; diff --git a/src/commands/version.rs b/src/commands/version.rs index def335420a..a7a3578193 100644 --- a/src/commands/version.rs +++ b/src/commands/version.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; -use crate::object::{Dictionary, Value}; +use crate::data::{Dictionary, Value}; use crate::parser::registry::Signature; use crate::prelude::*; use indexmap::IndexMap; @@ -39,6 +39,6 @@ pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result), - List(Vec>), + Table(Vec>), Block(Block), } @@ -220,8 +220,8 @@ impl fmt::Debug for ValueDebug<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self.value.item() { Value::Primitive(p) => p.debug(f), - Value::Object(o) => o.debug(f), - Value::List(l) => debug_list(l).fmt(f), + Value::Row(o) => o.debug(f), + Value::Table(l) => debug_list(l).fmt(f), Value::Block(_) => write!(f, "[[block]]"), Value::Binary(_) => write!(f, "[[binary]]"), } @@ -293,12 +293,12 @@ impl std::convert::TryFrom<&Tagged> for Vec { } } -impl<'a> std::convert::TryFrom<&'a Tagged> for &'a crate::object::Dictionary { +impl<'a> std::convert::TryFrom<&'a Tagged> for &'a crate::data::Dictionary { type Error = ShellError; - fn try_from(value: &'a Tagged) -> Result<&'a crate::object::Dictionary, ShellError> { + fn try_from(value: &'a Tagged) -> Result<&'a crate::data::Dictionary, ShellError> { match value.item() { - Value::Object(d) => Ok(d), + Value::Row(d) => Ok(d), v => Err(ShellError::type_error( "Dictionary", value.copy_span(v.type_name()), @@ -340,8 +340,8 @@ impl Value { pub(crate) fn type_name(&self) -> String { match self { Value::Primitive(p) => p.type_name(), - Value::Object(_) => format!("object"), - Value::List(_) => format!("list"), + Value::Row(_) => format!("object"), + Value::Table(_) => format!("list"), Value::Block(_) => format!("block"), Value::Binary(_) => format!("binary"), } @@ -351,26 +351,26 @@ impl Value { pub fn data_descriptors(&self) -> Vec { match self { Value::Primitive(_) => vec![], - Value::Object(o) => o + Value::Row(o) => o .entries .keys() .into_iter() .map(|x| x.to_string()) .collect(), Value::Block(_) => vec![], - Value::List(_) => vec![], + Value::Table(_) => vec![], Value::Binary(_) => vec![], } } pub(crate) fn get_data_by_key(&self, name: &str) -> Option<&Tagged> { match self { - Value::Object(o) => o.get_data_by_key(name), - Value::List(l) => { + Value::Row(o) => o.get_data_by_key(name), + Value::Table(l) => { for item in l { match item { Tagged { - item: Value::Object(o), + item: Value::Row(o), .. } => match o.get_data_by_key(name) { Some(v) => return Some(v), @@ -407,7 +407,7 @@ impl Value { let split_path: Vec<_> = path.split(".").collect(); - if let Value::Object(ref mut o) = new_obj { + if let Value::Row(ref mut o) = new_obj { let mut current = o; if split_path.len() == 1 { @@ -423,7 +423,7 @@ impl Value { Some(next) => { if idx == (split_path.len() - 2) { match &mut next.item { - Value::Object(o) => { + Value::Row(o) => { o.entries.insert( split_path[idx + 1].to_string(), Tagged::from_item(new_value, tag), @@ -435,7 +435,7 @@ impl Value { return Some(Tagged::from_item(new_obj, tag)); } else { match next.item { - Value::Object(ref mut o) => { + Value::Row(ref mut o) => { current = o; } _ => return None, @@ -460,7 +460,7 @@ impl Value { let split_path: Vec<_> = path.split(".").collect(); - if let Value::Object(ref mut o) = new_obj { + if let Value::Row(ref mut o) = new_obj { let mut current = o; for idx in 0..split_path.len() { match current.entries.get_mut(split_path[idx]) { @@ -470,7 +470,7 @@ impl Value { return Some(Tagged::from_item(new_obj, tag)); } else { match next.item { - Value::Object(ref mut o) => { + Value::Row(ref mut o) => { current = o; } _ => return None, @@ -488,9 +488,9 @@ impl Value { pub fn get_data(&self, desc: &String) -> MaybeOwned<'_, Value> { match self { p @ Value::Primitive(_) => MaybeOwned::Borrowed(p), - Value::Object(o) => o.get_data(desc), + Value::Row(o) => o.get_data(desc), Value::Block(_) => MaybeOwned::Owned(Value::nothing()), - Value::List(_) => MaybeOwned::Owned(Value::nothing()), + Value::Table(_) => MaybeOwned::Owned(Value::nothing()), Value::Binary(_) => MaybeOwned::Owned(Value::nothing()), } } @@ -504,8 +504,8 @@ impl Value { .map(|e| e.source(&b.source).to_string()), "; ", ), - Value::Object(_) => format!("[table: 1 row]"), - Value::List(l) => format!( + Value::Row(_) => format!("[table: 1 row]"), + Value::Table(l) => format!( "[table: {} {}]", l.len(), if l.len() == 1 { "row" } else { "rows" } diff --git a/src/object/command.rs b/src/data/command.rs similarity index 87% rename from src/object/command.rs rename to src/data/command.rs index 317fe5fa34..a2046aa7aa 100644 --- a/src/object/command.rs +++ b/src/data/command.rs @@ -1,5 +1,5 @@ use crate::commands::command::Command; -use crate::object::{TaggedDictBuilder, TaggedListBuilder, Value}; +use crate::data::{TaggedDictBuilder, TaggedListBuilder, Value}; use crate::parser::registry::{NamedType, PositionalType, Signature}; use crate::prelude::*; use std::ops::Deref; @@ -32,7 +32,10 @@ fn for_spec(name: &str, ty: &str, required: bool, tag: impl Into) -> Tagged spec.insert("name", Value::string(name)); spec.insert("type", Value::string(ty)); - spec.insert("required", Value::string(if required { "yes" } else { "no" })); + spec.insert( + "required", + Value::string(if required { "yes" } else { "no" }), + ); spec.into_tagged_value() } @@ -43,8 +46,8 @@ fn signature_dict(signature: Signature, tag: impl Into) -> Tagged { for arg in signature.positional.iter() { let is_required = match arg { - PositionalType::Mandatory(_,_) => true, - PositionalType::Optional(_,_) => false, + PositionalType::Mandatory(_, _) => true, + PositionalType::Optional(_, _) => false, }; sig.insert_tagged(for_spec(arg.name(), "argument", is_required, tag)); diff --git a/src/object/config.rs b/src/data/config.rs similarity index 92% rename from src/object/config.rs rename to src/data/config.rs index 53a1c446f8..cf384e8b68 100644 --- a/src/object/config.rs +++ b/src/data/config.rs @@ -1,7 +1,7 @@ use crate::commands::from_toml::convert_toml_value_to_nu_value; use crate::commands::to_toml::value_to_toml_value; +use crate::data::{Dictionary, Value}; use crate::errors::ShellError; -use crate::object::{Dictionary, Value}; use crate::prelude::*; use app_dirs::*; use indexmap::IndexMap; @@ -37,7 +37,7 @@ pub(crate) fn write_config(config: &IndexMap>) -> Result<( touch(&filename)?; let contents = - value_to_toml_value(&Value::Object(Dictionary::new(config.clone())).tagged_unknown())?; + value_to_toml_value(&Value::Row(Dictionary::new(config.clone())).tagged_unknown())?; let contents = toml::to_string(&contents)?; @@ -67,7 +67,7 @@ pub(crate) fn config(span: impl Into) -> Result Ok(entries), + Value::Row(Dictionary { entries }) => Ok(entries), other => Err(ShellError::type_error( "Dictionary", other.type_name().tagged(tag), diff --git a/src/object/dict.rs b/src/data/dict.rs similarity index 96% rename from src/object/dict.rs rename to src/data/dict.rs index e52ab5882a..eba68a7f8a 100644 --- a/src/object/dict.rs +++ b/src/data/dict.rs @@ -1,5 +1,5 @@ +use crate::data::{Primitive, Value}; use crate::prelude::*; -use crate::object::{Primitive, Value}; use derive_new::new; use indexmap::IndexMap; use serde::{Deserialize, Serialize}; @@ -64,7 +64,7 @@ impl PartialOrd for Dictionary { impl PartialEq for Dictionary { fn eq(&self, other: &Value) -> bool { match other { - Value::Object(d) => self == d, + Value::Row(d) => self == d, _ => false, } } @@ -123,7 +123,7 @@ impl TaggedListBuilder { } pub fn into_tagged_value(self) -> Tagged { - Value::List(self.list).tagged(self.tag) + Value::Table(self.list).tagged(self.tag) } } @@ -163,7 +163,7 @@ impl TaggedDictBuilder { } pub fn into_tagged_value(self) -> Tagged { - self.into_tagged_dict().map(Value::Object) + self.into_tagged_dict().map(Value::Row) } pub fn into_tagged_dict(self) -> Tagged { diff --git a/src/object/files.rs b/src/data/files.rs similarity index 96% rename from src/object/files.rs rename to src/data/files.rs index ba48aeab9c..47c6ae093f 100644 --- a/src/object/files.rs +++ b/src/data/files.rs @@ -1,5 +1,5 @@ +use crate::data::{TaggedDictBuilder, Value}; use crate::errors::ShellError; -use crate::object::{TaggedDictBuilder, Value}; use crate::prelude::*; #[derive(Debug)] diff --git a/src/object/into.rs b/src/data/into.rs similarity index 92% rename from src/object/into.rs rename to src/data/into.rs index 1d2648a7ad..749ab0601f 100644 --- a/src/object/into.rs +++ b/src/data/into.rs @@ -1,4 +1,4 @@ -use crate::object::{Primitive, Value}; +use crate::data::{Primitive, Value}; use crate::prelude::*; impl From for Value { diff --git a/src/object/meta.rs b/src/data/meta.rs similarity index 100% rename from src/object/meta.rs rename to src/data/meta.rs diff --git a/src/object/operators.rs b/src/data/operators.rs similarity index 100% rename from src/object/operators.rs rename to src/data/operators.rs diff --git a/src/object/process.rs b/src/data/process.rs similarity index 94% rename from src/object/process.rs rename to src/data/process.rs index 337f731b58..cf6a2fb148 100644 --- a/src/object/process.rs +++ b/src/data/process.rs @@ -1,4 +1,4 @@ -use crate::object::{TaggedDictBuilder, Value}; +use crate::data::{TaggedDictBuilder, Value}; use crate::prelude::*; use itertools::join; use sysinfo::ProcessExt; diff --git a/src/object/types.rs b/src/data/types.rs similarity index 100% rename from src/object/types.rs rename to src/data/types.rs diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 0bf31b5e71..ee241583fd 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -1,5 +1,5 @@ +use crate::data::base::Block; use crate::errors::Description; -use crate::object::base::Block; use crate::parser::{ hir::{self, Expression, RawExpression}, CommandRegistry, Text, @@ -66,7 +66,7 @@ pub(crate) fn evaluate_baseline_expr( exprs.push(expr); } - Ok(Value::List(exprs).tagged(Tag::unknown_origin(expr.span()))) + Ok(Value::Table(exprs).tagged(Tag::unknown_origin(expr.span()))) } RawExpression::Block(block) => Ok(Tagged::from_simple_spanned_item( Value::Block(Block::new(block.clone(), source.clone(), expr.span())), diff --git a/src/format/generic.rs b/src/format/generic.rs index 6142b1122b..57726f819c 100644 --- a/src/format/generic.rs +++ b/src/format/generic.rs @@ -1,5 +1,5 @@ +use crate::data::Value; use crate::format::{EntriesView, RenderView, TableView}; -use crate::object::Value; use crate::prelude::*; use derive_new::new; @@ -13,7 +13,7 @@ impl RenderView for GenericView<'_> { fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> { match self.value { Value::Primitive(p) => Ok(host.stdout(&p.format(None))), - Value::List(l) => { + Value::Table(l) => { let view = TableView::from_list(l); if let Some(view) = view { @@ -23,7 +23,7 @@ impl RenderView for GenericView<'_> { Ok(()) } - o @ Value::Object(_) => { + o @ Value::Row(_) => { let view = EntriesView::from_value(o); view.render_view(host)?; Ok(()) diff --git a/src/format/table.rs b/src/format/table.rs index 37ed516686..8deaf30aaf 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -1,5 +1,5 @@ +use crate::data::Value; use crate::format::RenderView; -use crate::object::Value; use crate::prelude::*; use derive_new::new; use textwrap::fill; @@ -45,7 +45,7 @@ impl TableView { for (idx, value) in values.iter().enumerate() { let mut row: Vec<(String, &'static str)> = match value { Tagged { - item: Value::Object(..), + item: Value::Row(..), .. } => headers .iter() diff --git a/src/format/vtable.rs b/src/format/vtable.rs index 08c4a72d0d..fe151224f4 100644 --- a/src/format/vtable.rs +++ b/src/format/vtable.rs @@ -1,5 +1,5 @@ +use crate::data::Value; use crate::format::RenderView; -use crate::object::Value; use crate::prelude::*; use derive_new::new; diff --git a/src/lib.rs b/src/lib.rs index 2e9b8fb061..4e16352e05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +7,12 @@ mod prelude; mod cli; mod commands; mod context; +mod data; mod env; mod errors; mod evaluate; mod format; mod git; -mod object; mod parser; mod plugin; mod shell; @@ -28,10 +28,10 @@ pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder; pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; pub use cli::cli; +pub use data::base::{Primitive, Value}; +pub use data::dict::{Dictionary, TaggedDictBuilder}; +pub use data::meta::{Span, Tag, Tagged, TaggedItem}; pub use errors::{CoerceInto, ShellError}; pub use num_traits::cast::ToPrimitive; -pub use object::base::{Primitive, Value}; -pub use object::dict::{Dictionary, TaggedDictBuilder}; -pub use object::meta::{Span, Tag, Tagged, TaggedItem}; pub use parser::parse::text::Text; pub use parser::registry::{EvaluatedArgs, NamedType, PositionalType, Signature}; diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs index d37df05157..33a23189f1 100644 --- a/src/parser/deserializer.rs +++ b/src/parser/deserializer.rs @@ -37,7 +37,7 @@ impl<'de> ConfigDeserializer<'de> { let value: Option> = if name == "rest" { let positional = self.call.args.slice_from(self.position); self.position += positional.len(); - Some(Value::List(positional).tagged_unknown()) // TODO: correct span + Some(Value::Table(positional).tagged_unknown()) // TODO: correct span } else { if self.call.args.has(name) { self.call.args.get(name).map(|x| x.clone()) @@ -240,14 +240,11 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { trace!(" Extracting {:?} for vec", value.val); match value.val.into_parts() { - (Value::List(items), _) => { + (Value::Table(items), _) => { let de = SeqDeserializer::new(&mut self, items.into_iter()); visitor.visit_seq(de) } - (other, tag) => Err(ShellError::type_error( - "Vec", - other.type_name().tagged(tag), - )), + (other, tag) => Err(ShellError::type_error("Vec", other.type_name().tagged(tag))), } } fn deserialize_tuple(mut self, len: usize, visitor: V) -> Result @@ -255,10 +252,14 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { V: Visitor<'de>, { let value = self.pop(); - trace!(" Extracting {:?} for tuple with {} elements", value.val, len); + trace!( + " Extracting {:?} for tuple with {} elements", + value.val, + len + ); match value.val.into_parts() { - (Value::List(items), _) => { + (Value::Table(items), _) => { let de = SeqDeserializer::new(&mut self, items.into_iter()); visitor.visit_seq(de) } @@ -298,7 +299,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { val: T, name: &'static str, fields: &'static [&'static str], - visitor: V + visitor: V, ) -> Result where T: serde::Serialize, @@ -364,7 +365,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { } => { let i: i64 = int.tagged(value.val.tag).coerce_into("converting to i64")?; visit::, _>(i.tagged(tag), name, fields, visitor) - }, + } Tagged { item: Value::Primitive(Primitive::String(string)), .. @@ -398,21 +399,20 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { } } -struct SeqDeserializer<'a, 'de: 'a, I: Iterator>> { +struct SeqDeserializer<'a, 'de: 'a, I: Iterator>> { de: &'a mut ConfigDeserializer<'de>, vals: I, } -impl<'a, 'de: 'a, I: Iterator>> SeqDeserializer<'a, 'de, I> { +impl<'a, 'de: 'a, I: Iterator>> SeqDeserializer<'a, 'de, I> { fn new(de: &'a mut ConfigDeserializer<'de>, vals: I) -> Self { - SeqDeserializer { - de, - vals, - } + SeqDeserializer { de, vals } } } -impl<'a, 'de: 'a, I: Iterator>> de::SeqAccess<'de> for SeqDeserializer<'a, 'de, I> { +impl<'a, 'de: 'a, I: Iterator>> de::SeqAccess<'de> + for SeqDeserializer<'a, 'de, I> +{ type Error = ShellError; fn next_element_seed(&mut self, seed: T) -> Result, Self::Error> @@ -441,10 +441,7 @@ struct StructDeserializer<'a, 'de: 'a> { impl<'a, 'de: 'a> StructDeserializer<'a, 'de> { fn new(de: &'a mut ConfigDeserializer<'de>, fields: &'static [&'static str]) -> Self { - StructDeserializer { - de, - fields, - } + StructDeserializer { de, fields } } } diff --git a/src/parser/parse/unit.rs b/src/parser/parse/unit.rs index a29ce15b9f..aa19580ac2 100644 --- a/src/parser/parse/unit.rs +++ b/src/parser/parse/unit.rs @@ -1,4 +1,4 @@ -use crate::object::base::Value; +use crate::data::base::Value; use crate::prelude::*; use serde::{Deserialize, Serialize}; use std::str::FromStr; diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 3915cc4566..744003cd74 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -18,7 +18,7 @@ impl Add { fn add(&self, value: Tagged) -> Result, ShellError> { let value_tag = value.tag(); match (value.item, self.value.clone()) { - (obj @ Value::Object(_), Some(v)) => match &self.field { + (obj @ Value::Row(_), Some(v)) => match &self.field { Some(f) => match obj.insert_data_at_path(value_tag, &f, v) { Some(v) => return Ok(v), None => { diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index 15e87d996e..aeda4ba09b 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -18,7 +18,7 @@ impl Edit { fn edit(&self, value: Tagged) -> Result, ShellError> { let value_tag = value.tag(); match (value.item, self.value.clone()) { - (obj @ Value::Object(_), Some(v)) => match &self.field { + (obj @ Value::Row(_), Some(v)) => match &self.field { Some(f) => match obj.replace_data_at_path(value_tag, &f, v) { Some(v) => return Ok(v), None => { diff --git a/src/plugins/embed.rs b/src/plugins/embed.rs index 3c7adb01bf..95140aa609 100644 --- a/src/plugins/embed.rs +++ b/src/plugins/embed.rs @@ -74,7 +74,7 @@ impl Plugin for Embed { root.insert_tagged( self.field.as_ref().unwrap(), Tagged { - item: Value::List(self.values.clone()), + item: Value::Table(self.values.clone()), tag: Tag::unknown(), }, ); diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index ec78dbb622..d75da41428 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -83,7 +83,7 @@ impl Inc { Value::Primitive(Primitive::String(ref s)) => { Ok(Tagged::from_item(self.apply(&s)?, value.tag())) } - Value::Object(_) => match self.field { + Value::Row(_) => match self.field { Some(ref f) => { let replacement = match value.item.get_data_by_path(value.tag(), f) { Some(result) => self.inc(result.map(|x| x.clone()))?, @@ -333,7 +333,7 @@ mod tests { match output[0].as_ref().unwrap() { ReturnSuccess::Value(Tagged { - item: Value::Object(o), + item: Value::Row(o), .. }) => assert_eq!( *o.get_data(&String::from("version")).borrow(), @@ -361,7 +361,7 @@ mod tests { match output[0].as_ref().unwrap() { ReturnSuccess::Value(Tagged { - item: Value::Object(o), + item: Value::Row(o), .. }) => assert_eq!( *o.get_data(&String::from("version")).borrow(), @@ -390,7 +390,7 @@ mod tests { match output[0].as_ref().unwrap() { ReturnSuccess::Value(Tagged { - item: Value::Object(o), + item: Value::Row(o), .. }) => assert_eq!( *o.get_data(&field).borrow(), diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 9b2f73c553..99700449b4 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -135,7 +135,7 @@ impl Str { Value::Primitive(Primitive::String(ref s)) => { Ok(Tagged::from_item(self.apply(&s)?, value.tag())) } - Value::Object(_) => match self.field { + Value::Row(_) => match self.field { Some(ref f) => { let replacement = match value.item.get_data_by_path(value.tag(), f) { Some(result) => self.strutils(result.map(|x| x.clone()))?, @@ -479,7 +479,7 @@ mod tests { match output[0].as_ref().unwrap() { ReturnSuccess::Value(Tagged { - item: Value::Object(o), + item: Value::Row(o), .. }) => assert_eq!( *o.get_data(&String::from("name")).borrow(), @@ -527,7 +527,7 @@ mod tests { match output[0].as_ref().unwrap() { ReturnSuccess::Value(Tagged { - item: Value::Object(o), + item: Value::Row(o), .. }) => assert_eq!( *o.get_data(&String::from("name")).borrow(), @@ -575,7 +575,7 @@ mod tests { match output[0].as_ref().unwrap() { ReturnSuccess::Value(Tagged { - item: Value::Object(o), + item: Value::Row(o), .. }) => assert_eq!( *o.get_data(&String::from("Nu_birthday")).borrow(), @@ -624,7 +624,7 @@ mod tests { match output[0].as_ref().unwrap() { ReturnSuccess::Value(Tagged { - item: Value::Object(o), + item: Value::Row(o), .. }) => assert_eq!( *o.get_data(&String::from("rustconf")).borrow(), @@ -679,7 +679,7 @@ mod tests { match output[0].as_ref().unwrap() { ReturnSuccess::Value(Tagged { - item: Value::Object(o), + item: Value::Row(o), .. }) => assert_eq!( *o.get_data(&String::from("staff")).borrow(), diff --git a/src/plugins/sys.rs b/src/plugins/sys.rs index 41b280ffe4..db7de6e625 100644 --- a/src/plugins/sys.rs +++ b/src/plugins/sys.rs @@ -119,7 +119,7 @@ async fn host(tag: Tag) -> Tagged { user_vec.push(Tagged::from_item(Value::string(user.username()), tag)); } } - let user_list = Value::List(user_vec); + let user_list = Value::Table(user_vec); dict.insert("users", user_list); dict.into_tagged_value() @@ -163,7 +163,7 @@ async fn disks(tag: Tag) -> Option { } if !output.is_empty() { - Some(Value::List(output)) + Some(Value::Table(output)) } else { None } @@ -205,7 +205,7 @@ async fn battery(tag: Tag) -> Option { } if !output.is_empty() { - Some(Value::List(output)) + Some(Value::Table(output)) } else { None } @@ -248,7 +248,7 @@ async fn temp(tag: Tag) -> Option { } if !output.is_empty() { - Some(Value::List(output)) + Some(Value::Table(output)) } else { None } @@ -273,7 +273,7 @@ async fn net(tag: Tag) -> Option { } } if !output.is_empty() { - Some(Value::List(output)) + Some(Value::Table(output)) } else { None } diff --git a/src/plugins/tree.rs b/src/plugins/tree.rs index 19e38b626c..3d571ca18c 100644 --- a/src/plugins/tree.rs +++ b/src/plugins/tree.rs @@ -17,14 +17,14 @@ impl TreeView { Value::Primitive(p) => { let _ = builder.add_empty_child(p.format(None)); } - Value::Object(o) => { + Value::Row(o) => { for (k, v) in o.entries.iter() { builder = builder.begin_child(k.clone()); Self::from_value_helper(v, builder); builder = builder.end_child(); } } - Value::List(l) => { + Value::Table(l) => { for elem in l.iter() { Self::from_value_helper(elem, builder); } diff --git a/src/prelude.rs b/src/prelude.rs index f800dc8cda..a491aff5de 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -55,20 +55,20 @@ pub(crate) use crate::commands::PerItemCommand; pub(crate) use crate::commands::RawCommandArgs; pub(crate) use crate::context::CommandRegistry; pub(crate) use crate::context::{Context, SpanSource}; +pub(crate) use crate::data::base as value; +pub(crate) use crate::data::meta::{Tag, Tagged, TaggedItem}; +pub(crate) use crate::data::types::ExtractType; +pub(crate) use crate::data::{Primitive, Value}; pub(crate) use crate::env::host::handle_unexpected; pub(crate) use crate::env::Host; pub(crate) use crate::errors::{CoerceInto, ShellError}; -pub(crate) use crate::object::base as value; -pub(crate) use crate::object::meta::{Tag, Tagged, TaggedItem}; -pub(crate) use crate::object::types::ExtractType; -pub(crate) use crate::object::{Primitive, Value}; pub(crate) use crate::parser::hir::SyntaxType; pub(crate) use crate::parser::parse::parser::Number; pub(crate) use crate::parser::registry::Signature; pub(crate) use crate::shell::filesystem_shell::FilesystemShell; +pub(crate) use crate::shell::help_shell::HelpShell; pub(crate) use crate::shell::shell_manager::ShellManager; pub(crate) use crate::shell::value_shell::ValueShell; -pub(crate) use crate::shell::help_shell::HelpShell; pub(crate) use crate::stream::{InputStream, OutputStream}; pub(crate) use crate::traits::{HasSpan, ToDebug}; pub(crate) use crate::Span; diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 1bb7796b5d..5b62d329a2 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -4,7 +4,7 @@ use crate::commands::mkdir::MkdirArgs; use crate::commands::mv::MoveArgs; use crate::commands::rm::RemoveArgs; use crate::context::SourceMap; -use crate::object::dir_entry_dict; +use crate::data::dir_entry_dict; use crate::prelude::*; use crate::shell::completer::NuCompleter; use crate::shell::shell::Shell; diff --git a/src/shell/help_shell.rs b/src/shell/help_shell.rs index 59f141b8c0..aebdeff391 100644 --- a/src/shell/help_shell.rs +++ b/src/shell/help_shell.rs @@ -4,7 +4,7 @@ use crate::commands::mkdir::MkdirArgs; use crate::commands::mv::MoveArgs; use crate::commands::rm::RemoveArgs; use crate::context::SourceMap; -use crate::object::{TaggedDictBuilder, command_dict}; +use crate::data::{command_dict, TaggedDictBuilder}; use crate::prelude::*; use crate::shell::shell::Shell; use std::ffi::OsStr; @@ -26,13 +26,16 @@ impl HelpShell { let value = command_dict(registry.get_command(&cmd).unwrap(), Tag::unknown()); spec.insert("name", cmd); - spec.insert("description", value.get_data_by_key("usage").unwrap().as_string().unwrap()); + spec.insert( + "description", + value.get_data_by_key("usage").unwrap().as_string().unwrap(), + ); spec.insert_tagged("details", value); specs.push(spec.into_tagged_value()); } - cmds.insert("help", Value::List(specs)); + cmds.insert("help", Value::Table(specs)); Ok(HelpShell { path: "/help".to_string(), @@ -47,8 +50,10 @@ impl HelpShell { let mut sh = HelpShell::index(®istry)?; if let Tagged { - item: Value::Primitive(Primitive::String(name)), .. - } = cmd { + item: Value::Primitive(Primitive::String(name)), + .. + } = cmd + { sh.set_path(format!("/help/{:}/details", name)); } @@ -76,7 +81,7 @@ impl HelpShell { } match viewed { Tagged { - item: Value::List(l), + item: Value::Table(l), .. } => { for item in l { diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 26dab3583f..84c8294625 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -43,7 +43,7 @@ impl ValueShell { } match viewed { Tagged { - item: Value::List(l), + item: Value::Table(l), .. } => { for item in l { @@ -55,7 +55,7 @@ impl ValueShell { } } - shell_entries + shell_entries } fn members(&self) -> VecDeque> { diff --git a/src/utils.rs b/src/utils.rs index 060b72d7a2..703f277254 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,6 +1,6 @@ +use crate::data::meta::Tagged; +use crate::data::Value; use crate::errors::ShellError; -use crate::object::meta::Tagged; -use crate::object::Value; use std::fmt; use std::ops::Div; use std::path::{Component, Path, PathBuf}; @@ -150,14 +150,14 @@ impl<'a> Iterator for TaggedValueIter<'a> { impl Tagged { fn is_dir(&self) -> bool { match self.item() { - Value::Object(_) | Value::List(_) => true, + Value::Row(_) | Value::Table(_) => true, _ => false, } } fn entries(&self) -> TaggedValueIter<'_> { match self.item() { - Value::Object(o) => { + Value::Row(o) => { let iter = o.entries.iter(); TaggedValueIter::List(iter) } @@ -321,8 +321,8 @@ impl FileStructure { #[cfg(test)] mod tests { use super::{FileStructure, Res, ValueResource, ValueStructure}; - use crate::object::meta::{Tag, Tagged}; - use crate::object::{TaggedDictBuilder, Value}; + use crate::data::meta::{Tag, Tagged}; + use crate::data::{TaggedDictBuilder, Value}; use pretty_assertions::assert_eq; use std::path::PathBuf; From b14fd12e4791c5a9bc022575292580f50e9fc456 Mon Sep 17 00:00:00 2001 From: est31 Date: Fri, 6 Sep 2019 10:34:31 +0200 Subject: [PATCH 021/342] Update rust-argon2 in Cargo.lock Rids us of crossbeam v0.5 and lots of other crates. For most users this only effects Cargo.lock though, as rust-argon2 is only compiled when targeting redox. --- Cargo.lock | 150 ++--------------------------------------------------- 1 file changed, 4 insertions(+), 146 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b8ec4ee79c..08fcb94a0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -333,21 +333,6 @@ dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-channel" version = "0.3.9" @@ -356,41 +341,6 @@ dependencies = [ "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam-deque" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "crossbeam-utils" version = "0.5.0" @@ -1349,15 +1299,6 @@ name = "linked-hash-map" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "lock_api" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "log" version = "0.4.8" @@ -1426,19 +1367,6 @@ dependencies = [ "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "memoffset" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "memoffset" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "mime" version = "0.3.13" @@ -1800,35 +1728,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "owning_ref" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "parking_lot_core" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "percent-encoding" version = "2.1.0" @@ -1997,18 +1896,6 @@ dependencies = [ "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rand" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rand" version = "0.6.5" @@ -2189,7 +2076,7 @@ dependencies = [ "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", - "rust-argon2 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2263,12 +2150,12 @@ dependencies = [ [[package]] name = "rust-argon2" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "blake2b_simd 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crossbeam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2337,16 +2224,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "scopeguard" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" - -[[package]] -name = "scopeguard" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "semver" version = "0.9.0" @@ -2534,11 +2411,6 @@ name = "sourcefile" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "stable_deref_trait" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "stackvector" version = "1.0.6" @@ -3136,11 +3008,7 @@ dependencies = [ "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" -"checksum crossbeam 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d1c92ff2d7a202d592f5a412d75cf421495c913817781c1cb383bf12a77e185f" "checksum crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c8ec7fcd21571dc78f96cc96243cab8d8f035247c3efd16c687be154c3fa9efa" -"checksum crossbeam-deque 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)" = "05e44b8cf3e1a625844d1750e1f7820da46044ff6d28f4d43e455ba3e5bb2c13" -"checksum crossbeam-epoch 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2449aaa4ec7ef96e5fb24db16024b935df718e9ae1cec0a1e68feeca2efca7b8" -"checksum crossbeam-epoch 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fedcd6772e37f3da2a9af9bf12ebe046c0dfe657992377b4df982a2b54cd37a9" "checksum crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "677d453a17e8bd2b913fa38e8b9cf04bcdbb5be790aa294f2389661d72036015" "checksum crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)" = "04973fa96e96579258a5091af6003abde64af786b860f18622b82e026cca60e6" "checksum crossterm 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9abce7d7c50e9823ea0c0dbeb8f16d7e247af06d75b4c6244ea0a0998b3a6f35" @@ -3240,7 +3108,6 @@ dependencies = [ "checksum line-wrap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f30344350a2a51da54c1d53be93fade8a237e545dbcc4bdbe635413f2117cab9" "checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" -"checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" @@ -3251,8 +3118,6 @@ dependencies = [ "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e6bcd6433cff03a4bfc3d9834d504467db1f1cf6d0ea765d37d330249ed629d" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" -"checksum memoffset 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ce6075db033bbbb7ee5a0bbd3a3186bbae616f57fb001c485c7ff77955f8177f" "checksum mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425" "checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" "checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" @@ -3285,9 +3150,6 @@ dependencies = [ "checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" -"checksum owning_ref 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "49a4b8ea2179e6a2e27411d3bca09ca6dd630821cf6894c6c7c8467a8ee7ef13" -"checksum parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f0802bff09003b291ba756dc7e79313e51cc31667e94afbe847def490424cde5" -"checksum parking_lot_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ad7f7e6ebdc79edff6fdcb87a55b620174f7a989e3eb31b65231f4af57f00b8c" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" "checksum pin-project 0.4.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e7dd6a2ad14b55463a4b80ca7b6c3b373921310b61fcb3de5455ad2dea21f7" @@ -3308,7 +3170,6 @@ dependencies = [ "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" "checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" "checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" @@ -3337,7 +3198,7 @@ dependencies = [ "checksum result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560" "checksum roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "153c367ce9fb8ef7afe637ef92bd083ba0f88b03ef3fcf0287d40be05ae0a61c" "checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051" -"checksum rust-argon2 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81ed8d04228b44a740c8d46ff872a28e50fff3d659f307ab4da2cc502e019ff3" +"checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" "checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" @@ -3347,8 +3208,6 @@ dependencies = [ "checksum safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e133ccc4f4d1cd4f89cc8a7ff618287d56dc7f638b8e38fc32c5fdcadc339dd5" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" "checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" -"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" -"checksum scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" @@ -3371,7 +3230,6 @@ dependencies = [ "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" -"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum stackvector 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1c4725650978235083241fab0fdc8e694c3de37821524e7534a1a9061d1068af" "checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" From 085973e2dbba18e80acfde545b98d2fd6fed7838 Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Fri, 6 Sep 2019 17:40:48 +0700 Subject: [PATCH 022/342] Update main.workflow --- .github/main.workflow | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 .github/main.workflow diff --git a/.github/main.workflow b/.github/main.workflow new file mode 100644 index 0000000000..cbbe6aea81 --- /dev/null +++ b/.github/main.workflow @@ -0,0 +1,8 @@ +workflow "New workflow" { + resolves = ["GitHub Action for Docker"] + on = "push" +} + +action "GitHub Action for Docker" { + uses = "actions/docker/cli@fe7ed3ce992160973df86480b83a2f8ed581cd50" +} From 19f97e6471c6b0d34ff87a5f04442c141ba5797c Mon Sep 17 00:00:00 2001 From: Artem Polishchuk Date: Fri, 6 Sep 2019 23:05:35 +0300 Subject: [PATCH 023/342] Add Fedora installation info --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 7d38fcdc82..e80a51e600 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,12 @@ $ docker run -it nushell/nu The second container is a bit smaller, if size is important to you. +## Packaging status + +### Fedora + +[COPR repo](https://copr.fedorainfracloud.org/coprs/atim/nushell/): `sudo dnf copr enable atim/nushell -y && sudo dnf install nushell -y` + # Philosophy Nu draws inspiration from projects like PowerShell, functional programming languages, and modern cli tools. Rather than thinking of files 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 is a table of rows, where each row represents an item in that directory. These values can be piped through a series of steps, in a series of commands called a 'pipeline'. From 1b2fdf7c1eb6666c74ef5b7700f87de6219a833e Mon Sep 17 00:00:00 2001 From: Patrick Meredith Date: Fri, 6 Sep 2019 23:20:13 -0400 Subject: [PATCH 024/342] Fix bug with ls globbing a single directory --- src/shell/filesystem_shell.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 5b62d329a2..c2bd9e5b9a 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -106,8 +106,7 @@ impl Shell for FilesystemShell { if entries.len() == 1 { if let Ok(entry) = &entries[0] { if entry.is_dir() { - let entries = std::fs::read_dir(&full_path); - + let entries = std::fs::read_dir(&entry); let entries = match entries { Err(e) => { if let Some(s) = args.nth(0) { @@ -126,6 +125,7 @@ impl Shell for FilesystemShell { } Ok(o) => o, }; + for entry in entries { let entry = entry?; let filepath = entry.path(); From ea24571c22d3cc31fbc07b2b7589a9832a321a65 Mon Sep 17 00:00:00 2001 From: Patrick Meredith Date: Fri, 6 Sep 2019 23:24:29 -0400 Subject: [PATCH 025/342] Remove added newline --- src/shell/filesystem_shell.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index c2bd9e5b9a..10ca864882 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -125,7 +125,6 @@ impl Shell for FilesystemShell { } Ok(o) => o, }; - for entry in entries { let entry = entry?; let filepath = entry.path(); From e2b9370f108dcd657d5fe29501289e6638783457 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 7 Sep 2019 16:59:13 +1200 Subject: [PATCH 026/342] Attempt to fix issue with ^C in Windows This fixes the error case if we ^C during running an external command. This needs testing across platforms before it lands. --- src/commands/classified.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 94e5656001..959a8f6125 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -342,7 +342,19 @@ impl ExternalCommand { match stream_next { StreamNext::Last => { - popen.wait()?; + let _ = popen.detach(); + loop { + match popen.poll() { + None => { + let _ = std::thread::sleep(std::time::Duration::new(0, 100000000)); + } + _ => { + let _ = popen.terminate(); + break; + } + } + } + println!(""); Ok(ClassifiedInputStream::new()) } StreamNext::External => { From 28fe31d5651c77efe57ff369459a8d0e2a9143b2 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 7 Sep 2019 19:32:07 +1200 Subject: [PATCH 027/342] Protect autoview against missing plugins --- src/commands/autoview.rs | 58 ++++++++++++++++++++-------------------- src/commands/command.rs | 4 +++ 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 844a093c4a..f970d23e4f 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -43,17 +43,36 @@ pub fn autoview( .. } = input[0usize] { - let binary = context.expect_command("binaryview"); - let result = binary.run(raw.with_input(input), &context.commands); - result.collect::>().await; + let binary = context.get_command("binaryview"); + if let Some(binary) = binary { + let result = binary.run(raw.with_input(input), &context.commands); + result.collect::>().await; + } else { + for i in input { + match i.item { + Value::Binary(b) => { + use pretty_hex::*; + println!("{:?}", b.hex_dump()); + } + _ => {} + } + } + }; } else if is_single_text_value(&input) { - let text = context.expect_command("textview"); - let result = text.run(raw.with_input(input), &context.commands); - result.collect::>().await; - } else if equal_shapes(&input) { - let table = context.expect_command("table"); - let result = table.run(raw.with_input(input), &context.commands); - result.collect::>().await; + let text = context.get_command("textview"); + if let Some(text) = text { + let result = text.run(raw.with_input(input), &context.commands); + result.collect::>().await; + } else { + for i in input { + match i.item { + Value::Primitive(Primitive::String(s)) => { + println!("{}", s); + } + _ => {} + } + } + } } else { let table = context.expect_command("table"); let result = table.run(raw.with_input(input), &context.commands); @@ -63,25 +82,6 @@ pub fn autoview( })) } -fn equal_shapes(input: &Vec>) -> bool { - let mut items = input.iter(); - - let item = match items.next() { - Some(item) => item, - None => return false, - }; - - let desc = item.data_descriptors(); - - for item in items { - if desc != item.data_descriptors() { - return false; - } - } - - true -} - fn is_single_text_value(input: &Vec>) -> bool { if input.len() != 1 { return false; diff --git a/src/commands/command.rs b/src/commands/command.rs index a79e797fd7..a719f33b7e 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -236,6 +236,10 @@ impl RunnableContext { .get_command(name) .expect(&format!("Expected command {}", name)) } + + pub fn get_command(&self, name: &str) -> Option> { + self.commands.get_command(name) + } } pub struct RunnablePerItemArgs { From ee301f9f54500917dc2fa1f3ccac27346c7cdc0e Mon Sep 17 00:00:00 2001 From: Pradeep Chhetri Date: Sat, 7 Sep 2019 23:49:15 +0800 Subject: [PATCH 028/342] Adds pwd command --- src/cli.rs | 1 + src/commands.rs | 2 ++ src/commands/pwd.rs | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 src/commands/pwd.rs diff --git a/src/cli.rs b/src/cli.rs index 66572d3d8e..706b767024 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -164,6 +164,7 @@ pub async fn cli() -> Result<(), Box> { context.add_commands(vec![ whole_stream_command(PS), + whole_stream_command(PWD), whole_stream_command(LS), whole_stream_command(CD), whole_stream_command(Size), diff --git a/src/commands.rs b/src/commands.rs index cd44fbb09b..e0e1c80e5a 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -39,6 +39,7 @@ pub(crate) mod plugin; pub(crate) mod post; pub(crate) mod prev; pub(crate) mod ps; +pub(crate) mod pwd; pub(crate) mod reject; pub(crate) mod reverse; pub(crate) mod rm; @@ -104,6 +105,7 @@ pub(crate) use pick::Pick; pub(crate) use post::Post; pub(crate) use prev::Previous; pub(crate) use ps::PS; +pub(crate) use pwd::PWD; pub(crate) use reject::Reject; pub(crate) use reverse::Reverse; pub(crate) use rm::Remove; diff --git a/src/commands/pwd.rs b/src/commands/pwd.rs new file mode 100644 index 0000000000..2fd88ce8b1 --- /dev/null +++ b/src/commands/pwd.rs @@ -0,0 +1,44 @@ +use crate::commands::WholeStreamCommand; +use crate::errors::ShellError; +use crate::data::{Dictionary, Value}; +use crate::parser::registry::Signature; +use crate::prelude::*; +use indexmap::IndexMap; + +pub struct PWD; + +impl WholeStreamCommand for PWD { + fn name(&self) -> &str { + "pwd" + } + + fn signature(&self) -> Signature { + Signature::build("pwd") + } + + fn usage(&self) -> &str { + "Output the current working directory." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + pwd(args, registry) + } +} + +pub fn pwd(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let span = args.call_info.name_span; + + let mut indexmap = IndexMap::new(); + indexmap.insert( + "name".to_string(), + Tagged::from_simple_spanned_item(Value::string(std::env::current_dir()?.to_string_lossy()), span), + ); + + let value = Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span); + Ok(OutputStream::one(value)) +} From 7913ae76f8e20a3b957c64a565d0d43fcd2d44e7 Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Sat, 7 Sep 2019 15:31:16 -0700 Subject: [PATCH 029/342] Expand pwd command Expand functionality of the pwd command to better handle the different types of shells (e.g. FilesystemShell, ValueShell, etc.). --- src/commands/command.rs | 2 +- src/commands/pwd.rs | 16 ++++------------ src/shell/filesystem_shell.rs | 22 ++++++++++++++++++++++ src/shell/help_shell.rs | 4 ++++ src/shell/shell.rs | 1 + src/shell/shell_manager.rs | 6 ++++++ src/shell/value_shell.rs | 12 ++++++++++++ 7 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/commands/command.rs b/src/commands/command.rs index 922f647fcc..6be179895b 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,7 +1,7 @@ use crate::context::{SourceMap, SpanSource}; +use crate::data::Value; use crate::errors::ShellError; use crate::evaluate::Scope; -use crate::data::Value; use crate::parser::hir; use crate::parser::{registry, ConfigDeserializer}; use crate::prelude::*; diff --git a/src/commands/pwd.rs b/src/commands/pwd.rs index 2fd88ce8b1..aa56c51653 100644 --- a/src/commands/pwd.rs +++ b/src/commands/pwd.rs @@ -1,9 +1,9 @@ use crate::commands::WholeStreamCommand; +// use crate::data::{Dictionary, Value}; use crate::errors::ShellError; -use crate::data::{Dictionary, Value}; use crate::parser::registry::Signature; use crate::prelude::*; -use indexmap::IndexMap; +// use indexmap::IndexMap; pub struct PWD; @@ -30,15 +30,7 @@ impl WholeStreamCommand for PWD { } pub fn pwd(args: CommandArgs, registry: &CommandRegistry) -> Result { + let shell_manager = args.shell_manager.clone(); let args = args.evaluate_once(registry)?; - let span = args.call_info.name_span; - - let mut indexmap = IndexMap::new(); - indexmap.insert( - "name".to_string(), - Tagged::from_simple_spanned_item(Value::string(std::env::current_dir()?.to_string_lossy()), span), - ); - - let value = Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span); - Ok(OutputStream::one(value)) + shell_manager.pwd(args) } diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 10ca864882..cd9d243502 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -946,6 +946,28 @@ impl Shell for FilesystemShell { self.path.clone() } + fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + let path = PathBuf::from(self.path()); + let p = match dunce::canonicalize(path.as_path()) { + Ok(p) => p, + Err(_) => { + return Err(ShellError::labeled_error( + "unable to show current directory", + "pwd command failed", + args.call_info.name_span, + )); + } + }; + + let mut stream = VecDeque::new(); + stream.push_back(ReturnSuccess::value( + Value::Primitive(Primitive::String(p.to_string_lossy().to_string())) + .simple_spanned(args.call_info.name_span), + )); + + Ok(stream.into()) + } + fn set_path(&mut self, path: String) { let pathbuf = PathBuf::from(&path); let path = match dunce::canonicalize(pathbuf.as_path()) { diff --git a/src/shell/help_shell.rs b/src/shell/help_shell.rs index aebdeff391..35c939d7d4 100644 --- a/src/shell/help_shell.rs +++ b/src/shell/help_shell.rs @@ -117,6 +117,10 @@ impl Shell for HelpShell { self.path.clone() } + fn pwd(&self, _: EvaluatedWholeStreamCommandArgs) -> Result { + Ok(OutputStream::empty()) + } + fn set_path(&mut self, path: String) { let _ = std::env::set_current_dir(&path); self.path = path.clone(); diff --git a/src/shell/shell.rs b/src/shell/shell.rs index dc1f104b6f..549aa79d22 100644 --- a/src/shell/shell.rs +++ b/src/shell/shell.rs @@ -20,6 +20,7 @@ pub trait Shell: std::fmt::Debug { fn mv(&self, args: MoveArgs, name: Span, path: &str) -> Result; fn rm(&self, args: RemoveArgs, name: Span, path: &str) -> Result; fn path(&self) -> String; + fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn set_path(&mut self, path: String); fn complete( diff --git a/src/shell/shell_manager.rs b/src/shell/shell_manager.rs index 136d9b0173..53984c9509 100644 --- a/src/shell/shell_manager.rs +++ b/src/shell/shell_manager.rs @@ -62,6 +62,12 @@ impl ShellManager { self.shells.lock().unwrap()[self.current_shell].path() } + pub fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + let env = self.shells.lock().unwrap(); + + env[self.current_shell].pwd(args) + } + pub fn set_path(&mut self, path: String) { self.shells.lock().unwrap()[self.current_shell].set_path(path) } diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 84c8294625..e14d5603a6 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -199,6 +199,18 @@ impl Shell for ValueShell { self.path.clone() } + fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + let path = PathBuf::from(&self.path()); + + let mut stream = VecDeque::new(); + stream.push_back(ReturnSuccess::value( + Value::Primitive(Primitive::String(path.to_string_lossy().to_string())) + .simple_spanned(args.call_info.name_span), + )); + + Ok(stream.into()) + } + fn set_path(&mut self, path: String) { let _ = std::env::set_current_dir(&path); self.path = path.clone(); From 7427ea51dff067cc14d1c312befb2f92a45115fc Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Sat, 7 Sep 2019 15:43:30 -0700 Subject: [PATCH 030/342] Removed commented out code. --- src/commands/pwd.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/commands/pwd.rs b/src/commands/pwd.rs index aa56c51653..37e2668bdb 100644 --- a/src/commands/pwd.rs +++ b/src/commands/pwd.rs @@ -1,9 +1,7 @@ use crate::commands::WholeStreamCommand; -// use crate::data::{Dictionary, Value}; use crate::errors::ShellError; use crate::parser::registry::Signature; use crate::prelude::*; -// use indexmap::IndexMap; pub struct PWD; From 4cdaed1ad4cdc6d91933e504e6e1f02c6ffce2ac Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 8 Sep 2019 11:43:53 +1200 Subject: [PATCH 031/342] Add echo command --- src/cli.rs | 1 + src/commands.rs | 2 ++ src/commands/echo.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 75 insertions(+) create mode 100644 src/commands/echo.rs diff --git a/src/cli.rs b/src/cli.rs index 706b767024..cfd9d8de6e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -210,6 +210,7 @@ pub async fn cli() -> Result<(), Box> { per_item_command(Open), per_item_command(Post), per_item_command(Where), + per_item_command(Echo), whole_stream_command(Config), whole_stream_command(SkipWhile), per_item_command(Enter), diff --git a/src/commands.rs b/src/commands.rs index e0e1c80e5a..f2885f369f 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -11,6 +11,7 @@ pub(crate) mod config; pub(crate) mod cp; pub(crate) mod date; pub(crate) mod debug; +pub(crate) mod echo; pub(crate) mod enter; pub(crate) mod exit; pub(crate) mod fetch; @@ -76,6 +77,7 @@ pub(crate) use config::Config; pub(crate) use cp::Cpy; pub(crate) use date::Date; pub(crate) use debug::Debug; +pub(crate) use echo::Echo; pub(crate) use enter::Enter; pub(crate) use exit::Exit; pub(crate) use fetch::Fetch; diff --git a/src/commands/echo.rs b/src/commands/echo.rs new file mode 100644 index 0000000000..f464630de7 --- /dev/null +++ b/src/commands/echo.rs @@ -0,0 +1,72 @@ +use crate::data::Value; +use crate::errors::ShellError; +use crate::prelude::*; + +use crate::parser::registry::Signature; + +pub struct Echo; + +impl PerItemCommand for Echo { + fn name(&self) -> &str { + "echo" + } + + fn signature(&self) -> Signature { + Signature::build("echo").rest(SyntaxType::Any) + } + + fn usage(&self) -> &str { + "Echo the argments back to the user." + } + + fn run( + &self, + call_info: &CallInfo, + registry: &CommandRegistry, + raw_args: &RawCommandArgs, + _input: Tagged, + ) -> Result { + run(call_info, registry, raw_args) + } +} + +fn run( + call_info: &CallInfo, + _registry: &CommandRegistry, + _raw_args: &RawCommandArgs, +) -> Result { + let name = call_info.name_span; + + let mut output = String::new(); + + let mut first = true; + + if let Some(ref positional) = call_info.args.positional { + for i in positional { + match i.as_string() { + Ok(s) => { + if !first { + output.push_str(" "); + } else { + first = false; + } + + output.push_str(&s); + } + _ => { + return Err(ShellError::labeled_error( + "Expect a string from pipeline", + "not a string-compatible value", + i.span(), + )); + } + } + } + } + + let stream = VecDeque::from(vec![Ok(ReturnSuccess::Value( + Value::string(output).simple_spanned(name), + ))]); + + Ok(stream.to_output_stream()) +} From 84628f298d55618ba13eaa201356c23679353916 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 8 Sep 2019 13:35:02 +1200 Subject: [PATCH 032/342] Finish fixing failing tests. --- src/commands/autoview.rs | 28 +++++++++++++++++++++++++++- tests/filters_test.rs | 8 ++++---- tests/tests.rs | 8 ++++++-- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index f970d23e4f..adce1df402 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -58,7 +58,7 @@ pub fn autoview( } } }; - } else if is_single_text_value(&input) { + } else if is_single_origined_text_value(&input) { let text = context.get_command("textview"); if let Some(text) = text { let result = text.run(raw.with_input(input), &context.commands); @@ -73,6 +73,15 @@ pub fn autoview( } } } + } else if is_single_text_value(&input) { + for i in input { + match i.item { + Value::Primitive(Primitive::String(s)) => { + println!("{}", s); + } + _ => {} + } + } } else { let table = context.expect_command("table"); let result = table.run(raw.with_input(input), &context.commands); @@ -96,3 +105,20 @@ fn is_single_text_value(input: &Vec>) -> bool { false } } + +fn is_single_origined_text_value(input: &Vec>) -> bool { + if input.len() != 1 { + return false; + } + if let Tagged { + item: Value::Primitive(Primitive::String(_)), + tag: Tag { + origin: Some(_), .. + }, + } = input[0] + { + true + } else { + false + } +} diff --git a/tests/filters_test.rs b/tests/filters_test.rs index b44ac5c47d..b08115a9c0 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -35,7 +35,7 @@ fn converts_structured_table_to_csv_text() { | to-csv | lines | nth 1 - | echo "$it" + | echo $it "# )); @@ -63,7 +63,7 @@ fn converts_structured_table_to_csv_text_skipping_headers_after_conversion() { | split-column "," a b c d origin | last 1 | to-csv --headerless - | echo "$it" + | echo $it "# )); @@ -261,7 +261,7 @@ fn converts_structured_table_to_tsv_text() { | to-tsv | lines | nth 1 - | echo "$it" + | echo $it "# )); @@ -289,7 +289,7 @@ fn converts_structured_table_to_tsv_text_skipping_headers_after_conversion() { | split-column "\t" a b c d origin | last 1 | to-tsv --headerless - | echo "$it" + | echo $it "# )); diff --git a/tests/tests.rs b/tests/tests.rs index 4affb44223..f3f810eb09 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -12,9 +12,13 @@ fn pipeline_helper() { | str --to-int | sum | echo "$it" - "#); + "#, + ); - assert_eq!(actual, r#"open los_tres_amigos.txt | from-csv | get rusty_luck | str --to-int | sum | echo "$it""#); + assert_eq!( + actual, + r#"open los_tres_amigos.txt | from-csv | get rusty_luck | str --to-int | sum | echo "$it""# + ); } #[test] From 9da896ad4e8ed9fc65dadc312b0c6322ada59201 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 8 Sep 2019 14:00:04 +1200 Subject: [PATCH 033/342] Attempt so simplify classified --- src/commands/classified.rs | 135 ++++++++++++------------------------- tests/helpers/mod.rs | 31 +++------ tests/tests.rs | 4 +- 3 files changed, 52 insertions(+), 118 deletions(-) diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 959a8f6125..277a925341 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -220,111 +220,60 @@ impl ExternalCommand { let mut process; - #[cfg(windows)] - { - process = Exec::shell(&self.name); + process = Exec::cmd(&self.name); - 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 row 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); - } else { - first = false; - } + 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.chars().all(|c| c.is_whitespace()) { - continue; + if arg.item.contains("$it") { + span = Some(arg.span()); } - - process = process.arg(&arg.replace("$it", &i.as_string()?)); + } + if let Some(span) = span { + return Err(ShellError::labeled_error( + "External $it needs string data", + "given row instead of string data", + span, + )); + } else { + return Err(ShellError::string("Error: $it needs string data")); } } - } else { + if !first { + process = process.arg("&&"); + process = process.arg(&self.name); + } else { + first = false; + } + for arg in &self.args { - let arg_chars: Vec<_> = arg.chars().collect(); - if arg_chars.len() > 1 - && arg_chars[0] == '"' - && arg_chars[arg_chars.len() - 1] == '"' - { - // quoted string - let new_arg: String = arg_chars[1..arg_chars.len() - 1].iter().collect(); - process = process.arg(new_arg); - } else { - process = process.arg(arg.item.clone()); + if arg.chars().all(|c| c.is_whitespace()) { + continue; } + + process = process.arg(&arg.replace("$it", &i.as_string()?)); + } + } + } else { + for arg in &self.args { + let arg_chars: Vec<_> = arg.chars().collect(); + if arg_chars.len() > 1 + && arg_chars[0] == '"' + && arg_chars[arg_chars.len() - 1] == '"' + { + // quoted string + let new_arg: String = arg_chars[1..arg_chars.len() - 1].iter().collect(); + process = process.arg(new_arg); + } else { + process = process.arg(arg.item.clone()); } } } - #[cfg(not(windows))] - { - let mut new_arg_string = self.name.to_string(); - if arg_string.contains("$it") { - let mut first = true; - for i in &inputs { - let i = match i.as_string() { - Err(_err) => { - let mut span = name_span; - for arg in &self.args { - if arg.item.contains("$it") { - span = arg.span(); - } - } - return Err(ShellError::labeled_error( - "External $it needs string data", - "given row instead of string data", - span, - )); - } - Ok(val) => val, - }; - - if !first { - new_arg_string.push_str("&&"); - new_arg_string.push_str(&self.name); - } else { - first = false; - } - - for arg in &self.args { - if arg.chars().all(|c| c.is_whitespace()) { - continue; - } - - new_arg_string.push_str(" "); - new_arg_string.push_str(&arg.replace("$it", &i)); - } - } - } else { - for arg in &self.args { - new_arg_string.push_str(" "); - new_arg_string.push_str(&arg); - } - } - - process = Exec::shell(new_arg_string); - } process = process.cwd(context.shell_manager.path()); let mut process = match stream_next { diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index c27738c79a..6aa7cb67b9 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -310,8 +310,7 @@ pub fn file_contents(full_path: impl AsRef) -> String { pub fn file_contents_binary(full_path: impl AsRef) -> Vec { let mut file = std::fs::File::open(full_path.as_ref()).expect("can not open file"); let mut contents = Vec::new(); - file.read_to_end(&mut contents) - .expect("can not read file"); + file.read_to_end(&mut contents).expect("can not read file"); contents } @@ -327,18 +326,6 @@ pub fn line_ending() -> String { } } -pub fn normalize_string(input: &str) -> String { - #[cfg(windows)] - { - input.to_string() - } - - #[cfg(not(windows))] - { - format!("\"{}\"", input) - } -} - pub fn create_file_at(full_path: impl AsRef) -> Result<(), std::io::Error> { let full_path = full_path.as_ref(); @@ -377,13 +364,13 @@ pub fn in_directory(str: impl AsRef) -> String { str.as_ref().display().to_string() } - pub fn pipeline(commands: &str) -> String { - commands.lines() - .skip(1) - .map(|line| line.trim()) - .collect::>() - .join(" ") - .trim_end() - .to_string() + commands + .lines() + .skip(1) + .map(|line| line.trim()) + .collect::>() + .join(" ") + .trim_end() + .to_string() } diff --git a/tests/tests.rs b/tests/tests.rs index f3f810eb09..25337edb09 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -38,9 +38,7 @@ fn external_has_correct_quotes() { r#"echo "hello world""# ); - let actual = h::normalize_string(&actual); - - assert_eq!(actual, r#""hello world""#); + assert_eq!(actual, r#"hello world"#); } #[test] From 159cf27e39322e0c3b28beec9214b972dc8757a2 Mon Sep 17 00:00:00 2001 From: Odin Dutton Date: Sun, 8 Sep 2019 15:10:08 +1000 Subject: [PATCH 034/342] Implement `cd -` to return to the last path for the FilesystemShell --- src/shell/filesystem_shell.rs | 25 +++++++++++++++++++++++-- tests/command_cd_tests.rs | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 1bb7796b5d..5908784640 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -15,6 +15,7 @@ use std::path::{Path, PathBuf}; pub struct FilesystemShell { pub(crate) path: String, + last_path: String, completer: NuCompleter, hinter: HistoryHinter, } @@ -29,6 +30,7 @@ impl Clone for FilesystemShell { fn clone(&self) -> Self { FilesystemShell { path: self.path.clone(), + last_path: self.path.clone(), completer: NuCompleter { file_completer: FilenameCompleter::new(), commands: self.completer.commands.clone(), @@ -44,6 +46,7 @@ impl FilesystemShell { Ok(FilesystemShell { path: path.to_string_lossy().to_string(), + last_path: path.to_string_lossy().to_string(), completer: NuCompleter { file_completer: FilenameCompleter::new(), commands, @@ -56,8 +59,10 @@ impl FilesystemShell { path: String, commands: CommandRegistry, ) -> Result { + let last_path = path.clone(); Ok(FilesystemShell { path, + last_path, completer: NuCompleter { file_completer: FilenameCompleter::new(), commands, @@ -182,14 +187,29 @@ impl Shell for FilesystemShell { Some(v) => { let target = v.as_path()?; let path = PathBuf::from(self.path()); - match dunce::canonicalize(path.join(target).as_path()) { + match dunce::canonicalize(path.join(&target).as_path()) { Ok(p) => p, Err(_) => { - return Err(ShellError::labeled_error( + let error = Err(ShellError::labeled_error( "Can not change to directory", "directory not found", v.span().clone(), )); + + if let Some(t) = target.to_str() { + if t == "-" { + match dunce::canonicalize(PathBuf::from(self.last_path.clone()).as_path()) { + Ok(p) => p, + Err(_) => { + return error; + } + } + } else { + return error; + } + } else { + return error; + } } } } @@ -959,6 +979,7 @@ impl Shell for FilesystemShell { pathbuf } }; + self.last_path = self.path.clone(); self.path = path.to_string_lossy().to_string(); } diff --git a/tests/command_cd_tests.rs b/tests/command_cd_tests.rs index 216bcc8c80..7c1a9f6b85 100644 --- a/tests/command_cd_tests.rs +++ b/tests/command_cd_tests.rs @@ -1,5 +1,7 @@ mod helpers; +use helpers::Playground; + #[test] fn cd_directory_not_found() { let actual = nu_error!( @@ -10,3 +12,35 @@ fn cd_directory_not_found() { assert!(actual.contains("dir_that_does_not_exist")); assert!(actual.contains("directory not found")); } + +#[test] +fn cd_back() { + Playground::setup("cd_test_back", |dirs, sandbox| { + sandbox + .mkdir("andres") + .mkdir("odin"); + + let odin = dirs.test().join("odin"); + let andres = dirs.test().join("andres"); + + nu!( + cwd: dirs.test(), + r#" + cd odin + mkdir a + cd ../andres + mkdir b + cd - + mkdir c + mkdir - + cd - + mkdir d + "# + ); + + assert!(odin.join("a").exists()); + assert!(andres.join("b").exists()); + assert!(odin.join("c").exists()); + assert!(odin.join("-").join("d").exists()); + }) +} From 448b1a4848af2125b7bcc92e323802373c6ed8e5 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 8 Sep 2019 19:06:15 +1200 Subject: [PATCH 035/342] Make some plugins optional, move ps to plugin --- Cargo.toml | 37 ++++++++++++++------- src/cli.rs | 1 - src/commands.rs | 2 -- src/commands/ps.rs | 76 ------------------------------------------- src/plugins/ps.rs | 80 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 106 insertions(+), 90 deletions(-) delete mode 100644 src/commands/ps.rs create mode 100644 src/plugins/ps.rs diff --git a/Cargo.toml b/Cargo.toml index dee6503e50..f9e13c9348 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,6 @@ git2 = { version = "0.10.0", default_features = false } dirs = "2.0.2" glob = "0.3.0" ctrlc = "3.1.3" -ptree = "0.2" surf = "1.0.2" url = "2.1.0" roxmltree = "0.7.0" @@ -62,29 +61,36 @@ subprocess = "0.1.18" mime = "0.3.13" regex = "1.2.1" pretty-hex = "0.1.0" -neso = { version = "0.5.0", optional = true } hex = "0.3.2" -crossterm = "0.10.2" tempfile = "3.1.0" -image = { version = "0.22.1", default_features = false, features = ["png_codec", "jpeg"] } semver = "0.9.0" -uuid = {version = "0.7.4", features = [ "v4", "serde" ]} -syntect = "3.2.0" -onig_sys = "=69.1.0" -heim = "0.0.7" which = "2.0.1" -battery = "0.7.4" +uuid = {version = "0.7.4", features = [ "v4", "serde" ]} 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" futures-timer = "0.3.0" pin-utils = "0.1.0-alpha.4" num-bigint = { version = "0.2.2", features = ["serde"] } bigdecimal = { version = "0.1.0", features = ["serde"] } +neso = { version = "0.5.0", optional = true } +crossterm = { version = "0.10.2", optional = true } +syntect = {version = "3.2.0", optional = true } +onig_sys = {version = "=69.1.0", optional = true } +heim = {version = "0.0.7", optional = true } +battery = {version = "0.7.4", optional = true } +rawkey = {version = "0.1.2", optional = true } +clipboard = {version = "0.5", optional = true } +ptree = {version = "0.2", optional = true } +image = { version = "0.22.1", default_features = false, features = ["png_codec", "jpeg"], optional = true } + [features] raw-key = ["rawkey", "neso"] +textview = ["syntect", "onig_sys", "crossterm"] +binaryview = ["image", "crossterm"] +sys = ["heim", "battery"] +ps = ["heim"] +all = ["raw-key", "textview", "binaryview", "sys", "ps", "clipboard", "ptree"] [dependencies.rusqlite] version = "0.20.0" @@ -128,18 +134,27 @@ path = "src/plugins/skip.rs" [[bin]] name = "nu_plugin_sys" path = "src/plugins/sys.rs" +required-features = ["sys"] + +[[bin]] +name = "nu_plugin_ps" +path = "src/plugins/ps.rs" +required-features = ["ps"] [[bin]] name = "nu_plugin_tree" path = "src/plugins/tree.rs" +required-features = ["tree"] [[bin]] name = "nu_plugin_binaryview" path = "src/plugins/binaryview.rs" +required-features = ["binaryview"] [[bin]] name = "nu_plugin_textview" path = "src/plugins/textview.rs" +required-features = ["textview"] [[bin]] name = "nu" diff --git a/src/cli.rs b/src/cli.rs index cfd9d8de6e..ec8c7085c6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -163,7 +163,6 @@ pub async fn cli() -> Result<(), Box> { use crate::commands::*; context.add_commands(vec![ - whole_stream_command(PS), whole_stream_command(PWD), whole_stream_command(LS), whole_stream_command(CD), diff --git a/src/commands.rs b/src/commands.rs index f2885f369f..c6cc7b7285 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -39,7 +39,6 @@ pub(crate) mod pick; pub(crate) mod plugin; pub(crate) mod post; pub(crate) mod prev; -pub(crate) mod ps; pub(crate) mod pwd; pub(crate) mod reject; pub(crate) mod reverse; @@ -106,7 +105,6 @@ pub(crate) use open::Open; pub(crate) use pick::Pick; pub(crate) use post::Post; pub(crate) use prev::Previous; -pub(crate) use ps::PS; pub(crate) use pwd::PWD; pub(crate) use reject::Reject; pub(crate) use reverse::Reverse; diff --git a/src/commands/ps.rs b/src/commands/ps.rs deleted file mode 100644 index cdacd7113c..0000000000 --- a/src/commands/ps.rs +++ /dev/null @@ -1,76 +0,0 @@ -use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; -use crate::data::TaggedDictBuilder; -use crate::prelude::*; -use std::time::Duration; -use std::usize; - -use futures::stream::{StreamExt, TryStreamExt}; -use heim::process::{self as process, Process, ProcessResult}; -use heim::units::{ratio, Ratio}; - -pub struct PS; - -impl WholeStreamCommand for PS { - fn name(&self) -> &str { - "ps" - } - - fn signature(&self) -> Signature { - Signature::build("ps") - } - - fn usage(&self) -> &str { - "View current processes." - } - - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - ps(args, registry) - } -} - -async fn usage(process: Process) -> ProcessResult<(process::Process, Ratio)> { - let usage_1 = process.cpu_usage().await?; - futures_timer::Delay::new(Duration::from_millis(100)).await?; - let usage_2 = process.cpu_usage().await?; - - Ok((process, usage_2 - usage_1)) -} - -fn ps(args: CommandArgs, registry: &CommandRegistry) -> Result { - let args = args.evaluate_once(registry)?; - let span = args.name_span(); - - let stream = async_stream_block! { - let processes = process::processes() - .map_ok(|process| { - // Note that there is no `.await` here, - // as we want to pass the returned future - // into the `.try_buffer_unordered`. - usage(process) - }) - .try_buffer_unordered(usize::MAX); - pin_utils::pin_mut!(processes); - - while let Some(res) = processes.next().await { - if let Ok((process, usage)) = res { - let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(span)); - dict.insert("pid", Value::int(process.pid())); - if let Ok(name) = process.name().await { - dict.insert("name", Value::string(name)); - } - if let Ok(status) = process.status().await { - dict.insert("status", Value::string(format!("{:?}", status))); - } - dict.insert("cpu", Value::number(usage.get::())); - yield ReturnSuccess::value(dict.into_tagged_value()); - } - } - }; - - Ok(stream.to_output_stream()) -} diff --git a/src/plugins/ps.rs b/src/plugins/ps.rs new file mode 100644 index 0000000000..0f06167bdf --- /dev/null +++ b/src/plugins/ps.rs @@ -0,0 +1,80 @@ +use futures::executor::block_on; +use futures::stream::{StreamExt, TryStreamExt}; + +use heim::process::{self as process, Process, ProcessResult}; +use heim::units::{ratio, Ratio}; +use std::usize; + +use nu::{ + serve_plugin, CallInfo, Plugin, ReturnSuccess, ReturnValue, ShellError, Signature, Tag, Tagged, + TaggedDictBuilder, Value, +}; +use std::time::Duration; + +struct Ps; +impl Ps { + fn new() -> Ps { + Ps + } +} + +async fn usage(process: Process) -> ProcessResult<(process::Process, Ratio)> { + let usage_1 = process.cpu_usage().await?; + futures_timer::Delay::new(Duration::from_millis(100)).await?; + let usage_2 = process.cpu_usage().await?; + + Ok((process, usage_2 - usage_1)) +} + +async fn ps(tag: Tag) -> Vec> { + let processes = process::processes() + .map_ok(|process| { + // Note that there is no `.await` here, + // as we want to pass the returned future + // into the `.try_buffer_unordered`. + usage(process) + }) + .try_buffer_unordered(usize::MAX); + pin_utils::pin_mut!(processes); + + let mut output = vec![]; + while let Some(res) = processes.next().await { + if let Ok((process, usage)) = res { + let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(tag.span)); + dict.insert("pid", Value::int(process.pid())); + if let Ok(name) = process.name().await { + dict.insert("name", Value::string(name)); + } + if let Ok(status) = process.status().await { + dict.insert("status", Value::string(format!("{:?}", status))); + } + dict.insert("cpu", Value::number(usage.get::())); + output.push(dict.into_tagged_value()); + } + } + + output +} + +impl Plugin for Ps { + fn config(&mut self) -> Result { + Ok(Signature::build("ps") + .desc("View information about system processes.") + .filter()) + } + + fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { + Ok(block_on(ps(Tag::unknown_origin(callinfo.name_span))) + .into_iter() + .map(ReturnSuccess::value) + .collect()) + } + + fn filter(&mut self, _: Tagged) -> Result, ShellError> { + Ok(vec![]) + } +} + +fn main() { + serve_plugin(&mut Ps::new()); +} From df9ff44956084c9edbe6473e6328b254a1f1043e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Sun, 8 Sep 2019 03:09:05 -0500 Subject: [PATCH 036/342] Filesystem change directory coverage. --- tests/command_cd_tests.rs | 132 ++++++++++++++++++++++++++++---------- tests/helpers/mod.rs | 8 ++- 2 files changed, 106 insertions(+), 34 deletions(-) diff --git a/tests/command_cd_tests.rs b/tests/command_cd_tests.rs index 7c1a9f6b85..c2d7329f37 100644 --- a/tests/command_cd_tests.rs +++ b/tests/command_cd_tests.rs @@ -1,9 +1,107 @@ mod helpers; use helpers::Playground; +use std::path::PathBuf; #[test] -fn cd_directory_not_found() { +fn filesytem_change_from_current_directory_using_relative_path() { + Playground::setup("cd_test_1", |dirs, _| { + let actual = nu!( + cwd: dirs.root(), + r#" + cd cd_test_1 + pwd | echo $it + "# + ); + + assert_eq!(PathBuf::from(actual), *dirs.test()); + }) +} + +#[test] +fn filesystem_change_from_current_directory_using_absolute_path() { + Playground::setup("cd_test_2", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + r#" + cd {} + pwd | echo $it + "#, + dirs.formats() + ); + + assert_eq!(PathBuf::from(actual), dirs.formats()); + }) +} + +#[test] +fn filesystem_switch_back_to_previous_working_directory() { + Playground::setup("cd_test_3", |dirs, sandbox| { + sandbox.mkdir("odin"); + + let actual = nu!( + cwd: dirs.test().join("odin"), + r#" + cd {} + cd - + pwd | echo $it + "#, + dirs.test() + ); + + assert_eq!(PathBuf::from(actual), dirs.test().join("odin")); + }) +} + +#[test] +fn filesystem_change_current_directory_to_parent_directory() { + Playground::setup("cd_test_4", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + r#" + cd .. + pwd | echo $it + "# + ); + + assert_eq!(PathBuf::from(actual), *dirs.root()); + }) +} + +#[test] +fn file_system_change_to_home_directory() { + Playground::setup("cd_test_5", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + r#" + cd ~ + pwd | echo $it + "# + ); + + assert_eq!(PathBuf::from(actual), dirs::home_dir().unwrap()); + }) +} + +#[test] +fn filesystem_change_to_a_directory_containing_spaces() { + Playground::setup("cd_test_6", |dirs, sandbox| { + sandbox.mkdir("robalino turner katz"); + + let actual = nu!( + cwd: dirs.test(), + r#" + cd "robalino turner katz" + pwd | echo $it + "# + ); + + assert_eq!(PathBuf::from(actual), dirs.test().join("robalino turner katz")); + }) +} + +#[test] +fn filesystem_directory_not_found() { let actual = nu_error!( cwd: "tests/fixtures", "cd dir_that_does_not_exist" @@ -12,35 +110,3 @@ fn cd_directory_not_found() { assert!(actual.contains("dir_that_does_not_exist")); assert!(actual.contains("directory not found")); } - -#[test] -fn cd_back() { - Playground::setup("cd_test_back", |dirs, sandbox| { - sandbox - .mkdir("andres") - .mkdir("odin"); - - let odin = dirs.test().join("odin"); - let andres = dirs.test().join("andres"); - - nu!( - cwd: dirs.test(), - r#" - cd odin - mkdir a - cd ../andres - mkdir b - cd - - mkdir c - mkdir - - cd - - mkdir d - "# - ); - - assert!(odin.join("a").exists()); - assert!(andres.join("b").exists()); - assert!(odin.join("c").exists()); - assert!(odin.join("-").join("d").exists()); - }) -} diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 6aa7cb67b9..1c2bfef0e0 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -227,8 +227,14 @@ impl Playground { playground_root.join(topic).display() )); + let root = + dunce::canonicalize(playground_root).expect(&format!( + "Couldn't canonicalize tests root path {}", + playground_root.display() + )); + let dirs = Dirs { - root: PathBuf::from(playground_root), + root, test, fixtures, }; From 77c2e4200ee3211b3e1f06edb2df53399e20578b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Sun, 8 Sep 2019 04:55:49 -0500 Subject: [PATCH 037/342] Filesystem cd refactor/cleanup. --- src/shell/filesystem_shell.rs | 54 +++++++++++------------------------ tests/command_cd_tests.rs | 27 ++++++++++++++---- 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 4a0d5b3f7d..221b088684 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -185,29 +185,20 @@ impl Shell for FilesystemShell { }, Some(v) => { let target = v.as_path()?; - let path = PathBuf::from(self.path()); - match dunce::canonicalize(path.join(&target).as_path()) { - Ok(p) => p, - Err(_) => { - let error = Err(ShellError::labeled_error( - "Can not change to directory", - "directory not found", - v.span().clone(), - )); - if let Some(t) = target.to_str() { - if t == "-" { - match dunce::canonicalize(PathBuf::from(self.last_path.clone()).as_path()) { - Ok(p) => p, - Err(_) => { - return error; - } - } - } else { - return error; - } - } else { - return error; + if PathBuf::from("-") == target { + PathBuf::from(&self.last_path) + } else { + let path = PathBuf::from(self.path()); + + match dunce::canonicalize(path.join(&target)) { + Ok(p) => p, + Err(_) => { + return Err(ShellError::labeled_error( + "Can not change to directory", + "directory not found", + v.span().clone(), + )) } } } @@ -215,23 +206,12 @@ impl Shell for FilesystemShell { }; let mut stream = VecDeque::new(); - match std::env::set_current_dir(&path) { - Ok(_) => {} - Err(_) => { - if let Some(directory) = args.nth(0) { - return Err(ShellError::labeled_error( - "Can not change to directory", - "directory not found", - directory.span(), - )); - } else { - return Err(ShellError::string("Can not change to directory")); - } - } - } - stream.push_back(ReturnSuccess::change_cwd( + + stream.push_back( + ReturnSuccess::change_cwd( path.to_string_lossy().to_string(), )); + Ok(stream.into()) } diff --git a/tests/command_cd_tests.rs b/tests/command_cd_tests.rs index c2d7329f37..ca066f660a 100644 --- a/tests/command_cd_tests.rs +++ b/tests/command_cd_tests.rs @@ -1,6 +1,6 @@ mod helpers; -use helpers::Playground; +use helpers::{Playground, Stub::*}; use std::path::PathBuf; #[test] @@ -53,9 +53,26 @@ fn filesystem_switch_back_to_previous_working_directory() { }) } +#[test] +fn filesytem_change_from_current_directory_using_relative_path_and_dash() { + Playground::setup("cd_test_4", |dirs, sandbox| { + sandbox.within("odin").mkdir("-"); // + + let actual = nu!( + cwd: dirs.test(), + r#" + cd odin/- + pwd | echo $it + "# + ); + + assert_eq!(PathBuf::from(actual), dirs.test().join("odin").join("-")); + }) +} + #[test] fn filesystem_change_current_directory_to_parent_directory() { - Playground::setup("cd_test_4", |dirs, _| { + Playground::setup("cd_test_5", |dirs, _| { let actual = nu!( cwd: dirs.test(), r#" @@ -70,7 +87,7 @@ fn filesystem_change_current_directory_to_parent_directory() { #[test] fn file_system_change_to_home_directory() { - Playground::setup("cd_test_5", |dirs, _| { + Playground::setup("cd_test_6", |dirs, _| { let actual = nu!( cwd: dirs.test(), r#" @@ -85,7 +102,7 @@ fn file_system_change_to_home_directory() { #[test] fn filesystem_change_to_a_directory_containing_spaces() { - Playground::setup("cd_test_6", |dirs, sandbox| { + Playground::setup("cd_test_7", |dirs, sandbox| { sandbox.mkdir("robalino turner katz"); let actual = nu!( @@ -109,4 +126,4 @@ fn filesystem_directory_not_found() { assert!(actual.contains("dir_that_does_not_exist")); assert!(actual.contains("directory not found")); -} +} \ No newline at end of file From f770409a6079dd3f616848b8486dfae4da59816a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Sun, 8 Sep 2019 05:15:55 -0500 Subject: [PATCH 038/342] cd '-' valueshell implementation and valueshell refactorings. --- src/shell/filesystem_shell.rs | 4 +- src/shell/value_shell.rs | 27 ++-- tests/command_cd_tests.rs | 243 +++++++++++++++++++++++++++++++++- 3 files changed, 258 insertions(+), 16 deletions(-) diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 221b088684..16cbf513f8 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -15,7 +15,7 @@ use std::path::{Path, PathBuf}; pub struct FilesystemShell { pub(crate) path: String, - last_path: String, + pub(crate) last_path: String, completer: NuCompleter, hinter: HistoryHinter, } @@ -211,7 +211,7 @@ impl Shell for FilesystemShell { ReturnSuccess::change_cwd( path.to_string_lossy().to_string(), )); - + Ok(stream.into()) } diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index e14d5603a6..010c7ae08e 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -10,16 +10,24 @@ use crate::utils::ValueStructure; use std::ffi::OsStr; use std::path::{Path, PathBuf}; -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct ValueShell { pub(crate) path: String, + pub(crate) last_path: String, pub(crate) value: Tagged, } +impl std::fmt::Debug for ValueShell { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "ValueShell @ {}", self.path) + } +} + impl ValueShell { pub fn new(value: Tagged) -> ValueShell { ValueShell { path: "/".to_string(), + last_path: "/".to_string(), value, } } @@ -76,7 +84,7 @@ impl Shell for ValueShell { } fn homedir(&self) -> Option { - dirs::home_dir() + Some(PathBuf::from("/")) } fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { @@ -126,6 +134,8 @@ impl Shell for ValueShell { if target == PathBuf::from("..") { cwd.pop(); + } else if target == PathBuf::from("-") { + cwd = PathBuf::from(&self.last_path); } else { match target.to_str() { Some(target) => match target.chars().nth(0) { @@ -200,19 +210,16 @@ impl Shell for ValueShell { } fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { - let path = PathBuf::from(&self.path()); - let mut stream = VecDeque::new(); - stream.push_back(ReturnSuccess::value( - Value::Primitive(Primitive::String(path.to_string_lossy().to_string())) - .simple_spanned(args.call_info.name_span), - )); - + stream.push_back(ReturnSuccess::value(Tagged::from_item( + Value::string(self.path()), + args.call_info.name_span, + ))); Ok(stream.into()) } fn set_path(&mut self, path: String) { - let _ = std::env::set_current_dir(&path); + self.last_path = self.path.clone(); self.path = path.clone(); } diff --git a/tests/command_cd_tests.rs b/tests/command_cd_tests.rs index ca066f660a..b9ad8ca12d 100644 --- a/tests/command_cd_tests.rs +++ b/tests/command_cd_tests.rs @@ -4,7 +4,7 @@ use helpers::{Playground, Stub::*}; use std::path::PathBuf; #[test] -fn filesytem_change_from_current_directory_using_relative_path() { +fn filesystem_change_from_current_directory_using_relative_path() { Playground::setup("cd_test_1", |dirs, _| { let actual = nu!( cwd: dirs.root(), @@ -56,7 +56,7 @@ fn filesystem_switch_back_to_previous_working_directory() { #[test] fn filesytem_change_from_current_directory_using_relative_path_and_dash() { Playground::setup("cd_test_4", |dirs, sandbox| { - sandbox.within("odin").mkdir("-"); // + sandbox.within("odin").mkdir("-"); let actual = nu!( cwd: dirs.test(), @@ -86,7 +86,7 @@ fn filesystem_change_current_directory_to_parent_directory() { } #[test] -fn file_system_change_to_home_directory() { +fn filesystem_change_to_home_directory() { Playground::setup("cd_test_6", |dirs, _| { let actual = nu!( cwd: dirs.test(), @@ -126,4 +126,239 @@ fn filesystem_directory_not_found() { assert!(actual.contains("dir_that_does_not_exist")); assert!(actual.contains("directory not found")); -} \ No newline at end of file +} + + +#[test] +fn valuesystem_change_from_current_path_using_relative_path() { + Playground::setup("cd_test_8", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "sample.toml", + r#" + [[bin]] + path = "src/plugins/turner.rs" + + [[bin]] + path = "src/plugins/robalino.rs" + + [[bin]] + path = "src/plugins/katz.rs" + "# + )]); + + let actual = nu!( + cwd: dirs.test(), + r#" + enter sample.toml + cd bin + pwd | echo $it + exit + "# + ); + + assert_eq!(PathBuf::from(actual), PathBuf::from("/bin")); + }) +} + +#[test] +fn valuesystem_change_from_current_path_using_absolute_path() { + Playground::setup("cd_test_9", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "sample.toml", + r#" + [dependencies] + turner-ts = "0.1.1" + robalino-tkd = "0.0.1" + katz-ember = "0.2.3" + + [[bin]] + path = "src/plugins/arepa.rs" + + [[bin]] + path = "src/plugins/bbq.rs" + "# + )]); + + let actual = nu!( + cwd: dirs.test(), + r#" + enter sample.toml + cd bin + cd /dependencies + pwd | echo $it + exit + "# + ); + + assert_eq!(PathBuf::from(actual), PathBuf::from("/dependencies")); + }) +} + +#[test] +fn valuesystem_switch_back_to_previous_working_path() { + Playground::setup("cd_test_10", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "sample.toml", + r#" + [dependencies] + turner-ts = "0.1.1" + robalino-tkd = "0.0.1" + katz-ember = "0.2.3" + odin-gf = "0.2.1" + + [[bin]] + path = "src/plugins/arepa.rs" + + [[bin]] + path = "src/plugins/bbq.rs" + "# + )]); + + let actual = nu!( + cwd: dirs.test(), + r#" + enter sample.toml + cd dependencies + cd /bin + cd - + pwd | echo $it + exit + "# + ); + + assert_eq!(PathBuf::from(actual), PathBuf::from("/dependencies")); + }) +} + +#[test] +fn valuesystem_change_from_current_path_using_relative_path_and_dash() { + Playground::setup("cd_test_11", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + - = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] + + [[bin]] + path = "src/plugins/arepa.rs" + + [[bin]] + path = "src/plugins/bbq.rs" + "# + )]); + + let actual = nu!( + cwd: dirs.test(), + r#" + enter sample.toml + cd package/- + cd /bin + cd - + pwd | echo $it + exit + "# + ); + + assert_eq!(PathBuf::from(actual), PathBuf::from("/package/-")); + }) +} + + +#[test] +fn valuesystem_change_current_path_to_parent_path() { + Playground::setup("cd_test_12", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + emberenios = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] + "# + )]); + + let actual = nu!( + cwd: dirs.test(), + r#" + enter sample.toml + cd package/emberenios + cd .. + pwd | echo $it + exit + "# + ); + + assert_eq!(PathBuf::from(actual), PathBuf::from("/package")); + }) +} + +#[test] +fn valuesystem_change_to_home_directory() { + Playground::setup("cd_test_13", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "sample.toml", + r#" + [paquete] + el = "pollo loco" + "# + )]); + + let actual = nu!( + cwd: dirs.test(), + r#" + enter sample.toml + cd paquete + cd ~ + pwd | echo $it + exit + "# + ); + + assert_eq!(PathBuf::from(actual), PathBuf::from("/")); + }) +} + +#[test] +fn valuesystem_change_to_a_path_containing_spaces() { + Playground::setup("cd_test_14", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "sample.toml", + r#" + ["pa que te"] + el = "pollo loco" + "# + )]); + + let actual = nu!( + cwd: dirs.test(), + r#" + enter sample.toml + cd "pa que te" + pwd | echo $it + exit + "# + ); + + assert_eq!(PathBuf::from(actual), PathBuf::from("/").join("pa que te")); + }) +} + +#[test] +fn valuesystem_path_not_found() { + let actual = nu_error!( + cwd: "tests/fixtures/formats", + r#" + enter cargo_sample.toml + cd im_a_path_that_does_not_exist + exit + "# + ); + + assert!(actual.contains("Can not change to path inside")); + assert!(actual.contains("No such path exists")); +} From c9c91121556c9857e4ad902a3071fa05b26b221f Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Sun, 8 Sep 2019 21:38:25 +0700 Subject: [PATCH 039/342] Build and publish docker img along with nu plugins * Add Package.Dockerfile as flexible build source * Add docker-compose.package.yml as intermediary config * CI will use new github action YAML format it only publish the docker image on git tag * Add debian:latest, debian:slim, and alpine as base image * Add documentation --- .editorconfig | 7 ++- .github/main.workflow | 8 --- .github/workflows/docker-publish.yml | 80 ++++++++++++++++++++++++++++ docker/Package.Dockerfile | 5 ++ docker/docker-compose.package.yml | 10 ++++ docs/docker.md | 46 ++++++++++++++++ 6 files changed, 147 insertions(+), 9 deletions(-) delete mode 100644 .github/main.workflow create mode 100644 .github/workflows/docker-publish.yml create mode 100644 docker/Package.Dockerfile create mode 100644 docker/docker-compose.package.yml create mode 100644 docs/docker.md diff --git a/.editorconfig b/.editorconfig index f6fb9f98d9..c5d100a733 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,4 +6,9 @@ indent_size = 4 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = false -end_of_line = lf \ No newline at end of file +end_of_line = lf + +[*.{yml,yaml}] +indent_size = 2 +charset = utf-8 +insert_final_newline = true \ No newline at end of file diff --git a/.github/main.workflow b/.github/main.workflow deleted file mode 100644 index cbbe6aea81..0000000000 --- a/.github/main.workflow +++ /dev/null @@ -1,8 +0,0 @@ -workflow "New workflow" { - resolves = ["GitHub Action for Docker"] - on = "push" -} - -action "GitHub Action for Docker" { - uses = "actions/docker/cli@fe7ed3ce992160973df86480b83a2f8ed581cd50" -} diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000000..a3c3e0f2af --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,80 @@ +name: Publish consumable Docker images + +on: + push: + tags: ['*.*.*'] + +jobs: + compile: + runs-on: ubuntu-latest + strategy: + matrix: + arch: + - x86_64-unknown-linux-musl + - x86_64-unknown-linux-gnu + steps: + - uses: actions/checkout@v1 + - run: cargo install cross + - name: compile for specific target + env: { arch: '${{ matrix.arch }}' } + run: | + cross build --target ${{ matrix.arch }} --release + # leave only the executable file + rm -rd target/${{ matrix.arch }}/release/{*/*,*.d,*.rlib,.fingerprint} + find . -empty -delete + - uses: actions/upload-artifact@master + with: + name: ${{ matrix.arch }} + path: target/${{ matrix.arch }}/release + + docker: + name: Build and publish docker images + needs: compile + runs-on: ubuntu-latest + strategy: + matrix: + base-image: [debian, 'debian:stable-slim', alpine] + include: + - { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl } + - { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu } + - { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu } + steps: + - uses: actions/checkout@v1 + - uses: actions/download-artifact@master + with: { name: '${{ matrix.arch }}', path: target/release } + - name: Build and publish exact version + run: | + REGISTRY=${REGISTRY,,}; export TAG=${GITHUB_REF##*/}-${{ matrix.tag }}; + + echo ${{ secrets.DOCKER_REGISTRY }} | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin + docker-compose --file docker/docker-compose.package.yml build + docker-compose --file docker/docker-compose.package.yml push # exact version + env: + BASE_IMAGE: ${{ matrix.base-image }} + REGISTRY: docker.pkg.github.com/${{ github.repository }} + + #region semantics tagging + - name: Retag and push without suffixing version + run: | + VERSION=${GITHUB_REF##*/} + docker tag ${REGISTRY,,}/nu:${VERSION}-${{ matrix.tag }} ${REGISTRY,,}/nu:${{ matrix.tag }} + docker tag ${REGISTRY,,}/nu:${VERSION}-${{ matrix.tag }} ${REGISTRY,,}/nu:${VERSION%%.*}-${{ matrix.tag }} + docker tag ${REGISTRY,,}/nu:${VERSION}-${{ matrix.tag }} ${REGISTRY,,}/nu:${VERSION%.*}-${{ matrix.tag }} + docker push ${REGISTRY,,}/nu:${VERSION%.*}-${{ matrix.tag }} # latest patch + docker push ${REGISTRY,,}/nu:${VERSION%%.*}-${{ matrix.tag }} # latest features + docker push ${REGISTRY,,}/nu:${{ matrix.tag }} # latest version + env: { REGISTRY: 'docker.pkg.github.com/${{ github.repository }}' } + - name: Retag and push debian as latest + if: matrix.tag == 'debian' + run: | + VERSION=${GITHUB_REF##*/} + docker tag ${REGISTRY,,}/nu:${{ matrix.tag }} ${REGISTRY,,}/nu:latest + docker tag ${REGISTRY,,}/nu:${VERSION}-${{ matrix.tag }} ${REGISTRY,,}/nu:${VERSION%.*} + docker tag ${REGISTRY,,}/nu:${VERSION}-${{ matrix.tag }} ${REGISTRY,,}/nu:${VERSION%%.*} + docker tag ${REGISTRY,,}/nu:${VERSION}-${{ matrix.tag }} ${REGISTRY,,}/nu:${VERSION} + docker push ${REGISTRY,,}/nu:${VERSION} # exact version + docker push ${REGISTRY,,}/nu:${VERSION%%.*} # latest features + docker push ${REGISTRY,,}/nu:${VERSION%.*} # latest patch + docker push ${REGISTRY,,}/nu:latest # latest version + env: { REGISTRY: 'docker.pkg.github.com/${{ github.repository }}' } + #endregion semantics tagging diff --git a/docker/Package.Dockerfile b/docker/Package.Dockerfile new file mode 100644 index 0000000000..8d61b03342 --- /dev/null +++ b/docker/Package.Dockerfile @@ -0,0 +1,5 @@ +ARG base +FROM ${base} + +COPY target/release/nu* /bin/ +ENTRYPOINT ["nu"] \ No newline at end of file diff --git a/docker/docker-compose.package.yml b/docker/docker-compose.package.yml new file mode 100644 index 0000000000..2192da8879 --- /dev/null +++ b/docker/docker-compose.package.yml @@ -0,0 +1,10 @@ +version: '3' + +services: + nushell: + image: ${REGISTRY}/nu:${TAG} + build: + context: .. + dockerfile: docker/Package.Dockerfile + args: + base: ${BASE_IMAGE} diff --git a/docs/docker.md b/docs/docker.md new file mode 100644 index 0000000000..dc8d37988c --- /dev/null +++ b/docs/docker.md @@ -0,0 +1,46 @@ +# Docker Guide + +| tag | base image | plugins | package manager | libs & bins | size | +| ------------------ | -------------------- | ------- | --------------- | ----------------------------------------------------------------------- | ----------- | +| `latest`,`debian` | `debian:latest` | yes | apt | **a lot**, including _glibc_ | ~(48+62) MB | +| `slim` | `debian:stable-slim` | yes | apt | all `nu:debian` image but exclude [this list][.slimify-excludes] | ~(26+62) MB | +| `alpine` | `alpine:latest` | yes | apk | all `nu:musl-busybox` image but include libcrypto, libssl, libtls, libz | ~(3+61) MB | + +[.slimify-excludes]: https://github.com/debuerreotype/debuerreotype/blob/master/scripts/.slimify-excludes +[distroless/base]: https://github.com/GoogleContainerTools/distroless/blob/master/base/README.md + +## Image Variants + +### `nu:` +This is the defacto image. If you are unsure about what your needs are, you probably want to use this one. It is designed to be used both as a throw away container (mount your source code and start the container to start your app), as well as the base to build other images off of. + +
example + +Let say you create a plugin in Rust. +- create a Dockerfile in your root project +```dockerfile +FROM nu:0.2 + +COPY /target/debug/nu_plugin_cowsay /bin/ +ENTRYPOINT ["nu"] +``` +- build your project first then run it via docker +```console +cargo build +docker run -it . +``` +
+ +### `nu:-slim` + +This image does not contain the common packages contained in the default tag and only contains the minimal packages needed to run `nu`. Unless you are working in an environment where only the `nu` image will be deployed and you have space constraints, we highly recommend using the alpine image if you aim for small image size. Only use this image if you really need **both** `glibc` and small image size. + +### `nu:-alpine` +This image is based on the popular [Alpine Linux project](http://alpinelinux.org/), available in [the alpine official image][alpine]. Alpine Linux is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general. + +This variant is highly recommended when final image size being as small as possible is desired. The main caveat to note is that it does use `musl` libc instead of `glibc` and friends, so certain software might run into issues depending on the depth of their libc requirements. However, most software doesn't have an issue with this, so this variant is usually a very safe choice. See [this Hacker News comment thread](https://news.ycombinator.com/item?id=10782897) for more discussion of the issues that might arise and some pro/con comparisons of using Alpine-based images. + +To minimize image size, it's uncommon for additional related tools (such as `git` or `bash`) to be included in Alpine-based images. Using this image as a base, add the things you need in your own Dockerfile (see the [alpine image description][alpine] for examples of how to install packages if you are unfamiliar). + +[musl]: http://www.musl-libc.org/ +[alpine]: https://hub.docker.com/_/alpine/ \ No newline at end of file From 21896b200cd4ec544f40cd416c977af4e4816d19 Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Sun, 8 Sep 2019 22:31:10 +0700 Subject: [PATCH 040/342] Add busybox as base image --- .github/workflows/docker-publish.yml | 11 +++++++---- docker/Package.Dockerfile | 3 ++- docker/docker-compose.package.yml | 1 + docs/docker.md | 29 +++++++++++++++++++++++----- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index a3c3e0f2af..353401e91b 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -33,11 +33,13 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - base-image: [debian, 'debian:stable-slim', alpine] + base-image: [debian, 'debian:stable-slim', alpine, 'busybox:glibc', 'busybox:musl'] include: - - { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl } - - { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu } - - { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu } + - { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl, plugin: true } + - { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu, plugin: true } + - { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu, plugin: true } + - { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, plugin: false } + - { tag: musl-busybox, base-image: 'busybox:musl', arch: x86_64-unknown-linux-musl, plugin: false } steps: - uses: actions/checkout@v1 - uses: actions/download-artifact@master @@ -45,6 +47,7 @@ jobs: - name: Build and publish exact version run: | REGISTRY=${REGISTRY,,}; export TAG=${GITHUB_REF##*/}-${{ matrix.tag }}; + export NU_BINS=target/release/$( [ ${{ matrix.plugin }} ] && nu* || nu ) echo ${{ secrets.DOCKER_REGISTRY }} | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin docker-compose --file docker/docker-compose.package.yml build diff --git a/docker/Package.Dockerfile b/docker/Package.Dockerfile index 8d61b03342..7bc2bfe291 100644 --- a/docker/Package.Dockerfile +++ b/docker/Package.Dockerfile @@ -1,5 +1,6 @@ +ARG artifact ARG base FROM ${base} -COPY target/release/nu* /bin/ +COPY ${artifact} /bin/ ENTRYPOINT ["nu"] \ No newline at end of file diff --git a/docker/docker-compose.package.yml b/docker/docker-compose.package.yml index 2192da8879..3622fa0cf6 100644 --- a/docker/docker-compose.package.yml +++ b/docker/docker-compose.package.yml @@ -8,3 +8,4 @@ services: dockerfile: docker/Package.Dockerfile args: base: ${BASE_IMAGE} + artifact: ${NU_BINS} diff --git a/docs/docker.md b/docs/docker.md index dc8d37988c..78db9a44c7 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -1,10 +1,12 @@ # Docker Guide -| tag | base image | plugins | package manager | libs & bins | size | -| ------------------ | -------------------- | ------- | --------------- | ----------------------------------------------------------------------- | ----------- | -| `latest`,`debian` | `debian:latest` | yes | apt | **a lot**, including _glibc_ | ~(48+62) MB | -| `slim` | `debian:stable-slim` | yes | apt | all `nu:debian` image but exclude [this list][.slimify-excludes] | ~(26+62) MB | -| `alpine` | `alpine:latest` | yes | apk | all `nu:musl-busybox` image but include libcrypto, libssl, libtls, libz | ~(3+61) MB | +| tag | base image | plugins | package manager | libs & bins | size | +| ----------------- | -------------------- | ------- | --------------- | ---------------------------------------------------------------- | ----------- | +| `latest`,`debian` | `debian:latest` | yes | apt | **a lot**, including _glibc_ | ~(48+62) MB | +| `slim` | `debian:stable-slim` | yes | apt | all `nu:debian` image but exclude [this list][.slimify-excludes] | ~(26+62) MB | +| `alpine` | `alpine:latest` | yes | apk | all `nu:musl-busybox` image + libcrypto, libssl, libtls, libz | ~(3+61) MB | +| `musl-busybox` | `busybox:musl` | no | — | GNU utils + _musl_ | ~(1+16) MB | +| `glibc-busybox` | `busybox:glibc` | no | — | GNU utils + _glibc_ | ~(3+17) MB | [.slimify-excludes]: https://github.com/debuerreotype/debuerreotype/blob/master/scripts/.slimify-excludes [distroless/base]: https://github.com/GoogleContainerTools/distroless/blob/master/base/README.md @@ -42,5 +44,22 @@ This variant is highly recommended when final image size being as small as possi To minimize image size, it's uncommon for additional related tools (such as `git` or `bash`) to be included in Alpine-based images. Using this image as a base, add the things you need in your own Dockerfile (see the [alpine image description][alpine] for examples of how to install packages if you are unfamiliar). +### `nu:--busybox` +This image is based on [Busybox](http://www.busybox.net/) which is a very good ingredient to craft space-efficient distributions. It combines tiny versions of many common UNIX utilities into a single small executable. It also provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox generally have fewer options than their full-featured GNU cousins; however, the options that are included provide the expected functionality and behave very much like their GNU counterparts. Basically, this image provides a fairly complete environment for any small or embedded system. + +> Use this only if you need common utilities like `tar`, `awk`, and many more but don't want extra blob like nushell plugins and others. + +
example + +```dockerfile +FROM nu:0.2-glibc-busybox + +ADD https://github.com/user/repo/releases/download/latest/nu_plugin_cowsay.tar.gz /tmp/ +RUN tar xzfv nu_plugin_cowsay.tar.gz -C /bin + +ENTRYPOINT ["nu"] +``` +
+ [musl]: http://www.musl-libc.org/ [alpine]: https://hub.docker.com/_/alpine/ \ No newline at end of file From fa53d59aeea396895bbc70356ac57da6b277710d Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Sun, 8 Sep 2019 22:59:35 +0700 Subject: [PATCH 041/342] Add scratch as base image --- .github/workflows/docker-publish.yml | 2 ++ docs/docker.md | 34 ++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 353401e91b..c8b56e680c 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -40,6 +40,8 @@ jobs: - { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu, plugin: true } - { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, plugin: false } - { tag: musl-busybox, base-image: 'busybox:musl', arch: x86_64-unknown-linux-musl, plugin: false } + - { tag: glibc, base-image: scratch, arch: x86_64-unknown-linux-gnu, plugin: false } + - { tag: musl, base-image: scratch, arch: x86_64-unknown-linux-musl, plugin: false } steps: - uses: actions/checkout@v1 - uses: actions/download-artifact@master diff --git a/docs/docker.md b/docs/docker.md index 78db9a44c7..7c00f05544 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -7,6 +7,8 @@ | `alpine` | `alpine:latest` | yes | apk | all `nu:musl-busybox` image + libcrypto, libssl, libtls, libz | ~(3+61) MB | | `musl-busybox` | `busybox:musl` | no | — | GNU utils + _musl_ | ~(1+16) MB | | `glibc-busybox` | `busybox:glibc` | no | — | GNU utils + _glibc_ | ~(3+17) MB | +| `glibc` | `scratch` | no | — | **only `nu` binary-executable** which depend on glibc runtime | ~17 MB | +| `musl` | `scratch` | no | — | **only `nu` binary-executable** statically linked to musl | ~16 MB | [.slimify-excludes]: https://github.com/debuerreotype/debuerreotype/blob/master/scripts/.slimify-excludes [distroless/base]: https://github.com/GoogleContainerTools/distroless/blob/master/base/README.md @@ -44,6 +46,38 @@ This variant is highly recommended when final image size being as small as possi To minimize image size, it's uncommon for additional related tools (such as `git` or `bash`) to be included in Alpine-based images. Using this image as a base, add the things you need in your own Dockerfile (see the [alpine image description][alpine] for examples of how to install packages if you are unfamiliar). +### `nu:-` +This image is based on [`scratch`](https://hub.docker.com/_/scratch) which doesn't create an extra layer. This variants can be handy in a project that uses multiple programming language as you need a lot of tools. By using this in [multi-stage build][], you can slim down the docker image that need to be pulled. + +[multi-stage build]: https://docs.docker.com/develop/develop-images/multistage-build/ + +
example + +- using `glibc` variant +```dockerfile +FROM nu:0.2-glibc as shell +FROM node:slim + +# Build your plugins + +COPY --from=shell /bin/nu /bin/ +# Something else +ENTRYPOINT ["nu"] +``` + +- using `musl` variant +```dockerfile +FROM nu:musl as shell +FROM go:alpine + +# Build your plugins + +COPY --from=shell /bin/nu /bin/ +# Something else +ENTRYPOINT ["nu"] +``` +
+ ### `nu:--busybox` This image is based on [Busybox](http://www.busybox.net/) which is a very good ingredient to craft space-efficient distributions. It combines tiny versions of many common UNIX utilities into a single small executable. It also provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox generally have fewer options than their full-featured GNU cousins; however, the options that are included provide the expected functionality and behave very much like their GNU counterparts. Basically, this image provides a fairly complete environment for any small or embedded system. From d99208619289f16e8fbe336cdff207f1b354d164 Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Sun, 8 Sep 2019 23:42:03 +0700 Subject: [PATCH 042/342] Add distroless as base image --- .github/workflows/docker-publish.yml | 16 +++++---- docs/docker.md | 49 +++++++++++++++++++++------- 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index c8b56e680c..30ee8b0418 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -35,13 +35,15 @@ jobs: matrix: base-image: [debian, 'debian:stable-slim', alpine, 'busybox:glibc', 'busybox:musl'] include: - - { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl, plugin: true } - - { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu, plugin: true } - - { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu, plugin: true } - - { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, plugin: false } - - { tag: musl-busybox, base-image: 'busybox:musl', arch: x86_64-unknown-linux-musl, plugin: false } - - { tag: glibc, base-image: scratch, arch: x86_64-unknown-linux-gnu, plugin: false } - - { tag: musl, base-image: scratch, arch: x86_64-unknown-linux-musl, plugin: false } + - { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl, plugin: true } + - { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu, plugin: true } + - { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu, plugin: true } + - { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, plugin: false } + - { tag: musl-busybox, base-image: 'busybox:musl', arch: x86_64-unknown-linux-musl, plugin: false } + - { tag: musl-distroless, base-image: 'gcr.io/distroless/static', arch: x86_64-unknown-linux-musl, plugin: false } + - { tag: glibc-distroless, base-image: 'gcr.io/distroless/base', arch: x86_64-unknown-linux-gnu, plugin: false } + - { tag: glibc, base-image: scratch, arch: x86_64-unknown-linux-gnu, plugin: false } + - { tag: musl, base-image: scratch, arch: x86_64-unknown-linux-musl, plugin: false } steps: - uses: actions/checkout@v1 - uses: actions/download-artifact@master diff --git a/docs/docker.md b/docs/docker.md index 7c00f05544..837707404c 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -1,14 +1,16 @@ # Docker Guide -| tag | base image | plugins | package manager | libs & bins | size | -| ----------------- | -------------------- | ------- | --------------- | ---------------------------------------------------------------- | ----------- | -| `latest`,`debian` | `debian:latest` | yes | apt | **a lot**, including _glibc_ | ~(48+62) MB | -| `slim` | `debian:stable-slim` | yes | apt | all `nu:debian` image but exclude [this list][.slimify-excludes] | ~(26+62) MB | -| `alpine` | `alpine:latest` | yes | apk | all `nu:musl-busybox` image + libcrypto, libssl, libtls, libz | ~(3+61) MB | -| `musl-busybox` | `busybox:musl` | no | — | GNU utils + _musl_ | ~(1+16) MB | -| `glibc-busybox` | `busybox:glibc` | no | — | GNU utils + _glibc_ | ~(3+17) MB | -| `glibc` | `scratch` | no | — | **only `nu` binary-executable** which depend on glibc runtime | ~17 MB | -| `musl` | `scratch` | no | — | **only `nu` binary-executable** statically linked to musl | ~16 MB | +| tag | base image | plugins | package manager | libs & bins | size | +| ------------------ | -------------------- | ------- | --------------- | ---------------------------------------------------------------- | ----------- | +| `latest`,`debian` | `debian:latest` | yes | apt | **a lot**, including _glibc_ | ~(48+62) MB | +| `slim` | `debian:stable-slim` | yes | apt | all `nu:debian` image but exclude [this list][.slimify-excludes] | ~(26+62) MB | +| `alpine` | `alpine:latest` | yes | apk | all `nu:musl-busybox` image + libcrypto, libssl, libtls, libz | ~(3+61) MB | +| `musl-busybox` | `busybox:musl` | no | — | GNU utils + _musl_ | ~(1+16) MB | +| `glibc-busybox` | `busybox:glibc` | no | — | GNU utils + _glibc_ | ~(3+17) MB | +| `musl-distroless` | `distroless/static` | no | — | see [here][distroless/base] | ~(2+16) MB | +| `glibc-distroless` | `distroless/base` | no | — | `distroless/static` with _glibc_ | ~(17+17) MB | +| `glibc` | `scratch` | no | — | **only `nu` binary-executable** which depend on glibc runtime | ~17 MB | +| `musl` | `scratch` | no | — | **only `nu` binary-executable** statically linked to musl | ~16 MB | [.slimify-excludes]: https://github.com/debuerreotype/debuerreotype/blob/master/scripts/.slimify-excludes [distroless/base]: https://github.com/GoogleContainerTools/distroless/blob/master/base/README.md @@ -36,8 +38,7 @@ docker run -it . ### `nu:-slim` - -This image does not contain the common packages contained in the default tag and only contains the minimal packages needed to run `nu`. Unless you are working in an environment where only the `nu` image will be deployed and you have space constraints, we highly recommend using the alpine image if you aim for small image size. Only use this image if you really need **both** `glibc` and small image size. +This image does not contain the common packages contained in the default tag and only contains the minimal packages needed to run `nu`. Unless you are working in an environment where only the `nu` image will be deployed and you have space constraints, it's highly recommended to use the alpine image if you aim for small image size. Only use this image if you really need **both** `glibc` and small image size. ### `nu:-alpine` This image is based on the popular [Alpine Linux project](http://alpinelinux.org/), available in [the alpine official image][alpine]. Alpine Linux is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general. @@ -78,6 +79,30 @@ ENTRYPOINT ["nu"] ``` +### `nu:--distroless` +This image is base on [Distroless](https://github.com/GoogleContainerTools/distroless) which usually to contain only your application and its runtime dependencies. This image do not contain package managers, shells or any other programs you would expect to find in a standard Linux distribution except for nushell itself. All distroless variant always contains: +- ca-certificates +- A /etc/passwd entry for a root user +- A /tmp directory +- tzdata + +As for `glibc-distroless` variant, it **adds**: +- glibc +- libssl +- openssl + +> Most likely you want to use this in CI/CD environment for plugins that can be statically compiled. + +
example + +```dockerfile +FROM nu:musl-distroless + +COPY target/x86_64-unknown-linux-musl/release/nu_plugin_* /bin/ +ENTRYPOINT ["nu"] +``` +
+ ### `nu:--busybox` This image is based on [Busybox](http://www.busybox.net/) which is a very good ingredient to craft space-efficient distributions. It combines tiny versions of many common UNIX utilities into a single small executable. It also provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox generally have fewer options than their full-featured GNU cousins; however, the options that are included provide the expected functionality and behave very much like their GNU counterparts. Basically, this image provides a fairly complete environment for any small or embedded system. @@ -89,7 +114,7 @@ This image is based on [Busybox](http://www.busybox.net/) which is a very good i FROM nu:0.2-glibc-busybox ADD https://github.com/user/repo/releases/download/latest/nu_plugin_cowsay.tar.gz /tmp/ -RUN tar xzfv nu_plugin_cowsay.tar.gz -C /bin +RUN tar xzfv nu_plugin_cowsay.tar.gz -C /bin --strip=1 nu_plugin_cowsay ENTRYPOINT ["nu"] ``` From 160bd7c535383c965f45fcc7d0d59572fcdf45df Mon Sep 17 00:00:00 2001 From: Milan Lesichkov Date: Sun, 8 Sep 2019 18:57:28 +0100 Subject: [PATCH 043/342] Spell check fixed --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e80a51e600..28f5c15656 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A modern shell for the GitHub era # Status -This project has reached a minimum-viable product level of quality. While contributors dogfood it as their daily driver, it may be instable for some commands. Future releases will work fill out missing features and improve stability. Its design is also subject to change as it matures. +This project has reached a minimum-viable product level of quality. While contributors dogfood it as their daily driver, it may be unstable for some commands. Future releases will work fill out missing features and improve stability. Its design is also subject to change as it matures. Nu comes with a set 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. From 24ba0d93c704719f719ce698ad69c75132210205 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Sun, 8 Sep 2019 13:02:55 -0400 Subject: [PATCH 044/342] test building akin to azure-pipelines (without release) to hopefully shorten circleci builds Signed-off-by: Vanessa Sochat --- .circleci/config.yml | 16 +--------------- docker/Dockerfile | 1 + docker/Dockerfile.nu-base | 22 +++++++++++++++------- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 80dd80017d..e1c665e444 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -9,15 +9,6 @@ version: 2.1 commands: - check_token: - description: Check that QUAY_TOKEN is provided in environment - steps: - - run: - if [[ -z "${QUAY_TOKEN}" ]]; then - echo "QUAY_TOKEN is undefined. Add to CircleCI environment to continue." - exit 1; - fi - pull_cache: description: Pulls Quay.io docker images usable for our cache steps: @@ -46,7 +37,6 @@ workflows: ignore: - master before_build: - - check_token - pull_cache after_build: - run: @@ -76,7 +66,6 @@ workflows: tags: only: /^v.*/ before_build: - - check_token - pull_cache after_build: - run: @@ -90,7 +79,6 @@ workflows: echo "Version for Docker tag is ${DOCKER_TAG}" docker tag quay.io/nushell/nu-base:latest quay.io/nushell/nu-base:${DOCKER_TAG} docker tag quay.io/nushell/nu:latest quay.io/nushell/nu:${DOCKER_TAG} - docker login -u="nushell+circleci" -p="${QUAY_TOKEN}" quay.io docker push quay.io/nushell/nu-base docker push quay.io/nushell/nu @@ -105,9 +93,8 @@ workflows: registry: quay.io tag: devel dockerfile: docker/Dockerfile.nu-base - extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest + extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest --build-arg RELEASE=true before_build: - - check_token - pull_cache filters: branches: @@ -120,6 +107,5 @@ workflows: - run: name: Publish Development Docker Tags command: | - docker login -u="nushell+circleci" -p="${QUAY_TOKEN}" quay.io docker push quay.io/nushell/nu-base:devel docker push quay.io/nushell/nu:devel diff --git a/docker/Dockerfile b/docker/Dockerfile index d8bc40f657..fa089e77d2 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -3,3 +3,4 @@ FROM quay.io/nushell/nu-base:${FROMTAG} as base FROM rust:1.37-slim COPY --from=base /usr/local/bin/nu /usr/local/bin/nu ENTRYPOINT ["nu"] +CMD ["-l", "info"] diff --git a/docker/Dockerfile.nu-base b/docker/Dockerfile.nu-base index b322efb5b2..faba0213a5 100644 --- a/docker/Dockerfile.nu-base +++ b/docker/Dockerfile.nu-base @@ -1,4 +1,4 @@ -FROM rust:1.37-slim +FROM ubuntu:16.04 # docker build -f docker/Dockerfile.nu-base -t nushell/nu-base . # docker run -it nushell/nu-base @@ -7,12 +7,20 @@ ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y libssl-dev \ libxcb-composite0-dev \ libx11-dev \ - pkg-config - -RUN USER=root cargo new --bin /code + pkg-config \ + curl +ARG RELEASE=false WORKDIR /code -ADD . /code -RUN cargo build --release && cargo run --release -RUN cp target/release/nu /usr/local/bin +COPY ./rust-toolchain ./rust-toolchain +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` +ENV PATH=/root/.cargo/bin:$PATH +COPY . /code +RUN echo "##vso[task.prependpath]/root/.cargo/bin" && \ + rustc -Vv && \ + if $RELEASE; then cargo build --release && cargo run --release; \ + cp target/release/nu /usr/local/bin; \ + else cargo build; \ + cp target/debug/nu /usr/local/bin; fi; ENTRYPOINT ["nu"] +CMD ["-l", "info"] From 99d5dae83a93abbe8b7a2c017c947e3488ffe535 Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Mon, 9 Sep 2019 01:29:52 +0700 Subject: [PATCH 045/342] Fix artifact is missing Signed-off-by: Fahmi Akbar Wildana --- .github/workflows/docker-publish.yml | 2 +- docker/Package.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 30ee8b0418..ee24f94b42 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -51,7 +51,7 @@ jobs: - name: Build and publish exact version run: | REGISTRY=${REGISTRY,,}; export TAG=${GITHUB_REF##*/}-${{ matrix.tag }}; - export NU_BINS=target/release/$( [ ${{ matrix.plugin }} ] && nu* || nu ) + export NU_BINS=target/release/$( [ ${{ matrix.plugin }} = true ] && echo nu* || echo nu ) echo ${{ secrets.DOCKER_REGISTRY }} | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin docker-compose --file docker/docker-compose.package.yml build diff --git a/docker/Package.Dockerfile b/docker/Package.Dockerfile index 7bc2bfe291..5bdc216408 100644 --- a/docker/Package.Dockerfile +++ b/docker/Package.Dockerfile @@ -1,6 +1,6 @@ -ARG artifact ARG base FROM ${base} +ARG artifact COPY ${artifact} /bin/ ENTRYPOINT ["nu"] \ No newline at end of file From da6d6467f3145e37d0954d196119c8655e8f15b1 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Sun, 8 Sep 2019 17:45:26 -0400 Subject: [PATCH 046/342] adding to circle config to test nightly builds Signed-off-by: Vanessa Sochat --- .circleci/config.yml | 58 ++++++++++++++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 10 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e1c665e444..dbc60e702f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -10,11 +10,15 @@ version: 2.1 commands: pull_cache: - description: Pulls Quay.io docker images usable for our cache + description: Pulls Quay.io docker images (latest) for our cache + parameters: + tag: + type: string + default: "devel" steps: - - run: docker pull quay.io/nushell/nu:latest - - run: docker pull quay.io/nushell/nu-base:latest - + - run: echo "Tag is << parameters.tag >>" + - run: docker pull quay.io/nushell/nu:<< parameters.tag >> + - run: docker pull quay.io/nushell/nu-base:<< parameters.tag >> orbs: # https://circleci.com/orbs/registry/orb/circleci/docker @@ -31,7 +35,7 @@ workflows: image: nushell/nu-base tag: latest dockerfile: docker/Dockerfile.nu-base - extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest + extra_build_args: --cache-from=quay.io/nushell/nu-base:devel filters: branches: ignore: @@ -53,20 +57,21 @@ workflows: build_with_deploy: jobs: - # Deploy versioned and latest images on tags (releases) only. + # Deploy versioned and latest images on tags (releases) only - builds --release. - docker/publish: image: nushell/nu-base registry: quay.io tag: latest dockerfile: docker/Dockerfile.nu-base - extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest + extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest --build-arg RELEASE=true filters: branches: ignore: /.*/ tags: only: /^v.*/ before_build: - - pull_cache + - run: docker pull quay.io/nushell/nu:latest + - run: docker pull quay.io/nushell/nu-base:latest after_build: - run: name: Build Multistage (smaller) container @@ -83,7 +88,7 @@ workflows: docker push quay.io/nushell/nu - # publish devel to Docker Hub on merge to master + # publish devel to Docker Hub on merge to master (doesn't build --release) build_with_deploy_devel: jobs: @@ -93,7 +98,7 @@ workflows: registry: quay.io tag: devel dockerfile: docker/Dockerfile.nu-base - extra_build_args: --cache-from=quay.io/nushell/nu-base:latest,quay.io/nushell/nu:latest --build-arg RELEASE=true + extra_build_args: --cache-from=quay.io/nushell/nu-base:devel before_build: - pull_cache filters: @@ -109,3 +114,36 @@ workflows: command: | docker push quay.io/nushell/nu-base:devel docker push quay.io/nushell/nu:devel + + nightly: + jobs: + - docker/publish: + image: nushell/nu-base + registry: quay.io + tag: nightly + dockerfile: docker/Dockerfile.nu-base + extra_build_args: --cache-from=quay.io/nushell/nu-base:nightly --build-arg RELEASE=true + filters: + branches: + only: master + before_build: + - run: docker pull quay.io/nushell/nu:nightly + - run: docker pull quay.io/nushell/nu-base:nightly + after_build: + - run: + name: Build Multistage (smaller) container + command: | + docker build -f docker/Dockerfile -t quay.io/nushell/nu:nightly . + - run: + name: Publish Nightly Nushell Containers + command: | + docker push quay.io/nushell/nu-base:nightly + docker push quay.io/nushell/nu:nightly + + triggers: + - schedule: + cron: "0 22 * * *" + filters: + branches: + only: + - master From d900d8b4c77d3031ffda6a7a67c27bbd26a0994a Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Mon, 9 Sep 2019 05:41:58 +0700 Subject: [PATCH 047/342] Fix can't execute entrypoint Signed-off-by: Fahmi Akbar Wildana --- .github/workflows/docker-publish.yml | 1 + docker/Package.Dockerfile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index ee24f94b42..a726ef9661 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -52,6 +52,7 @@ jobs: run: | REGISTRY=${REGISTRY,,}; export TAG=${GITHUB_REF##*/}-${{ matrix.tag }}; export NU_BINS=target/release/$( [ ${{ matrix.plugin }} = true ] && echo nu* || echo nu ) + chmod +x $NU_BINS echo ${{ secrets.DOCKER_REGISTRY }} | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin docker-compose --file docker/docker-compose.package.yml build diff --git a/docker/Package.Dockerfile b/docker/Package.Dockerfile index 5bdc216408..fd3015c280 100644 --- a/docker/Package.Dockerfile +++ b/docker/Package.Dockerfile @@ -3,4 +3,4 @@ FROM ${base} ARG artifact COPY ${artifact} /bin/ -ENTRYPOINT ["nu"] \ No newline at end of file +ENTRYPOINT ["/bin/nu"] \ No newline at end of file From 7c541000a13998028e97c45872ec1eea04785426 Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Mon, 9 Sep 2019 07:15:51 +0700 Subject: [PATCH 048/342] Iterate over tag rather than base-image Signed-off-by: Fahmi Akbar Wildana --- .github/workflows/docker-publish.yml | 11 ++++++++++- docs/docker.md | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index a726ef9661..f8bb3474a8 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -33,7 +33,16 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - base-image: [debian, 'debian:stable-slim', alpine, 'busybox:glibc', 'busybox:musl'] + tag: + - alpine + - slim + - debian + - glibc-busybox + - musl-busybox + - musl-distroless + - glibc-distroless + - glibc + - musl include: - { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl, plugin: true } - { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu, plugin: true } diff --git a/docs/docker.md b/docs/docker.md index 837707404c..17ed135e92 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -2,7 +2,7 @@ | tag | base image | plugins | package manager | libs & bins | size | | ------------------ | -------------------- | ------- | --------------- | ---------------------------------------------------------------- | ----------- | -| `latest`,`debian` | `debian:latest` | yes | apt | **a lot**, including _glibc_ | ~(48+62) MB | +| `latest`, `debian` | `debian:latest` | yes | apt | **a lot**, including _glibc_ | ~(48+62) MB | | `slim` | `debian:stable-slim` | yes | apt | all `nu:debian` image but exclude [this list][.slimify-excludes] | ~(26+62) MB | | `alpine` | `alpine:latest` | yes | apk | all `nu:musl-busybox` image + libcrypto, libssl, libtls, libz | ~(3+61) MB | | `musl-busybox` | `busybox:musl` | no | — | GNU utils + _musl_ | ~(1+16) MB | From 0ca7aaa56f2042f02fe34f88164e0894bcefd50b Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Mon, 9 Sep 2019 16:45:55 +0700 Subject: [PATCH 049/342] Add libz for glibc-{busybox,distroless} Signed-off-by: Fahmi Akbar Wildana --- .dockerignore | 6 ++++++ .github/workflows/docker-publish.yml | 5 +++-- docker/Package.Dockerfile | 1 + docker/Package.patch.Dockerfile | 9 +++++++++ docker/docker-compose.package.yml | 2 +- 5 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 .dockerignore create mode 100644 docker/Package.patch.Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..9965837eab --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +* +!target/debug/nu* +!target/release/nu* +!dist/* +!LICENSE +!*.md \ No newline at end of file diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index f8bb3474a8..1e6f1881d8 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -47,10 +47,10 @@ jobs: - { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl, plugin: true } - { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu, plugin: true } - { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu, plugin: true } - - { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, plugin: false } + - { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: true } - { tag: musl-busybox, base-image: 'busybox:musl', arch: x86_64-unknown-linux-musl, plugin: false } - { tag: musl-distroless, base-image: 'gcr.io/distroless/static', arch: x86_64-unknown-linux-musl, plugin: false } - - { tag: glibc-distroless, base-image: 'gcr.io/distroless/base', arch: x86_64-unknown-linux-gnu, plugin: false } + - { tag: glibc-distroless, base-image: 'gcr.io/distroless/base', arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: true } - { tag: glibc, base-image: scratch, arch: x86_64-unknown-linux-gnu, plugin: false } - { tag: musl, base-image: scratch, arch: x86_64-unknown-linux-musl, plugin: false } steps: @@ -61,6 +61,7 @@ jobs: run: | REGISTRY=${REGISTRY,,}; export TAG=${GITHUB_REF##*/}-${{ matrix.tag }}; export NU_BINS=target/release/$( [ ${{ matrix.plugin }} = true ] && echo nu* || echo nu ) + export PATCH=$([ ${{ matrix.use-patch }} = true ] && echo .patch || echo '') chmod +x $NU_BINS echo ${{ secrets.DOCKER_REGISTRY }} | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin diff --git a/docker/Package.Dockerfile b/docker/Package.Dockerfile index fd3015c280..a9040d2900 100644 --- a/docker/Package.Dockerfile +++ b/docker/Package.Dockerfile @@ -3,4 +3,5 @@ FROM ${base} ARG artifact COPY ${artifact} /bin/ + ENTRYPOINT ["/bin/nu"] \ No newline at end of file diff --git a/docker/Package.patch.Dockerfile b/docker/Package.patch.Dockerfile new file mode 100644 index 0000000000..250146f88e --- /dev/null +++ b/docker/Package.patch.Dockerfile @@ -0,0 +1,9 @@ +ARG base +FROM debian:stable-slim AS patch +FROM ${base} + +ARG artifact +COPY ${artifact} /bin/ + +COPY --from=patch /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libz.so.1 +ENTRYPOINT ["/bin/nu"] \ No newline at end of file diff --git a/docker/docker-compose.package.yml b/docker/docker-compose.package.yml index 3622fa0cf6..9be36544eb 100644 --- a/docker/docker-compose.package.yml +++ b/docker/docker-compose.package.yml @@ -5,7 +5,7 @@ services: image: ${REGISTRY}/nu:${TAG} build: context: .. - dockerfile: docker/Package.Dockerfile + dockerfile: docker/Package${PATCH}.Dockerfile args: base: ${BASE_IMAGE} artifact: ${NU_BINS} From cf2c19706e31e1f7149c0343927f832e153394e0 Mon Sep 17 00:00:00 2001 From: Maximilian Roos Date: Mon, 9 Sep 2019 06:03:01 -0400 Subject: [PATCH 050/342] fix build warnings & add CI --- .azure/azure-pipelines.yml | 2 ++ src/commands/config.rs | 2 +- src/plugins/binaryview.rs | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 50ebcf1e24..2c36e99691 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -21,5 +21,7 @@ steps: rustc -Vv echo "##vso[task.prependpath]$HOME/.cargo/bin" displayName: Install Rust + - bash: RUSTFLAGS="-D warnings" cargo build --all-features + displayName: Build - bash: RUSTFLAGS="-D warnings" cargo test displayName: Run tests diff --git a/src/commands/config.rs b/src/commands/config.rs index c1f000d04d..5dfb28bf31 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -114,7 +114,7 @@ pub fn config( let key = v.to_string(); if result.contains_key(&key) { - result.remove(&key); + result.swap_remove(&key); config::write_config(&result)?; } else { return Err(ShellError::string(&format!( diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index c321e8115c..a6b8df8990 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -435,7 +435,7 @@ pub fn view_contents_interactive( let cursor = cursor(); let _ = cursor.show(); - let screen = RawScreen::disable_raw_mode(); + let _screen = RawScreen::disable_raw_mode(); Ok(()) } From 1277bfe0fb256059232dbbfd565bd4ffa9b5c6c6 Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 9 Sep 2019 13:02:25 +0200 Subject: [PATCH 051/342] Fix setting configuration params Fixes #627 Fixes a regression caused by #579, specifically commit cc8872b4eec3f39896ccb11d9c25a30a79c04dd7 . The code was intended to perform a comparison between the wanted output type and "Tagged" in order to be able to provide a special-cased path for Tagged. When I wrote the code, I used "name" as a variable name and only later realized that it shadowed the "name" param to the function, so I renamed it to type_name, but forgot to change the comparison. This broke the special-casing, as the name param only contains the name of the struct without generics (like "Tagged"), while `std::any::type_name` (in the current implementation) contains the full paths of the struct including all generic params (like "nu::object::meta::Tagged"). --- src/parser/deserializer.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs index 33a23189f1..d4d492966c 100644 --- a/src/parser/deserializer.rs +++ b/src/parser/deserializer.rs @@ -328,7 +328,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { let type_name = std::any::type_name::(); let tagged_val_name = std::any::type_name::>(); - if name == tagged_val_name { + if type_name == tagged_val_name { return visit::, _>(value.val, name, fields, visitor); } From 1d3483b59015d9cfe67de624cad0e8bbb600fe95 Mon Sep 17 00:00:00 2001 From: est31 Date: Mon, 9 Sep 2019 13:39:43 +0200 Subject: [PATCH 052/342] Add a test --- src/parser/deserializer.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs index d4d492966c..d5427766b5 100644 --- a/src/parser/deserializer.rs +++ b/src/parser/deserializer.rs @@ -467,3 +467,27 @@ impl<'a, 'de: 'a> de::SeqAccess<'de> for StructDeserializer<'a, 'de> { return Some(self.fields.len()); } } + +#[cfg(test)] +mod tests { + use super::*; + use std::any::type_name; + #[test] + fn check_type_name_properties() { + // This ensures that certain properties for the + // std::any::type_name function hold, that + // this code relies on. The type_name docs explicitly + // mention that the actual format of the output + // is unspecified and change is likely. + // This test makes sure that such change is detected + // by this test failing, and not things silently breaking. + // Specifically, we rely on this behaviour further above + // in the file to special case Tagged parsing. + let tuple = type_name::<()>(); + let tagged_tuple = type_name::>(); + let tagged_value = type_name::>(); + assert!(tuple != tagged_tuple); + assert!(tuple != tagged_value); + assert!(tagged_tuple != tagged_value); + } +} From 4a2172a7f2eecacff35cce7f3ebd24ebc31835a3 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Mon, 9 Sep 2019 12:44:07 -0400 Subject: [PATCH 053/342] tweaking nightly build - didnt seem to go off Signed-off-by: Vanessa Sochat --- .circleci/config.yml | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index dbc60e702f..0093753215 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -116,6 +116,13 @@ workflows: docker push quay.io/nushell/nu:devel nightly: + triggers: + - schedule: + cron: "0 0 * * *" + filters: + branches: + only: + - master jobs: - docker/publish: image: nushell/nu-base @@ -123,9 +130,6 @@ workflows: tag: nightly dockerfile: docker/Dockerfile.nu-base extra_build_args: --cache-from=quay.io/nushell/nu-base:nightly --build-arg RELEASE=true - filters: - branches: - only: master before_build: - run: docker pull quay.io/nushell/nu:nightly - run: docker pull quay.io/nushell/nu-base:nightly @@ -139,11 +143,3 @@ workflows: command: | docker push quay.io/nushell/nu-base:nightly docker push quay.io/nushell/nu:nightly - - triggers: - - schedule: - cron: "0 22 * * *" - filters: - branches: - only: - - master From 6b2a7d6793d8b5d3aa8df195b9175c46e36a7234 Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Mon, 9 Sep 2019 23:48:29 +0700 Subject: [PATCH 054/342] Fix .dockerignore compatibility with .circleci/ Signed-off-by: Fahmi Akbar Wildana --- .dockerignore | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.dockerignore b/.dockerignore index 9965837eab..62bfc948d1 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,6 @@ * -!target/debug/nu* -!target/release/nu* +!target/debug/* +!target/release/* !dist/* !LICENSE !*.md \ No newline at end of file From d1167151fc8a0a566189b31735cc9b85142e8273 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 10 Sep 2019 05:10:52 +1200 Subject: [PATCH 055/342] Add support for light tables --- src/commands/config.rs | 2 +- src/format/table.rs | 44 +++++++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/commands/config.rs b/src/commands/config.rs index c1f000d04d..440432c275 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,8 +1,8 @@ use crate::prelude::*; use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{config, Value}; +use crate::errors::ShellError; use crate::parser::hir::SyntaxType; use crate::parser::registry::{self}; use std::iter::FromIterator; diff --git a/src/format/table.rs b/src/format/table.rs index 8deaf30aaf..717e3c4a27 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -16,6 +16,11 @@ pub struct TableView { entries: Vec>, } +enum TableMode { + Light, + Normal, +} + impl TableView { fn merge_descriptors(values: &[Tagged]) -> Vec { let mut ret = vec![]; @@ -198,15 +203,36 @@ impl RenderView for TableView { } let mut table = Table::new(); - table.set_format( - FormatBuilder::new() - .column_separator('│') - .separator(LinePosition::Top, LineSeparator::new('━', '┯', ' ', ' ')) - .separator(LinePosition::Title, LineSeparator::new('─', '┼', ' ', ' ')) - .separator(LinePosition::Bottom, LineSeparator::new('━', '┷', ' ', ' ')) - .padding(1, 1) - .build(), - ); + + let table_mode = crate::data::config::config(Span::unknown())? + .get("table_mode") + .map(|s| match s.as_string().unwrap().as_ref() { + "light" => TableMode::Light, + _ => TableMode::Normal, + }) + .unwrap_or(TableMode::Normal); + + match table_mode { + TableMode::Light => { + table.set_format( + FormatBuilder::new() + .separator(LinePosition::Title, LineSeparator::new('─', '─', ' ', ' ')) + .padding(1, 1) + .build(), + ); + } + _ => { + table.set_format( + FormatBuilder::new() + .column_separator('│') + .separator(LinePosition::Top, LineSeparator::new('━', '┯', ' ', ' ')) + .separator(LinePosition::Title, LineSeparator::new('─', '┼', ' ', ' ')) + .separator(LinePosition::Bottom, LineSeparator::new('━', '┷', ' ', ' ')) + .padding(1, 1) + .build(), + ); + } + } let header: Vec = self .headers From 4d3e7efe25135e3e7bb83250cc17d645a1bac178 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 9 Sep 2019 10:43:10 -0700 Subject: [PATCH 056/342] Close a bunch of holes in external command args Previously, there was a single parsing rule for "bare words" that applied to both internal and external commands. This meant that, because `cargo +nightly` needed to work, we needed to add `+` as a valid character in bare words. The number of characters continued to grow, and the situation was becoming untenable. The current strategy would eventually eat up all syntax and make it impossible to add syntax like `@foo` to internal commands. This patch significantly restricts bare words and introduces a new token type (`ExternalWord`). An `ExternalWord` expands to an error in the internal syntax, but expands to a bare word in the external syntax. `ExternalWords` are highlighted in grey in the shell. --- src/errors.rs | 15 +++ src/evaluate/evaluator.rs | 7 +- src/parser/hir.rs | 8 ++ src/parser/hir/baseline_parse.rs | 46 ++++--- src/parser/hir/baseline_parse_tokens.rs | 16 +-- src/parser/parse/call_node.rs | 16 +++ src/parser/parse/parser.rs | 161 +++++++++++++++++++----- src/parser/parse/pipeline.rs | 36 ++++++ src/parser/parse/token_tree.rs | 21 ++-- src/parser/parse/token_tree_builder.rs | 32 ++++- src/parser/parse/tokens.rs | 6 +- src/parser/parse_command.rs | 3 +- src/shell/helper.rs | 6 +- 13 files changed, 300 insertions(+), 73 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 3eb8e33e9c..d97435d226 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -38,6 +38,7 @@ pub enum ArgumentError { MissingMandatoryFlag(String), MissingMandatoryPositional(String), MissingValueForName(String), + InvalidExternalWord, } #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] @@ -136,6 +137,16 @@ impl ShellError { .start() } + pub(crate) fn invalid_external_word(span: Span) -> ShellError { + ProximateShellError::ArgumentError { + command: "Invalid argument to Nu command (did you mean to call an external command?)" + .into(), + error: ArgumentError::InvalidExternalWord, + span, + } + .start() + } + pub(crate) fn parse_error( error: nom::Err<(nom5_locate::LocatedSpan<&str>, nom::error::ErrorKind)>, ) -> ShellError { @@ -190,6 +201,10 @@ impl ShellError { error, span, } => match error { + ArgumentError::InvalidExternalWord => Diagnostic::new( + Severity::Error, + format!("Invalid bare word for Nu command (did you intend to invoke an external command?)")) + .with_label(Label::new_primary(span)), ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new( Severity::Error, format!( diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index ee241583fd..6419ab73a6 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -1,5 +1,5 @@ use crate::data::base::Block; -use crate::errors::Description; +use crate::errors::{ArgumentError, Description}; use crate::parser::{ hir::{self, Expression, RawExpression}, CommandRegistry, Text, @@ -39,6 +39,11 @@ pub(crate) fn evaluate_baseline_expr( ) -> Result, ShellError> { match &expr.item { RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(literal), source)), + RawExpression::ExternalWord => Err(ShellError::argument_error( + "Invalid external word", + ArgumentError::InvalidExternalWord, + expr.span(), + )), 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), diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 3e155cc059..aaf5bb7711 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -83,6 +83,7 @@ impl ToDebug for Call { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum RawExpression { Literal(Literal), + ExternalWord, Synthetic(Synthetic), Variable(Variable), Binary(Box), @@ -113,6 +114,7 @@ impl RawExpression { match self { RawExpression::Literal(literal) => literal.type_name(), RawExpression::Synthetic(synthetic) => synthetic.type_name(), + RawExpression::ExternalWord => "externalword", RawExpression::FilePath(..) => "filepath", RawExpression::Variable(..) => "variable", RawExpression::List(..) => "list", @@ -189,6 +191,7 @@ impl ToDebug for Expression { match self.item() { RawExpression::Literal(l) => l.tagged(self.span()).fmt_debug(f, source), RawExpression::FilePath(p) => write!(f, "{}", p.display()), + RawExpression::ExternalWord => write!(f, "{}", self.span().slice(source)), 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)), @@ -225,6 +228,11 @@ impl From> for Expression { } } +/// Literals are expressions that are: +/// +/// 1. Copy +/// 2. Can be evaluated without additional context +/// 3. Evaluation cannot produce an error #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Literal { Number(Number), diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index d76a88d510..4437a6d38b 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -1,10 +1,14 @@ use crate::context::Context; +use crate::errors::ShellError; 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() { +pub fn baseline_parse_single_token( + token: &Token, + source: &Text, +) -> Result { + Ok(match *token.item() { RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), RawToken::Size(int, unit) => { hir::Expression::size(int.to_number(source), unit, token.span()) @@ -14,17 +18,22 @@ pub fn baseline_parse_single_token(token: &Token, source: &Text) -> hir::Express hir::Expression::it_variable(span, token.span()) } RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::External(span) => hir::Expression::external_command(span, token.span()), + RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), RawToken::Bare => hir::Expression::bare(token.span()), - } + }) } -pub fn baseline_parse_token_as_number(token: &Token, source: &Text) -> hir::Expression { - match *token.item() { +pub fn baseline_parse_token_as_number( + token: &Token, + source: &Text, +) -> Result { + Ok(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::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), RawToken::Variable(span) => hir::Expression::variable(span, token.span()), RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), RawToken::Size(number, unit) => { @@ -32,33 +41,38 @@ pub fn baseline_parse_token_as_number(token: &Token, source: &Text) -> hir::Expr } 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() { +pub fn baseline_parse_token_as_string( + token: &Token, + source: &Text, +) -> Result { + Ok(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::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), RawToken::Variable(span) => hir::Expression::variable(span, token.span()), RawToken::Number(_) => 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()), - } + }) } pub fn baseline_parse_token_as_path( token: &Token, context: &Context, source: &Text, -) -> hir::Expression { - match *token.item() { +) -> Result { + Ok(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::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), RawToken::Variable(span) => hir::Expression::variable(span, token.span()), RawToken::Number(_) => hir::Expression::bare(token.span()), RawToken::Size(_, _) => hir::Expression::bare(token.span()), @@ -69,7 +83,7 @@ pub fn baseline_parse_token_as_path( RawToken::String(span) => { hir::Expression::file_path(expand_path(span.slice(source), context), token.span()) } - } + }) } pub fn expand_path(string: &str, context: &Context) -> PathBuf { diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index ca9b0bb37c..13c7630fe1 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -33,7 +33,6 @@ pub fn baseline_parse_tokens( Ok(exprs) } - #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum SyntaxType { Any, @@ -62,7 +61,7 @@ impl std::fmt::Display for SyntaxType { SyntaxType::Path => write!(f, "Path"), SyntaxType::Binary => write!(f, "Binary"), SyntaxType::Block => write!(f, "Block"), - SyntaxType::Boolean => write!(f, "Boolean") + SyntaxType::Boolean => write!(f, "Boolean"), } } } @@ -81,7 +80,7 @@ pub fn baseline_parse_next_expr( match (syntax_type, next) { (SyntaxType::Path, TokenNode::Token(token)) => { - return Ok(baseline_parse_token_as_path(token, context, source)) + return baseline_parse_token_as_path(token, context, source) } (SyntaxType::Path, token) => { @@ -92,7 +91,7 @@ pub fn baseline_parse_next_expr( } (SyntaxType::String, TokenNode::Token(token)) => { - return Ok(baseline_parse_token_as_string(token, source)); + return baseline_parse_token_as_string(token, source); } (SyntaxType::String, token) => { @@ -103,7 +102,7 @@ pub fn baseline_parse_next_expr( } (SyntaxType::Number, TokenNode::Token(token)) => { - return Ok(baseline_parse_token_as_number(token, source)); + return Ok(baseline_parse_token_as_number(token, source)?); } (SyntaxType::Number, token) => { @@ -115,7 +114,7 @@ pub fn baseline_parse_next_expr( // TODO: More legit member processing (SyntaxType::Member, TokenNode::Token(token)) => { - return Ok(baseline_parse_token_as_string(token, source)); + return baseline_parse_token_as_string(token, source); } (SyntaxType::Member, token) => { @@ -245,7 +244,7 @@ pub fn baseline_parse_semantic_token( source: &Text, ) -> Result { match token { - TokenNode::Token(token) => Ok(baseline_parse_single_token(token, source)), + TokenNode::Token(token) => baseline_parse_single_token(token, source), TokenNode::Call(_call) => unimplemented!(), TokenNode::Delimited(delimited) => baseline_parse_delimited(delimited, context, source), TokenNode::Pipeline(_pipeline) => unimplemented!(), @@ -315,7 +314,8 @@ pub fn baseline_parse_path( RawToken::Number(_) | RawToken::Size(..) | RawToken::Variable(_) - | RawToken::External(_) => { + | RawToken::ExternalCommand(_) + | RawToken::ExternalWord => { return Err(ShellError::type_error( "String", token.type_name().simple_spanned(part), diff --git a/src/parser/parse/call_node.rs b/src/parser/parse/call_node.rs index 2869abb449..eb715cd376 100644 --- a/src/parser/parse/call_node.rs +++ b/src/parser/parse/call_node.rs @@ -1,5 +1,7 @@ use crate::parser::TokenNode; +use crate::traits::ToDebug; use getset::Getters; +use std::fmt; #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters)] pub struct CallNode { @@ -24,3 +26,17 @@ impl CallNode { } } } + +impl ToDebug for CallNode { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + write!(f, "{}", self.head.debug(source))?; + + if let Some(children) = &self.children { + for child in children { + write!(f, "{}", child.debug(source))? + } + } + + Ok(()) + } +} diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index f230c36c22..a691fb2444 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -236,12 +236,34 @@ pub fn bare(input: NomSpan) -> IResult { let start = input.offset; let (input, _) = take_while1(is_start_bare_char)(input)?; let (input, _) = take_while(is_bare_char)(input)?; + + let next_char = &input.fragment.chars().nth(0); + + if let Some(next_char) = next_char { + if is_external_word_char(*next_char) { + return Err(nom::Err::Error(nom::error::make_error( + input, + nom::error::ErrorKind::TakeWhile1, + ))); + } + } + let end = input.offset; Ok((input, TokenTreeBuilder::spanned_bare((start, end)))) }) } +pub fn external_word(input: NomSpan) -> IResult { + trace_step(input, "bare", move |input| { + let start = input.offset; + let (input, _) = take_while1(is_external_word_char)(input)?; + let end = input.offset; + + Ok((input, TokenTreeBuilder::spanned_external_word((start, end)))) + }) +} + pub fn var(input: NomSpan) -> IResult { trace_step(input, "var", move |input| { let start = input.offset; @@ -364,8 +386,17 @@ pub fn size(input: NomSpan) -> IResult { pub fn leaf(input: NomSpan) -> IResult { trace_step(input, "leaf", move |input| { - let (input, node) = - alt((size, string, operator, flag, shorthand, var, external, bare))(input)?; + let (input, node) = alt(( + size, + string, + operator, + flag, + shorthand, + var, + external, + bare, + external_word, + ))(input)?; Ok((input, node)) }) @@ -582,26 +613,13 @@ pub fn pipeline(input: NomSpan) -> IResult { } fn make_call_list( - head: Option<( - Option, - Tagged, - Option - )>, - items: Vec<( - NomSpan, - Option, - Tagged, - Option, - )>, + head: Option<(Option, Tagged, Option)>, + items: Vec<(NomSpan, Option, Tagged, Option)>, ) -> Vec { let mut out = vec![]; if let Some(head) = head { - let el = PipelineElement::new( - None, - head.0.map(Span::from), - head.1, - head.2.map(Span::from)); + let el = PipelineElement::new(None, head.0.map(Span::from), head.1, head.2.map(Span::from)); out.push(el); } @@ -611,7 +629,8 @@ fn make_call_list( Some(pipe).map(Span::from), ws1.map(Span::from), call, - ws2.map(Span::from)); + ws2.map(Span::from), + ); out.push(el); } @@ -628,40 +647,39 @@ fn int(frag: &str, neg: Option) -> i64 { } } +fn is_external_word_char(c: char) -> bool { + match c { + ';' | '|' | '#' | '-' | '"' | '\'' | '$' | '(' | ')' | '[' | ']' | '{' | '}' | '`' => false, + other if other.is_whitespace() => false, + _ => true, + } +} + fn is_start_bare_char(c: char) -> bool { match c { + '+' => false, _ if c.is_alphabetic() => true, - _ if c.is_numeric() => true, '.' => true, '\\' => true, '/' => true, '_' => true, '-' => true, - '@' => true, - '*' => true, - '?' => true, '~' => true, - '+' => true, _ => false, } } fn is_bare_char(c: char) -> bool { match c { + '+' => false, _ if c.is_alphanumeric() => true, - ':' => true, '.' => true, '\\' => true, '/' => true, '_' => true, '-' => true, - '@' => true, - '*' => true, - '?' => true, '=' => true, '~' => true, - '+' => true, - '%' => true, _ => false, } } @@ -724,6 +742,44 @@ mod tests { } } + macro_rules! equal_tokens { + ($source:tt -> $tokens:expr) => { + let result = apply(pipeline, "pipeline", $source); + let (expected_tree, expected_source) = TokenTreeBuilder::build($tokens); + + if result != expected_tree { + let debug_result = format!("{}", result.debug($source)); + let debug_expected = format!("{}", expected_tree.debug(&expected_source)); + + if debug_result == debug_expected { + assert_eq!( + result, expected_tree, + "NOTE: actual and expected had equivalent debug serializations, source={:?}, debug_expected={:?}", + $source, + debug_expected + ) + } else { + assert_eq!(debug_result, debug_expected) + } + } + + // apply(pipeline, "pipeline", r#"cargo +nightly run"#), + // build_token(b::pipeline(vec![( + // None, + // b::call( + // b::bare("cargo"), + // vec![ + // b::sp(), + // b::external_word("+nightly"), + // b::sp(), + // b::bare("run") + // ] + // ), + // None + // )])) + }; + } + #[test] fn test_integer() { assert_leaf! { @@ -854,7 +910,7 @@ mod tests { fn test_external() { assert_leaf! { parsers [ external ] - "^ls" -> 0..3 { External(span(1, 3)) } + "^ls" -> 0..3 { ExternalCommand(span(1, 3)) } } } @@ -1058,6 +1114,46 @@ mod tests { ); } + #[test] + fn test_external_word() { + let _ = pretty_env_logger::try_init(); + + equal_tokens!( + "cargo +nightly run" -> + b::pipeline(vec![( + None, + b::call( + b::bare("cargo"), + vec![ + b::sp(), + b::external_word("+nightly"), + b::sp(), + b::bare("run") + ] + ), + None + )]) + ); + + equal_tokens!( + "rm foo%bar" -> + b::pipeline(vec![( + None, + b::call(b::bare("rm"), vec![b::sp(), b::external_word("foo%bar"),]), + None + )]) + ); + + equal_tokens!( + "rm foo%bar" -> + b::pipeline(vec![( + None, + b::call(b::bare("rm"), vec![b::sp(), b::external_word("foo%bar"),]), + None + )]) + ); + } + #[test] fn test_smoke_pipeline() { let _ = pretty_env_logger::try_init(); @@ -1178,7 +1274,6 @@ mod tests { } fn build_token(block: CurriedToken) -> TokenNode { - let mut builder = TokenTreeBuilder::new(); - block(&mut builder) + TokenTreeBuilder::build(block).0 } } diff --git a/src/parser/parse/pipeline.rs b/src/parser/parse/pipeline.rs index 75155d143a..64a899c179 100644 --- a/src/parser/parse/pipeline.rs +++ b/src/parser/parse/pipeline.rs @@ -1,7 +1,9 @@ use crate::parser::CallNode; +use crate::traits::ToDebug; use crate::{Span, Tagged}; use derive_new::new; use getset::Getters; +use std::fmt; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)] pub struct Pipeline { @@ -9,6 +11,20 @@ pub struct Pipeline { pub(crate) post_ws: Option, } +impl ToDebug for Pipeline { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + for part in &self.parts { + write!(f, "{}", part.debug(source))?; + } + + if let Some(post_ws) = self.post_ws { + write!(f, "{}", post_ws.slice(source))? + } + + Ok(()) + } +} + #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] pub struct PipelineElement { pub pipe: Option, @@ -17,3 +33,23 @@ pub struct PipelineElement { call: Tagged, pub post_ws: Option, } + +impl ToDebug for PipelineElement { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + if let Some(pipe) = self.pipe { + write!(f, "{}", pipe.slice(source))?; + } + + if let Some(pre_ws) = self.pre_ws { + write!(f, "{}", pre_ws.slice(source))?; + } + + write!(f, "{}", self.call.debug(source))?; + + if let Some(post_ws) = self.post_ws { + write!(f, "{}", post_ws.slice(source))?; + } + + Ok(()) + } +} diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index df189a1a0c..f69c176e97 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -1,5 +1,6 @@ use crate::errors::ShellError; use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*}; +use crate::traits::ToDebug; use crate::{Span, Tagged, Text}; use derive_new::new; use enum_utils::FromStr; @@ -22,6 +23,12 @@ pub enum TokenNode { Path(Tagged), } +impl ToDebug for TokenNode { + fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { + write!(f, "{:?}", self.old_debug(&Text::from(source))) + } +} + pub struct DebugTokenNode<'a> { node: &'a TokenNode, source: &'a Text, @@ -34,11 +41,11 @@ impl fmt::Debug for DebugTokenNode<'_> { TokenNode::Call(s) => { write!(f, "(")?; - write!(f, "{:?}", s.head().debug(self.source))?; + write!(f, "{}", s.head().debug(self.source))?; if let Some(children) = s.children() { for child in children { - write!(f, "{:?}", child.debug(self.source))?; + write!(f, "{}", child.debug(self.source))?; } } @@ -57,7 +64,7 @@ impl fmt::Debug for DebugTokenNode<'_> { )?; for child in d.children() { - write!(f, "{:?}", child.debug(self.source))?; + write!(f, "{:?}", child.old_debug(self.source))?; } write!( @@ -70,7 +77,7 @@ impl fmt::Debug for DebugTokenNode<'_> { } ) } - TokenNode::Pipeline(_) => write!(f, ""), + TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)), TokenNode::Error(s) => write!(f, " for {:?}", s.span().slice(self.source)), rest => write!(f, "{}", rest.span().slice(self.source)), } @@ -115,7 +122,7 @@ impl TokenNode { .to_string() } - pub fn debug<'a>(&'a self, source: &'a Text) -> DebugTokenNode<'a> { + pub fn old_debug<'a>(&'a self, source: &'a Text) -> DebugTokenNode<'a> { DebugTokenNode { node: self, source } } @@ -140,7 +147,7 @@ impl TokenNode { pub fn is_external(&self) -> bool { match self { TokenNode::Token(Tagged { - item: RawToken::External(..), + item: RawToken::ExternalCommand(..), .. }) => true, _ => false, @@ -150,7 +157,7 @@ impl TokenNode { pub fn expect_external(&self) -> Span { match self { TokenNode::Token(Tagged { - item: RawToken::External(span), + item: RawToken::ExternalCommand(span), .. }) => *span, _ => panic!("Only call expect_external if you checked is_external first"), diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index 9dd1ebc16c..8034e8b0c7 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -14,15 +14,19 @@ use derive_new::new; pub struct TokenTreeBuilder { #[new(default)] pos: usize, + + #[new(default)] + output: String, } pub type CurriedToken = Box TokenNode + 'static>; pub type CurriedCall = Box Tagged + 'static>; impl TokenTreeBuilder { - pub fn build(block: impl FnOnce(&mut Self) -> TokenNode) -> TokenNode { + pub fn build(block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { let mut builder = TokenTreeBuilder::new(); - block(&mut builder) + let node = block(&mut builder); + (node, builder.output) } pub fn pipeline(input: Vec<(Option<&str>, CurriedCall, Option<&str>)>) -> CurriedToken { @@ -56,7 +60,8 @@ impl TokenTreeBuilder { pipe, pre_span.map(Span::from), call, - post_span.map(Span::from))); + post_span.map(Span::from), + )); loop { match input.next() { @@ -147,9 +152,27 @@ impl TokenTreeBuilder { )) } + pub fn external_word(input: impl Into) -> CurriedToken { + let input = input.into(); + + Box::new(move |b| { + let (start, end) = b.consume(&input); + b.pos = end; + + TokenTreeBuilder::spanned_external_word((start, end)) + }) + } + + pub fn spanned_external_word(input: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::ExternalWord, + input.into(), + )) + } + pub fn spanned_external(input: impl Into, span: impl Into) -> TokenNode { TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::External(input.into()), + RawToken::ExternalCommand(input.into()), span.into(), )) } @@ -422,6 +445,7 @@ impl TokenTreeBuilder { fn consume(&mut self, input: &str) -> (usize, usize) { let start = self.pos; self.pos += input.len(); + self.output.push_str(input); (start, self.pos) } } diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index ed9c1f72a4..0bb2e3f17d 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -10,7 +10,8 @@ pub enum RawToken { Size(RawNumber, Unit), String(Span), Variable(Span), - External(Span), + ExternalCommand(Span), + ExternalWord, Bare, } @@ -50,7 +51,8 @@ impl RawToken { RawToken::Size(..) => "Size", RawToken::String(_) => "String", RawToken::Variable(_) => "Variable", - RawToken::External(_) => "External", + RawToken::ExternalCommand(_) => "ExternalCommand", + RawToken::ExternalWord => "ExternalWord", RawToken::Bare => "String", } } diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index 33ad25e6f3..e0fc9d86fc 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -6,6 +6,7 @@ use crate::parser::{ hir::{self, NamedArguments}, Flag, RawToken, TokenNode, }; +use crate::traits::ToDebug; use crate::{Span, Tag, Tagged, Text}; use log::trace; @@ -248,7 +249,7 @@ pub fn trace_remaining(desc: &'static str, tail: hir::TokensIterator<'_>, source itertools::join( tail.debug_remaining() .iter() - .map(|i| format!("%{:?}%", i.debug(source))), + .map(|i| format!("%{}%", i.debug(&source))), " " ) ); diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 8e21c50ec8..462f375291 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -136,9 +136,13 @@ fn paint_token_node(token_node: &TokenNode, line: &str) -> String { .. }) => Color::Green.normal().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { - item: RawToken::External(..), + item: RawToken::ExternalCommand(..), .. }) => Color::Cyan.bold().paint(token_node.span().slice(line)), + TokenNode::Token(Tagged { + item: RawToken::ExternalWord, + .. + }) => Color::Black.bold().paint(token_node.span().slice(line)), }; styled.to_string() From d4240ffb4d8691c69d24b85de84b1fa78dfed483 Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Tue, 10 Sep 2019 01:10:45 +0700 Subject: [PATCH 057/342] =?UTF-8?q?Delete=20.dockerignore=20=E2=9A=A0?= =?UTF-8?q?=EF=B8=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit something weird about CircleCI build it can't find target/release/nu although it's whitelisted in the .dockerignore 🤔 --- .dockerignore | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 62bfc948d1..0000000000 --- a/.dockerignore +++ /dev/null @@ -1,6 +0,0 @@ -* -!target/debug/* -!target/release/* -!dist/* -!LICENSE -!*.md \ No newline at end of file From 45201cb28427241ea9428faa2cac8c8f8461dedf Mon Sep 17 00:00:00 2001 From: Maximilian Roos Date: Mon, 9 Sep 2019 17:04:14 -0400 Subject: [PATCH 058/342] combine build & test --- .azure/azure-pipelines.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 2c36e99691..b384e8df38 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -21,7 +21,5 @@ steps: rustc -Vv echo "##vso[task.prependpath]$HOME/.cargo/bin" displayName: Install Rust - - bash: RUSTFLAGS="-D warnings" cargo build --all-features - displayName: Build - - bash: RUSTFLAGS="-D warnings" cargo test + - bash: RUSTFLAGS="-D warnings" cargo test --all-features displayName: Run tests From f61144006f6f5cdb657869ab2c3527f8787ae20b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 10 Sep 2019 05:08:01 -0500 Subject: [PATCH 059/342] config test harness. --- src/commands/config.rs | 45 +++++++++---- src/data/config.rs | 94 ++++++++++++++++++--------- src/lib.rs | 1 + tests/command_config_test.rs | 120 +++++++++++++++++++++++++++++++++++ tests/helpers/mod.rs | 22 +++++-- 5 files changed, 237 insertions(+), 45 deletions(-) create mode 100644 tests/command_config_test.rs diff --git a/src/commands/config.rs b/src/commands/config.rs index 440432c275..c60ea2f2d5 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,16 +1,17 @@ -use crate::prelude::*; - use crate::commands::WholeStreamCommand; use crate::data::{config, Value}; use crate::errors::ShellError; use crate::parser::hir::SyntaxType; use crate::parser::registry::{self}; +use crate::prelude::*; use std::iter::FromIterator; +use std::path::PathBuf; pub struct Config; #[derive(Deserialize)] pub struct ConfigArgs { + load: Option>, set: Option<(Tagged, Tagged)>, get: Option>, clear: Tagged, @@ -25,6 +26,7 @@ impl WholeStreamCommand for Config { fn signature(&self) -> Signature { Signature::build("config") + .named("load", SyntaxType::Path) .named("set", SyntaxType::Any) .named("get", SyntaxType::Any) .named("remove", SyntaxType::Any) @@ -47,6 +49,7 @@ impl WholeStreamCommand for Config { pub fn config( ConfigArgs { + load, set, get, clear, @@ -55,7 +58,15 @@ pub fn config( }: ConfigArgs, RunnableContext { name, .. }: RunnableContext, ) -> Result { - let mut result = crate::data::config::config(name)?; + let name_span = name; + + let configuration = if let Some(supplied) = load { + Some(supplied.item().clone()) + } else { + None + }; + + let mut result = crate::data::config::read(name_span, &configuration)?; if let Some(v) = get { let key = v.to_string(); @@ -63,15 +74,27 @@ pub fn config( .get(&key) .ok_or_else(|| ShellError::string(&format!("Missing key {} in config", key)))?; - return Ok( - stream![value.clone()].into(), // futures::stream::once(futures::future::ready(ReturnSuccess::Value(value.clone()))).into(), - ); + let mut results = VecDeque::new(); + + match value { + Tagged { + item: Value::Table(list), + .. + } => { + for l in list { + results.push_back(ReturnSuccess::value(l.clone())); + } + } + x => results.push_back(ReturnSuccess::value(x.clone())), + } + + return Ok(results.to_output_stream()); } if let Some((key, value)) = set { result.insert(key.to_string(), value.clone()); - config::write_config(&result)?; + config::write(&result, &configuration)?; return Ok(stream![Tagged::from_simple_spanned_item( Value::Row(result.into()), @@ -87,7 +110,7 @@ pub fn config( { result.clear(); - config::write_config(&result)?; + config::write(&result, &configuration)?; return Ok(stream![Tagged::from_simple_spanned_item( Value::Row(result.into()), @@ -101,10 +124,10 @@ pub fn config( tag: Tag { span, .. }, } = path { - let path = config::config_path()?; + let path = config::default_path_for(&configuration)?; return Ok(stream![Tagged::from_simple_spanned_item( - Value::Primitive(Primitive::Path(path)), + Value::string(path.to_string_lossy()), span )] .from_input_stream()); @@ -115,7 +138,7 @@ pub fn config( if result.contains_key(&key) { result.remove(&key); - config::write_config(&result)?; + config::write(&result, &configuration)?; } else { return Err(ShellError::string(&format!( "{} does not exist in config", diff --git a/src/data/config.rs b/src/data/config.rs index cf384e8b68..08296c09a6 100644 --- a/src/data/config.rs +++ b/src/data/config.rs @@ -11,52 +11,60 @@ use std::fs::{self, OpenOptions}; use std::io; use std::path::{Path, PathBuf}; -const APP_INFO: AppInfo = AppInfo { - name: "nu", - author: "nu shell developers", -}; - #[derive(Deserialize, Serialize)] struct Config { #[serde(flatten)] extra: IndexMap>, } -pub(crate) fn config_path() -> Result { - let location = app_root(AppDataType::UserConfig, &APP_INFO) - .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; +pub const APP_INFO: AppInfo = AppInfo { + name: "nu", + author: "nu shell developers", +}; - Ok(location.join("config.toml")) +pub fn config_path() -> Result { + let path = app_root(AppDataType::UserConfig, &APP_INFO) + .map_err(|err| ShellError::string(&format!("Couldn't open config path:\n{}", err)))?; + + Ok(path) } -pub(crate) fn write_config(config: &IndexMap>) -> Result<(), ShellError> { - let location = app_root(AppDataType::UserConfig, &APP_INFO) - .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; - - let filename = location.join("config.toml"); - touch(&filename)?; - - let contents = - value_to_toml_value(&Value::Row(Dictionary::new(config.clone())).tagged_unknown())?; - - let contents = toml::to_string(&contents)?; - - fs::write(&filename, &contents)?; - - Ok(()) +pub fn default_path() -> Result { + default_path_for(&None) } -pub(crate) fn config(span: impl Into) -> Result>, ShellError> { - let span = span.into(); +pub fn default_path_for(file: &Option) -> Result { + let filename = &mut config_path()?; + let filename = match file { + None => { + filename.push("config.toml"); + filename + } + Some(file) => { + filename.push(file); + filename + } + }; - let location = app_root(AppDataType::UserConfig, &APP_INFO) - .map_err(|err| ShellError::string(&format!("Couldn't open config file:\n{}", err)))?; + Ok(filename.clone()) +} + +pub fn read( + span: impl Into, + at: &Option, +) -> Result>, ShellError> { + let filename = default_path()?; + + let filename = match at { + None => filename, + Some(ref file) => file.clone(), + }; - let filename = location.join("config.toml"); touch(&filename)?; trace!("config file = {}", filename.display()); + let span = span.into(); let contents = fs::read_to_string(filename) .map(|v| v.simple_spanned(span)) .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?; @@ -75,6 +83,34 @@ pub(crate) fn config(span: impl Into) -> Result) -> Result>, ShellError> { + read(span, &None) +} + +pub fn write( + config: &IndexMap>, + at: &Option, +) -> Result<(), ShellError> { + let filename = &mut default_path()?; + let filename = match at { + None => filename, + Some(file) => { + filename.pop(); + filename.push(file); + filename + } + }; + + let contents = + value_to_toml_value(&Value::Row(Dictionary::new(config.clone())).tagged_unknown())?; + + let contents = toml::to_string(&contents)?; + + fs::write(&filename, &contents)?; + + Ok(()) +} + // A simple implementation of `% touch path` (ignores existing files) fn touch(path: &Path) -> io::Result<()> { match OpenOptions::new().create(true).write(true).open(path) { diff --git a/src/lib.rs b/src/lib.rs index 4e16352e05..a18343df9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder; pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; pub use cli::cli; +pub use data::config::{APP_INFO, config_path}; pub use data::base::{Primitive, Value}; pub use data::dict::{Dictionary, TaggedDictBuilder}; pub use data::meta::{Span, Tag, Tagged, TaggedItem}; diff --git a/tests/command_config_test.rs b/tests/command_config_test.rs new file mode 100644 index 0000000000..41038ddb12 --- /dev/null +++ b/tests/command_config_test.rs @@ -0,0 +1,120 @@ +mod helpers; + +use helpers as h; +use helpers::{Playground, Stub::*}; + +use std::path::PathBuf; + +#[test] +fn has_default_configuration_file() { + let expected = "config.toml"; + + Playground::setup("config_test_1", |dirs, _| { + + nu!(cwd: dirs.root(), "config"); + + assert_eq!( + dirs.config_path().join(expected), + nu::config_path().unwrap().join(expected) + ); + }) +} + +#[test] +fn shows_path_of_configuration_file() { + let expected = "config.toml"; + + Playground::setup("config_test_2", |dirs, _| { + + let actual = nu!( + cwd: dirs.test(), + "config --path | echo $it" + ); + + assert_eq!(PathBuf::from(actual), dirs.config_path().join(expected)); + }); +} + +#[test] +fn use_different_configuration() { + Playground::setup("config_test_3", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "test_3.toml", + r#" + caballero_1 = "Andrés N. Robalino" + caballero_2 = "Jonathan Turner" + caballero_3 = "Yehuda katz" + "# + )]); + + let actual = nu!( + cwd: dirs.root(), + "config --get caballero_1 --load {}/test_3.toml | echo $it", + dirs.test() + ); + + assert_eq!(actual, "Andrés N. Robalino"); + }); + + h::delete_file_at(nu::config_path().unwrap().join("test_3.toml")); +} + +#[test] +fn sets_configuration_value() { + Playground::setup("config_test_4", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "test_4.toml", + r#" + caballero_1 = "Andrés N. Robalino" + caballero_2 = "Jonathan Turner" + caballero_3 = "Yehuda katz" + "# + )]); + + nu!( + cwd: dirs.test(), + "config --load test_4.toml --set [caballero_4 jonas]" + ); + + let actual = nu!( + cwd: dirs.root(), + r#"open "{}/test_4.toml" | get caballero_4 | echo $it"#, + dirs.config_path() + ); + + assert_eq!(actual, "jonas"); + }); + + h::delete_file_at(nu::config_path().unwrap().join("test_4.toml")); +} + +#[test] +fn removes_configuration_value() { + Playground::setup("config_test_5", |dirs, sandbox| { + sandbox + .with_files(vec![FileWithContent( + "test_5.toml", + r#" + caballeros = [1, 1, 1] + podershell = [1, 1, 1] + "# + )]); + + nu!( + cwd: dirs.test(), + "config --load test_5.toml --remove podershell" + ); + + let actual = nu_error!( + cwd: dirs.root(), + r#"open "{}/test_5.toml" | get podershell | echo $it"#, + dirs.config_path() + ); + + assert!(actual.contains("table missing column")); + }); + + h::delete_file_at(nu::config_path().unwrap().join("test_5.toml")); +} \ No newline at end of file diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 1c2bfef0e0..04fd889925 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -4,6 +4,7 @@ use glob::glob; pub use std::path::Path; pub use std::path::PathBuf; +use app_dirs::{get_app_root, AppDataType}; use getset::Getters; use std::io::Read; use tempfile::{tempdir, TempDir}; @@ -177,6 +178,10 @@ impl Dirs { pub fn formats(&self) -> PathBuf { PathBuf::from(self.fixtures.join("formats")) } + + pub fn config_path(&self) -> PathBuf { + get_app_root(AppDataType::UserConfig, &nu::APP_INFO).unwrap() + } } impl Playground { @@ -227,11 +232,10 @@ impl Playground { playground_root.join(topic).display() )); - let root = - dunce::canonicalize(playground_root).expect(&format!( - "Couldn't canonicalize tests root path {}", - playground_root.display() - )); + let root = dunce::canonicalize(playground_root).expect(&format!( + "Couldn't canonicalize tests root path {}", + playground_root.display() + )); let dirs = Dirs { root, @@ -332,6 +336,14 @@ pub fn line_ending() -> String { } } +pub fn delete_file_at(full_path: impl AsRef) { + let full_path = full_path.as_ref(); + + if full_path.exists() { + std::fs::remove_file(full_path).expect("can not delete file"); + } +} + pub fn create_file_at(full_path: impl AsRef) -> Result<(), std::io::Error> { let full_path = full_path.as_ref(); From 11ef00749140dcd7304b4195309c6dc555e8cc72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 10 Sep 2019 05:28:15 -0500 Subject: [PATCH 060/342] Paths can be displayed as strings. --- src/commands/config.rs | 2 +- src/data/base.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/commands/config.rs b/src/commands/config.rs index c60ea2f2d5..78f1ad399c 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -127,7 +127,7 @@ pub fn config( let path = config::default_path_for(&configuration)?; return Ok(stream![Tagged::from_simple_spanned_item( - Value::string(path.to_string_lossy()), + Value::Primitive(Primitive::Path(path)), span )] .from_input_stream()); diff --git a/src/data/base.rs b/src/data/base.rs index b48d692123..02ce4982b1 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -558,6 +558,7 @@ impl Value { Value::Primitive(Primitive::Decimal(x)) => Ok(format!("{}", x)), Value::Primitive(Primitive::Int(x)) => Ok(format!("{}", x)), Value::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)), + Value::Primitive(Primitive::Path(x)) => Ok(format!("{}", x.display())), // TODO: this should definitely be more general with better errors other => Err(ShellError::string(format!( "Expected string, got {:?}", From ba8383ae2fc129d95d2463241811f4ae6fea0773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 10 Sep 2019 07:00:25 -0500 Subject: [PATCH 061/342] to-[csv/tsv] fixes. --- src/commands/to_csv.rs | 10 ++++++++-- src/commands/to_tsv.rs | 8 +++++++- src/plugins/binaryview.rs | 2 +- tests/commands_test.rs | 2 +- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 0d13cab8fc..615e49cbf8 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -37,6 +37,9 @@ pub fn value_to_csv_value(v: &Value) -> Value { Value::Primitive(Primitive::String(s)) => Value::Primitive(Primitive::String(s.clone())), Value::Primitive(Primitive::Nothing) => Value::Primitive(Primitive::Nothing), Value::Primitive(Primitive::Boolean(b)) => Value::Primitive(Primitive::Boolean(b.clone())), + Value::Primitive(Primitive::Decimal(f)) => Value::Primitive(Primitive::Decimal(f.clone())), + Value::Primitive(Primitive::Int(i)) => Value::Primitive(Primitive::Int(i.clone())), + Value::Primitive(Primitive::Path(x)) => Value::Primitive(Primitive::Path(x.clone())), Value::Primitive(Primitive::Bytes(b)) => Value::Primitive(Primitive::Bytes(b.clone())), Value::Primitive(Primitive::Date(d)) => Value::Primitive(Primitive::Date(d.clone())), Value::Row(o) => Value::Row(o.clone()), @@ -51,8 +54,11 @@ fn to_string_helper(v: &Value) -> Result { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), Value::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?), - Value::Table(_) => return Ok(String::from("[list list]")), - Value::Row(_) => return Ok(String::from("[object]")), + Value::Primitive(Primitive::Decimal(_)) => Ok(v.as_string()?), + Value::Primitive(Primitive::Int(_)) => Ok(v.as_string()?), + Value::Primitive(Primitive::Path(_)) => Ok(v.as_string()?), + Value::Table(_) => return Ok(String::from("[Table]")), + Value::Row(_) => return Ok(String::from("[Row]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), _ => return Err(ShellError::string("Unexpected value")), } diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index 2842db2315..a0cbf1c1bd 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -37,6 +37,9 @@ pub fn value_to_tsv_value(v: &Value) -> Value { Value::Primitive(Primitive::String(s)) => Value::Primitive(Primitive::String(s.clone())), Value::Primitive(Primitive::Nothing) => Value::Primitive(Primitive::Nothing), Value::Primitive(Primitive::Boolean(b)) => Value::Primitive(Primitive::Boolean(b.clone())), + Value::Primitive(Primitive::Decimal(f)) => Value::Primitive(Primitive::Decimal(f.clone())), + Value::Primitive(Primitive::Int(i)) => Value::Primitive(Primitive::Int(i.clone())), + Value::Primitive(Primitive::Path(x)) => Value::Primitive(Primitive::Path(x.clone())), Value::Primitive(Primitive::Bytes(b)) => Value::Primitive(Primitive::Bytes(b.clone())), Value::Primitive(Primitive::Date(d)) => Value::Primitive(Primitive::Date(d.clone())), Value::Row(o) => Value::Row(o.clone()), @@ -51,10 +54,13 @@ fn to_string_helper(v: &Value) -> Result { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), Value::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?), + Value::Primitive(Primitive::Decimal(_)) => Ok(v.as_string()?), + Value::Primitive(Primitive::Int(_)) => Ok(v.as_string()?), + Value::Primitive(Primitive::Path(_)) => Ok(v.as_string()?), Value::Table(_) => return Ok(String::from("[table]")), Value::Row(_) => return Ok(String::from("[row]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), - _ => Err(ShellError::string("Unexpected value")), + _ => return Err(ShellError::string("Unexpected value")), } } diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index c321e8115c..a6b8df8990 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -435,7 +435,7 @@ pub fn view_contents_interactive( let cursor = cursor(); let _ = cursor.show(); - let screen = RawScreen::disable_raw_mode(); + let _screen = RawScreen::disable_raw_mode(); Ok(()) } diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 4e66aa7ab3..5188aee0aa 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -63,7 +63,7 @@ fn save_can_write_out_csv() { ); let actual = h::file_contents(expected_file); - assert!(actual.contains("[list list],A shell for the GitHub era,2018,ISC,nu,0.2.0")); + assert!(actual.contains("[Table],A shell for the GitHub era,2018,ISC,nu,0.2.0")); }) } From b15bb2c667c122b161a01131c2143e96b39430e3 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 10 Sep 2019 08:31:21 -0700 Subject: [PATCH 062/342] Added glob patterns to the syntax shapes Bare words now represent literal file names, and globs are a different syntax shape called "Pattern". This allows commands like `cp` to ask for a pattern as a source and a literal file as a target. This also means that attempting to pass a glob to a command that expects a literal path will produce an error. --- src/commands/cp.rs | 2 +- src/commands/to_bson.rs | 1 + src/commands/to_json.rs | 1 + src/commands/to_sqlite.rs | 1 + src/commands/to_toml.rs | 1 + src/commands/to_yaml.rs | 1 + src/data/base.rs | 8 ++++ src/evaluate/evaluator.rs | 1 + src/parser/hir.rs | 10 ++++- src/parser/hir/baseline_parse.rs | 49 +++++++++++++++++++++++++ src/parser/hir/baseline_parse_tokens.rs | 16 +++++++- src/parser/parse/parser.rs | 35 +++++++++++++++++- src/parser/parse/token_tree_builder.rs | 18 +++++++++ src/parser/parse/tokens.rs | 2 + src/shell/helper.rs | 4 ++ 15 files changed, 146 insertions(+), 4 deletions(-) diff --git a/src/commands/cp.rs b/src/commands/cp.rs index 8160fc9d2d..491e18b1aa 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -21,7 +21,7 @@ impl PerItemCommand for Cpy { fn signature(&self) -> Signature { Signature::build("cp") - .required("src", SyntaxType::Path) + .required("src", SyntaxType::Pattern) .required("dst", SyntaxType::Path) .named("file", SyntaxType::Any) .switch("recursive") diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index bb0355a5e9..a77bebeacc 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -50,6 +50,7 @@ pub fn value_to_bson_value(v: &Tagged) -> Result { } Value::Primitive(Primitive::Nothing) => Bson::Null, Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()), + Value::Primitive(Primitive::Pattern(p)) => Bson::String(p.clone()), Value::Primitive(Primitive::Path(s)) => Bson::String(s.display().to_string()), Value::Table(l) => Bson::Array( l.iter() diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index f53fbd8d28..35c03af32d 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -45,6 +45,7 @@ pub fn value_to_json_value(v: &Tagged) -> Result::coerce_into(i.tagged(v.tag), "converting to JSON number")?, )), Value::Primitive(Primitive::Nothing) => serde_json::Value::Null, + Value::Primitive(Primitive::Pattern(s)) => serde_json::Value::String(s.clone()), Value::Primitive(Primitive::String(s)) => serde_json::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => serde_json::Value::String(s.display().to_string()), diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index 7580c3f4b7..0fd392f345 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -91,6 +91,7 @@ fn nu_value_to_sqlite_string(v: Value) -> String { Primitive::Int(i) => format!("{}", i), Primitive::Decimal(f) => format!("{}", f), Primitive::Bytes(u) => format!("{}", u), + Primitive::Pattern(s) => format!("'{}'", s.replace("'", "''")), Primitive::String(s) => format!("'{}'", s.replace("'", "''")), Primitive::Boolean(true) => "1".into(), Primitive::Boolean(_) => "0".into(), diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 7bca9840e9..e18e152363 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -44,6 +44,7 @@ pub fn value_to_toml_value(v: &Tagged) -> Result toml::Value::Integer(i.tagged(v.tag).coerce_into("converting to TOML integer")?) } Value::Primitive(Primitive::Nothing) => toml::Value::String("".to_string()), + Value::Primitive(Primitive::Pattern(s)) => toml::Value::String(s.clone()), Value::Primitive(Primitive::String(s)) => toml::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => toml::Value::String(s.display().to_string()), diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index 129deebdf6..915827252d 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -42,6 +42,7 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result::coerce_into(i.tagged(v.tag), "converting to YAML number")?, )), Value::Primitive(Primitive::Nothing) => serde_yaml::Value::Null, + Value::Primitive(Primitive::Pattern(s)) => serde_yaml::Value::String(s.clone()), Value::Primitive(Primitive::String(s)) => serde_yaml::Value::String(s.clone()), Value::Primitive(Primitive::Path(s)) => serde_yaml::Value::String(s.display().to_string()), diff --git a/src/data/base.rs b/src/data/base.rs index b48d692123..6707d64020 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -20,6 +20,7 @@ pub enum Primitive { Decimal(BigDecimal), Bytes(u64), String(String), + Pattern(String), Boolean(bool), Date(DateTime), Path(PathBuf), @@ -53,6 +54,7 @@ impl Primitive { Int(_) => "int", Decimal(_) => "decimal", Bytes(_) => "bytes", + Pattern(_) => "pattern", String(_) => "string", Boolean(_) => "boolean", Date(_) => "date", @@ -71,6 +73,7 @@ impl Primitive { Path(path) => write!(f, "{}", path.display()), Decimal(decimal) => write!(f, "{}", decimal), Bytes(bytes) => write!(f, "{}", bytes), + Pattern(string) => write!(f, "{:?}", string), String(string) => write!(f, "{:?}", string), Boolean(boolean) => write!(f, "{}", boolean), Date(date) => write!(f, "{}", date), @@ -108,6 +111,7 @@ impl Primitive { } Primitive::Int(i) => format!("{}", i), Primitive::Decimal(decimal) => format!("{}", decimal), + Primitive::Pattern(s) => format!("{}", s), Primitive::String(s) => format!("{}", s), Primitive::Boolean(b) => match (b, field_name) { (true, None) => format!("Yes"), @@ -577,6 +581,10 @@ impl Value { Value::Primitive(Primitive::String(s.into())) } + pub fn pattern(s: impl Into) -> Value { + Value::Primitive(Primitive::String(s.into())) + } + pub fn path(s: impl Into) -> Value { Value::Primitive(Primitive::Path(s.into())) } diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 6419ab73a6..52edf69818 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -114,6 +114,7 @@ fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged int.into(), hir::Literal::Size(int, unit) => unit.compute(int), hir::Literal::String(span) => Value::string(span.slice(source)), + hir::Literal::GlobPattern => Value::pattern(literal.span().slice(source)), hir::Literal::Bare => Value::string(literal.span().slice(source)), }; diff --git a/src/parser/hir.rs b/src/parser/hir.rs index aaf5bb7711..90bb38796a 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -17,7 +17,7 @@ use crate::evaluate::Scope; pub(crate) use self::baseline_parse::{ baseline_parse_single_token, baseline_parse_token_as_number, baseline_parse_token_as_path, - baseline_parse_token_as_string, + baseline_parse_token_as_pattern, baseline_parse_token_as_string, }; pub(crate) use self::baseline_parse_tokens::{baseline_parse_next_expr, TokensIterator}; pub(crate) use self::binary::Binary; @@ -90,6 +90,7 @@ pub enum RawExpression { Block(Vec), List(Vec), Path(Box), + FilePath(PathBuf), ExternalCommand(ExternalCommand), @@ -164,6 +165,10 @@ impl Expression { Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into()) } + pub(crate) fn pattern(tag: impl Into) -> Expression { + RawExpression::Literal(Literal::GlobPattern).tagged(tag.into()) + } + pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { Tagged::from_simple_spanned_item( RawExpression::Variable(Variable::Other(inner.into())), @@ -238,6 +243,7 @@ pub enum Literal { Number(Number), Size(Number, Unit), String(Span), + GlobPattern, Bare, } @@ -247,6 +253,7 @@ impl ToDebug for Tagged<&Literal> { Literal::Number(number) => write!(f, "{:?}", *number), Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), Literal::String(span) => write!(f, "{}", span.slice(source)), + Literal::GlobPattern => write!(f, "{}", self.span().slice(source)), Literal::Bare => write!(f, "{}", self.span().slice(source)), } } @@ -259,6 +266,7 @@ impl Literal { Literal::Size(..) => "size", Literal::String(..) => "string", Literal::Bare => "string", + Literal::GlobPattern => "pattern", } } } diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index 4437a6d38b..5248bde5f9 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -1,6 +1,7 @@ use crate::context::Context; use crate::errors::ShellError; use crate::parser::{hir, RawToken, Token}; +use crate::TaggedItem; use crate::Text; use std::path::PathBuf; @@ -20,6 +21,7 @@ pub fn baseline_parse_single_token( RawToken::Variable(span) => hir::Expression::variable(span, token.span()), RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), + RawToken::GlobPattern => hir::Expression::pattern(token.span()), RawToken::Bare => hir::Expression::bare(token.span()), }) } @@ -40,6 +42,12 @@ pub fn baseline_parse_token_as_number( hir::Expression::size(number.to_number(source), unit, token.span()) } RawToken::Bare => hir::Expression::bare(token.span()), + RawToken::GlobPattern => { + return Err(ShellError::type_error( + "Number", + "glob pattern".to_string().tagged(token.tag()), + )) + } RawToken::String(span) => hir::Expression::string(span, token.span()), }) } @@ -58,6 +66,12 @@ pub fn baseline_parse_token_as_string( RawToken::Number(_) => hir::Expression::bare(token.span()), RawToken::Size(_, _) => hir::Expression::bare(token.span()), RawToken::Bare => hir::Expression::bare(token.span()), + RawToken::GlobPattern => { + return Err(ShellError::type_error( + "String", + "glob pattern".tagged(token.tag()), + )) + } RawToken::String(span) => hir::Expression::string(span, token.span()), }) } @@ -80,6 +94,41 @@ pub fn baseline_parse_token_as_path( expand_path(token.span().slice(source), context), token.span(), ), + RawToken::GlobPattern => { + return Err(ShellError::type_error( + "Path", + "glob pattern".tagged(token.tag()), + )) + } + RawToken::String(span) => { + hir::Expression::file_path(expand_path(span.slice(source), context), token.span()) + } + }) +} + +pub fn baseline_parse_token_as_pattern( + token: &Token, + context: &Context, + source: &Text, +) -> Result { + Ok(match *token.item() { + RawToken::Variable(span) if span.slice(source) == "it" => { + hir::Expression::it_variable(span, token.span()) + } + RawToken::ExternalCommand(_) => { + return Err(ShellError::syntax_error( + "Invalid external command".to_string().tagged(token.tag()), + )) + } + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), + RawToken::Variable(span) => hir::Expression::variable(span, token.span()), + RawToken::Number(_) => hir::Expression::bare(token.span()), + RawToken::Size(_, _) => hir::Expression::bare(token.span()), + RawToken::GlobPattern => hir::Expression::pattern(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()) } diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index 13c7630fe1..ac2c703d3a 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -4,7 +4,7 @@ use crate::parser::{ hir, hir::{ baseline_parse_single_token, baseline_parse_token_as_number, baseline_parse_token_as_path, - baseline_parse_token_as_string, + baseline_parse_token_as_pattern, baseline_parse_token_as_string, }, DelimitedNode, Delimiter, PathNode, RawToken, TokenNode, }; @@ -43,6 +43,7 @@ pub enum SyntaxType { Variable, Number, Path, + Pattern, Binary, Block, Boolean, @@ -59,6 +60,7 @@ impl std::fmt::Display for SyntaxType { SyntaxType::Variable => write!(f, "Variable"), SyntaxType::Number => write!(f, "Number"), SyntaxType::Path => write!(f, "Path"), + SyntaxType::Pattern => write!(f, "Pattern"), SyntaxType::Binary => write!(f, "Binary"), SyntaxType::Block => write!(f, "Block"), SyntaxType::Boolean => write!(f, "Boolean"), @@ -90,6 +92,17 @@ pub fn baseline_parse_next_expr( )) } + (SyntaxType::Pattern, TokenNode::Token(token)) => { + return baseline_parse_token_as_pattern(token, context, source) + } + + (SyntaxType::Pattern, token) => { + return Err(ShellError::type_error( + "Path", + token.type_name().simple_spanned(token.span()), + )) + } + (SyntaxType::String, TokenNode::Token(token)) => { return baseline_parse_token_as_string(token, source); } @@ -315,6 +328,7 @@ pub fn baseline_parse_path( | RawToken::Size(..) | RawToken::Variable(_) | RawToken::ExternalCommand(_) + | RawToken::GlobPattern | RawToken::ExternalWord => { return Err(ShellError::type_error( "String", diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index a691fb2444..66656619d9 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -231,6 +231,29 @@ pub fn external(input: NomSpan) -> IResult { }) } +pub fn pattern(input: NomSpan) -> IResult { + trace_step(input, "bare", move |input| { + let start = input.offset; + let (input, _) = take_while1(is_start_glob_char)(input)?; + let (input, _) = take_while(is_glob_char)(input)?; + + let next_char = &input.fragment.chars().nth(0); + + if let Some(next_char) = next_char { + if is_external_word_char(*next_char) { + return Err(nom::Err::Error(nom::error::make_error( + input, + nom::error::ErrorKind::TakeWhile1, + ))); + } + } + + let end = input.offset; + + Ok((input, TokenTreeBuilder::spanned_pattern((start, end)))) + }) +} + pub fn bare(input: NomSpan) -> IResult { trace_step(input, "bare", move |input| { let start = input.offset; @@ -240,7 +263,7 @@ pub fn bare(input: NomSpan) -> IResult { let next_char = &input.fragment.chars().nth(0); if let Some(next_char) = next_char { - if is_external_word_char(*next_char) { + if is_external_word_char(*next_char) || *next_char == '*' { return Err(nom::Err::Error(nom::error::make_error( input, nom::error::ErrorKind::TakeWhile1, @@ -395,6 +418,7 @@ pub fn leaf(input: NomSpan) -> IResult { var, external, bare, + pattern, external_word, ))(input)?; @@ -655,6 +679,14 @@ fn is_external_word_char(c: char) -> bool { } } +fn is_start_glob_char(c: char) -> bool { + is_start_bare_char(c) || c == '*' +} + +fn is_glob_char(c: char) -> bool { + is_bare_char(c) || c == '*' +} + fn is_start_bare_char(c: char) -> bool { match c { '+' => false, @@ -680,6 +712,7 @@ fn is_bare_char(c: char) -> bool { '-' => true, '=' => true, '~' => true, + ':' => true, _ => false, } } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index 8034e8b0c7..ae1b344c44 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -152,6 +152,24 @@ impl TokenTreeBuilder { )) } + pub fn pattern(input: impl Into) -> CurriedToken { + let input = input.into(); + + Box::new(move |b| { + let (start, end) = b.consume(&input); + b.pos = end; + + TokenTreeBuilder::spanned_pattern((start, end)) + }) + } + + pub fn spanned_pattern(input: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::Bare, + input.into(), + )) + } + pub fn external_word(input: impl Into) -> CurriedToken { let input = input.into(); diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index 0bb2e3f17d..b599852499 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -12,6 +12,7 @@ pub enum RawToken { Variable(Span), ExternalCommand(Span), ExternalWord, + GlobPattern, Bare, } @@ -53,6 +54,7 @@ impl RawToken { RawToken::Variable(_) => "Variable", RawToken::ExternalCommand(_) => "ExternalCommand", RawToken::ExternalWord => "ExternalWord", + RawToken::GlobPattern => "GlobPattern", RawToken::Bare => "String", } } diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 462f375291..16802657db 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -123,6 +123,10 @@ fn paint_token_node(token_node: &TokenNode, line: &str) -> String { item: RawToken::Size(..), .. }) => Color::Purple.bold().paint(token_node.span().slice(line)), + TokenNode::Token(Tagged { + item: RawToken::GlobPattern, + .. + }) => Color::Cyan.normal().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { item: RawToken::String(..), .. From 540e93aa3ada4d722cc6eac7df64322cb83e9097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 10 Sep 2019 12:26:56 -0500 Subject: [PATCH 063/342] question mark character can also be in glob patterns. --- src/commands/ls.rs | 2 +- src/parser/parse/parser.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/commands/ls.rs b/src/commands/ls.rs index 7096141507..d2a0f6c032 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -10,7 +10,7 @@ impl WholeStreamCommand for LS { } fn signature(&self) -> Signature { - Signature::build("ls").optional("path", SyntaxType::Path) + Signature::build("ls").optional("path", SyntaxType::Pattern) } fn usage(&self) -> &str { diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 66656619d9..0be05af062 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -713,6 +713,7 @@ fn is_bare_char(c: char) -> bool { '=' => true, '~' => true, ':' => true, + '?' => true, _ => false, } } From 149ccc4fd32635581e2d5656d17871e74848070c Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Mon, 9 Sep 2019 16:45:55 +0700 Subject: [PATCH 064/342] Fix glibc-{busybox,distroless} * Add libdl.so.2 for glibc-busybox * Change base-image of glibc-distroless to gcr.io/distroless/cc --- .github/workflows/docker-publish.yml | 20 +++++++++---------- docker/Package.glibc-busybox.Dockerfile | 10 ++++++++++ ...le => Package.glibc-distroless.Dockerfile} | 0 docs/docker.md | 2 +- 4 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 docker/Package.glibc-busybox.Dockerfile rename docker/{Package.patch.Dockerfile => Package.glibc-distroless.Dockerfile} (100%) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 1e6f1881d8..e84cefd3ab 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -44,15 +44,15 @@ jobs: - glibc - musl include: - - { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl, plugin: true } - - { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu, plugin: true } - - { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu, plugin: true } - - { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: true } - - { tag: musl-busybox, base-image: 'busybox:musl', arch: x86_64-unknown-linux-musl, plugin: false } - - { tag: musl-distroless, base-image: 'gcr.io/distroless/static', arch: x86_64-unknown-linux-musl, plugin: false } - - { tag: glibc-distroless, base-image: 'gcr.io/distroless/base', arch: x86_64-unknown-linux-gnu, plugin: false, use-patch: true } - - { tag: glibc, base-image: scratch, arch: x86_64-unknown-linux-gnu, plugin: false } - - { tag: musl, base-image: scratch, arch: x86_64-unknown-linux-musl, plugin: false } + - { tag: alpine, base-image: alpine, arch: x86_64-unknown-linux-musl, plugin: true } + - { tag: slim, base-image: 'debian:stable-slim', arch: x86_64-unknown-linux-gnu, plugin: true } + - { tag: debian, base-image: debian, arch: x86_64-unknown-linux-gnu, plugin: true } + - { tag: glibc-busybox, base-image: 'busybox:glibc', arch: x86_64-unknown-linux-gnu, use-patch: true } + - { tag: musl-busybox, base-image: 'busybox:musl', arch: x86_64-unknown-linux-musl, } + - { tag: musl-distroless, base-image: 'gcr.io/distroless/static', arch: x86_64-unknown-linux-musl, } + - { tag: glibc-distroless, base-image: 'gcr.io/distroless/cc', arch: x86_64-unknown-linux-gnu, use-patch: true } + - { tag: glibc, base-image: scratch, arch: x86_64-unknown-linux-gnu, } + - { tag: musl, base-image: scratch, arch: x86_64-unknown-linux-musl, } steps: - uses: actions/checkout@v1 - uses: actions/download-artifact@master @@ -61,7 +61,7 @@ jobs: run: | REGISTRY=${REGISTRY,,}; export TAG=${GITHUB_REF##*/}-${{ matrix.tag }}; export NU_BINS=target/release/$( [ ${{ matrix.plugin }} = true ] && echo nu* || echo nu ) - export PATCH=$([ ${{ matrix.use-patch }} = true ] && echo .patch || echo '') + export PATCH=$([ ${{ matrix.use-patch }} = true ] && echo .${{ matrix.tag }} || echo '') chmod +x $NU_BINS echo ${{ secrets.DOCKER_REGISTRY }} | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin diff --git a/docker/Package.glibc-busybox.Dockerfile b/docker/Package.glibc-busybox.Dockerfile new file mode 100644 index 0000000000..76ddd9fd75 --- /dev/null +++ b/docker/Package.glibc-busybox.Dockerfile @@ -0,0 +1,10 @@ +ARG base +FROM debian:stable-slim AS patch +FROM ${base} + +ARG artifact +COPY ${artifact} /bin/ + +COPY --from=patch /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libz.so.1 +COPY --from=patch /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libdl.so.2 +ENTRYPOINT ["/bin/nu"] \ No newline at end of file diff --git a/docker/Package.patch.Dockerfile b/docker/Package.glibc-distroless.Dockerfile similarity index 100% rename from docker/Package.patch.Dockerfile rename to docker/Package.glibc-distroless.Dockerfile diff --git a/docs/docker.md b/docs/docker.md index 17ed135e92..b51f4e0cd0 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -8,7 +8,7 @@ | `musl-busybox` | `busybox:musl` | no | — | GNU utils + _musl_ | ~(1+16) MB | | `glibc-busybox` | `busybox:glibc` | no | — | GNU utils + _glibc_ | ~(3+17) MB | | `musl-distroless` | `distroless/static` | no | — | see [here][distroless/base] | ~(2+16) MB | -| `glibc-distroless` | `distroless/base` | no | — | `distroless/static` with _glibc_ | ~(17+17) MB | +| `glibc-distroless` | `distroless/cc` | no | — | `distroless/static` with _glibc_ | ~(17+17) MB | | `glibc` | `scratch` | no | — | **only `nu` binary-executable** which depend on glibc runtime | ~17 MB | | `musl` | `scratch` | no | — | **only `nu` binary-executable** statically linked to musl | ~16 MB | From 095e5ac69fbbc049febc50ce74c9fdc73d0e5cc2 Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Mon, 9 Sep 2019 16:45:55 +0700 Subject: [PATCH 065/342] Add librt.so.1 for glibc-busybox --- docker/Package.glibc-busybox.Dockerfile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker/Package.glibc-busybox.Dockerfile b/docker/Package.glibc-busybox.Dockerfile index 76ddd9fd75..48b6e9fc88 100644 --- a/docker/Package.glibc-busybox.Dockerfile +++ b/docker/Package.glibc-busybox.Dockerfile @@ -5,6 +5,7 @@ FROM ${base} ARG artifact COPY ${artifact} /bin/ -COPY --from=patch /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libz.so.1 -COPY --from=patch /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libdl.so.2 +COPY --from=patch /lib/x86_64-linux-gnu/libz* /lib/x86_64-linux-gnu/ +COPY --from=patch /lib/x86_64-linux-gnu/libdl* /lib/x86_64-linux-gnu/ +COPY --from=patch /lib/x86_64-linux-gnu/librt* /lib/x86_64-linux-gnu/ ENTRYPOINT ["/bin/nu"] \ No newline at end of file From 62e6cc4dae3da0813dea813541ce1a5d76fd2c9b Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Mon, 9 Sep 2019 16:45:55 +0700 Subject: [PATCH 066/342] Add libgcc_s.so.1 for glibc-busybox --- docker/Package.glibc-busybox.Dockerfile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docker/Package.glibc-busybox.Dockerfile b/docker/Package.glibc-busybox.Dockerfile index 48b6e9fc88..bdd4c18dd0 100644 --- a/docker/Package.glibc-busybox.Dockerfile +++ b/docker/Package.glibc-busybox.Dockerfile @@ -1,11 +1,12 @@ ARG base -FROM debian:stable-slim AS patch +FROM gcr.io/distroless/cc AS patch FROM ${base} ARG artifact COPY ${artifact} /bin/ -COPY --from=patch /lib/x86_64-linux-gnu/libz* /lib/x86_64-linux-gnu/ -COPY --from=patch /lib/x86_64-linux-gnu/libdl* /lib/x86_64-linux-gnu/ -COPY --from=patch /lib/x86_64-linux-gnu/librt* /lib/x86_64-linux-gnu/ +COPY --from=patch /lib/x86_64-linux-gnu/libz* /lib/x86_64-linux-gnu/ +COPY --from=patch /lib/x86_64-linux-gnu/libdl* /lib/x86_64-linux-gnu/ +COPY --from=patch /lib/x86_64-linux-gnu/librt* /lib/x86_64-linux-gnu/ +COPY --from=patch /lib/x86_64-linux-gnu/libgcc_s* /lib/x86_64-linux-gnu/ ENTRYPOINT ["/bin/nu"] \ No newline at end of file From 58b7800172fe754818ac21a8a210ea4d7970c5f5 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 10 Sep 2019 20:23:22 -0700 Subject: [PATCH 067/342] Migrate most uses of the Span concept to Tag Also migrate mv, rm and commands like that to taking a SyntaxType::Pattern instead of a SyntaxType::Path for their first argument. --- Cargo.lock | 8 +- Cargo.toml | 2 +- src/cli.rs | 25 ++- src/commands/cd.rs | 3 +- src/commands/classified.rs | 31 ++-- src/commands/clip.rs | 2 +- src/commands/command.rs | 30 ++-- src/commands/config.rs | 44 ++--- src/commands/cp.rs | 8 +- src/commands/date.rs | 44 ++--- src/commands/echo.rs | 8 +- src/commands/enter.rs | 6 +- src/commands/fetch.rs | 76 +++----- src/commands/first.rs | 2 +- src/commands/from_bson.rs | 14 +- src/commands/from_csv.rs | 12 +- src/commands/from_ini.rs | 12 +- src/commands/from_json.rs | 18 +- src/commands/from_sqlite.rs | 12 +- src/commands/from_toml.rs | 12 +- src/commands/from_tsv.rs | 12 +- src/commands/from_xml.rs | 12 +- src/commands/from_yaml.rs | 12 +- src/commands/get.rs | 6 +- src/commands/help.rs | 8 +- src/commands/last.rs | 2 +- src/commands/lines.rs | 8 +- src/commands/ls.rs | 17 +- src/commands/mkdir.rs | 2 +- src/commands/mv.rs | 8 +- src/commands/nth.rs | 2 +- src/commands/open.rs | 55 ++---- src/commands/pick.rs | 2 +- src/commands/post.rs | 82 ++++----- src/commands/reject.rs | 2 +- src/commands/rm.rs | 4 +- src/commands/save.rs | 32 ++-- src/commands/shells.rs | 6 +- src/commands/size.rs | 10 +- src/commands/skip_while.rs | 2 +- src/commands/sort_by.rs | 2 +- src/commands/split_column.rs | 14 +- src/commands/split_row.rs | 7 +- src/commands/tags.rs | 4 +- src/commands/to_bson.rs | 32 ++-- src/commands/to_csv.rs | 10 +- src/commands/to_json.rs | 12 +- src/commands/to_sqlite.rs | 6 +- src/commands/to_toml.rs | 12 +- src/commands/to_tsv.rs | 10 +- src/commands/to_yaml.rs | 12 +- src/commands/trim.rs | 4 +- src/commands/version.rs | 8 +- src/commands/where_.rs | 11 +- src/commands/which_.rs | 13 +- src/context.rs | 12 +- src/data/base.rs | 18 +- src/data/config.rs | 12 +- src/data/into.rs | 4 +- src/data/meta.rs | 164 +++++++++++++----- src/errors.rs | 96 +++++----- src/evaluate/evaluator.rs | 53 +++--- src/format/table.rs | 2 +- src/lib.rs | 4 +- src/parser.rs | 4 +- src/parser/deserializer.rs | 6 +- src/parser/hir.rs | 65 +++---- src/parser/hir/baseline_parse.rs | 106 ++++++------ src/parser/hir/baseline_parse_tokens.rs | 161 ++++++++--------- src/parser/hir/external_command.rs | 2 +- src/parser/hir/named.rs | 5 +- src/parser/parse/files.rs | 35 ++-- src/parser/parse/flag.rs | 4 +- src/parser/parse/parser.rs | 157 ++++++++++------- src/parser/parse/pipeline.rs | 10 +- src/parser/parse/token_tree.rs | 44 ++--- src/parser/parse/token_tree_builder.rs | 221 ++++++++++-------------- src/parser/parse/tokens.rs | 32 ++-- src/parser/parse_command.rs | 27 ++- src/parser/registry.rs | 38 ++-- src/plugins/add.rs | 8 +- src/plugins/edit.rs | 6 +- src/plugins/embed.rs | 6 +- src/plugins/inc.rs | 42 +++-- src/plugins/ps.rs | 4 +- src/plugins/skip.rs | 8 +- src/plugins/str.rs | 14 +- src/plugins/sum.rs | 12 +- src/plugins/sys.rs | 2 +- src/prelude.rs | 5 +- src/shell/filesystem_shell.rs | 115 ++++++------ src/shell/help_shell.rs | 19 +- src/shell/helper.rs | 38 ++-- src/shell/shell.rs | 14 +- src/shell/shell_manager.rs | 8 +- src/shell/value_shell.rs | 32 ++-- src/traits.rs | 4 +- 97 files changed, 1174 insertions(+), 1255 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08fcb94a0b..22af928844 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1474,8 +1474,8 @@ dependencies = [ ] [[package]] -name = "nom5_locate" -version = "0.1.1" +name = "nom_locate" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1531,7 +1531,7 @@ dependencies = [ "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3129,7 +3129,7 @@ dependencies = [ "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b" -"checksum nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4312467f8b28d909344b934207e502212fa5a3adf1bff7428b0b86a666223d" +"checksum nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f932834fd8e391fc7710e2ba17e8f9f8645d846b55aa63207e17e110a1e1ce35" "checksum ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602" "checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" diff --git a/Cargo.toml b/Cargo.toml index f9e13c9348..c4369b94b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ ctrlc = "3.1.3" surf = "1.0.2" url = "2.1.0" roxmltree = "0.7.0" -nom5_locate = "0.1.1" +nom_locate = "1.0.0" enum-utils = "0.1.1" unicode-xid = "0.2.0" serde_ini = "0.2.0" diff --git a/src/cli.rs b/src/cli.rs index ec8c7085c6..c29617ca92 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -266,7 +266,7 @@ pub async fn cli() -> Result<(), Box> { context.shell_manager.clone(), ))); - let edit_mode = crate::data::config::config(Span::unknown())? + let edit_mode = crate::data::config::config(Tag::unknown())? .get("edit_mode") .map(|s| match s.as_string().unwrap().as_ref() { "vi" => EditMode::Vi, @@ -344,7 +344,7 @@ async fn process_line(readline: Result, ctx: &mut Context Ok(line) if line.trim() == "" => LineResult::Success(line.clone()), Ok(line) => { - let result = match crate::parser::parse(&line) { + let result = match crate::parser::parse(&line, uuid::Uuid::nil()) { Err(err) => { return LineResult::Error(line.clone(), err); } @@ -366,7 +366,7 @@ async fn process_line(readline: Result, ctx: &mut Context .commands .push(ClassifiedCommand::Internal(InternalCommand { command: whole_stream_command(autoview::Autoview), - name_span: Span::unknown(), + name_tag: Tag::unknown(), args: hir::Call::new( Box::new(hir::Expression::synthetic_string("autoview")), None, @@ -486,10 +486,10 @@ fn classify_command( match call { // If the command starts with `^`, treat it as an external command no matter what call if call.head().is_external() => { - let name_span = call.head().expect_external(); - let name = name_span.slice(source); + let name_tag = call.head().expect_external(); + let name = name_tag.slice(source); - Ok(external_command(call, source, name.tagged(name_span))) + Ok(external_command(call, source, name.tagged(name_tag))) } // Otherwise, if the command is a bare word, we'll need to triage it @@ -511,19 +511,19 @@ fn classify_command( Ok(ClassifiedCommand::Internal(InternalCommand { command, - name_span: head.span().clone(), + name_tag: head.tag(), args, })) } // otherwise, it's an external command - false => Ok(external_command(call, source, name.tagged(head.span()))), + false => Ok(external_command(call, source, name.tagged(head.tag()))), } } // If the command is something else (like a number or a variable), that is currently unsupported. // We might support `$somevar` as a curried command in the future. - call => Err(ShellError::invalid_command(call.head().span())), + call => Err(ShellError::invalid_command(call.head().tag())), } } @@ -540,10 +540,7 @@ fn external_command( .iter() .filter_map(|i| match i { TokenNode::Whitespace(_) => None, - other => Some(Tagged::from_simple_spanned_item( - other.as_external_arg(source), - other.span(), - )), + other => Some(other.as_external_arg(source).tagged(other.tag())), }) .collect(), None => vec![], @@ -553,7 +550,7 @@ fn external_command( ClassifiedCommand::External(ExternalCommand { name: name.to_string(), - name_span: tag.span, + name_tag: tag, args: arg_list_strings, }) } diff --git a/src/commands/cd.rs b/src/commands/cd.rs index a84e66fce9..a3f5a8d89b 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -10,8 +10,7 @@ impl WholeStreamCommand for CD { } fn signature(&self) -> Signature { - Signature::build("cd") - .optional("directory", SyntaxType::Path) + Signature::build("cd").optional("directory", SyntaxShape::Path) } fn usage(&self) -> &str { diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 277a925341..4d15bce122 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -86,7 +86,7 @@ pub(crate) enum ClassifiedCommand { pub(crate) struct InternalCommand { pub(crate) command: Arc, - pub(crate) name_span: Span, + pub(crate) name_tag: Tag, pub(crate) args: hir::Call, } @@ -108,7 +108,7 @@ impl InternalCommand { let result = context.run_command( self.command, - self.name_span.clone(), + self.name_tag.clone(), context.source_map.clone(), self.args, &source, @@ -133,21 +133,18 @@ impl InternalCommand { match value { Tagged { item: Value::Primitive(Primitive::String(cmd)), - .. + tag, } => { context.shell_manager.insert_at_current(Box::new( HelpShell::for_command( - Tagged::from_simple_spanned_item( - Value::string(cmd), - Span::unknown(), - ), - &context.registry().clone(), + Value::string(cmd).tagged(tag), + &context.registry(), )?, )); } _ => { context.shell_manager.insert_at_current(Box::new( - HelpShell::index(&context.registry().clone())?, + HelpShell::index(&context.registry())?, )); } } @@ -189,7 +186,7 @@ impl InternalCommand { pub(crate) struct ExternalCommand { pub(crate) name: String, - pub(crate) name_span: Span, + pub(crate) name_tag: Tag, pub(crate) args: Vec>, } @@ -208,7 +205,7 @@ impl ExternalCommand { ) -> Result { let stdin = input.stdin; let inputs: Vec> = input.objects.into_vec().await; - let name_span = self.name_span.clone(); + let name_tag = self.name_tag.clone(); trace!(target: "nu::run::external", "-> {}", self.name); trace!(target: "nu::run::external", "inputs = {:?}", inputs); @@ -227,17 +224,17 @@ impl ExternalCommand { for i in &inputs { if i.as_string().is_err() { - let mut span = None; + let mut tag = None; for arg in &self.args { if arg.item.contains("$it") { - span = Some(arg.span()); + tag = Some(arg.tag()); } } - if let Some(span) = span { + if let Some(tag) = tag { return Err(ShellError::labeled_error( "External $it needs string data", "given row instead of string data", - span, + tag, )); } else { return Err(ShellError::string("Error: $it needs string data")); @@ -314,9 +311,7 @@ impl ExternalCommand { let stdout = popen.stdout.take().unwrap(); let file = futures::io::AllowStdIo::new(stdout); let stream = Framed::new(file, LinesCodec {}); - let stream = stream.map(move |line| { - Tagged::from_simple_spanned_item(Value::string(line.unwrap()), name_span) - }); + let stream = stream.map(move |line| Value::string(line.unwrap()).tagged(name_tag)); Ok(ClassifiedInputStream::from_input_stream( stream.boxed() as BoxStream<'static, Tagged> )) diff --git a/src/commands/clip.rs b/src/commands/clip.rs index 9bf7fa9f3a..2ef5bfac1d 100644 --- a/src/commands/clip.rs +++ b/src/commands/clip.rs @@ -51,7 +51,7 @@ pub mod clipboard { Ok(OutputStream::from(stream)) } - async fn inner_clip(input: Vec>, name: Span) -> OutputStream { + async fn inner_clip(input: Vec>, name: Tag) -> OutputStream { let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap(); let mut new_copy_data = String::new(); diff --git a/src/commands/command.rs b/src/commands/command.rs index 6be179895b..99352a7b18 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -18,7 +18,7 @@ pub struct UnevaluatedCallInfo { pub args: hir::Call, pub source: Text, pub source_map: SourceMap, - pub name_span: Span, + pub name_tag: Tag, } impl ToDebug for UnevaluatedCallInfo { @@ -38,7 +38,7 @@ impl UnevaluatedCallInfo { Ok(CallInfo { args, source_map: self.source_map, - name_span: self.name_span, + name_tag: self.name_tag, }) } @@ -74,7 +74,7 @@ impl UnevaluatedCallInfo { pub struct CallInfo { pub args: registry::EvaluatedArgs, pub source_map: SourceMap, - pub name_span: Span, + pub name_tag: Tag, } impl CallInfo { @@ -89,7 +89,7 @@ impl CallInfo { args: T::deserialize(&mut deserializer)?, context: RunnablePerItemContext { shell_manager: shell_manager.clone(), - name: self.name_span, + name: self.name_tag, }, callback, }) @@ -158,7 +158,7 @@ impl CommandArgs { let host = self.host.clone(); let args = self.evaluate_once(registry)?; let (input, args) = args.split(); - let name_span = args.call_info.name_span; + let name_tag = args.call_info.name_tag; let mut deserializer = ConfigDeserializer::from_call_info(args.call_info); Ok(RunnableArgs { @@ -167,7 +167,7 @@ impl CommandArgs { input, commands: registry.clone(), shell_manager, - name: name_span, + name: name_tag, source_map, host, }, @@ -191,7 +191,7 @@ impl CommandArgs { let host = self.host.clone(); let args = self.evaluate_once(registry)?; let (input, args) = args.split(); - let name_span = args.call_info.name_span; + let name_tag = args.call_info.name_tag; let mut deserializer = ConfigDeserializer::from_call_info(args.call_info); Ok(RunnableRawArgs { @@ -200,7 +200,7 @@ impl CommandArgs { input, commands: registry.clone(), shell_manager, - name: name_span, + name: name_tag, source_map, host, }, @@ -212,7 +212,7 @@ impl CommandArgs { pub struct RunnablePerItemContext { pub shell_manager: ShellManager, - pub name: Span, + pub name: Tag, } impl RunnablePerItemContext { @@ -227,7 +227,7 @@ pub struct RunnableContext { pub host: Arc>, pub commands: CommandRegistry, pub source_map: SourceMap, - pub name: Span, + pub name: Tag, } impl RunnableContext { @@ -311,8 +311,8 @@ impl EvaluatedWholeStreamCommandArgs { } } - pub fn name_span(&self) -> Span { - self.args.call_info.name_span + pub fn name_tag(&self) -> Tag { + self.args.call_info.name_tag } pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) { @@ -471,12 +471,6 @@ impl ReturnSuccess { pub fn action(input: CommandAction) -> ReturnValue { Ok(ReturnSuccess::Action(input)) } - - pub fn spanned_value(input: Value, span: Span) -> ReturnValue { - Ok(ReturnSuccess::Value(Tagged::from_simple_spanned_item( - input, span, - ))) - } } pub trait WholeStreamCommand: Send + Sync { diff --git a/src/commands/config.rs b/src/commands/config.rs index 78f1ad399c..c1eae91944 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,7 +1,7 @@ use crate::commands::WholeStreamCommand; use crate::data::{config, Value}; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::{self}; use crate::prelude::*; use std::iter::FromIterator; @@ -26,10 +26,10 @@ impl WholeStreamCommand for Config { fn signature(&self) -> Signature { Signature::build("config") - .named("load", SyntaxType::Path) - .named("set", SyntaxType::Any) - .named("get", SyntaxType::Any) - .named("remove", SyntaxType::Any) + .named("load", SyntaxShape::Path) + .named("set", SyntaxShape::Any) + .named("get", SyntaxShape::Any) + .named("remove", SyntaxShape::Any) .switch("clear") .switch("path") } @@ -96,41 +96,21 @@ pub fn config( config::write(&result, &configuration)?; - return Ok(stream![Tagged::from_simple_spanned_item( - Value::Row(result.into()), - value.span() - )] - .from_input_stream()); + return Ok(stream![Value::Row(result.into()).tagged(value.tag())].from_input_stream()); } - if let Tagged { - item: true, - tag: Tag { span, .. }, - } = clear - { + if let Tagged { item: true, tag } = clear { result.clear(); config::write(&result, &configuration)?; - return Ok(stream![Tagged::from_simple_spanned_item( - Value::Row(result.into()), - span - )] - .from_input_stream()); + return Ok(stream![Value::Row(result.into()).tagged(tag)].from_input_stream()); } - if let Tagged { - item: true, - tag: Tag { span, .. }, - } = path - { + if let Tagged { item: true, tag } = path { let path = config::default_path_for(&configuration)?; - return Ok(stream![Tagged::from_simple_spanned_item( - Value::Primitive(Primitive::Path(path)), - span - )] - .from_input_stream()); + return Ok(stream![Value::Primitive(Primitive::Path(path)).tagged(tag)].from_input_stream()); } if let Some(v) = remove { @@ -146,9 +126,9 @@ pub fn config( ))); } - let obj = VecDeque::from_iter(vec![Value::Row(result.into()).simple_spanned(v.span())]); + let obj = VecDeque::from_iter(vec![Value::Row(result.into()).tagged(v.tag())]); return Ok(obj.from_input_stream()); } - return Ok(vec![Value::Row(result.into()).simple_spanned(name)].into()); + return Ok(vec![Value::Row(result.into()).tagged(name)].into()); } diff --git a/src/commands/cp.rs b/src/commands/cp.rs index 491e18b1aa..bf20c74ce9 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -1,6 +1,6 @@ use crate::commands::command::RunnablePerItemContext; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; use std::path::PathBuf; @@ -21,9 +21,9 @@ impl PerItemCommand for Cpy { fn signature(&self) -> Signature { Signature::build("cp") - .required("src", SyntaxType::Pattern) - .required("dst", SyntaxType::Path) - .named("file", SyntaxType::Any) + .required("src", SyntaxShape::Pattern) + .required("dst", SyntaxShape::Path) + .named("file", SyntaxShape::Any) .switch("recursive") } diff --git a/src/commands/date.rs b/src/commands/date.rs index 7d3307fe58..6df9e27209 100644 --- a/src/commands/date.rs +++ b/src/commands/date.rs @@ -1,5 +1,5 @@ -use crate::errors::ShellError; use crate::data::{Dictionary, Value}; +use crate::errors::ShellError; use crate::prelude::*; use chrono::{DateTime, Local, Utc}; @@ -33,58 +33,40 @@ impl WholeStreamCommand for Date { } } -pub fn date_to_value(dt: DateTime, span: Span) -> Tagged +pub fn date_to_value(dt: DateTime, tag: Tag) -> Tagged where T::Offset: Display, { let mut indexmap = IndexMap::new(); - indexmap.insert( - "year".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.year()), span), - ); - indexmap.insert( - "month".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.month()), span), - ); - indexmap.insert( - "day".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.day()), span), - ); - indexmap.insert( - "hour".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.hour()), span), - ); - indexmap.insert( - "minute".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.minute()), span), - ); - indexmap.insert( - "second".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.second()), span), - ); + indexmap.insert("year".to_string(), Value::int(dt.year()).tagged(tag)); + indexmap.insert("month".to_string(), Value::int(dt.month()).tagged(tag)); + indexmap.insert("day".to_string(), Value::int(dt.day()).tagged(tag)); + indexmap.insert("hour".to_string(), Value::int(dt.hour()).tagged(tag)); + indexmap.insert("minute".to_string(), Value::int(dt.minute()).tagged(tag)); + indexmap.insert("second".to_string(), Value::int(dt.second()).tagged(tag)); let tz = dt.offset(); indexmap.insert( "timezone".to_string(), - Tagged::from_simple_spanned_item(Value::string(format!("{}", tz)), span), + Value::string(format!("{}", tz)).tagged(tag), ); - Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span) + Value::Row(Dictionary::from(indexmap)).tagged(tag) } pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let mut date_out = VecDeque::new(); - let span = args.call_info.name_span; + let tag = args.call_info.name_tag; let value = if args.has("utc") { let utc: DateTime = Utc::now(); - date_to_value(utc, span) + date_to_value(utc, tag) } else { let local: DateTime = Local::now(); - date_to_value(local, span) + date_to_value(local, tag) }; date_out.push_back(value); diff --git a/src/commands/echo.rs b/src/commands/echo.rs index f464630de7..453041bbcd 100644 --- a/src/commands/echo.rs +++ b/src/commands/echo.rs @@ -12,7 +12,7 @@ impl PerItemCommand for Echo { } fn signature(&self) -> Signature { - Signature::build("echo").rest(SyntaxType::Any) + Signature::build("echo").rest(SyntaxShape::Any) } fn usage(&self) -> &str { @@ -35,7 +35,7 @@ fn run( _registry: &CommandRegistry, _raw_args: &RawCommandArgs, ) -> Result { - let name = call_info.name_span; + let name = call_info.name_tag; let mut output = String::new(); @@ -57,7 +57,7 @@ fn run( return Err(ShellError::labeled_error( "Expect a string from pipeline", "not a string-compatible value", - i.span(), + i.tag(), )); } } @@ -65,7 +65,7 @@ fn run( } let stream = VecDeque::from(vec![Ok(ReturnSuccess::Value( - Value::string(output).simple_spanned(name), + Value::string(output).tagged(name), ))]); Ok(stream.to_output_stream()) diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 7b42793e12..4148d03c5f 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -14,7 +14,7 @@ impl PerItemCommand for Enter { } fn signature(&self) -> registry::Signature { - Signature::build("enter").required("location", SyntaxType::Block) + Signature::build("enter").required("location", SyntaxShape::Block) } fn usage(&self) -> &str { @@ -70,7 +70,7 @@ impl PerItemCommand for Enter { crate::commands::open::fetch( &full_path, &location_clone, - Span::unknown(), + Tag::unknown(), ) .await.unwrap(); @@ -103,7 +103,7 @@ impl PerItemCommand for Enter { }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, }, }; let mut result = converter.run( diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index e87ddf8347..07ef0c3e0f 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -2,14 +2,13 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; use crate::data::Value; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::Signature; use crate::prelude::*; use mime::Mime; use std::path::PathBuf; use std::str::FromStr; use surf::mime; -use uuid::Uuid; pub struct Fetch; impl PerItemCommand for Fetch { @@ -19,7 +18,7 @@ impl PerItemCommand for Fetch { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxType::Path) + .required("path", SyntaxShape::Path) .switch("raw") } @@ -52,14 +51,14 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_span = path.span(); + let path_tag = path.tag(); let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); let stream = async_stream_block! { - let result = fetch(&path_str, path_span).await; + let result = fetch(&path_str, path_tag).await; if let Err(e) = result { yield Err(e); @@ -99,7 +98,7 @@ fn run( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); @@ -130,13 +129,13 @@ fn run( pub async fn fetch( location: &str, - span: Span, + tag: Tag, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { if let Err(_) = url::Url::parse(location) { return Err(ShellError::labeled_error( "Incomplete or incorrect url", "expected a full url", - span, + tag, )); } @@ -152,13 +151,10 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( @@ -167,13 +163,10 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { @@ -181,16 +174,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load binary file", "could not load", - span, + tag, ) })?; Ok(( None, Value::Binary(buf), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )) } @@ -200,13 +190,10 @@ pub async fn fetch( ShellError::labeled_error( "Could not load svg from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::IMAGE, image_ty) => { @@ -214,16 +201,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load image file", "could not load", - span, + tag, ) })?; Ok(( Some(image_ty.to_string()), Value::Binary(buf), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )) } @@ -233,13 +217,10 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { @@ -260,23 +241,17 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )) } (ty, sub_ty) => Ok(( None, Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), } @@ -284,10 +259,7 @@ pub async fn fetch( None => Ok(( None, Value::string(format!("No content type found")), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), }, @@ -295,7 +267,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "URL could not be opened", "url not found", - span, + tag, )); } } diff --git a/src/commands/first.rs b/src/commands/first.rs index 6381d5def6..77c9a4f695 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -17,7 +17,7 @@ impl WholeStreamCommand for First { fn signature(&self) -> Signature { Signature::build("first") - .required("amount", SyntaxType::Literal) + .required("amount", SyntaxShape::Literal) } fn usage(&self) -> &str { diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index e2f5421bd9..b39754a196 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ExpectedRange; use crate::data::{Primitive, TaggedDictBuilder, Value}; +use crate::errors::ExpectedRange; use crate::prelude::*; use bson::{decode_document, spec::BinarySubtype, Bson}; use std::str::FromStr; @@ -198,7 +198,7 @@ pub fn from_bson_bytes_to_value( fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -208,24 +208,24 @@ fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result - match from_bson_bytes_to_value(vb, span) { + match from_bson_bytes_to_value(vb, tag) { Ok(x) => yield ReturnSuccess::value(x), Err(_) => { yield Err(ShellError::labeled_error_with_secondary( "Could not parse as BSON", "input cannot be parsed as BSON", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )) } } _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index c872e77360..68296eb11b 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -86,7 +86,7 @@ fn from_csv( }: FromCSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let values: Vec> = input.values.collect().await; @@ -105,15 +105,15 @@ fn from_csv( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_span, + name_tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_csv_string_to_value(concat_string, skip_headers, name_span) { + match from_csv_string_to_value(concat_string, skip_headers, name_tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -126,9 +126,9 @@ fn from_csv( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as CSV", "input cannot be parsed as CSV", - name_span, + name_tag, "value originates from here", - last_tag.span, + last_tag, )) } , } diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index 0e128a22c4..8409cf8489 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -64,7 +64,7 @@ pub fn from_ini_string_to_value( fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -84,15 +84,15 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_ini_string_to_value(concat_string, span) { + match from_ini_string_to_value(concat_string, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -105,9 +105,9 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let values: Vec> = input.values.collect().await; @@ -91,9 +91,9 @@ fn from_json( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_span, + name_tag, "value originates from here", - value_tag.span, + value_tag, )), } @@ -106,7 +106,7 @@ fn from_json( continue; } - match from_json_string_to_value(json_str.to_string(), name_span) { + match from_json_string_to_value(json_str.to_string(), name_tag) { Ok(x) => yield ReturnSuccess::value(x), Err(_) => { @@ -114,15 +114,15 @@ fn from_json( yield Err(ShellError::labeled_error_with_secondary( "Could nnot parse as JSON", "input cannot be parsed as JSON", - name_span, + name_tag, "value originates from here", - last_tag.span)) + last_tag)) } } } } } else { - match from_json_string_to_value(concat_string, name_span) { + match from_json_string_to_value(concat_string, name_tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { @@ -137,9 +137,9 @@ fn from_json( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as JSON", "input cannot be parsed as JSON", - name_span, + name_tag, "value originates from here", - last_tag.span)) + last_tag)) } } } diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index c19c4fef10..1b04a59fd0 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -128,7 +128,7 @@ pub fn from_sqlite_bytes_to_value( fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -138,7 +138,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result - match from_sqlite_bytes_to_value(vb, span) { + match from_sqlite_bytes_to_value(vb, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -151,18 +151,18 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index c1338cb01f..29db38a77e 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -68,7 +68,7 @@ pub fn from_toml( registry: &CommandRegistry, ) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -88,15 +88,15 @@ pub fn from_toml( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_toml_string_to_value(concat_string, span) { + match from_toml_string_to_value(concat_string, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -109,9 +109,9 @@ pub fn from_toml( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as TOML", "input cannot be parsed as TOML", - span, + tag, "value originates from here", - last_tag.span, + last_tag, )) } , } diff --git a/src/commands/from_tsv.rs b/src/commands/from_tsv.rs index 92696c1aaf..66f070a5df 100644 --- a/src/commands/from_tsv.rs +++ b/src/commands/from_tsv.rs @@ -87,7 +87,7 @@ fn from_tsv( }: FromTSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let values: Vec> = input.values.collect().await; @@ -106,15 +106,15 @@ fn from_tsv( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_span, + name_tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_tsv_string_to_value(concat_string, skip_headers, name_span) { + match from_tsv_string_to_value(concat_string, skip_headers, name_tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -127,9 +127,9 @@ fn from_tsv( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as TSV", "input cannot be parsed as TSV", - name_span, + name_tag, "value originates from here", - last_tag.span, + last_tag, )) } , } diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index 2c3f94cddc..f80d428f43 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -83,7 +83,7 @@ pub fn from_xml_string_to_value( fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -103,15 +103,15 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_xml_string_to_value(concat_string, span) { + match from_xml_string_to_value(concat_string, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -124,9 +124,9 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -117,15 +117,15 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_yaml_string_to_value(concat_string, span) { + match from_yaml_string_to_value(concat_string, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -138,9 +138,9 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result Signature { - Signature::build("get").rest(SyntaxType::Member) + Signature::build("get").rest(SyntaxShape::Member) } fn usage(&self) -> &str { @@ -47,7 +47,7 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result registry::Signature { - Signature::build("help").rest(SyntaxType::Any) + Signature::build("help").rest(SyntaxShape::Any) } fn usage(&self) -> &str { @@ -27,11 +27,11 @@ impl PerItemCommand for Help { _raw_args: &RawCommandArgs, _input: Tagged, ) -> Result { - let span = call_info.name_span; + let tag = call_info.name_tag; if call_info.args.len() == 0 { return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( - Tagged::from_simple_spanned_item(Value::nothing(), span), + Value::nothing().tagged(tag), )))] .into()); } diff --git a/src/commands/last.rs b/src/commands/last.rs index 1f9cc62a7f..fd7a3ecea2 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Last { } fn signature(&self) -> Signature { - Signature::build("last").required("amount", SyntaxType::Number) + Signature::build("last").required("amount", SyntaxShape::Number) } fn usage(&self) -> &str { diff --git a/src/commands/lines.rs b/src/commands/lines.rs index 15a467224f..d2a9cdffd1 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{Primitive, Value}; +use crate::errors::ShellError; use crate::prelude::*; use log::trace; @@ -32,7 +32,7 @@ impl WholeStreamCommand for Lines { fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let input: InputStream = trace_stream!(target: "nu::trace_stream::lines", "input" = input); @@ -58,9 +58,9 @@ fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result>, +} + impl WholeStreamCommand for LS { fn name(&self) -> &str { "ls" } fn signature(&self) -> Signature { - Signature::build("ls").optional("path", SyntaxType::Pattern) + Signature::build("ls").optional("path", SyntaxShape::Pattern) } fn usage(&self) -> &str { @@ -22,12 +28,11 @@ impl WholeStreamCommand for LS { args: CommandArgs, registry: &CommandRegistry, ) -> Result { - ls(args, registry) + args.process(registry, ls)?.run() + // ls(args, registry) } } -fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result { - let shell_manager = args.shell_manager.clone(); - let args = args.evaluate_once(registry)?; - shell_manager.ls(args) +fn ls(LsArgs { path }: LsArgs, context: RunnableContext) -> Result { + context.shell_manager.ls(path, context.name) } diff --git a/src/commands/mkdir.rs b/src/commands/mkdir.rs index 9dec9a3142..8bf8a97d4a 100644 --- a/src/commands/mkdir.rs +++ b/src/commands/mkdir.rs @@ -17,7 +17,7 @@ impl PerItemCommand for Mkdir { } fn signature(&self) -> Signature { - Signature::build("mkdir").rest(SyntaxType::Path) + Signature::build("mkdir").rest(SyntaxShape::Path) } fn usage(&self) -> &str { diff --git a/src/commands/mv.rs b/src/commands/mv.rs index 130e5996e8..2ace1fa05f 100644 --- a/src/commands/mv.rs +++ b/src/commands/mv.rs @@ -1,6 +1,6 @@ use crate::commands::command::RunnablePerItemContext; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; use std::path::PathBuf; @@ -20,9 +20,9 @@ impl PerItemCommand for Move { fn signature(&self) -> Signature { Signature::build("mv") - .required("source", SyntaxType::Path) - .required("destination", SyntaxType::Path) - .named("file", SyntaxType::Any) + .required("source", SyntaxShape::Pattern) + .required("destination", SyntaxShape::Path) + .named("file", SyntaxShape::Any) } fn usage(&self) -> &str { diff --git a/src/commands/nth.rs b/src/commands/nth.rs index 98ab6a10a9..bf397e1bcf 100644 --- a/src/commands/nth.rs +++ b/src/commands/nth.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Nth { } fn signature(&self) -> Signature { - Signature::build("nth").required("amount", SyntaxType::Any) + Signature::build("nth").required("amount", SyntaxShape::Any) } fn usage(&self) -> &str { diff --git a/src/commands/open.rs b/src/commands/open.rs index 32a99c6f17..57f035ac63 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -2,11 +2,10 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; use crate::data::Value; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::Signature; use crate::prelude::*; use std::path::{Path, PathBuf}; -use uuid::Uuid; pub struct Open; impl PerItemCommand for Open { @@ -16,7 +15,7 @@ impl PerItemCommand for Open { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxType::Path) + .required("path", SyntaxShape::Path) .switch("raw") } @@ -53,7 +52,7 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_span = path.span(); + let path_span = path.tag(); let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); @@ -100,7 +99,7 @@ fn run( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); @@ -132,7 +131,7 @@ fn run( pub async fn fetch( cwd: &PathBuf, location: &str, - span: Span, + tag: Tag, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { let mut cwd = cwd.clone(); @@ -144,10 +143,7 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => { @@ -163,19 +159,13 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -183,10 +173,7 @@ pub async fn fetch( Ok(( None, Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )) } @@ -201,19 +188,13 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -221,10 +202,7 @@ pub async fn fetch( Ok(( None, Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )) } @@ -232,10 +210,7 @@ pub async fn fetch( _ => Ok(( None, Value::Binary(bytes), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -245,7 +220,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "File could not be opened", "file not found", - span, + tag, )); } } @@ -253,7 +228,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "File could not be opened", "file not found", - span, + tag, )); } } diff --git a/src/commands/pick.rs b/src/commands/pick.rs index bc5f8df401..2fe21dbb49 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(SyntaxType::Any) + Signature::build("pick").rest(SyntaxShape::Any) } fn usage(&self) -> &str { diff --git a/src/commands/post.rs b/src/commands/post.rs index 8790c929a7..88717a4667 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -1,8 +1,8 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; -use crate::errors::ShellError; use crate::data::Value; -use crate::parser::hir::SyntaxType; +use crate::errors::ShellError; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::Signature; use crate::prelude::*; use base64::encode; @@ -10,7 +10,7 @@ use mime::Mime; use std::path::PathBuf; use std::str::FromStr; use surf::mime; -use uuid::Uuid; + pub struct Post; impl PerItemCommand for Post { @@ -20,10 +20,10 @@ impl PerItemCommand for Post { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxType::Any) - .required("body", SyntaxType::Any) - .named("user", SyntaxType::Any) - .named("password", SyntaxType::Any) + .required("path", SyntaxShape::Any) + .required("body", SyntaxShape::Any) + .named("user", SyntaxShape::Any) + .named("password", SyntaxShape::Any) .switch("raw") } @@ -63,7 +63,7 @@ fn run( file => file.clone(), }; let path_str = path.as_string()?; - let path_span = path.span(); + let path_span = path.tag(); let has_raw = call_info.args.has("raw"); let user = call_info.args.get("user").map(|x| x.as_string().unwrap()); let password = call_info @@ -109,7 +109,7 @@ fn run( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); @@ -143,7 +143,7 @@ pub async fn post( body: &Tagged, user: Option, password: Option, - span: Span, + tag: Tag, registry: &CommandRegistry, raw_args: &RawCommandArgs, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { @@ -189,7 +189,7 @@ pub async fn post( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, }, }; let mut result = converter.run( @@ -211,7 +211,7 @@ pub async fn post( return Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", - span, + *tag, )); } } @@ -227,7 +227,7 @@ pub async fn post( return Err(ShellError::labeled_error( "Could not automatically convert table", "needs manual conversion", - tag.span, + *tag, )); } } @@ -243,13 +243,10 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( @@ -258,13 +255,10 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { @@ -272,16 +266,13 @@ pub async fn post( ShellError::labeled_error( "Could not load binary file", "could not load", - span, + tag, ) })?; Ok(( None, Value::Binary(buf), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )) } @@ -290,16 +281,13 @@ pub async fn post( ShellError::labeled_error( "Could not load image file", "could not load", - span, + tag, ) })?; Ok(( Some(image_ty.to_string()), Value::Binary(buf), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )) } @@ -309,13 +297,10 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { @@ -336,13 +321,10 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )) } @@ -352,10 +334,7 @@ pub async fn post( "Not yet supported MIME type: {} {}", ty, sub_ty )), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), } @@ -363,10 +342,7 @@ pub async fn post( None => Ok(( None, Value::string(format!("No content type found")), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), }, @@ -374,7 +350,7 @@ pub async fn post( return Err(ShellError::labeled_error( "URL could not be opened", "url not found", - span, + tag, )); } } @@ -382,7 +358,7 @@ pub async fn post( Err(ShellError::labeled_error( "Expected a url", "needs a url", - span, + tag, )) } } diff --git a/src/commands/reject.rs b/src/commands/reject.rs index 49dbc4dd36..ea09bd7495 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Reject { } fn signature(&self) -> Signature { - Signature::build("reject").rest(SyntaxType::Member) + Signature::build("reject").rest(SyntaxShape::Member) } fn usage(&self) -> &str { diff --git a/src/commands/rm.rs b/src/commands/rm.rs index 60bb5c6e4a..ac5aeaae7d 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -1,6 +1,6 @@ use crate::commands::command::RunnablePerItemContext; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; use std::path::PathBuf; @@ -20,7 +20,7 @@ impl PerItemCommand for Remove { fn signature(&self) -> Signature { Signature::build("rm") - .required("path", SyntaxType::Path) + .required("path", SyntaxShape::Pattern) .switch("recursive") } diff --git a/src/commands/save.rs b/src/commands/save.rs index 26bf8cb16d..66d9732d61 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; pub struct Save; macro_rules! process_string { - ($input:ident, $name_span:ident) => {{ + ($input:ident, $name_tag:ident) => {{ let mut result_string = String::new(); for res in $input { match res { @@ -21,7 +21,7 @@ macro_rules! process_string { yield core::task::Poll::Ready(Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", - $name_span, + $name_tag, ))); } } @@ -31,7 +31,7 @@ macro_rules! process_string { } macro_rules! process_string_return_success { - ($result_vec:ident, $name_span:ident) => {{ + ($result_vec:ident, $name_tag:ident) => {{ let mut result_string = String::new(); for res in $result_vec { match res { @@ -45,7 +45,7 @@ macro_rules! process_string_return_success { yield core::task::Poll::Ready(Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during text save", - $name_span, + $name_tag, ))); } } @@ -55,7 +55,7 @@ macro_rules! process_string_return_success { } macro_rules! process_binary_return_success { - ($result_vec:ident, $name_span:ident) => {{ + ($result_vec:ident, $name_tag:ident) => {{ let mut result_binary: Vec = Vec::new(); for res in $result_vec { match res { @@ -71,7 +71,7 @@ macro_rules! process_binary_return_success { yield core::task::Poll::Ready(Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during binary save", - $name_span, + $name_tag, ))); } } @@ -93,7 +93,7 @@ impl WholeStreamCommand for Save { fn signature(&self) -> Signature { Signature::build("save") - .optional("path", SyntaxType::Path) + .optional("path", SyntaxShape::Path) .switch("raw") } @@ -127,7 +127,7 @@ fn save( raw_args: RawCommandArgs, ) -> Result { let mut full_path = PathBuf::from(shell_manager.path()); - let name_span = name; + let name_tag = name; let source_map = source_map.clone(); let stream = async_stream_block! { @@ -145,7 +145,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - name_span, + name_tag, )); } }, @@ -153,7 +153,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - name_span, + name_tag, )); } } @@ -161,7 +161,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - name_span, + name_tag, )); } } else { @@ -185,21 +185,21 @@ fn save( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, } }; let mut result = converter.run(new_args.with_input(input), ®istry); let result_vec: Vec> = result.drain_vec().await; if converter.is_binary() { - process_binary_return_success!(result_vec, name_span) + process_binary_return_success!(result_vec, name_tag) } else { - process_string_return_success!(result_vec, name_span) + process_string_return_success!(result_vec, name_tag) } } else { - process_string!(input, name_span) + process_string!(input, name_tag) } } else { - process_string!(input, name_span) + process_string!(input, name_tag) } } else { Ok(string_from(&input).into_bytes()) diff --git a/src/commands/shells.rs b/src/commands/shells.rs index 856c23e7be..2aee2c8564 100644 --- a/src/commands/shells.rs +++ b/src/commands/shells.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::TaggedDictBuilder; +use crate::errors::ShellError; use crate::prelude::*; pub struct Shells; @@ -29,10 +29,10 @@ impl WholeStreamCommand for Shells { fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result { let mut shells_out = VecDeque::new(); - let span = args.call_info.name_span; + let tag = args.call_info.name_tag; for (index, shell) in args.shell_manager.shells.lock().unwrap().iter().enumerate() { - let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(span)); + let mut dict = TaggedDictBuilder::new(tag); if index == args.shell_manager.current_shell { dict.insert(" ", "X".to_string()); diff --git a/src/commands/size.rs b/src/commands/size.rs index d8383efcf8..51f8d22c1d 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{TaggedDictBuilder, Value}; +use crate::errors::ShellError; use crate::prelude::*; pub struct Size; @@ -29,7 +29,7 @@ impl WholeStreamCommand for Size { fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result { let input = args.input; - let span = args.call_info.name_span; + let tag = args.call_info.name_tag; Ok(input .values .map(move |v| match v.item { @@ -37,9 +37,9 @@ fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - v.span(), + v.tag(), )), }) .to_output_stream()) @@ -71,7 +71,7 @@ fn count(contents: &str, tag: impl Into) -> Tagged { } let mut dict = TaggedDictBuilder::new(tag); - //TODO: add back in name when we have it in the span + //TODO: add back in name when we have it in the tag //dict.insert("name", Value::string(name)); dict.insert("lines", Value::int(lines)); dict.insert("words", Value::int(words)); diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index 90ba24b996..041caf300e 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for SkipWhile { fn signature(&self) -> Signature { Signature::build("skip-while") - .required("condition", SyntaxType::Block) + .required("condition", SyntaxShape::Block) .filter() } diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index edce33b963..8058b7889e 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -15,7 +15,7 @@ impl WholeStreamCommand for SortBy { } fn signature(&self) -> Signature { - Signature::build("sort-by").rest(SyntaxType::String) + Signature::build("sort-by").rest(SyntaxShape::String) } fn usage(&self) -> &str { diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index 2df02cac16..00e2609f26 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{Primitive, TaggedDictBuilder, Value}; +use crate::errors::ShellError; use crate::prelude::*; use log::trace; @@ -21,9 +21,9 @@ impl WholeStreamCommand for SplitColumn { fn signature(&self) -> Signature { Signature::build("split-column") - .required("separator", SyntaxType::Any) + .required("separator", SyntaxShape::Any) .switch("collapse-empty") - .rest(SyntaxType::Member) + .rest(SyntaxShape::Member) } fn usage(&self) -> &str { @@ -40,7 +40,11 @@ impl WholeStreamCommand for SplitColumn { } fn split_column( - SplitColumnArgs { separator, rest, collapse_empty}: SplitColumnArgs, + SplitColumnArgs { + separator, + rest, + collapse_empty, + }: SplitColumnArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { Ok(input @@ -92,7 +96,7 @@ fn split_column( "requires string input", name, "value originates from here", - v.span(), + v.tag(), )), }) .to_output_stream()) diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index d4f3824a80..e70e5cfa84 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{Primitive, Value}; +use crate::errors::ShellError; use crate::prelude::*; use log::trace; @@ -17,8 +17,7 @@ impl WholeStreamCommand for SplitRow { } fn signature(&self) -> Signature { - Signature::build("split-row") - .required("separator", SyntaxType::Any) + Signature::build("split-row").required("separator", SyntaxShape::Any) } fn usage(&self) -> &str { @@ -63,7 +62,7 @@ fn split_row( "requires string input", name, "value originates from here", - v.span(), + v.tag(), ))); result } diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 3f112482ca..2b45105c2d 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{TaggedDictBuilder, Value}; +use crate::errors::ShellError; use crate::prelude::*; pub struct Tags; @@ -36,7 +36,7 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result Result { Ok(Bson::Document(doc)) } -fn shell_encode_document( - writer: &mut Vec, - doc: Document, - span: Span, -) -> Result<(), ShellError> { +fn shell_encode_document(writer: &mut Vec, doc: Document, tag: Tag) -> Result<(), ShellError> { match encode_document(writer, &doc) { Err(e) => Err(ShellError::labeled_error( format!("Failed to encode document due to: {:?}", e), "requires BSON-compatible document", - span, + tag, )), _ => Ok(()), } } -fn bson_value_to_bytes(bson: Bson, span: Span) -> Result, ShellError> { +fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result, ShellError> { let mut out = Vec::new(); match bson { Bson::Array(a) => { for v in a.into_iter() { match v { - Bson::Document(d) => shell_encode_document(&mut out, d, span)?, + Bson::Document(d) => shell_encode_document(&mut out, d, tag)?, _ => { return Err(ShellError::labeled_error( format!("All top level values must be Documents, got {:?}", v), "requires BSON-compatible document", - span, + tag, )) } } } } - Bson::Document(d) => shell_encode_document(&mut out, d, span)?, + Bson::Document(d) => shell_encode_document(&mut out, d, tag)?, _ => { return Err(ShellError::labeled_error( format!("All top level values must be Documents, got {:?}", bson), "requires BSON-compatible document", - span, + tag, )) } } @@ -236,7 +232,7 @@ fn bson_value_to_bytes(bson: Bson, span: Span) -> Result, ShellError> { fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -252,23 +248,23 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { - match bson_value_to_bytes(bson_value, name_span) { + match bson_value_to_bytes(bson_value, name_tag) { Ok(x) => yield ReturnSuccess::value( - Value::Binary(x).simple_spanned(name_span), + Value::Binary(x).tagged(name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with BSON-compatible structure.span() from pipeline", + "Expected a table with BSON-compatible structure.tag() from pipeline", "requires BSON-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with BSON-compatible structure from pipeline", "requires BSON-compatible input", - name_span)) + name_tag)) } } }; diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 615e49cbf8..fd77fdcb6f 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -134,7 +134,7 @@ fn to_csv( ToCSVArgs { headerless }: ToCSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let input: Vec> = input.values.collect().await; @@ -155,15 +155,15 @@ fn to_csv( } else { x }; - yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span)) + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag)) } _ => { yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with CSV-compatible structure.span() from pipeline", + "Expected a table with CSV-compatible structure.tag() from pipeline", "requires CSV-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )) } } diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 35c03af32d..c6b5d9c016 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -80,7 +80,7 @@ fn json_list(input: &Vec>) -> Result, Shell fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -98,21 +98,21 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { match serde_json::to_string(&json_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).simple_spanned(name_span), + Value::Primitive(Primitive::String(x)).tagged(name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with JSON-compatible structure.span() from pipeline", + "Expected a table with JSON-compatible structure.tag() from pipeline", "requires JSON-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with JSON-compatible structure from pipeline", "requires JSON-compatible input", - name_span)) + name_tag)) } } }; diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index 0fd392f345..580dba96bf 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -200,7 +200,7 @@ fn sqlite_input_stream_to_bytes( fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -208,9 +208,9 @@ fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result yield ReturnSuccess::value(out), _ => { yield Err(ShellError::labeled_error( - "Expected a table with SQLite-compatible structure.span() from pipeline", + "Expected a table with SQLite-compatible structure.tag() from pipeline", "requires SQLite-compatible input", - name_span, + name_tag, )) }, } diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index e18e152363..cf8f2b11f3 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -75,7 +75,7 @@ fn collect_values(input: &Vec>) -> Result, ShellE fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -93,21 +93,21 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { match toml::to_string(&toml_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).simple_spanned(name_span), + Value::Primitive(Primitive::String(x)).tagged(name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with TOML-compatible structure.span() from pipeline", + "Expected a table with TOML-compatible structure.tag() from pipeline", "requires TOML-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with TOML-compatible structure from pipeline", "requires TOML-compatible input", - name_span)) + name_tag)) } } }; diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index a0cbf1c1bd..7bce174d01 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -133,7 +133,7 @@ fn to_tsv( ToTSVArgs { headerless }: ToTSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let input: Vec> = input.values.collect().await; @@ -154,15 +154,15 @@ fn to_tsv( } else { x }; - yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span)) + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag)) } _ => { yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with TSV-compatible structure.span() from pipeline", + "Expected a table with TSV-compatible structure.tag() from pipeline", "requires TSV-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )) } } diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index 915827252d..fc4412da01 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -76,7 +76,7 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -94,21 +94,21 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result { match serde_yaml::to_string(&yaml_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).simple_spanned(name_span), + Value::Primitive(Primitive::String(x)).tagged(name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with YAML-compatible structure.span() from pipeline", + "Expected a table with YAML-compatible structure.tag() from pipeline", "requires YAML-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with YAML-compatible structure from pipeline", "requires YAML-compatible input", - name_span)) + name_tag)) } } }; diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 865f6b50bb..11ed025394 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::Value; +use crate::errors::ShellError; use crate::prelude::*; pub struct Trim; @@ -34,7 +34,7 @@ fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result Result { let args = args.evaluate_once(registry)?; - let span = args.call_info.name_span; + let tag = args.call_info.name_tag; let mut indexmap = IndexMap::new(); indexmap.insert( "version".to_string(), - Tagged::from_simple_spanned_item(Value::string(clap::crate_version!()), span), + Value::string(clap::crate_version!()).tagged(tag), ); - let value = Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span); + let value = Value::Row(Dictionary::from(indexmap)).tagged(tag); Ok(OutputStream::one(value)) } diff --git a/src/commands/where_.rs b/src/commands/where_.rs index b09c341b4a..673c6dda84 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,6 +1,6 @@ use crate::commands::PerItemCommand; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry; use crate::prelude::*; @@ -12,8 +12,7 @@ impl PerItemCommand for Where { } fn signature(&self) -> registry::Signature { - Signature::build("where") - .required("condition", SyntaxType::Block) + Signature::build("where").required("condition", SyntaxShape::Block) } fn usage(&self) -> &str { @@ -43,16 +42,14 @@ impl PerItemCommand for Where { VecDeque::new() } } - Err(e) => { - return Err(e) - } + Err(e) => return Err(e), } } Tagged { tag, .. } => { return Err(ShellError::labeled_error( "Expected a condition", "where needs a condition", - tag.span, + *tag, )) } }; diff --git a/src/commands/which_.rs b/src/commands/which_.rs index 06c93f63f5..905515848c 100644 --- a/src/commands/which_.rs +++ b/src/commands/which_.rs @@ -1,5 +1,5 @@ -use crate::errors::ShellError; use crate::data::Value; +use crate::errors::ShellError; use crate::prelude::*; use crate::commands::WholeStreamCommand; @@ -13,8 +13,7 @@ impl WholeStreamCommand for Which { } fn signature(&self) -> Signature { - Signature::build("which") - .required("name", SyntaxType::Any) + Signature::build("which").required("name", SyntaxShape::Any) } fn usage(&self) -> &str { @@ -34,7 +33,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result 0 { @@ -53,7 +52,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result Result( &mut self, command: Arc, - name_span: Span, + name_tag: Tag, source_map: SourceMap, args: hir::Call, source: &Text, input: InputStream, ) -> OutputStream { - let command_args = self.command_args(args, input, source, source_map, name_span); + let command_args = self.command_args(args, input, source, source_map, name_tag); command.run(command_args, self.registry()) } @@ -135,13 +135,13 @@ impl Context { args: hir::Call, source: &Text, source_map: SourceMap, - name_span: Span, + name_tag: Tag, ) -> UnevaluatedCallInfo { UnevaluatedCallInfo { args, source: source.clone(), source_map, - name_span, + name_tag, } } @@ -151,12 +151,12 @@ impl Context { input: InputStream, source: &Text, source_map: SourceMap, - name_span: Span, + name_tag: Tag, ) -> CommandArgs { CommandArgs { host: self.host.clone(), shell_manager: self.shell_manager.clone(), - call_info: self.call_info(args, source, source_map, name_span), + call_info: self.call_info(args, source, source_map, name_tag), input, } } diff --git a/src/data/base.rs b/src/data/base.rs index aa491ebdcb..fdd5542dc8 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -145,7 +145,7 @@ pub struct Operation { pub struct Block { pub(crate) expressions: Vec, pub(crate) source: Text, - pub(crate) span: Span, + pub(crate) tag: Tag, } impl Block { @@ -153,7 +153,7 @@ impl Block { let scope = Scope::new(value.clone()); if self.expressions.len() == 0 { - return Ok(Value::nothing().simple_spanned(self.span)); + return Ok(Value::nothing().tagged(self.tag)); } let mut last = None; @@ -247,7 +247,7 @@ impl std::convert::TryFrom<&Tagged> for Block { Value::Block(block) => Ok(block.clone()), v => Err(ShellError::type_error( "Block", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -263,7 +263,7 @@ impl std::convert::TryFrom<&Tagged> for i64 { } v => Err(ShellError::type_error( "Integer", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -277,7 +277,7 @@ impl std::convert::TryFrom<&Tagged> for String { Value::Primitive(Primitive::String(s)) => Ok(s.clone()), v => Err(ShellError::type_error( "String", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -291,7 +291,7 @@ impl std::convert::TryFrom<&Tagged> for Vec { Value::Binary(b) => Ok(b.clone()), v => Err(ShellError::type_error( "Binary", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -305,7 +305,7 @@ impl<'a> std::convert::TryFrom<&'a Tagged> for &'a crate::data::Dictionar Value::Row(d) => Ok(d), v => Err(ShellError::type_error( "Dictionary", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -327,7 +327,7 @@ impl std::convert::TryFrom>> for Switch { Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present), v => Err(ShellError::type_error( "Boolean", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), }, } @@ -639,7 +639,7 @@ impl Tagged { Value::Primitive(Primitive::Path(path)) => Ok(path.clone()), other => Err(ShellError::type_error( "Path", - other.type_name().tagged(self.span()), + other.type_name().tagged(self.tag()), )), } } diff --git a/src/data/config.rs b/src/data/config.rs index 08296c09a6..6b4d1383f5 100644 --- a/src/data/config.rs +++ b/src/data/config.rs @@ -50,7 +50,7 @@ pub fn default_path_for(file: &Option) -> Result { } pub fn read( - span: impl Into, + tag: impl Into, at: &Option, ) -> Result>, ShellError> { let filename = default_path()?; @@ -64,15 +64,15 @@ pub fn read( trace!("config file = {}", filename.display()); - let span = span.into(); + let tag = tag.into(); let contents = fs::read_to_string(filename) - .map(|v| v.simple_spanned(span)) + .map(|v| v.tagged(tag)) .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?; let parsed: toml::Value = toml::from_str(&contents) .map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?; - let value = convert_toml_value_to_nu_value(&parsed, Tag::unknown_origin(span)); + let value = convert_toml_value_to_nu_value(&parsed, tag); let tag = value.tag(); match value.item { Value::Row(Dictionary { entries }) => Ok(entries), @@ -83,8 +83,8 @@ pub fn read( } } -pub(crate) fn config(span: impl Into) -> Result>, ShellError> { - read(span, &None) +pub(crate) fn config(tag: impl Into) -> Result>, ShellError> { + read(tag, &None) } pub fn write( diff --git a/src/data/into.rs b/src/data/into.rs index 749ab0601f..3d764fa0c1 100644 --- a/src/data/into.rs +++ b/src/data/into.rs @@ -15,8 +15,8 @@ impl From for Value { impl> Tagged { pub fn into_tagged_value(self) -> Tagged { - let value_span = self.span(); + let value_tag = self.tag(); let value = self.item.into(); - value.simple_spanned(value_span) + value.tagged(value_tag) } } diff --git a/src/data/meta.rs b/src/data/meta.rs index f1d2b6713d..d472a8add9 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -5,6 +5,7 @@ use derive_new::new; use getset::Getters; use serde::Deserialize; use serde::Serialize; +use std::path::{Path, PathBuf}; use uuid::Uuid; #[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] @@ -13,9 +14,15 @@ pub struct Tagged { pub item: T, } -impl HasSpan for Tagged { - fn span(&self) -> Span { - self.tag.span +impl HasTag for Tagged { + fn tag(&self) -> Tag { + self.tag + } +} + +impl AsRef for Tagged { + fn as_ref(&self) -> &Path { + self.item.as_ref() } } @@ -24,10 +31,6 @@ pub trait TaggedItem: Sized { Tagged::from_item(self, tag.into()) } - fn simple_spanned(self, span: impl Into) -> Tagged { - Tagged::from_simple_spanned_item(self, span.into()) - } - // For now, this is a temporary facility. In many cases, there are other useful spans that we // could be using, such as the original source spans of JSON or Toml files, but we don't yet // have the infrastructure to make that work. @@ -53,14 +56,8 @@ impl std::ops::Deref for Tagged { } impl Tagged { - pub fn spanned(self, span: impl Into) -> Tagged { - Tagged::from_item( - self.item, - Tag { - span: span.into(), - origin: None, - }, - ) + pub fn with_tag(self, tag: impl Into) -> Tagged { + Tagged::from_item(self.item, tag) } pub fn from_item(item: T, tag: impl Into) -> Tagged { @@ -70,41 +67,26 @@ impl Tagged { } } - pub fn from_simple_spanned_item(item: T, span: impl Into) -> Tagged { - Tagged::from_item( - item, - Tag { - span: span.into(), - origin: None, - }, - ) - } - pub fn map(self, input: impl FnOnce(T) -> U) -> Tagged { let tag = self.tag(); let mapped = input(self.item); - Tagged::from_item(mapped, tag.clone()) + Tagged::from_item(mapped, tag) } - pub(crate) fn copy_span(&self, output: U) -> Tagged { - let span = self.span(); - - Tagged::from_simple_spanned_item(output, span) + pub(crate) fn copy_tag(&self, output: U) -> Tagged { + Tagged::from_item(output, self.tag()) } pub fn source(&self, source: &Text) -> Text { - Text::from(self.span().slice(source)) - } - - pub fn span(&self) -> Span { - self.tag.span + Text::from(self.tag().slice(source)) } pub fn tag(&self) -> Tag { self.tag } + // TODO: This should not be optional pub fn origin(&self) -> Option { self.tag.origin } @@ -126,20 +108,14 @@ impl Tagged { } } -impl From<&Tagged> for Span { - fn from(input: &Tagged) -> Span { - input.span() - } -} - -impl From<&Span> for Span { - fn from(input: &Span) -> Span { +impl From<&Tag> for Tag { + fn from(input: &Tag) -> Tag { *input } } -impl From> for Span { - fn from(input: nom5_locate::LocatedSpan<&str>) -> Span { +impl From> for Span { + fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Span { Span { start: input.offset, end: input.offset + input.fragment.len(), @@ -147,8 +123,18 @@ impl From> for Span { } } -impl From<(nom5_locate::LocatedSpan, nom5_locate::LocatedSpan)> for Span { - fn from(input: (nom5_locate::LocatedSpan, nom5_locate::LocatedSpan)) -> Span { +impl + From<( + nom_locate::LocatedSpanEx, + nom_locate::LocatedSpanEx, + )> for Span +{ + fn from( + input: ( + nom_locate::LocatedSpanEx, + nom_locate::LocatedSpanEx, + ), + ) -> Span { Span { start: input.0.offset, end: input.1.offset, @@ -197,6 +183,36 @@ impl From<&Span> for Tag { } } +impl From<(usize, usize, Uuid)> for Tag { + fn from((start, end, origin): (usize, usize, Uuid)) -> Self { + Tag { + origin: Some(origin), + span: Span { start, end }, + } + } +} + +impl From<(usize, usize, Option)> for Tag { + fn from((start, end, origin): (usize, usize, Option)) -> Self { + Tag { + origin, + span: Span { start, end }, + } + } +} + +impl From> for Tag { + fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Tag { + Tag { + origin: Some(input.extra), + span: Span { + start: input.offset, + end: input.offset + input.fragment.len(), + }, + } + } +} + impl From for Span { fn from(tag: Tag) -> Self { tag.span @@ -214,12 +230,36 @@ impl Tag { Tag { origin: None, span } } + pub fn unknown_span(origin: Uuid) -> Tag { + Tag { + origin: Some(origin), + span: Span::unknown(), + } + } + pub fn unknown() -> Tag { Tag { origin: None, span: Span::unknown(), } } + + pub fn until(&self, other: impl Into) -> Tag { + let other = other.into(); + debug_assert!(self.origin == other.origin, "Can only merge two tags with the same origin"); + + Tag { + span: Span { + start: self.span.start, + end: other.span.end + }, + origin: self.origin + } + } + + pub fn slice<'a>(&self, source: &'a str) -> &'a str { + self.span.slice(source) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)] @@ -284,3 +324,33 @@ impl language_reporting::ReportingSpan for Span { self.end } } + +impl language_reporting::ReportingSpan for Tag { + fn with_start(&self, start: usize) -> Self { + Tag { + span: Span { + start, + end: self.span.end, + }, + origin: self.origin, + } + } + + fn with_end(&self, end: usize) -> Self { + Tag { + span: Span { + start: self.span.start, + end, + }, + origin: self.origin, + } + } + + fn start(&self) -> usize { + self.span.start + } + + fn end(&self) -> usize { + self.span.end + } +} diff --git a/src/errors.rs b/src/errors.rs index d97435d226..3dfa644d4c 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -14,20 +14,22 @@ pub enum Description { impl Description { pub fn from(value: Tagged>) -> Description { - let value_span = value.span(); let value_tag = value.tag(); - match value_span { - Span { start: 0, end: 0 } => Description::Synthetic(value.item.into()), + match value_tag { + Tag { + span: crate::data::meta::Span { start: 0, end: 0 }, + .. + } => Description::Synthetic(value.item.into()), _ => Description::Source(Tagged::from_item(value.item.into(), value_tag)), } } } impl Description { - fn into_label(self) -> Result, String> { + fn into_label(self) -> Result, String> { match self { - Description::Source(s) => Ok(Label::new_primary(s.span()).with_message(s.item)), + Description::Source(s) => Ok(Label::new_primary(s.tag()).with_message(s.item)), Description::Synthetic(s) => Err(s), } } @@ -81,7 +83,7 @@ impl ShellError { ) -> ShellError { ProximateShellError::RangeError { kind: expected.into(), - actual_kind: actual.copy_span(format!("{:?}", actual.item)), + actual_kind: actual.copy_tag(format!("{:?}", actual.item)), operation, } .start() @@ -116,9 +118,9 @@ impl ShellError { ProximateShellError::MissingProperty { subpath, expr }.start() } - pub(crate) fn missing_value(span: Option, reason: impl Into) -> ShellError { + pub(crate) fn missing_value(tag: Option, reason: impl Into) -> ShellError { ProximateShellError::MissingValue { - span, + tag, reason: reason.into(), } .start() @@ -127,28 +129,31 @@ impl ShellError { pub(crate) fn argument_error( command: impl Into, kind: ArgumentError, - span: Span, + tag: Tag, ) -> ShellError { ProximateShellError::ArgumentError { command: command.into(), error: kind, - span, + tag, } .start() } - pub(crate) fn invalid_external_word(span: Span) -> ShellError { + pub(crate) fn invalid_external_word(tag: Tag) -> ShellError { ProximateShellError::ArgumentError { command: "Invalid argument to Nu command (did you mean to call an external command?)" .into(), error: ArgumentError::InvalidExternalWord, - span, + tag, } .start() } pub(crate) fn parse_error( - error: nom::Err<(nom5_locate::LocatedSpan<&str>, nom::error::ErrorKind)>, + error: nom::Err<( + nom_locate::LocatedSpanEx<&str, uuid::Uuid>, + nom::error::ErrorKind, + )>, ) -> ShellError { use language_reporting::*; @@ -164,34 +169,34 @@ impl ShellError { } nom::Err::Failure(span) | nom::Err::Error(span) => { let diagnostic = Diagnostic::new(Severity::Error, format!("Parse Error")) - .with_label(Label::new_primary(Span::from(span.0))); + .with_label(Label::new_primary(Tag::from(span.0))); ShellError::diagnostic(diagnostic) } } } - pub(crate) fn diagnostic(diagnostic: Diagnostic) -> ShellError { + pub(crate) fn diagnostic(diagnostic: Diagnostic) -> ShellError { ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start() } - pub(crate) fn to_diagnostic(self) -> Diagnostic { + pub(crate) fn to_diagnostic(self) -> Diagnostic { match self.error { ProximateShellError::String(StringError { title, .. }) => { Diagnostic::new(Severity::Error, title) } ProximateShellError::InvalidCommand { command } => { Diagnostic::new(Severity::Error, "Invalid command") - .with_label(Label::new_primary(command.span)) + .with_label(Label::new_primary(command)) } - ProximateShellError::MissingValue { span, reason } => { + ProximateShellError::MissingValue { tag, reason } => { let mut d = Diagnostic::new( Severity::Bug, format!("Internal Error (missing value) :: {}", reason), ); - if let Some(span) = span { - d = d.with_label(Label::new_primary(span)); + if let Some(tag) = tag { + d = d.with_label(Label::new_primary(tag)); } d @@ -199,12 +204,12 @@ impl ShellError { ProximateShellError::ArgumentError { command, error, - span, + tag, } => match error { ArgumentError::InvalidExternalWord => Diagnostic::new( Severity::Error, format!("Invalid bare word for Nu command (did you intend to invoke an external command?)")) - .with_label(Label::new_primary(span)), + .with_label(Label::new_primary(tag)), ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new( Severity::Error, format!( @@ -214,7 +219,7 @@ impl ShellError { Color::Black.bold().paint(name) ), ) - .with_label(Label::new_primary(span)), + .with_label(Label::new_primary(tag)), ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new( Severity::Error, format!( @@ -224,7 +229,7 @@ impl ShellError { ), ) .with_label( - Label::new_primary(span).with_message(format!("requires {} parameter", name)), + Label::new_primary(tag).with_message(format!("requires {} parameter", name)), ), ArgumentError::MissingValueForName(name) => Diagnostic::new( Severity::Error, @@ -235,17 +240,17 @@ impl ShellError { Color::Black.bold().paint(name) ), ) - .with_label(Label::new_primary(span)), + .with_label(Label::new_primary(tag)), }, ProximateShellError::TypeError { expected, actual: Tagged { item: Some(actual), - tag: Tag { span, .. }, + tag, }, } => Diagnostic::new(Severity::Error, "Type Error").with_label( - Label::new_primary(span) + Label::new_primary(tag) .with_message(format!("Expected {}, found {}", expected, actual)), ), @@ -254,10 +259,10 @@ impl ShellError { actual: Tagged { item: None, - tag: Tag { span, .. }, + tag }, } => Diagnostic::new(Severity::Error, "Type Error") - .with_label(Label::new_primary(span).with_message(expected)), + .with_label(Label::new_primary(tag).with_message(expected)), ProximateShellError::RangeError { kind, @@ -265,10 +270,10 @@ impl ShellError { actual_kind: Tagged { item, - tag: Tag { span, .. }, + tag }, } => Diagnostic::new(Severity::Error, "Range Error").with_label( - Label::new_primary(span).with_message(format!( + Label::new_primary(tag).with_message(format!( "Expected to convert {} to {} while {}, but it was out of range", item, kind.desc(), @@ -279,11 +284,11 @@ impl ShellError { ProximateShellError::SyntaxError { problem: Tagged { - tag: Tag { span, .. }, + tag, .. }, } => Diagnostic::new(Severity::Error, "Syntax Error") - .with_label(Label::new_primary(span).with_message("Unexpected external command")), + .with_label(Label::new_primary(tag).with_message("Unexpected external command")), ProximateShellError::MissingProperty { subpath, expr } => { let subpath = subpath.into_label(); @@ -306,8 +311,8 @@ impl ShellError { ProximateShellError::Diagnostic(diag) => diag.diagnostic, ProximateShellError::CoerceError { left, right } => { Diagnostic::new(Severity::Error, "Coercion error") - .with_label(Label::new_primary(left.span()).with_message(left.item)) - .with_label(Label::new_secondary(right.span()).with_message(right.item)) + .with_label(Label::new_primary(left.tag()).with_message(left.item)) + .with_label(Label::new_secondary(right.tag()).with_message(right.item)) } } } @@ -315,26 +320,29 @@ impl ShellError { pub fn labeled_error( msg: impl Into, label: impl Into, - span: impl Into, + tag: impl Into, ) -> ShellError { ShellError::diagnostic( Diagnostic::new(Severity::Error, msg.into()) - .with_label(Label::new_primary(span.into()).with_message(label.into())), + .with_label(Label::new_primary(tag.into()).with_message(label.into())), ) } pub fn labeled_error_with_secondary( msg: impl Into, primary_label: impl Into, - primary_span: Span, + primary_span: impl Into, secondary_label: impl Into, - secondary_span: Span, + secondary_span: impl Into, ) -> ShellError { ShellError::diagnostic( Diagnostic::new_error(msg.into()) - .with_label(Label::new_primary(primary_span).with_message(primary_label.into())) .with_label( - Label::new_secondary(secondary_span).with_message(secondary_label.into()), + Label::new_primary(primary_span.into()).with_message(primary_label.into()), + ) + .with_label( + Label::new_secondary(secondary_span.into()) + .with_message(secondary_label.into()), ), ) } @@ -409,13 +417,13 @@ pub enum ProximateShellError { expr: Description, }, MissingValue { - span: Option, + tag: Option, reason: String, }, ArgumentError { command: String, error: ArgumentError, - span: Span, + tag: Tag, }, RangeError { kind: ExpectedRange, @@ -447,7 +455,7 @@ impl ToDebug for ProximateShellError { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ShellDiagnostic { - pub(crate) diagnostic: Diagnostic, + pub(crate) diagnostic: Diagnostic, } impl PartialEq for ShellDiagnostic { diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 52edf69818..b9db82b546 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -38,13 +38,13 @@ pub(crate) fn evaluate_baseline_expr( source: &Text, ) -> Result, ShellError> { match &expr.item { - RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(literal), source)), + RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_tag(literal), source)), RawExpression::ExternalWord => Err(ShellError::argument_error( "Invalid external word", ArgumentError::InvalidExternalWord, - expr.span(), + expr.tag(), )), - RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.span())), + RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.tag())), 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), @@ -53,13 +53,10 @@ pub(crate) fn evaluate_baseline_expr( let right = evaluate_baseline_expr(binary.right(), registry, scope, source)?; match left.compare(binary.op(), &*right) { - Ok(result) => Ok(Tagged::from_simple_spanned_item( - Value::boolean(result), - expr.span(), - )), + Ok(result) => Ok(Value::boolean(result).tagged(expr.tag())), Err((left_type, right_type)) => Err(ShellError::coerce_error( - binary.left().copy_span(left_type), - binary.right().copy_span(right_type), + binary.left().copy_tag(left_type), + binary.right().copy_tag(right_type), )), } } @@ -71,12 +68,14 @@ pub(crate) fn evaluate_baseline_expr( exprs.push(expr); } - Ok(Value::Table(exprs).tagged(Tag::unknown_origin(expr.span()))) + Ok(Value::Table(exprs).tagged(expr.tag())) + } + RawExpression::Block(block) => { + Ok( + Value::Block(Block::new(block.clone(), source.clone(), expr.tag())) + .tagged(expr.tag()), + ) } - RawExpression::Block(block) => Ok(Tagged::from_simple_spanned_item( - Value::Block(Block::new(block.clone(), source.clone(), expr.span())), - expr.span(), - )), RawExpression::Path(path) => { let value = evaluate_baseline_expr(path.head(), registry, scope, source)?; let mut item = value; @@ -92,18 +91,12 @@ pub(crate) fn evaluate_baseline_expr( )) } Some(next) => { - item = Tagged::from_simple_spanned_item( - next.clone().item, - (expr.span().start, name.span().end), - ) + item = next.clone().item.tagged(expr.tag()); } }; } - Ok(Tagged::from_simple_spanned_item( - item.item().clone(), - expr.span(), - )) + Ok(item.item().clone().tagged(expr.tag())) } RawExpression::Boolean(_boolean) => unimplemented!(), } @@ -113,9 +106,9 @@ fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged int.into(), hir::Literal::Size(int, unit) => unit.compute(int), - hir::Literal::String(span) => Value::string(span.slice(source)), - hir::Literal::GlobPattern => Value::pattern(literal.span().slice(source)), - hir::Literal::Bare => Value::string(literal.span().slice(source)), + hir::Literal::String(tag) => Value::string(tag.slice(source)), + hir::Literal::GlobPattern => Value::pattern(literal.tag().slice(source)), + hir::Literal::Bare => Value::string(literal.tag().slice(source)), }; literal.map(|_| result) @@ -127,12 +120,12 @@ fn evaluate_reference( source: &Text, ) -> Result, ShellError> { match name { - hir::Variable::It(span) => Ok(scope.it.item.clone().simple_spanned(span)), - hir::Variable::Other(span) => Ok(scope + hir::Variable::It(tag) => Ok(scope.it.item.clone().tagged(*tag)), + hir::Variable::Other(tag) => Ok(scope .vars - .get(span.slice(source)) + .get(tag.slice(source)) .map(|v| v.clone()) - .unwrap_or_else(|| Value::nothing().simple_spanned(span))), + .unwrap_or_else(|| Value::nothing().tagged(*tag))), } } @@ -142,6 +135,6 @@ fn evaluate_external( _source: &Text, ) -> Result, ShellError> { Err(ShellError::syntax_error( - "Unexpected external command".tagged(external.name()), + "Unexpected external command".tagged(*external.name()), )) } diff --git a/src/format/table.rs b/src/format/table.rs index 717e3c4a27..286be222c3 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -204,7 +204,7 @@ impl RenderView for TableView { let mut table = Table::new(); - let table_mode = crate::data::config::config(Span::unknown())? + let table_mode = crate::data::config::config(Tag::unknown())? .get("table_mode") .map(|s| match s.as_string().unwrap().as_ref() { "light" => TableMode::Light, diff --git a/src/lib.rs b/src/lib.rs index a18343df9b..60546e3854 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ mod utils; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::context::{SourceMap, SpanSource}; pub use crate::env::host::BasicHost; -pub use crate::parser::hir::SyntaxType; +pub use crate::parser::hir::SyntaxShape; pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder; pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; @@ -31,7 +31,7 @@ pub use cli::cli; pub use data::config::{APP_INFO, config_path}; pub use data::base::{Primitive, Value}; pub use data::dict::{Dictionary, TaggedDictBuilder}; -pub use data::meta::{Span, Tag, Tagged, TaggedItem}; +pub use data::meta::{Tag, Tagged, TaggedItem}; pub use errors::{CoerceInto, ShellError}; pub use num_traits::cast::ToPrimitive; pub use parser::parse::text::Text; diff --git a/src/parser.rs b/src/parser.rs index 2fd891efb0..3b3ac1a136 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -21,10 +21,10 @@ pub(crate) use parse::unit::Unit; pub(crate) use parse_command::parse_command; pub(crate) use registry::CommandRegistry; -pub fn parse(input: &str) -> Result { +pub fn parse(input: &str, origin: uuid::Uuid) -> Result { let _ = pretty_env_logger::try_init(); - match pipeline(nom_input(input)) { + match pipeline(nom_input(input, origin)) { Ok((_rest, val)) => Ok(val), Err(err) => Err(ShellError::parse_error(err)), } diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs index d5427766b5..f9b9146e50 100644 --- a/src/parser/deserializer.rs +++ b/src/parser/deserializer.rs @@ -37,7 +37,7 @@ impl<'de> ConfigDeserializer<'de> { let value: Option> = if name == "rest" { let positional = self.call.args.slice_from(self.position); self.position += positional.len(); - Some(Value::Table(positional).tagged_unknown()) // TODO: correct span + Some(Value::Table(positional).tagged_unknown()) // TODO: correct tag } else { if self.call.args.has(name) { self.call.args.get(name).map(|x| x.clone()) @@ -52,9 +52,7 @@ impl<'de> ConfigDeserializer<'de> { self.stack.push(DeserializerItem { key_struct_field: Some((name.to_string(), name)), - val: value.unwrap_or_else(|| { - Value::nothing().tagged(Tag::unknown_origin(self.call.name_span)) - }), + val: value.unwrap_or_else(|| Value::nothing().tagged(self.call.name_tag)), }); Ok(()) diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 90bb38796a..96eb7272a6 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -25,7 +25,7 @@ pub(crate) use self::external_command::ExternalCommand; pub(crate) use self::named::NamedArguments; pub(crate) use self::path::Path; -pub use self::baseline_parse_tokens::SyntaxType; +pub use self::baseline_parse_tokens::SyntaxShape; pub fn path(head: impl Into, tail: Vec>>) -> Path { Path::new( @@ -131,72 +131,57 @@ impl RawExpression { pub type Expression = Tagged; impl Expression { - pub(crate) fn number(i: impl Into, span: impl Into) -> Expression { - Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Number(i.into())), span) + pub(crate) fn number(i: impl Into, tag: impl Into) -> Expression { + RawExpression::Literal(Literal::Number(i.into())).tagged(tag.into()) } pub(crate) fn size( i: impl Into, unit: impl Into, - span: impl Into, + tag: impl Into, ) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::Literal(Literal::Size(i.into(), unit.into())), - span, - ) + RawExpression::Literal(Literal::Size(i.into(), unit.into())).tagged(tag.into()) } pub(crate) fn synthetic_string(s: impl Into) -> Expression { RawExpression::Synthetic(Synthetic::String(s.into())).tagged_unknown() } - pub(crate) fn string(inner: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::Literal(Literal::String(inner.into())), - outer.into(), - ) + pub(crate) fn string(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Literal(Literal::String(inner.into())).tagged(outer.into()) } - pub(crate) fn file_path(path: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item(RawExpression::FilePath(path.into()), outer.into()) + pub(crate) fn file_path(path: impl Into, outer: impl Into) -> Expression { + RawExpression::FilePath(path.into()).tagged(outer) } - pub(crate) fn bare(span: impl Into) -> Expression { - Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into()) + pub(crate) fn bare(tag: impl Into) -> Expression { + RawExpression::Literal(Literal::Bare).tagged(tag) } pub(crate) fn pattern(tag: impl Into) -> Expression { RawExpression::Literal(Literal::GlobPattern).tagged(tag.into()) } - pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::Variable(Variable::Other(inner.into())), - outer.into(), - ) + pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Variable(Variable::Other(inner.into())).tagged(outer) } - pub(crate) fn external_command(inner: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::ExternalCommand(ExternalCommand::new(inner.into())), - outer.into(), - ) + pub(crate) fn external_command(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::ExternalCommand(ExternalCommand::new(inner.into())).tagged(outer) } - pub(crate) fn it_variable(inner: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::Variable(Variable::It(inner.into())), - outer.into(), - ) + pub(crate) fn it_variable(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Variable(Variable::It(inner.into())).tagged(outer) } } impl ToDebug for Expression { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { match self.item() { - RawExpression::Literal(l) => l.tagged(self.span()).fmt_debug(f, source), + RawExpression::Literal(l) => l.tagged(self.tag()).fmt_debug(f, source), RawExpression::FilePath(p) => write!(f, "{}", p.display()), - RawExpression::ExternalWord => write!(f, "{}", self.span().slice(source)), + RawExpression::ExternalWord => write!(f, "{}", self.tag().slice(source)), 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)), @@ -242,7 +227,7 @@ impl From> for Expression { pub enum Literal { Number(Number), Size(Number, Unit), - String(Span), + String(Tag), GlobPattern, Bare, } @@ -252,9 +237,9 @@ impl ToDebug for Tagged<&Literal> { match self.item() { Literal::Number(number) => write!(f, "{:?}", *number), Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), - Literal::String(span) => write!(f, "{}", span.slice(source)), - Literal::GlobPattern => write!(f, "{}", self.span().slice(source)), - Literal::Bare => write!(f, "{}", self.span().slice(source)), + Literal::String(tag) => write!(f, "{}", tag.slice(source)), + Literal::GlobPattern => write!(f, "{}", self.tag().slice(source)), + Literal::Bare => write!(f, "{}", self.tag().slice(source)), } } } @@ -273,6 +258,6 @@ impl Literal { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Variable { - It(Span), - Other(Span), + It(Tag), + Other(Tag), } diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index 5248bde5f9..267494f27c 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -10,19 +10,19 @@ pub fn baseline_parse_single_token( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), + RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()), RawToken::Size(int, unit) => { - hir::Expression::size(int.to_number(source), unit, token.span()) + hir::Expression::size(int.to_number(source), unit, token.tag()) } - RawToken::String(span) => hir::Expression::string(span, token.span()), - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::String(tag) => hir::Expression::string(tag, token.tag()), + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) } - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::GlobPattern => hir::Expression::pattern(token.span()), - RawToken::Bare => hir::Expression::bare(token.span()), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::GlobPattern => hir::Expression::pattern(token.tag()), + RawToken::Bare => hir::Expression::bare(token.tag()), }) } @@ -31,24 +31,24 @@ pub fn baseline_parse_token_as_number( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) } - RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()), RawToken::Size(number, unit) => { - hir::Expression::size(number.to_number(source), unit, token.span()) + hir::Expression::size(number.to_number(source), unit, token.tag()) } - RawToken::Bare => hir::Expression::bare(token.span()), + RawToken::Bare => hir::Expression::bare(token.tag()), RawToken::GlobPattern => { return Err(ShellError::type_error( "Number", "glob pattern".to_string().tagged(token.tag()), )) } - RawToken::String(span) => hir::Expression::string(span, token.span()), + RawToken::String(tag) => hir::Expression::string(tag, token.tag()), }) } @@ -57,22 +57,22 @@ pub fn baseline_parse_token_as_string( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) } - RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Number(_) => hir::Expression::bare(token.span()), - RawToken::Size(_, _) => hir::Expression::bare(token.span()), - RawToken::Bare => hir::Expression::bare(token.span()), + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::Number(_) => hir::Expression::bare(token.tag()), + RawToken::Size(_, _) => hir::Expression::bare(token.tag()), + RawToken::Bare => hir::Expression::bare(token.tag()), RawToken::GlobPattern => { return Err(ShellError::type_error( "String", "glob pattern".tagged(token.tag()), )) } - RawToken::String(span) => hir::Expression::string(span, token.span()), + RawToken::String(tag) => hir::Expression::string(tag, token.tag()), }) } @@ -82,26 +82,25 @@ pub fn baseline_parse_token_as_path( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) + } + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::Number(_) => hir::Expression::bare(token.tag()), + RawToken::Size(_, _) => hir::Expression::bare(token.tag()), + RawToken::Bare => { + hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag()) } - RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Number(_) => 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::GlobPattern => { return Err(ShellError::type_error( "Path", "glob pattern".tagged(token.tag()), )) } - RawToken::String(span) => { - hir::Expression::file_path(expand_path(span.slice(source), context), token.span()) + RawToken::String(tag) => { + hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag()) } }) } @@ -112,25 +111,24 @@ pub fn baseline_parse_token_as_pattern( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) } RawToken::ExternalCommand(_) => { return Err(ShellError::syntax_error( "Invalid external command".to_string().tagged(token.tag()), )) } - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Number(_) => hir::Expression::bare(token.span()), - RawToken::Size(_, _) => hir::Expression::bare(token.span()), - RawToken::GlobPattern => hir::Expression::pattern(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()) + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::Number(_) => hir::Expression::bare(token.tag()), + RawToken::Size(_, _) => hir::Expression::bare(token.tag()), + RawToken::GlobPattern => hir::Expression::pattern(token.tag()), + RawToken::Bare => { + hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag()) + } + RawToken::String(tag) => { + hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag()) } }) } diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index ac2c703d3a..8413bd07e1 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -8,7 +8,7 @@ use crate::parser::{ }, DelimitedNode, Delimiter, PathNode, RawToken, TokenNode, }; -use crate::{Span, Tag, Tagged, TaggedItem, Text}; +use crate::{Tag, Tagged, TaggedItem, Text}; use derive_new::new; use log::trace; use serde::{Deserialize, Serialize}; @@ -17,7 +17,7 @@ pub fn baseline_parse_tokens( token_nodes: &mut TokensIterator<'_>, context: &Context, source: &Text, - syntax_type: SyntaxType, + syntax_type: SyntaxShape, ) -> Result, ShellError> { let mut exprs: Vec = vec![]; @@ -34,7 +34,7 @@ pub fn baseline_parse_tokens( } #[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub enum SyntaxType { +pub enum SyntaxShape { Any, List, Literal, @@ -49,21 +49,21 @@ pub enum SyntaxType { Boolean, } -impl std::fmt::Display for SyntaxType { +impl std::fmt::Display for SyntaxShape { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - SyntaxType::Any => write!(f, "Any"), - SyntaxType::List => write!(f, "List"), - SyntaxType::Literal => write!(f, "Literal"), - SyntaxType::String => write!(f, "String"), - SyntaxType::Member => write!(f, "Member"), - SyntaxType::Variable => write!(f, "Variable"), - SyntaxType::Number => write!(f, "Number"), - SyntaxType::Path => write!(f, "Path"), - SyntaxType::Pattern => write!(f, "Pattern"), - SyntaxType::Binary => write!(f, "Binary"), - SyntaxType::Block => write!(f, "Block"), - SyntaxType::Boolean => write!(f, "Boolean"), + SyntaxShape::Any => write!(f, "Any"), + SyntaxShape::List => write!(f, "List"), + SyntaxShape::Literal => write!(f, "Literal"), + SyntaxShape::String => write!(f, "String"), + SyntaxShape::Member => write!(f, "Member"), + SyntaxShape::Variable => write!(f, "Variable"), + SyntaxShape::Number => write!(f, "Number"), + SyntaxShape::Path => write!(f, "Path"), + SyntaxShape::Pattern => write!(f, "Pattern"), + SyntaxShape::Binary => write!(f, "Binary"), + SyntaxShape::Block => write!(f, "Block"), + SyntaxShape::Boolean => write!(f, "Boolean"), } } } @@ -72,7 +72,7 @@ pub fn baseline_parse_next_expr( tokens: &mut TokensIterator, context: &Context, source: &Text, - syntax_type: SyntaxType, + syntax_type: SyntaxShape, ) -> Result { let next = tokens .next() @@ -81,69 +81,69 @@ pub fn baseline_parse_next_expr( trace!(target: "nu::parser::parse_one_expr", "syntax_type={:?}, token={:?}", syntax_type, next); match (syntax_type, next) { - (SyntaxType::Path, TokenNode::Token(token)) => { + (SyntaxShape::Path, TokenNode::Token(token)) => { return baseline_parse_token_as_path(token, context, source) } - (SyntaxType::Path, token) => { + (SyntaxShape::Path, token) => { return Err(ShellError::type_error( "Path", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } - (SyntaxType::Pattern, TokenNode::Token(token)) => { + (SyntaxShape::Pattern, TokenNode::Token(token)) => { return baseline_parse_token_as_pattern(token, context, source) } - (SyntaxType::Pattern, token) => { + (SyntaxShape::Pattern, token) => { return Err(ShellError::type_error( "Path", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } - (SyntaxType::String, TokenNode::Token(token)) => { + (SyntaxShape::String, TokenNode::Token(token)) => { return baseline_parse_token_as_string(token, source); } - (SyntaxType::String, token) => { + (SyntaxShape::String, token) => { return Err(ShellError::type_error( "String", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } - (SyntaxType::Number, TokenNode::Token(token)) => { + (SyntaxShape::Number, TokenNode::Token(token)) => { return Ok(baseline_parse_token_as_number(token, source)?); } - (SyntaxType::Number, token) => { + (SyntaxShape::Number, token) => { return Err(ShellError::type_error( "Numeric", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } // TODO: More legit member processing - (SyntaxType::Member, TokenNode::Token(token)) => { + (SyntaxShape::Member, TokenNode::Token(token)) => { return baseline_parse_token_as_string(token, source); } - (SyntaxType::Member, token) => { + (SyntaxShape::Member, token) => { return Err(ShellError::type_error( "member", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } - (SyntaxType::Any, _) => {} - (SyntaxType::List, _) => {} - (SyntaxType::Literal, _) => {} - (SyntaxType::Variable, _) => {} - (SyntaxType::Binary, _) => {} - (SyntaxType::Block, _) => {} - (SyntaxType::Boolean, _) => {} + (SyntaxShape::Any, _) => {} + (SyntaxShape::List, _) => {} + (SyntaxShape::Literal, _) => {} + (SyntaxShape::Variable, _) => {} + (SyntaxShape::Binary, _) => {} + (SyntaxShape::Block, _) => {} + (SyntaxShape::Boolean, _) => {} }; let first = baseline_parse_semantic_token(next, context, source)?; @@ -162,7 +162,7 @@ pub fn baseline_parse_next_expr( return Err(ShellError::labeled_error( "Expected something after an operator", "operator", - op.span(), + op.tag(), )) } Some(token) => baseline_parse_semantic_token(token, context, source)?, @@ -171,75 +171,66 @@ pub fn baseline_parse_next_expr( // We definitely have a binary expression here -- let's see if we should coerce it into a block match syntax_type { - SyntaxType::Any => { - let span = (first.span().start, second.span().end); + SyntaxShape::Any => { + let tag = first.tag().until(second.tag()); let binary = hir::Binary::new(first, op, second); let binary = hir::RawExpression::Binary(Box::new(binary)); - let binary = Tagged::from_simple_spanned_item(binary, span); + let binary = binary.tagged(tag); Ok(binary) } - SyntaxType::Block => { - let span = (first.span().start, second.span().end); + SyntaxShape::Block => { + let tag = first.tag().until(second.tag()); let path: Tagged = match first { Tagged { item: hir::RawExpression::Literal(hir::Literal::Bare), - tag: Tag { span, .. }, + tag, } => { - let string = - Tagged::from_simple_spanned_item(span.slice(source).to_string(), span); + let string = tag.slice(source).to_string().tagged(tag); let path = hir::Path::new( - Tagged::from_simple_spanned_item( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), - (0, 0), - ), + // TODO: Deal with synthetic nodes that have no representation at all in source + hir::RawExpression::Variable(hir::Variable::It(Tag::unknown())) + .tagged(Tag::unknown()), vec![string], ); let path = hir::RawExpression::Path(Box::new(path)); - Tagged::from_simple_spanned_item(path, first.span()) + path.tagged(first.tag()) } Tagged { item: hir::RawExpression::Literal(hir::Literal::String(inner)), - tag: Tag { span, .. }, + tag, } => { - let string = - Tagged::from_simple_spanned_item(inner.slice(source).to_string(), span); + let string = inner.slice(source).to_string().tagged(tag); let path = hir::Path::new( - Tagged::from_simple_spanned_item( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), - (0, 0), - ), + // TODO: Deal with synthetic nodes that have no representation at all in source + hir::RawExpression::Variable(hir::Variable::It(Tag::unknown())) + .tagged_unknown(), vec![string], ); let path = hir::RawExpression::Path(Box::new(path)); - Tagged::from_simple_spanned_item(path, first.span()) + path.tagged(first.tag()) } Tagged { item: hir::RawExpression::Variable(..), .. } => first, - Tagged { - tag: Tag { span, .. }, - item, - } => { + Tagged { tag, item } => { return Err(ShellError::labeled_error( "The first part of an un-braced block must be a column name", item.type_name(), - span, + tag, )) } }; let binary = hir::Binary::new(path, op, second); let binary = hir::RawExpression::Binary(Box::new(binary)); - let binary = Tagged::from_simple_spanned_item(binary, span); + let binary = binary.tagged(tag); let block = hir::RawExpression::Block(vec![binary]); - let block = Tagged::from_simple_spanned_item(block, span); + let block = block.tagged(tag); Ok(block) } @@ -265,11 +256,11 @@ pub fn baseline_parse_semantic_token( "Unexpected operator".tagged(op.tag), )), TokenNode::Flag(flag) => Err(ShellError::syntax_error("Unexpected flag".tagged(flag.tag))), - TokenNode::Member(span) => Err(ShellError::syntax_error( - "BUG: Top-level member".tagged(span), + TokenNode::Member(tag) => Err(ShellError::syntax_error( + "BUG: Top-level member".tagged(*tag), )), - TokenNode::Whitespace(span) => Err(ShellError::syntax_error( - "BUG: Whitespace found during parse".tagged(span), + TokenNode::Whitespace(tag) => Err(ShellError::syntax_error( + "BUG: Whitespace found during parse".tagged(*tag), )), TokenNode::Error(error) => Err(*error.item.clone()), TokenNode::Path(path) => baseline_parse_path(path, context, source), @@ -288,11 +279,11 @@ pub fn baseline_parse_delimited( &mut TokensIterator::new(children), context, source, - SyntaxType::Any, + SyntaxShape::Any, )?; let expr = hir::RawExpression::Block(exprs); - Ok(Tagged::from_simple_spanned_item(expr, token.span())) + Ok(expr.tagged(token.tag())) } Delimiter::Paren => unimplemented!(), Delimiter::Square => { @@ -301,11 +292,11 @@ pub fn baseline_parse_delimited( &mut TokensIterator::new(children), context, source, - SyntaxType::Any, + SyntaxShape::Any, )?; let expr = hir::RawExpression::List(exprs); - Ok(expr.tagged(Tag::unknown_origin(token.span()))) + Ok(expr.tagged(token.tag())) } } } @@ -322,8 +313,8 @@ pub fn baseline_parse_path( for part in token.tail() { let string = match part { TokenNode::Token(token) => match token.item() { - RawToken::Bare => token.span().slice(source), - RawToken::String(span) => span.slice(source), + RawToken::Bare => token.tag().slice(source), + RawToken::String(tag) => tag.slice(source), RawToken::Number(_) | RawToken::Size(..) | RawToken::Variable(_) @@ -332,26 +323,26 @@ pub fn baseline_parse_path( | RawToken::ExternalWord => { return Err(ShellError::type_error( "String", - token.type_name().simple_spanned(part), + token.type_name().tagged(part.tag()), )) } }, - TokenNode::Member(span) => span.slice(source), + TokenNode::Member(tag) => tag.slice(source), // TODO: Make this impossible other => { return Err(ShellError::syntax_error( - format!("{} in path", other.type_name()).tagged(other.span()), + format!("{} in path", other.type_name()).tagged(other.tag()), )) } } .to_string(); - tail.push(string.simple_spanned(part)); + tail.push(string.tagged(part.tag())); } - Ok(hir::path(head, tail).simple_spanned(token).into()) + Ok(hir::path(head, tail).tagged(token.tag()).into()) } #[derive(Debug, new)] diff --git a/src/parser/hir/external_command.rs b/src/parser/hir/external_command.rs index 8511cce1e0..28865330d5 100644 --- a/src/parser/hir/external_command.rs +++ b/src/parser/hir/external_command.rs @@ -9,7 +9,7 @@ use std::fmt; )] #[get = "pub(crate)"] pub struct ExternalCommand { - name: Span, + name: Tag, } impl ToDebug for ExternalCommand { diff --git a/src/parser/hir/named.rs b/src/parser/hir/named.rs index 96d5132fb8..838f643be5 100644 --- a/src/parser/hir/named.rs +++ b/src/parser/hir/named.rs @@ -1,7 +1,6 @@ use crate::parser::hir::Expression; use crate::parser::Flag; use crate::prelude::*; -use crate::Span; use derive_new::new; use indexmap::IndexMap; use log::trace; @@ -11,7 +10,7 @@ use std::fmt; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum NamedValue { AbsentSwitch, - PresentSwitch(Span), + PresentSwitch(Tag), AbsentValue, Value(Expression), } @@ -27,7 +26,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(tag) => write!(f, " --{}", tag.slice(source))?, NamedValue::AbsentValue => continue, NamedValue::Value(expr) => write!(f, " --{} {}", name, expr.debug(source))?, } diff --git a/src/parser/parse/files.rs b/src/parser/parse/files.rs index 173da54a80..6cedb1e99c 100644 --- a/src/parser/parse/files.rs +++ b/src/parser/parse/files.rs @@ -1,6 +1,7 @@ -use crate::Span; +use crate::Tag; use derive_new::new; use language_reporting::{FileName, Location}; +use uuid::Uuid; #[derive(new, Debug, Clone)] pub struct Files { @@ -8,26 +9,30 @@ pub struct Files { } impl language_reporting::ReportingFiles for Files { - type Span = Span; - type FileId = usize; + type Span = Tag; + type FileId = Uuid; fn byte_span( &self, - _file: Self::FileId, + file: Self::FileId, from_index: usize, to_index: usize, ) -> Option { - Some(Span::from((from_index, to_index))) + Some(Tag::from((from_index, to_index, file))) } - fn file_id(&self, _span: Self::Span) -> Self::FileId { - 0 + + fn file_id(&self, tag: Self::Span) -> Self::FileId { + tag.origin.unwrap() } + fn file_name(&self, _file: Self::FileId) -> FileName { FileName::Verbatim(format!("shell")) } + fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option { unimplemented!("byte_index") } + fn location(&self, _file: Self::FileId, byte_index: usize) -> Option { let source = &self.snippet; let mut seen_lines = 0; @@ -51,14 +56,15 @@ impl language_reporting::ReportingFiles for Files { None } } - fn line_span(&self, _file: Self::FileId, lineno: usize) -> Option { + + fn line_span(&self, file: Self::FileId, lineno: usize) -> Option { let source = &self.snippet; let mut seen_lines = 0; let mut seen_bytes = 0; for (pos, _) in source.match_indices('\n') { if seen_lines == lineno { - return Some(Span::from((seen_bytes, pos))); + return Some(Tag::from((seen_bytes, pos, file))); } else { seen_lines += 1; seen_bytes = pos + 1; @@ -66,17 +72,18 @@ impl language_reporting::ReportingFiles for Files { } if seen_lines == 0 { - Some(Span::from((0, self.snippet.len() - 1))) + Some(Tag::from((0, self.snippet.len() - 1, file))) } else { None } } - fn source(&self, span: Self::Span) -> Option { - if span.start > span.end { + + fn source(&self, tag: Self::Span) -> Option { + if tag.span.start > tag.span.end { return None; - } else if span.end >= self.snippet.len() { + } else if tag.span.end >= self.snippet.len() { return None; } - Some(self.snippet[span.start..span.end].to_string()) + Some(tag.slice(&self.snippet).to_string()) } } diff --git a/src/parser/parse/flag.rs b/src/parser/parse/flag.rs index 096d69879f..09d1e86337 100644 --- a/src/parser/parse/flag.rs +++ b/src/parser/parse/flag.rs @@ -1,4 +1,4 @@ -use crate::Span; +use crate::Tag; use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; @@ -13,5 +13,5 @@ pub enum FlagKind { #[get = "pub(crate)"] pub struct Flag { kind: FlagKind, - name: Span, + name: Tag, } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 0be05af062..f30eb2c11b 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -5,7 +5,7 @@ use crate::parser::parse::{ tokens::*, unit::*, }; use crate::prelude::*; -use crate::{Span, Tagged}; +use crate::{Tag, Tagged}; use nom; use nom::branch::*; use nom::bytes::complete::*; @@ -18,15 +18,16 @@ use log::trace; use nom::dbg; use nom::*; use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice}; -use nom5_locate::{position, LocatedSpan}; +use nom_locate::{position, LocatedSpanEx}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::str::FromStr; +use uuid::Uuid; -pub type NomSpan<'a> = LocatedSpan<&'a str>; +pub type NomSpan<'a> = LocatedSpanEx<&'a str, Uuid>; -pub fn nom_input(s: &str) -> NomSpan<'_> { - LocatedSpan::new(s) +pub fn nom_input(s: &str, origin: Uuid) -> NomSpan<'_> { + LocatedSpanEx::new_extra(s, origin) } macro_rules! operator { @@ -38,7 +39,7 @@ macro_rules! operator { Ok(( input, - TokenTreeBuilder::spanned_op(tag.fragment, (start, end)), + TokenTreeBuilder::tagged_op(tag.fragment, (start, end, input.extra)), )) } }; @@ -159,14 +160,14 @@ pub fn raw_number(input: NomSpan) -> IResult> { Ok((input, dot)) => input, // it's just an integer - Err(_) => return Ok((input, RawNumber::int((start, input.offset)))), + Err(_) => return Ok((input, RawNumber::int((start, input.offset, input.extra)))), }; let (input, tail) = digit1(input)?; let end = input.offset; - Ok((input, RawNumber::decimal((start, end)))) + Ok((input, RawNumber::decimal((start, end, input.extra)))) }) } @@ -189,7 +190,7 @@ pub fn dq_string(input: NomSpan) -> IResult { let end = input.offset; Ok(( input, - TokenTreeBuilder::spanned_string((start1, end1), (start, end)), + TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), )) }) } @@ -206,7 +207,7 @@ pub fn sq_string(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_string((start1, end1), (start, end)), + TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), )) }) } @@ -226,7 +227,7 @@ pub fn external(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_external(bare, (start, end)), + TokenTreeBuilder::tagged_external(bare, (start, end, input.extra)), )) }) } @@ -250,7 +251,10 @@ pub fn pattern(input: NomSpan) -> IResult { let end = input.offset; - Ok((input, TokenTreeBuilder::spanned_pattern((start, end)))) + Ok(( + input, + TokenTreeBuilder::tagged_pattern((start, end, input.extra)), + )) }) } @@ -263,7 +267,7 @@ pub fn bare(input: NomSpan) -> IResult { let next_char = &input.fragment.chars().nth(0); if let Some(next_char) = next_char { - if is_external_word_char(*next_char) || *next_char == '*' { + if is_external_word_char(*next_char) || is_glob_specific_char(*next_char) { return Err(nom::Err::Error(nom::error::make_error( input, nom::error::ErrorKind::TakeWhile1, @@ -273,7 +277,10 @@ pub fn bare(input: NomSpan) -> IResult { let end = input.offset; - Ok((input, TokenTreeBuilder::spanned_bare((start, end)))) + Ok(( + input, + TokenTreeBuilder::tagged_bare((start, end, input.extra)), + )) }) } @@ -283,7 +290,10 @@ pub fn external_word(input: NomSpan) -> IResult { let (input, _) = take_while1(is_external_word_char)(input)?; let end = input.offset; - Ok((input, TokenTreeBuilder::spanned_external_word((start, end)))) + Ok(( + input, + TokenTreeBuilder::tagged_external_word((start, end, input.extra)), + )) }) } @@ -296,7 +306,7 @@ pub fn var(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_var(bare.span(), (start, end)), + TokenTreeBuilder::tagged_var(bare.tag(), (start, end, input.extra)), )) }) } @@ -309,7 +319,10 @@ pub fn member(input: NomSpan) -> IResult { let end = input.offset; - Ok((input, TokenTreeBuilder::spanned_member((start, end)))) + Ok(( + input, + TokenTreeBuilder::tagged_member((start, end, input.extra)), + )) }) } @@ -322,7 +335,7 @@ pub fn flag(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_flag(bare.span(), (start, end)), + TokenTreeBuilder::tagged_flag(bare.tag(), (start, end, input.extra)), )) }) } @@ -336,7 +349,7 @@ pub fn shorthand(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_shorthand(bare.span(), (start, end)), + TokenTreeBuilder::tagged_shorthand(bare.tag(), (start, end, input.extra)), )) }) } @@ -369,7 +382,7 @@ pub fn raw_unit(input: NomSpan) -> IResult> { Ok(( input, - Tagged::from_simple_spanned_item(Unit::from(unit.fragment), (start, end)), + Unit::from(unit.fragment).tagged((start, end, input.extra)), )) }) } @@ -389,7 +402,7 @@ pub fn size(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_size((number.item, *size), (start, end)), + TokenTreeBuilder::tagged_size((number.item, *size), (start, end, input.extra)), )) } else { let end = input.offset; @@ -401,7 +414,7 @@ pub fn size(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_number(number.item, number.tag), + TokenTreeBuilder::tagged_number(number.item, number.tag), )) } }) @@ -455,18 +468,18 @@ fn make_token_list( let mut nodes = vec![]; if let Some(sp_left) = sp_left { - nodes.push(TokenNode::Whitespace(Span::from(sp_left))); + nodes.push(TokenNode::Whitespace(Tag::from(sp_left))); } nodes.push(first); for (ws, token) in list { - nodes.push(TokenNode::Whitespace(Span::from(ws))); + nodes.push(TokenNode::Whitespace(Tag::from(ws))); nodes.push(token); } if let Some(sp_right) = sp_right { - nodes.push(TokenNode::Whitespace(Span::from(sp_right))); + nodes.push(TokenNode::Whitespace(Tag::from(sp_right))); } nodes @@ -478,7 +491,10 @@ pub fn whitespace(input: NomSpan) -> IResult { let (input, ws1) = space1(input)?; let right = input.offset; - Ok((input, TokenTreeBuilder::spanned_ws((left, right)))) + Ok(( + input, + TokenTreeBuilder::tagged_ws((left, right, input.extra)), + )) }) } @@ -508,7 +524,7 @@ pub fn delimited_paren(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_parens(items, (left, right)), + TokenTreeBuilder::tagged_parens(items, (left, right, input.extra)), )) }) } @@ -539,7 +555,7 @@ pub fn delimited_square(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_square(items, (left, right)), + TokenTreeBuilder::tagged_square(items, (left, right, input.extra)), )) }) } @@ -556,7 +572,10 @@ pub fn delimited_brace(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_brace(items.unwrap_or_else(|| vec![]), (left, right)), + TokenTreeBuilder::tagged_brace( + items.unwrap_or_else(|| vec![]), + (left, right, input.extra), + ), )) }) } @@ -567,7 +586,10 @@ pub fn raw_call(input: NomSpan) -> IResult> { let (input, items) = token_list(input)?; let right = input.offset; - Ok((input, TokenTreeBuilder::spanned_call(items, (left, right)))) + Ok(( + input, + TokenTreeBuilder::tagged_call(items, (left, right, input.extra)), + )) }) } @@ -581,7 +603,7 @@ pub fn path(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_path((head, tail), (left, right)), + TokenTreeBuilder::tagged_path((head, tail), (left, right, input.extra)), )) }) } @@ -628,9 +650,9 @@ pub fn pipeline(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_pipeline( - (make_call_list(head, items), tail.map(Span::from)), - (start, end), + TokenTreeBuilder::tagged_pipeline( + (make_call_list(head, items), tail.map(Tag::from)), + (start, end, input.extra), ), )) }) @@ -643,17 +665,17 @@ fn make_call_list( let mut out = vec![]; if let Some(head) = head { - let el = PipelineElement::new(None, head.0.map(Span::from), head.1, head.2.map(Span::from)); + let el = PipelineElement::new(None, head.0.map(Tag::from), head.1, head.2.map(Tag::from)); out.push(el); } for (pipe, ws1, call, ws2) in items { let el = PipelineElement::new( - Some(pipe).map(Span::from), - ws1.map(Span::from), + Some(pipe).map(Tag::from), + ws1.map(Tag::from), call, - ws2.map(Span::from), + ws2.map(Tag::from), ); out.push(el); @@ -679,12 +701,17 @@ fn is_external_word_char(c: char) -> bool { } } +/// These characters appear in globs and not bare words +fn is_glob_specific_char(c: char) -> bool { + c == '*' || c == '?' +} + fn is_start_glob_char(c: char) -> bool { - is_start_bare_char(c) || c == '*' + is_start_bare_char(c) || is_glob_specific_char(c) } fn is_glob_char(c: char) -> bool { - is_bare_char(c) || c == '*' + is_bare_char(c) || is_glob_specific_char(c) } fn is_start_bare_char(c: char) -> bool { @@ -779,7 +806,7 @@ mod tests { macro_rules! equal_tokens { ($source:tt -> $tokens:expr) => { let result = apply(pipeline, "pipeline", $source); - let (expected_tree, expected_source) = TokenTreeBuilder::build($tokens); + let (expected_tree, expected_source) = TokenTreeBuilder::build(uuid::Uuid::nil(), $tokens); if result != expected_tree { let debug_result = format!("{}", result.debug($source)); @@ -818,12 +845,12 @@ mod tests { fn test_integer() { assert_leaf! { parsers [ size ] - "123" -> 0..3 { Number(RawNumber::int((0, 3)).item) } + "123" -> 0..3 { Number(RawNumber::int((0, 3, test_uuid())).item) } } assert_leaf! { parsers [ size ] - "-123" -> 0..4 { Number(RawNumber::int((0, 4)).item) } + "-123" -> 0..4 { Number(RawNumber::int((0, 4, test_uuid())).item) } } } @@ -831,12 +858,12 @@ mod tests { fn test_size() { assert_leaf! { parsers [ size ] - "123MB" -> 0..5 { Size(RawNumber::int((0, 3)).item, Unit::MB) } + "123MB" -> 0..5 { Size(RawNumber::int((0, 3, test_uuid())).item, Unit::MB) } } assert_leaf! { parsers [ size ] - "10GB" -> 0..4 { Size(RawNumber::int((0, 2)).item, Unit::GB) } + "10GB" -> 0..4 { Size(RawNumber::int((0, 2, test_uuid())).item, Unit::GB) } } } @@ -874,12 +901,12 @@ mod tests { fn test_string() { assert_leaf! { parsers [ string dq_string ] - r#""hello world""# -> 0..13 { String(span(1, 12)) } + r#""hello world""# -> 0..13 { String(tag(1, 12)) } } assert_leaf! { parsers [ string sq_string ] - r"'hello world'" -> 0..13 { String(span(1, 12)) } + r"'hello world'" -> 0..13 { String(tag(1, 12)) } } } @@ -931,12 +958,12 @@ mod tests { fn test_variable() { assert_leaf! { parsers [ var ] - "$it" -> 0..3 { Variable(span(1, 3)) } + "$it" -> 0..3 { Variable(tag(1, 3)) } } assert_leaf! { parsers [ var ] - "$name" -> 0..5 { Variable(span(1, 5)) } + "$name" -> 0..5 { Variable(tag(1, 5)) } } } @@ -944,7 +971,7 @@ mod tests { fn test_external() { assert_leaf! { parsers [ external ] - "^ls" -> 0..3 { ExternalCommand(span(1, 3)) } + "^ls" -> 0..3 { ExternalCommand(tag(1, 3)) } } } @@ -1260,7 +1287,7 @@ mod tests { desc: &str, string: &str, ) -> T { - match f(NomSpan::new(string)) { + match f(NomSpan::new_extra(string, uuid::Uuid::nil())) { Ok(v) => v.1, Err(other) => { println!("{:?}", other); @@ -1270,44 +1297,46 @@ mod tests { } } - fn span(left: usize, right: usize) -> Span { - Span::from((left, right)) + fn tag(left: usize, right: usize) -> Tag { + Tag::from((left, right, uuid::Uuid::nil())) } fn delimited( - delimiter: Delimiter, + delimiter: Tagged, children: Vec, left: usize, right: usize, ) -> TokenNode { - let node = DelimitedNode::new(delimiter, children); - let spanned = Tagged::from_simple_spanned_item(node, (left, right)); + let node = DelimitedNode::new(*delimiter, children); + let spanned = node.tagged((left, right, delimiter.tag.origin)); TokenNode::Delimited(spanned) } fn path(head: TokenNode, tail: Vec, left: usize, right: usize) -> TokenNode { + let tag = head.tag(); + let node = PathNode::new( Box::new(head), tail.into_iter().map(TokenNode::Token).collect(), ); - let spanned = Tagged::from_simple_spanned_item(node, (left, right)); + let spanned = node.tagged((left, right, tag.origin)); TokenNode::Path(spanned) } - fn leaf_token(token: RawToken, left: usize, right: usize) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right))) - } - fn token(token: RawToken, left: usize, right: usize) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right))) + TokenNode::Token(token.tagged((left, right, uuid::Uuid::nil()))) } fn build(block: CurriedNode) -> T { - let mut builder = TokenTreeBuilder::new(); + let mut builder = TokenTreeBuilder::new(uuid::Uuid::nil()); block(&mut builder) } fn build_token(block: CurriedToken) -> TokenNode { - TokenTreeBuilder::build(block).0 + TokenTreeBuilder::build(uuid::Uuid::nil(), block).0 + } + + fn test_uuid() -> uuid::Uuid { + uuid::Uuid::nil() } } diff --git a/src/parser/parse/pipeline.rs b/src/parser/parse/pipeline.rs index 64a899c179..42bbe23a18 100644 --- a/src/parser/parse/pipeline.rs +++ b/src/parser/parse/pipeline.rs @@ -1,6 +1,6 @@ use crate::parser::CallNode; use crate::traits::ToDebug; -use crate::{Span, Tagged}; +use crate::{Tag, Tagged}; use derive_new::new; use getset::Getters; use std::fmt; @@ -8,7 +8,7 @@ use std::fmt; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)] pub struct Pipeline { pub(crate) parts: Vec, - pub(crate) post_ws: Option, + pub(crate) post_ws: Option, } impl ToDebug for Pipeline { @@ -27,11 +27,11 @@ impl ToDebug for Pipeline { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] pub struct PipelineElement { - pub pipe: Option, - pub pre_ws: Option, + pub pipe: Option, + pub pre_ws: Option, #[get = "pub(crate)"] call: Tagged, - pub post_ws: Option, + pub post_ws: Option, } impl ToDebug for PipelineElement { diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index f69c176e97..e0072360e8 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -1,7 +1,7 @@ use crate::errors::ShellError; use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*}; use crate::traits::ToDebug; -use crate::{Span, Tagged, Text}; +use crate::{Tag, Tagged, Text}; use derive_new::new; use enum_utils::FromStr; use getset::Getters; @@ -16,8 +16,8 @@ pub enum TokenNode { Pipeline(Tagged), Operator(Tagged), Flag(Tagged), - Member(Span), - Whitespace(Span), + Member(Tag), + Whitespace(Tag), Error(Tagged>), Path(Tagged), @@ -78,31 +78,31 @@ impl fmt::Debug for DebugTokenNode<'_> { ) } TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)), - TokenNode::Error(s) => write!(f, " for {:?}", s.span().slice(self.source)), - rest => write!(f, "{}", rest.span().slice(self.source)), + TokenNode::Error(s) => write!(f, " for {:?}", s.tag().slice(self.source)), + rest => write!(f, "{}", rest.tag().slice(self.source)), } } } -impl From<&TokenNode> for Span { - fn from(token: &TokenNode) -> Span { - token.span() +impl From<&TokenNode> for Tag { + fn from(token: &TokenNode) -> Tag { + token.tag() } } impl TokenNode { - pub fn span(&self) -> Span { + pub fn tag(&self) -> Tag { match self { - TokenNode::Token(t) => t.span(), - TokenNode::Call(s) => s.span(), - TokenNode::Delimited(s) => s.span(), - TokenNode::Pipeline(s) => s.span(), - TokenNode::Operator(s) => s.span(), - TokenNode::Flag(s) => s.span(), + TokenNode::Token(t) => t.tag(), + TokenNode::Call(s) => s.tag(), + TokenNode::Delimited(s) => s.tag(), + TokenNode::Pipeline(s) => s.tag(), + TokenNode::Operator(s) => s.tag(), + TokenNode::Flag(s) => s.tag(), TokenNode::Member(s) => *s, TokenNode::Whitespace(s) => *s, - TokenNode::Error(s) => s.span(), - TokenNode::Path(s) => s.span(), + TokenNode::Error(s) => s.tag(), + TokenNode::Path(s) => s.tag(), } } @@ -127,11 +127,11 @@ impl TokenNode { } pub fn as_external_arg(&self, source: &Text) -> String { - self.span().slice(source).to_string() + self.tag().slice(source).to_string() } pub fn source<'a>(&self, source: &'a Text) -> &'a str { - self.span().slice(source) + self.tag().slice(source) } pub fn is_bare(&self) -> bool { @@ -154,12 +154,12 @@ impl TokenNode { } } - pub fn expect_external(&self) -> Span { + pub fn expect_external(&self) -> Tag { match self { TokenNode::Token(Tagged { - item: RawToken::ExternalCommand(span), + item: RawToken::ExternalCommand(tag), .. - }) => *span, + }) => *tag, _ => panic!("Only call expect_external if you checked is_external first"), } } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index ae1b344c44..1ed8383f4c 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -7,8 +7,8 @@ use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, PathNode, Token use crate::parser::parse::tokens::{RawNumber, RawToken}; use crate::parser::parse::unit::Unit; use crate::parser::CallNode; -use crate::Span; use derive_new::new; +use uuid::Uuid; #[derive(new)] pub struct TokenTreeBuilder { @@ -17,14 +17,16 @@ pub struct TokenTreeBuilder { #[new(default)] output: String, + + origin: Uuid, } pub type CurriedToken = Box TokenNode + 'static>; pub type CurriedCall = Box Tagged + 'static>; impl TokenTreeBuilder { - pub fn build(block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { - let mut builder = TokenTreeBuilder::new(); + pub fn build(origin: Uuid, block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { + let mut builder = TokenTreeBuilder::new(origin); let node = block(&mut builder); (node, builder.output) } @@ -52,50 +54,37 @@ impl TokenTreeBuilder { .expect("A pipeline must contain at least one element"); let pipe = None; - let pre_span = pre.map(|pre| b.consume(&pre)); + let pre_tag = pre.map(|pre| b.consume_tag(&pre)); let call = call(b); - let post_span = post.map(|post| b.consume(&post)); + let post_tag = post.map(|post| b.consume_tag(&post)); - out.push(PipelineElement::new( - pipe, - pre_span.map(Span::from), - call, - post_span.map(Span::from), - )); + out.push(PipelineElement::new(pipe, pre_tag, call, post_tag)); loop { match input.next() { None => break, Some((pre, call, post)) => { - let pipe = Some(Span::from(b.consume("|"))); - let pre_span = pre.map(|pre| b.consume(&pre)); + let pipe = Some(b.consume_tag("|")); + let pre_span = pre.map(|pre| b.consume_tag(&pre)); let call = call(b); - let post_span = post.map(|post| b.consume(&post)); + let post_span = post.map(|post| b.consume_tag(&post)); - out.push(PipelineElement::new( - pipe, - pre_span.map(Span::from), - call, - post_span.map(Span::from), - )); + out.push(PipelineElement::new(pipe, pre_span, call, post_span)); } } } let end = b.pos; - TokenTreeBuilder::spanned_pipeline((out, None), (start, end)) + TokenTreeBuilder::tagged_pipeline((out, None), (start, end, b.origin)) }) } - pub fn spanned_pipeline( - input: (Vec, Option), - span: impl Into, + pub fn tagged_pipeline( + input: (Vec, Option), + tag: impl Into, ) -> TokenNode { - TokenNode::Pipeline(Tagged::from_simple_spanned_item( - Pipeline::new(input.0, input.1.into()), - span, - )) + TokenNode::Pipeline(Pipeline::new(input.0, input.1.into()).tagged(tag.into())) } pub fn op(input: impl Into) -> CurriedToken { @@ -106,12 +95,12 @@ impl TokenTreeBuilder { b.pos = end; - TokenTreeBuilder::spanned_op(input, (start, end)) + TokenTreeBuilder::tagged_op(input, (start, end, b.origin)) }) } - pub fn spanned_op(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Operator(Tagged::from_simple_spanned_item(input.into(), span.into())) + pub fn tagged_op(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Operator(input.into().tagged(tag.into())) } pub fn string(input: impl Into) -> CurriedToken { @@ -123,15 +112,15 @@ impl TokenTreeBuilder { let (_, end) = b.consume("\""); b.pos = end; - TokenTreeBuilder::spanned_string((inner_start, inner_end), (start, end)) + TokenTreeBuilder::tagged_string( + (inner_start, inner_end, b.origin), + (start, end, b.origin), + ) }) } - pub fn spanned_string(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::String(input.into()), - span.into(), - )) + pub fn tagged_string(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::String(input.into()).tagged(tag.into())) } pub fn bare(input: impl Into) -> CurriedToken { @@ -141,15 +130,12 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::spanned_bare((start, end)) + TokenTreeBuilder::tagged_bare((start, end, b.origin)) }) } - pub fn spanned_bare(input: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Bare, - input.into(), - )) + pub fn tagged_bare(tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Bare.tagged(tag.into())) } pub fn pattern(input: impl Into) -> CurriedToken { @@ -159,15 +145,12 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::spanned_pattern((start, end)) + TokenTreeBuilder::tagged_pattern((start, end, b.origin)) }) } - pub fn spanned_pattern(input: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Bare, - input.into(), - )) + pub fn tagged_pattern(input: impl Into) -> TokenNode { + TokenNode::Token(RawToken::GlobPattern.tagged(input.into())) } pub fn external_word(input: impl Into) -> CurriedToken { @@ -177,22 +160,16 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::spanned_external_word((start, end)) + TokenTreeBuilder::tagged_external_word((start, end, b.origin)) }) } - pub fn spanned_external_word(input: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::ExternalWord, - input.into(), - )) + pub fn tagged_external_word(input: impl Into) -> TokenNode { + TokenNode::Token(RawToken::ExternalWord.tagged(input.into())) } - pub fn spanned_external(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::ExternalCommand(input.into()), - span.into(), - )) + pub fn tagged_external(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::ExternalCommand(input.into()).tagged(tag.into())) } pub fn int(input: impl Into) -> CurriedToken { @@ -202,7 +179,10 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&int.to_string()); b.pos = end; - TokenTreeBuilder::spanned_number(RawNumber::Int((start, end).into()), (start, end)) + TokenTreeBuilder::tagged_number( + RawNumber::Int((start, end, b.origin).into()), + (start, end, b.origin), + ) }) } @@ -213,15 +193,15 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&decimal.to_string()); b.pos = end; - TokenTreeBuilder::spanned_number(RawNumber::Decimal((start, end).into()), (start, end)) + TokenTreeBuilder::tagged_number( + RawNumber::Decimal((start, end, b.origin).into()), + (start, end, b.origin), + ) }) } - pub fn spanned_number(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Number(input.into()), - span.into(), - )) + pub fn tagged_number(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Number(input.into()).tagged(tag.into())) } pub fn size(int: impl Into, unit: impl Into) -> CurriedToken { @@ -233,23 +213,20 @@ impl TokenTreeBuilder { let (_, end_unit) = b.consume(unit.as_str()); b.pos = end_unit; - TokenTreeBuilder::spanned_size( - (RawNumber::Int((start_int, end_int).into()), unit), - (start_int, end_unit), + TokenTreeBuilder::tagged_size( + (RawNumber::Int((start_int, end_int, b.origin).into()), unit), + (start_int, end_unit, b.origin), ) }) } - pub fn spanned_size( + pub fn tagged_size( input: (impl Into, impl Into), - span: impl Into, + tag: impl Into, ) -> TokenNode { let (int, unit) = (input.0.into(), input.1.into()); - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Size(int, unit), - span, - )) + TokenNode::Token(RawToken::Size(int, unit).tagged(tag.into())) } pub fn path(head: CurriedToken, tail: Vec) -> CurriedToken { @@ -267,15 +244,12 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::spanned_path((head, output), (start, end)) + TokenTreeBuilder::tagged_path((head, output), (start, end, b.origin)) }) } - pub fn spanned_path(input: (TokenNode, Vec), span: impl Into) -> TokenNode { - TokenNode::Path(Tagged::from_simple_spanned_item( - PathNode::new(Box::new(input.0), input.1), - span, - )) + pub fn tagged_path(input: (TokenNode, Vec), tag: impl Into) -> TokenNode { + TokenNode::Path(PathNode::new(Box::new(input.0), input.1).tagged(tag.into())) } pub fn var(input: impl Into) -> CurriedToken { @@ -285,15 +259,12 @@ impl TokenTreeBuilder { let (start, _) = b.consume("$"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::spanned_var((inner_start, end), (start, end)) + TokenTreeBuilder::tagged_var((inner_start, end, b.origin), (start, end, b.origin)) }) } - pub fn spanned_var(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Variable(input.into()), - span.into(), - )) + pub fn tagged_var(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Variable(input.into()).tagged(tag.into())) } pub fn flag(input: impl Into) -> CurriedToken { @@ -303,15 +274,12 @@ impl TokenTreeBuilder { let (start, _) = b.consume("--"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::spanned_flag((inner_start, end), (start, end)) + TokenTreeBuilder::tagged_flag((inner_start, end, b.origin), (start, end, b.origin)) }) } - pub fn spanned_flag(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Flag(Tagged::from_simple_spanned_item( - Flag::new(FlagKind::Longhand, input.into()), - span.into(), - )) + pub fn tagged_flag(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Flag(Flag::new(FlagKind::Longhand, input.into()).tagged(tag.into())) } pub fn shorthand(input: impl Into) -> CurriedToken { @@ -321,15 +289,12 @@ impl TokenTreeBuilder { let (start, _) = b.consume("-"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::spanned_shorthand((inner_start, end), (start, end)) + TokenTreeBuilder::tagged_shorthand((inner_start, end, b.origin), (start, end, b.origin)) }) } - pub fn spanned_shorthand(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Flag(Tagged::from_simple_spanned_item( - Flag::new(FlagKind::Shorthand, input.into()), - span.into(), - )) + pub fn tagged_shorthand(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Flag(Flag::new(FlagKind::Shorthand, input.into()).tagged(tag.into())) } pub fn member(input: impl Into) -> CurriedToken { @@ -337,12 +302,12 @@ impl TokenTreeBuilder { Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::spanned_member((start, end)) + TokenTreeBuilder::tagged_member((start, end, b.origin)) }) } - pub fn spanned_member(span: impl Into) -> TokenNode { - TokenNode::Member(span.into()) + pub fn tagged_member(tag: impl Into) -> TokenNode { + TokenNode::Member(tag.into()) } pub fn call(head: CurriedToken, input: Vec) -> CurriedCall { @@ -358,11 +323,11 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::spanned_call(nodes, (start, end)) + TokenTreeBuilder::tagged_call(nodes, (start, end, b.origin)) }) } - pub fn spanned_call(input: Vec, span: impl Into) -> Tagged { + pub fn tagged_call(input: Vec, tag: impl Into) -> Tagged { if input.len() == 0 { panic!("BUG: spanned call (TODO)") } @@ -372,7 +337,7 @@ impl TokenTreeBuilder { let head = input.next().unwrap(); let tail = input.collect(); - Tagged::from_simple_spanned_item(CallNode::new(Box::new(head), tail), span) + CallNode::new(Box::new(head), tail).tagged(tag.into()) } pub fn parens(input: Vec) -> CurriedToken { @@ -385,15 +350,12 @@ impl TokenTreeBuilder { let (_, end) = b.consume(")"); - TokenTreeBuilder::spanned_parens(output, (start, end)) + TokenTreeBuilder::tagged_parens(output, (start, end, b.origin)) }) } - pub fn spanned_parens(input: impl Into>, span: impl Into) -> TokenNode { - TokenNode::Delimited(Tagged::from_simple_spanned_item( - DelimitedNode::new(Delimiter::Paren, input.into()), - span, - )) + pub fn tagged_parens(input: impl Into>, tag: impl Into) -> TokenNode { + TokenNode::Delimited(DelimitedNode::new(Delimiter::Paren, input.into()).tagged(tag.into())) } pub fn square(input: Vec) -> CurriedToken { @@ -406,15 +368,12 @@ impl TokenTreeBuilder { let (_, end) = b.consume("]"); - TokenTreeBuilder::spanned_square(output, (start, end)) + TokenTreeBuilder::tagged_square(output, (start, end, b.origin)) }) } - pub fn spanned_square(input: impl Into>, span: impl Into) -> TokenNode { - TokenNode::Delimited(Tagged::from_simple_spanned_item( - DelimitedNode::new(Delimiter::Square, input.into()), - span, - )) + pub fn tagged_square(input: impl Into>, tag: impl Into) -> TokenNode { + TokenNode::Delimited(DelimitedNode::new(Delimiter::Square, input.into()).tagged(tag.into())) } pub fn braced(input: Vec) -> CurriedToken { @@ -427,21 +386,18 @@ impl TokenTreeBuilder { let (_, end) = b.consume(" }"); - TokenTreeBuilder::spanned_brace(output, (start, end)) + TokenTreeBuilder::tagged_brace(output, (start, end, b.origin)) }) } - pub fn spanned_brace(input: impl Into>, span: impl Into) -> TokenNode { - TokenNode::Delimited(Tagged::from_simple_spanned_item( - DelimitedNode::new(Delimiter::Brace, input.into()), - span, - )) + pub fn tagged_brace(input: impl Into>, tag: impl Into) -> TokenNode { + TokenNode::Delimited(DelimitedNode::new(Delimiter::Brace, input.into()).tagged(tag.into())) } pub fn sp() -> CurriedToken { Box::new(|b| { let (start, end) = b.consume(" "); - TokenNode::Whitespace(Span::from((start, end))) + TokenNode::Whitespace(Tag::from((start, end, b.origin))) }) } @@ -450,14 +406,12 @@ impl TokenTreeBuilder { Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::spanned_ws((start, end)) + TokenTreeBuilder::tagged_ws((start, end, b.origin)) }) } - pub fn spanned_ws(span: impl Into) -> TokenNode { - let span = span.into(); - - TokenNode::Whitespace(span.into()) + pub fn tagged_ws(tag: impl Into) -> TokenNode { + TokenNode::Whitespace(tag.into()) } fn consume(&mut self, input: &str) -> (usize, usize) { @@ -466,4 +420,11 @@ impl TokenTreeBuilder { self.output.push_str(input); (start, self.pos) } + + fn consume_tag(&mut self, input: &str) -> Tag { + let start = self.pos; + self.pos += input.len(); + self.output.push_str(input); + (start, self.pos, self.origin).into() + } } diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index b599852499..d796a8fcb7 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -1,6 +1,6 @@ use crate::parser::parse::unit::*; use crate::prelude::*; -use crate::{Span, Tagged, Text}; +use crate::{Tagged, Text}; use std::fmt; use std::str::FromStr; @@ -8,9 +8,9 @@ use std::str::FromStr; pub enum RawToken { Number(RawNumber), Size(RawNumber, Unit), - String(Span), - Variable(Span), - ExternalCommand(Span), + String(Tag), + Variable(Tag), + ExternalCommand(Tag), ExternalWord, GlobPattern, Bare, @@ -18,28 +18,28 @@ pub enum RawToken { #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum RawNumber { - Int(Span), - Decimal(Span), + Int(Tag), + Decimal(Tag), } impl RawNumber { - pub fn int(span: impl Into) -> Tagged { - let span = span.into(); + pub fn int(tag: impl Into) -> Tagged { + let tag = tag.into(); - RawNumber::Int(span).tagged(span) + RawNumber::Int(tag).tagged(tag) } - pub fn decimal(span: impl Into) -> Tagged { - let span = span.into(); + pub fn decimal(tag: impl Into) -> Tagged { + let tag = tag.into(); - RawNumber::Decimal(span).tagged(span) + RawNumber::Decimal(tag).tagged(tag) } pub(crate) fn to_number(self, source: &Text) -> Number { match self { - RawNumber::Int(span) => Number::Int(BigInt::from_str(span.slice(source)).unwrap()), - RawNumber::Decimal(span) => { - Number::Decimal(BigDecimal::from_str(span.slice(source)).unwrap()) + RawNumber::Int(tag) => Number::Int(BigInt::from_str(tag.slice(source)).unwrap()), + RawNumber::Decimal(tag) => { + Number::Decimal(BigDecimal::from_str(tag.slice(source)).unwrap()) } } } @@ -78,6 +78,6 @@ pub struct DebugToken<'a> { impl fmt::Debug for DebugToken<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.node.span().slice(self.source)) + write!(f, "{}", self.node.tag().slice(self.source)) } } diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index e0fc9d86fc..36ba82f8e5 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -7,7 +7,7 @@ use crate::parser::{ Flag, RawToken, TokenNode, }; use crate::traits::ToDebug; -use crate::{Span, Tag, Tagged, Text}; +use crate::{Tag, Tagged, TaggedItem, Text}; use log::trace; pub fn parse_command( @@ -33,7 +33,7 @@ pub fn parse_command( .collect() }); - match parse_command_tail(&config, context, children, source, call.span())? { + match parse_command_tail(&config, context, children, source, call.tag())? { None => Ok(hir::Call::new(Box::new(head), None, None)), Some((positional, named)) => Ok(hir::Call::new(Box::new(head), positional, named)), } @@ -49,12 +49,9 @@ fn parse_command_head(head: &TokenNode) -> Result { ) => Ok(spanned.map(|_| hir::RawExpression::Literal(hir::Literal::Bare))), TokenNode::Token(Tagged { - item: RawToken::String(inner_span), - tag: Tag { span, origin: None }, - }) => Ok(Tagged::from_simple_spanned_item( - hir::RawExpression::Literal(hir::Literal::String(*inner_span)), - *span, - )), + item: RawToken::String(inner_tag), + tag, + }) => Ok(hir::RawExpression::Literal(hir::Literal::String(*inner_tag)).tagged(*tag)), other => Err(ShellError::unexpected(&format!( "command head -> {:?}", @@ -68,7 +65,7 @@ fn parse_command_tail( context: &Context, tail: Option>, source: &Text, - command_span: Span, + command_tag: Tag, ) -> Result>, Option)>, ShellError> { let tail = &mut match &tail { None => hir::TokensIterator::new(&[]), @@ -89,7 +86,7 @@ fn parse_command_tail( named.insert_switch(name, flag); } NamedType::Mandatory(syntax_type) => { - match extract_mandatory(config, name, tail, source, command_span) { + match extract_mandatory(config, name, tail, source, command_tag) { Err(err) => return Err(err), // produce a correct diagnostic Ok((pos, flag)) => { tail.move_to(pos); @@ -98,7 +95,7 @@ fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), - flag.span(), + flag.tag(), )); } @@ -119,7 +116,7 @@ fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), - flag.span(), + flag.tag(), )); } @@ -150,7 +147,7 @@ fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryPositional(arg.name().to_string()), - command_span, + command_tag, )); } } @@ -208,7 +205,7 @@ fn extract_mandatory( name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Text, - span: Span, + tag: Tag, ) -> Result<(usize, Tagged), ShellError> { let flag = tokens.extract(|t| t.as_flag(name, source)); @@ -216,7 +213,7 @@ fn extract_mandatory( None => Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryFlag(name.to_string()), - span, + tag, )), Some((pos, flag)) => { diff --git a/src/parser/registry.rs b/src/parser/registry.rs index 7112d91118..e199be192b 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -1,7 +1,7 @@ // TODO: Temporary redirect pub(crate) use crate::context::CommandRegistry; use crate::evaluate::{evaluate_baseline_expr, Scope}; -use crate::parser::{hir, hir::SyntaxType, parse_command, CallNode}; +use crate::parser::{hir, hir::SyntaxShape, parse_command, CallNode}; use crate::prelude::*; use derive_new::new; use indexmap::IndexMap; @@ -12,35 +12,35 @@ use std::fmt; #[derive(Debug, Serialize, Deserialize, Clone)] pub enum NamedType { Switch, - Mandatory(SyntaxType), - Optional(SyntaxType), + Mandatory(SyntaxShape), + Optional(SyntaxShape), } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum PositionalType { - Mandatory(String, SyntaxType), - Optional(String, SyntaxType), + Mandatory(String, SyntaxShape), + Optional(String, SyntaxShape), } impl PositionalType { - pub fn mandatory(name: &str, ty: SyntaxType) -> PositionalType { + pub fn mandatory(name: &str, ty: SyntaxShape) -> PositionalType { PositionalType::Mandatory(name.to_string(), ty) } pub fn mandatory_any(name: &str) -> PositionalType { - PositionalType::Mandatory(name.to_string(), SyntaxType::Any) + PositionalType::Mandatory(name.to_string(), SyntaxShape::Any) } pub fn mandatory_block(name: &str) -> PositionalType { - PositionalType::Mandatory(name.to_string(), SyntaxType::Block) + PositionalType::Mandatory(name.to_string(), SyntaxShape::Block) } - pub fn optional(name: &str, ty: SyntaxType) -> PositionalType { + pub fn optional(name: &str, ty: SyntaxShape) -> PositionalType { PositionalType::Optional(name.to_string(), ty) } pub fn optional_any(name: &str) -> PositionalType { - PositionalType::Optional(name.to_string(), SyntaxType::Any) + PositionalType::Optional(name.to_string(), SyntaxShape::Any) } pub(crate) fn name(&self) -> &str { @@ -50,7 +50,7 @@ impl PositionalType { } } - pub(crate) fn syntax_type(&self) -> SyntaxType { + pub(crate) fn syntax_type(&self) -> SyntaxShape { match *self { PositionalType::Mandatory(_, t) => t, PositionalType::Optional(_, t) => t, @@ -66,7 +66,7 @@ pub struct Signature { #[new(default)] pub positional: Vec, #[new(value = "None")] - pub rest_positional: Option, + pub rest_positional: Option, #[new(default)] pub named: IndexMap, #[new(value = "false")] @@ -83,21 +83,21 @@ impl Signature { self } - pub fn required(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn required(mut self, name: impl Into, ty: impl Into) -> Signature { self.positional .push(PositionalType::Mandatory(name.into(), ty.into())); self } - pub fn optional(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn optional(mut self, name: impl Into, ty: impl Into) -> Signature { self.positional .push(PositionalType::Optional(name.into(), ty.into())); self } - pub fn named(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn named(mut self, name: impl Into, ty: impl Into) -> Signature { self.named .insert(name.into(), NamedType::Optional(ty.into())); @@ -107,7 +107,7 @@ impl Signature { pub fn required_named( mut self, name: impl Into, - ty: impl Into, + ty: impl Into, ) -> Signature { self.named .insert(name.into(), NamedType::Mandatory(ty.into())); @@ -126,7 +126,7 @@ impl Signature { self } - pub fn rest(mut self, ty: SyntaxType) -> Signature { + pub fn rest(mut self, ty: SyntaxShape) -> Signature { self.rest_positional = Some(ty); self } @@ -312,10 +312,10 @@ pub(crate) fn evaluate_args( for (name, value) in n.named.iter() { match value { - hir::named::NamedValue::PresentSwitch(span) => { + hir::named::NamedValue::PresentSwitch(tag) => { results.insert( name.clone(), - Tagged::from_simple_spanned_item(Value::boolean(true), *span), + Value::boolean(true).tagged(*tag), ); } hir::named::NamedValue::Value(expr) => { diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 744003cd74..03e1d42828 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tagged, Value, + SyntaxShape, Tagged, Value, }; struct Add { @@ -44,9 +44,9 @@ impl Plugin for Add { fn config(&mut self) -> Result { Ok(Signature::build("add") .desc("Add a new field to the table.") - .required("Field", SyntaxType::String) - .required("Value", SyntaxType::String) - .rest(SyntaxType::String) + .required("Field", SyntaxShape::String) + .required("Value", SyntaxShape::String) + .rest(SyntaxShape::String) .filter()) } diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index aeda4ba09b..db116fedf5 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tagged, Value, + SyntaxShape, Tagged, Value, }; struct Edit { @@ -43,8 +43,8 @@ impl Plugin for Edit { fn config(&mut self) -> Result { Ok(Signature::build("edit") .desc("Edit an existing column to have a new value.") - .required("Field", SyntaxType::String) - .required("Value", SyntaxType::String) + .required("Field", SyntaxShape::String) + .required("Value", SyntaxShape::String) .filter()) } diff --git a/src/plugins/embed.rs b/src/plugins/embed.rs index 95140aa609..646db80918 100644 --- a/src/plugins/embed.rs +++ b/src/plugins/embed.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tag, Tagged, TaggedDictBuilder, Value, + SyntaxShape, Tag, Tagged, TaggedDictBuilder, Value, }; struct Embed { @@ -37,8 +37,8 @@ impl Plugin for Embed { fn config(&mut self) -> Result { Ok(Signature::build("embed") .desc("Embeds a new field to the table.") - .required("Field", SyntaxType::String) - .rest(SyntaxType::String) + .required("Field", SyntaxShape::String) + .rest(SyntaxShape::String) .filter()) } diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index d75da41428..4422195be8 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tagged, TaggedItem, Value, + SyntaxShape, Tagged, TaggedItem, Value, }; enum Action { @@ -120,7 +120,7 @@ impl Plugin for Inc { .switch("major") .switch("minor") .switch("patch") - .rest(SyntaxType::String) + .rest(SyntaxShape::String) .filter()) } @@ -181,18 +181,20 @@ mod tests { use super::{Inc, SemVerAction}; use indexmap::IndexMap; use nu::{ - CallInfo, EvaluatedArgs, Plugin, ReturnSuccess, SourceMap, Span, Tag, Tagged, - TaggedDictBuilder, TaggedItem, Value, + CallInfo, EvaluatedArgs, Plugin, ReturnSuccess, SourceMap, Tag, Tagged, TaggedDictBuilder, + TaggedItem, Value, }; struct CallStub { + origin: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } impl CallStub { - fn new() -> CallStub { + fn new(origin: uuid::Uuid) -> CallStub { CallStub { + origin, positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -201,14 +203,14 @@ mod tests { fn with_long_flag(&mut self, name: &str) -> &mut Self { self.flags.insert( name.to_string(), - Value::boolean(true).simple_spanned(Span::unknown()), + Value::boolean(true).tagged(Tag::unknown()), ); self } fn with_parameter(&mut self, name: &str) -> &mut Self { self.positionals - .push(Value::string(name.to_string()).simple_spanned(Span::unknown())); + .push(Value::string(name.to_string()).tagged(Tag::unknown_span(self.origin))); self } @@ -216,7 +218,7 @@ mod tests { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), source_map: SourceMap::new(), - name_span: Span::unknown(), + name_tag: Tag::unknown_span(self.origin), } } } @@ -243,7 +245,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new().with_long_flag("major").create()) + .begin_filter(CallStub::new(test_uuid()).with_long_flag("major").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -253,7 +255,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new().with_long_flag("minor").create()) + .begin_filter(CallStub::new(test_uuid()).with_long_flag("minor").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -263,7 +265,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new().with_long_flag("patch").create()) + .begin_filter(CallStub::new(test_uuid()).with_long_flag("patch").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -274,7 +276,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new() + CallStub::new(test_uuid()) .with_long_flag("major") .with_long_flag("minor") .create(), @@ -288,7 +290,11 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new().with_parameter("package.version").create()) + .begin_filter( + CallStub::new(test_uuid()) + .with_parameter("package.version") + .create() + ) .is_ok()); assert_eq!(plugin.field, Some("package.version".to_string())); @@ -321,7 +327,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new() + CallStub::new(test_uuid()) .with_long_flag("major") .with_parameter("version") .create() @@ -349,7 +355,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new() + CallStub::new(test_uuid()) .with_long_flag("minor") .with_parameter("version") .create() @@ -378,7 +384,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new() + CallStub::new(test_uuid()) .with_long_flag("patch") .with_parameter(&field) .create() @@ -399,4 +405,8 @@ mod tests { _ => {} } } + + fn test_uuid() -> uuid::Uuid { + uuid::Uuid::nil() + } } diff --git a/src/plugins/ps.rs b/src/plugins/ps.rs index 0f06167bdf..342bd5eb32 100644 --- a/src/plugins/ps.rs +++ b/src/plugins/ps.rs @@ -40,7 +40,7 @@ async fn ps(tag: Tag) -> Vec> { let mut output = vec![]; while let Some(res) = processes.next().await { if let Ok((process, usage)) = res { - let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(tag.span)); + let mut dict = TaggedDictBuilder::new(tag); dict.insert("pid", Value::int(process.pid())); if let Ok(name) = process.name().await { dict.insert("name", Value::string(name)); @@ -64,7 +64,7 @@ impl Plugin for Ps { } fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(ps(Tag::unknown_origin(callinfo.name_span))) + Ok(block_on(ps(Tag::unknown_origin(callinfo.name_tag))) .into_iter() .map(ReturnSuccess::value) .collect()) diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs index cb259e99b9..efd3231525 100644 --- a/src/plugins/skip.rs +++ b/src/plugins/skip.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, CoerceInto, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, - Signature, SyntaxType, Tagged, TaggedItem, Value, + Signature, SyntaxShape, Tagged, TaggedItem, Value, }; struct Skip { @@ -15,9 +15,9 @@ impl Skip { impl Plugin for Skip { fn config(&mut self) -> Result { - Ok(Signature::build("skip") + Ok(Signature::build("skip") .desc("Skip a number of rows") - .rest(SyntaxType::Number) + .rest(SyntaxShape::Number) .filter()) } fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { @@ -34,7 +34,7 @@ impl Plugin for Skip { return Err(ShellError::labeled_error( "Unrecognized type in params", "expected an integer", - arg.span(), + arg.tag(), )) } } diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 99700449b4..c9e82ab15c 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tagged, Value, + SyntaxShape, Tagged, Value, }; use regex::Regex; @@ -174,7 +174,7 @@ impl Plugin for Str { .switch("to-int") .switch("replace") .switch("find-replace") - .rest(SyntaxType::Member) + .rest(SyntaxShape::Member) .filter()) } @@ -261,7 +261,7 @@ mod tests { use super::{Action, ReplaceAction, Str}; use indexmap::IndexMap; use nu::{ - CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Span, Tag, Tagged, + CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Tag, Tagged, TaggedDictBuilder, TaggedItem, Value, }; use num_bigint::BigInt; @@ -277,6 +277,7 @@ mod tests { } struct CallStub { + origin: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } @@ -284,6 +285,7 @@ mod tests { impl CallStub { fn new() -> CallStub { CallStub { + origin: uuid::Uuid::nil(), positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -292,14 +294,14 @@ mod tests { fn with_long_flag(&mut self, name: &str) -> &mut Self { self.flags.insert( name.to_string(), - Value::boolean(true).simple_spanned(Span::unknown()), + Value::boolean(true).tagged(Tag::unknown()), ); self } fn with_parameter(&mut self, name: &str) -> &mut Self { self.positionals - .push(Value::string(name.to_string()).simple_spanned(Span::unknown())); + .push(Value::string(name.to_string()).tagged(Tag::unknown())); self } @@ -307,7 +309,7 @@ mod tests { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), source_map: SourceMap::new(), - name_span: Span::unknown(), + name_tag: Tag::unknown_span(self.origin), } } } diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs index 32ecd7a9ce..ffb39cb90b 100644 --- a/src/plugins/sum.rs +++ b/src/plugins/sum.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - Tag, Tagged, Value, + Tagged, TaggedItem, Value, }; struct Sum { @@ -18,11 +18,10 @@ impl Sum { match &self.total { Some(Tagged { item: Value::Primitive(Primitive::Int(j)), - tag: Tag { span, .. }, + tag, }) => { //TODO: handle overflow - self.total = - Some(Tagged::from_simple_spanned_item(Value::int(i + j), span)); + self.total = Some(Value::int(i + j).tagged(*tag)); Ok(()) } None => { @@ -38,11 +37,10 @@ impl Sum { match self.total { Some(Tagged { item: Value::Primitive(Primitive::Bytes(j)), - tag: Tag { span, .. }, + tag, }) => { //TODO: handle overflow - self.total = - Some(Tagged::from_simple_spanned_item(Value::bytes(b + j), span)); + self.total = Some(Value::bytes(b + j).tagged(tag)); Ok(()) } None => { diff --git a/src/plugins/sys.rs b/src/plugins/sys.rs index db7de6e625..8030a86b88 100644 --- a/src/plugins/sys.rs +++ b/src/plugins/sys.rs @@ -315,7 +315,7 @@ impl Plugin for Sys { } fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(sysinfo(Tag::unknown_origin(callinfo.name_span))) + Ok(block_on(sysinfo(Tag::unknown_origin(callinfo.name_tag))) .into_iter() .map(ReturnSuccess::value) .collect()) diff --git a/src/prelude.rs b/src/prelude.rs index a491aff5de..d58e7989a6 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -62,7 +62,7 @@ pub(crate) use crate::data::{Primitive, Value}; pub(crate) use crate::env::host::handle_unexpected; pub(crate) use crate::env::Host; pub(crate) use crate::errors::{CoerceInto, ShellError}; -pub(crate) use crate::parser::hir::SyntaxType; +pub(crate) use crate::parser::hir::SyntaxShape; pub(crate) use crate::parser::parse::parser::Number; pub(crate) use crate::parser::registry::Signature; pub(crate) use crate::shell::filesystem_shell::FilesystemShell; @@ -70,8 +70,7 @@ pub(crate) use crate::shell::help_shell::HelpShell; pub(crate) use crate::shell::shell_manager::ShellManager; pub(crate) use crate::shell::value_shell::ValueShell; pub(crate) use crate::stream::{InputStream, OutputStream}; -pub(crate) use crate::traits::{HasSpan, ToDebug}; -pub(crate) use crate::Span; +pub(crate) use crate::traits::{HasTag, ToDebug}; pub(crate) use crate::Text; pub(crate) use bigdecimal::BigDecimal; pub(crate) use futures::stream::BoxStream; diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 16cbf513f8..1d26fa1630 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -81,23 +81,27 @@ impl Shell for FilesystemShell { dirs::home_dir() } - fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + fn ls( + &self, + pattern: Option>, + command_tag: Tag, + ) -> Result { let cwd = self.path(); let mut full_path = PathBuf::from(self.path()); - match &args.nth(0) { - Some(value) => full_path.push(Path::new(&value.as_path()?)), + match &pattern { + Some(value) => full_path.push((*value).as_ref()), _ => {} } let entries: Vec<_> = match glob::glob(&full_path.to_string_lossy()) { Ok(files) => files.collect(), Err(_) => { - if let Some(source) = args.nth(0) { + if let Some(source) = pattern { return Err(ShellError::labeled_error( "Invalid pattern", "Invalid pattern", - source.span(), + source.tag(), )); } else { return Err(ShellError::string("Invalid pattern.")); @@ -114,17 +118,17 @@ impl Shell for FilesystemShell { let entries = std::fs::read_dir(&entry); let entries = match entries { Err(e) => { - if let Some(s) = args.nth(0) { + if let Some(s) = pattern { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - s.span(), + s.tag(), )); } else { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - args.name_span(), + command_tag, )); } } @@ -138,11 +142,7 @@ impl Shell for FilesystemShell { } else { Path::new(&filepath) }; - let value = dir_entry_dict( - filename, - &entry.metadata()?, - Tag::unknown_origin(args.call_info.name_span), - )?; + let value = dir_entry_dict(filename, &entry.metadata()?, command_tag)?; shell_entries.push_back(ReturnSuccess::value(value)) } return Ok(shell_entries.to_output_stream()); @@ -159,11 +159,7 @@ impl Shell for FilesystemShell { Path::new(&entry) }; let metadata = std::fs::metadata(&entry)?; - let value = dir_entry_dict( - filename, - &metadata, - Tag::unknown_origin(args.call_info.name_span), - )?; + let value = dir_entry_dict(filename, &metadata, command_tag)?; shell_entries.push_back(ReturnSuccess::value(value)) } } @@ -179,7 +175,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Can not change to home directory", "can not go to home", - args.call_info.name_span, + args.call_info.name_tag, )) } }, @@ -197,7 +193,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Can not change to directory", "directory not found", - v.span().clone(), + v.tag().clone(), )) } } @@ -207,8 +203,7 @@ impl Shell for FilesystemShell { let mut stream = VecDeque::new(); - stream.push_back( - ReturnSuccess::change_cwd( + stream.push_back(ReturnSuccess::change_cwd( path.to_string_lossy().to_string(), )); @@ -222,10 +217,10 @@ impl Shell for FilesystemShell { dst, recursive, }: CopyArgs, - name: Span, + name: Tag, path: &str, ) -> Result { - let name_span = name; + let name_tag = name; let mut source = PathBuf::from(path); let mut destination = PathBuf::from(path); @@ -280,7 +275,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -296,7 +291,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -332,7 +327,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -346,7 +341,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -360,7 +355,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Copy aborted. Not a valid path", "Copy aborted. Not a valid path", - name_span, + name_tag, )) } } @@ -370,7 +365,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -406,7 +401,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -420,7 +415,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -453,7 +448,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Copy aborted. Not a valid path", "Copy aborted. Not a valid path", - name_span, + name_tag, )) } } @@ -480,7 +475,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Copy aborted. Not a valid destination", "Copy aborted. Not a valid destination", - name_span, + name_tag, )) } } @@ -489,7 +484,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Copy aborted. (Does {:?} exist?)", destination_file_name), format!("Copy aborted. (Does {:?} exist?)", destination_file_name), - &dst.span(), + dst.tag(), )); } } @@ -500,7 +495,7 @@ impl Shell for FilesystemShell { fn mkdir( &self, MkdirArgs { rest: directories }: MkdirArgs, - name: Span, + name: Tag, path: &str, ) -> Result { let full_path = PathBuf::from(path); @@ -525,7 +520,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( reason.to_string(), reason.to_string(), - dir.span(), + dir.tag(), )) } Ok(_) => {} @@ -538,10 +533,10 @@ impl Shell for FilesystemShell { fn mv( &self, MoveArgs { src, dst }: MoveArgs, - name: Span, + name: Tag, path: &str, ) -> Result { - let name_span = name; + let name_tag = name; let mut source = PathBuf::from(path); let mut destination = PathBuf::from(path); @@ -567,7 +562,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Rename aborted. Not a valid destination", "Rename aborted. Not a valid destination", - dst.span(), + dst.tag(), )) } } @@ -581,7 +576,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Rename aborted. Not a valid entry name", "Rename aborted. Not a valid entry name", - name_span, + name_tag, )) } }; @@ -593,7 +588,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Rename aborted. {:}", e.to_string()), format!("Rename aborted. {:}", e.to_string()), - name_span, + name_tag, )) } }; @@ -617,7 +612,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -640,7 +635,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -662,7 +657,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -715,7 +710,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -739,7 +734,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -762,7 +757,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -794,7 +789,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Rename aborted. Not a valid entry name", "Rename aborted. Not a valid entry name", - name_span, + name_tag, )) } }; @@ -818,7 +813,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -830,7 +825,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Rename aborted. (Does {:?} exist?)", destination_file_name), format!("Rename aborted. (Does {:?} exist?)", destination_file_name), - dst.span(), + dst.tag(), )); } } @@ -841,16 +836,16 @@ impl Shell for FilesystemShell { fn rm( &self, RemoveArgs { target, recursive }: RemoveArgs, - name: Span, + name: Tag, path: &str, ) -> Result { - let name_span = name; + let name_tag = name; if target.item.to_str() == Some(".") || target.item.to_str() == Some("..") { return Err(ShellError::labeled_error( "Remove aborted. \".\" or \"..\" may not be removed.", "Remove aborted. \".\" or \"..\" may not be removed.", - target.span(), + target.tag(), )); } @@ -882,7 +877,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("{:?} is a directory. Try using \"--recursive\".", file), format!("{:?} is a directory. Try using \"--recursive\".", file), - target.span(), + target.tag(), )); } } @@ -899,7 +894,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Remove aborted. Not a valid path", "Remove aborted. Not a valid path", - name_span, + name_tag, )) } } @@ -919,7 +914,7 @@ impl Shell for FilesystemShell { "Directory {:?} found somewhere inside. Try using \"--recursive\".", path_file_name ), - target.span(), + target.tag(), )); } @@ -933,7 +928,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Remove aborted. {:}", e.to_string()), format!("Remove aborted. {:}", e.to_string()), - name_span, + name_tag, )) } } @@ -954,7 +949,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "unable to show current directory", "pwd command failed", - args.call_info.name_span, + args.call_info.name_tag, )); } }; @@ -962,7 +957,7 @@ impl Shell for FilesystemShell { let mut stream = VecDeque::new(); stream.push_back(ReturnSuccess::value( Value::Primitive(Primitive::String(p.to_string_lossy().to_string())) - .simple_spanned(args.call_info.name_span), + .tagged(args.call_info.name_tag), )); Ok(stream.into()) diff --git a/src/shell/help_shell.rs b/src/shell/help_shell.rs index 35c939d7d4..25f1b9c428 100644 --- a/src/shell/help_shell.rs +++ b/src/shell/help_shell.rs @@ -126,7 +126,11 @@ impl Shell for HelpShell { self.path = path.clone(); } - fn ls(&self, _args: EvaluatedWholeStreamCommandArgs) -> Result { + fn ls( + &self, + _pattern: Option>, + _command_tag: Tag, + ) -> Result { Ok(self .commands() .map(|x| ReturnSuccess::value(x)) @@ -161,24 +165,19 @@ impl Shell for HelpShell { Ok(stream.into()) } - fn cp(&self, _args: CopyArgs, _name: Span, _path: &str) -> Result { + fn cp(&self, _args: CopyArgs, _name: Tag, _path: &str) -> Result { Ok(OutputStream::empty()) } - fn mv(&self, _args: MoveArgs, _name: Span, _path: &str) -> Result { + fn mv(&self, _args: MoveArgs, _name: Tag, _path: &str) -> Result { Ok(OutputStream::empty()) } - fn mkdir( - &self, - _args: MkdirArgs, - _name: Span, - _path: &str, - ) -> Result { + fn mkdir(&self, _args: MkdirArgs, _name: Tag, _path: &str) -> Result { Ok(OutputStream::empty()) } - fn rm(&self, _args: RemoveArgs, _name: Span, _path: &str) -> Result { + fn rm(&self, _args: RemoveArgs, _name: Tag, _path: &str) -> Result { Ok(OutputStream::empty()) } diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 16802657db..6fb4544352 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -66,7 +66,7 @@ impl Highlighter for Helper { } fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { - let tokens = crate::parser::pipeline(nom_input(line)); + let tokens = crate::parser::pipeline(nom_input(line, uuid::Uuid::nil())); match tokens { Err(_) => Cow::Borrowed(line), @@ -106,47 +106,47 @@ impl Highlighter for Helper { fn paint_token_node(token_node: &TokenNode, line: &str) -> String { let styled = match token_node { - TokenNode::Call(..) => Color::Cyan.bold().paint(token_node.span().slice(line)), - TokenNode::Whitespace(..) => Color::White.normal().paint(token_node.span().slice(line)), - TokenNode::Flag(..) => Color::Black.bold().paint(token_node.span().slice(line)), - TokenNode::Member(..) => Color::Yellow.bold().paint(token_node.span().slice(line)), - TokenNode::Path(..) => Color::Green.bold().paint(token_node.span().slice(line)), - TokenNode::Error(..) => Color::Red.bold().paint(token_node.span().slice(line)), - TokenNode::Delimited(..) => Color::White.paint(token_node.span().slice(line)), - TokenNode::Operator(..) => Color::White.normal().paint(token_node.span().slice(line)), - TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.span().slice(line)), + TokenNode::Call(..) => Color::Cyan.bold().paint(token_node.tag().slice(line)), + TokenNode::Whitespace(..) => Color::White.normal().paint(token_node.tag().slice(line)), + TokenNode::Flag(..) => Color::Black.bold().paint(token_node.tag().slice(line)), + TokenNode::Member(..) => Color::Yellow.bold().paint(token_node.tag().slice(line)), + TokenNode::Path(..) => Color::Green.bold().paint(token_node.tag().slice(line)), + TokenNode::Error(..) => Color::Red.bold().paint(token_node.tag().slice(line)), + TokenNode::Delimited(..) => Color::White.paint(token_node.tag().slice(line)), + TokenNode::Operator(..) => Color::White.normal().paint(token_node.tag().slice(line)), + TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::Number(..), .. - }) => Color::Purple.bold().paint(token_node.span().slice(line)), + }) => Color::Purple.bold().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::Size(..), .. - }) => Color::Purple.bold().paint(token_node.span().slice(line)), + }) => Color::Purple.bold().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::GlobPattern, .. - }) => Color::Cyan.normal().paint(token_node.span().slice(line)), + }) => Color::Cyan.normal().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::String(..), .. - }) => Color::Green.normal().paint(token_node.span().slice(line)), + }) => Color::Green.normal().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::Variable(..), .. - }) => Color::Yellow.bold().paint(token_node.span().slice(line)), + }) => Color::Yellow.bold().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::Bare, .. - }) => Color::Green.normal().paint(token_node.span().slice(line)), + }) => Color::Green.normal().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::ExternalCommand(..), .. - }) => Color::Cyan.bold().paint(token_node.span().slice(line)), + }) => Color::Cyan.bold().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::ExternalWord, .. - }) => Color::Black.bold().paint(token_node.span().slice(line)), + }) => Color::Black.bold().paint(token_node.tag().slice(line)), }; styled.to_string() @@ -166,7 +166,7 @@ fn paint_pipeline_element(pipeline_element: &PipelineElement, line: &str) -> Str styled.push_str( &Color::Cyan .bold() - .paint(pipeline_element.call().head().span().slice(line)) + .paint(pipeline_element.call().head().tag().slice(line)) .to_string(), ); diff --git a/src/shell/shell.rs b/src/shell/shell.rs index 549aa79d22..c567e474a3 100644 --- a/src/shell/shell.rs +++ b/src/shell/shell.rs @@ -13,12 +13,16 @@ pub trait Shell: std::fmt::Debug { fn name(&self, source_map: &SourceMap) -> String; fn homedir(&self) -> Option; - fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; + fn ls( + &self, + pattern: Option>, + command_tag: Tag, + ) -> Result; fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; - fn cp(&self, args: CopyArgs, name: Span, path: &str) -> Result; - fn mkdir(&self, args: MkdirArgs, name: Span, path: &str) -> Result; - fn mv(&self, args: MoveArgs, name: Span, path: &str) -> Result; - fn rm(&self, args: RemoveArgs, name: Span, path: &str) -> Result; + fn cp(&self, args: CopyArgs, name: Tag, path: &str) -> Result; + fn mkdir(&self, args: MkdirArgs, name: Tag, path: &str) -> Result; + fn mv(&self, args: MoveArgs, name: Tag, path: &str) -> Result; + fn rm(&self, args: RemoveArgs, name: Tag, path: &str) -> Result; fn path(&self) -> String; fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn set_path(&mut self, path: String); diff --git a/src/shell/shell_manager.rs b/src/shell/shell_manager.rs index 53984c9509..c4c42367ed 100644 --- a/src/shell/shell_manager.rs +++ b/src/shell/shell_manager.rs @@ -115,10 +115,14 @@ impl ShellManager { env[self.current_shell].homedir() } - pub fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + pub fn ls( + &self, + path: Option>, + command_tag: Tag, + ) -> Result { let env = self.shells.lock().unwrap(); - env[self.current_shell].ls(args) + env[self.current_shell].ls(path, command_tag) } pub fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 010c7ae08e..175e232e7b 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -87,13 +87,15 @@ impl Shell for ValueShell { Some(PathBuf::from("/")) } - fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + fn ls( + &self, + target: Option>, + command_name: Tag, + ) -> Result { let mut full_path = PathBuf::from(self.path()); - let target = args.nth(0); - - match target { - Some(value) => full_path.push(Path::new(&value.as_path()?)), + match &target { + Some(value) => full_path.push(value.as_ref()), _ => {} } @@ -101,18 +103,18 @@ impl Shell for ValueShell { value_system.walk_decorate(&self.value)?; if !value_system.exists(&full_path) { - if let Some(target) = target { + if let Some(target) = &target { return Err(ShellError::labeled_error( "Can not list entries inside", "No such path exists", - target.span(), + target.tag(), )); } return Err(ShellError::labeled_error( "Can not list entries inside", "No such path exists", - args.call_info.name_span, + command_name, )); } @@ -157,14 +159,14 @@ impl Shell for ValueShell { return Err(ShellError::labeled_error( "Can not change to path inside", "No such path exists", - destination.span(), + destination.tag(), )); } return Err(ShellError::labeled_error( "Can not change to path inside", "No such path exists", - args.call_info.name_span, + args.call_info.name_tag, )); } @@ -173,7 +175,7 @@ impl Shell for ValueShell { Ok(stream.into()) } - fn cp(&self, _args: CopyArgs, name: Span, _path: &str) -> Result { + fn cp(&self, _args: CopyArgs, name: Tag, _path: &str) -> Result { Err(ShellError::labeled_error( "cp not currently supported on values", "not currently supported", @@ -181,7 +183,7 @@ impl Shell for ValueShell { )) } - fn mv(&self, _args: MoveArgs, name: Span, _path: &str) -> Result { + fn mv(&self, _args: MoveArgs, name: Tag, _path: &str) -> Result { Err(ShellError::labeled_error( "mv not currently supported on values", "not currently supported", @@ -189,7 +191,7 @@ impl Shell for ValueShell { )) } - fn mkdir(&self, _args: MkdirArgs, name: Span, _path: &str) -> Result { + fn mkdir(&self, _args: MkdirArgs, name: Tag, _path: &str) -> Result { Err(ShellError::labeled_error( "mkdir not currently supported on values", "not currently supported", @@ -197,7 +199,7 @@ impl Shell for ValueShell { )) } - fn rm(&self, _args: RemoveArgs, name: Span, _path: &str) -> Result { + fn rm(&self, _args: RemoveArgs, name: Tag, _path: &str) -> Result { Err(ShellError::labeled_error( "rm not currently supported on values", "not currently supported", @@ -213,7 +215,7 @@ impl Shell for ValueShell { let mut stream = VecDeque::new(); stream.push_back(ReturnSuccess::value(Tagged::from_item( Value::string(self.path()), - args.call_info.name_span, + args.call_info.name_tag, ))); Ok(stream.into()) } diff --git a/src/traits.rs b/src/traits.rs index 5b022c444f..677d019ad8 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -12,8 +12,8 @@ impl fmt::Display for Debuggable<'_, T> { } } -pub trait HasSpan { - fn span(&self) -> Span; +pub trait HasTag { + fn tag(&self) -> Tag; } pub trait ToDebug: Sized { From bee7c5639cc78d65e6656518627a86009a522500 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 11 Sep 2019 19:53:05 +1200 Subject: [PATCH 068/342] Revert "Migrate most uses of the Span concept to Tag" --- Cargo.lock | 8 +- Cargo.toml | 2 +- src/cli.rs | 25 +-- src/commands/cd.rs | 3 +- src/commands/classified.rs | 31 ++-- src/commands/clip.rs | 2 +- src/commands/command.rs | 30 ++-- src/commands/config.rs | 44 +++-- src/commands/cp.rs | 8 +- src/commands/date.rs | 44 +++-- src/commands/echo.rs | 8 +- src/commands/enter.rs | 6 +- src/commands/fetch.rs | 76 +++++--- src/commands/first.rs | 2 +- src/commands/from_bson.rs | 14 +- src/commands/from_csv.rs | 12 +- src/commands/from_ini.rs | 12 +- src/commands/from_json.rs | 18 +- src/commands/from_sqlite.rs | 12 +- src/commands/from_toml.rs | 12 +- src/commands/from_tsv.rs | 12 +- src/commands/from_xml.rs | 12 +- src/commands/from_yaml.rs | 12 +- src/commands/get.rs | 6 +- src/commands/help.rs | 8 +- src/commands/last.rs | 2 +- src/commands/lines.rs | 8 +- src/commands/ls.rs | 17 +- src/commands/mkdir.rs | 2 +- src/commands/mv.rs | 8 +- src/commands/nth.rs | 2 +- src/commands/open.rs | 55 ++++-- src/commands/pick.rs | 2 +- src/commands/post.rs | 82 +++++---- src/commands/reject.rs | 2 +- src/commands/rm.rs | 4 +- src/commands/save.rs | 32 ++-- src/commands/shells.rs | 6 +- src/commands/size.rs | 10 +- src/commands/skip_while.rs | 2 +- src/commands/sort_by.rs | 2 +- src/commands/split_column.rs | 14 +- src/commands/split_row.rs | 7 +- src/commands/tags.rs | 4 +- src/commands/to_bson.rs | 32 ++-- src/commands/to_csv.rs | 10 +- src/commands/to_json.rs | 12 +- src/commands/to_sqlite.rs | 6 +- src/commands/to_toml.rs | 12 +- src/commands/to_tsv.rs | 10 +- src/commands/to_yaml.rs | 12 +- src/commands/trim.rs | 4 +- src/commands/version.rs | 8 +- src/commands/where_.rs | 11 +- src/commands/which_.rs | 13 +- src/context.rs | 12 +- src/data/base.rs | 18 +- src/data/config.rs | 12 +- src/data/into.rs | 4 +- src/data/meta.rs | 164 +++++------------- src/errors.rs | 96 +++++----- src/evaluate/evaluator.rs | 53 +++--- src/format/table.rs | 2 +- src/lib.rs | 4 +- src/parser.rs | 4 +- src/parser/deserializer.rs | 6 +- src/parser/hir.rs | 65 ++++--- src/parser/hir/baseline_parse.rs | 106 ++++++------ src/parser/hir/baseline_parse_tokens.rs | 161 +++++++++-------- src/parser/hir/external_command.rs | 2 +- src/parser/hir/named.rs | 5 +- src/parser/parse/files.rs | 35 ++-- src/parser/parse/flag.rs | 4 +- src/parser/parse/parser.rs | 157 +++++++---------- src/parser/parse/pipeline.rs | 10 +- src/parser/parse/token_tree.rs | 44 ++--- src/parser/parse/token_tree_builder.rs | 221 ++++++++++++++---------- src/parser/parse/tokens.rs | 32 ++-- src/parser/parse_command.rs | 27 +-- src/parser/registry.rs | 38 ++-- src/plugins/add.rs | 8 +- src/plugins/edit.rs | 6 +- src/plugins/embed.rs | 6 +- src/plugins/inc.rs | 42 ++--- src/plugins/ps.rs | 4 +- src/plugins/skip.rs | 8 +- src/plugins/str.rs | 14 +- src/plugins/sum.rs | 12 +- src/plugins/sys.rs | 2 +- src/prelude.rs | 5 +- src/shell/filesystem_shell.rs | 115 ++++++------ src/shell/help_shell.rs | 19 +- src/shell/helper.rs | 38 ++-- src/shell/shell.rs | 14 +- src/shell/shell_manager.rs | 8 +- src/shell/value_shell.rs | 32 ++-- src/traits.rs | 4 +- 97 files changed, 1255 insertions(+), 1174 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22af928844..08fcb94a0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1474,8 +1474,8 @@ dependencies = [ ] [[package]] -name = "nom_locate" -version = "1.0.0" +name = "nom5_locate" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1531,7 +1531,7 @@ dependencies = [ "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", "neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3129,7 +3129,7 @@ dependencies = [ "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b" -"checksum nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f932834fd8e391fc7710e2ba17e8f9f8645d846b55aa63207e17e110a1e1ce35" +"checksum nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4312467f8b28d909344b934207e502212fa5a3adf1bff7428b0b86a666223d" "checksum ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602" "checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" diff --git a/Cargo.toml b/Cargo.toml index c4369b94b6..f9e13c9348 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ ctrlc = "3.1.3" surf = "1.0.2" url = "2.1.0" roxmltree = "0.7.0" -nom_locate = "1.0.0" +nom5_locate = "0.1.1" enum-utils = "0.1.1" unicode-xid = "0.2.0" serde_ini = "0.2.0" diff --git a/src/cli.rs b/src/cli.rs index c29617ca92..ec8c7085c6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -266,7 +266,7 @@ pub async fn cli() -> Result<(), Box> { context.shell_manager.clone(), ))); - let edit_mode = crate::data::config::config(Tag::unknown())? + let edit_mode = crate::data::config::config(Span::unknown())? .get("edit_mode") .map(|s| match s.as_string().unwrap().as_ref() { "vi" => EditMode::Vi, @@ -344,7 +344,7 @@ async fn process_line(readline: Result, ctx: &mut Context Ok(line) if line.trim() == "" => LineResult::Success(line.clone()), Ok(line) => { - let result = match crate::parser::parse(&line, uuid::Uuid::nil()) { + let result = match crate::parser::parse(&line) { Err(err) => { return LineResult::Error(line.clone(), err); } @@ -366,7 +366,7 @@ async fn process_line(readline: Result, ctx: &mut Context .commands .push(ClassifiedCommand::Internal(InternalCommand { command: whole_stream_command(autoview::Autoview), - name_tag: Tag::unknown(), + name_span: Span::unknown(), args: hir::Call::new( Box::new(hir::Expression::synthetic_string("autoview")), None, @@ -486,10 +486,10 @@ fn classify_command( match call { // If the command starts with `^`, treat it as an external command no matter what call if call.head().is_external() => { - let name_tag = call.head().expect_external(); - let name = name_tag.slice(source); + let name_span = call.head().expect_external(); + let name = name_span.slice(source); - Ok(external_command(call, source, name.tagged(name_tag))) + Ok(external_command(call, source, name.tagged(name_span))) } // Otherwise, if the command is a bare word, we'll need to triage it @@ -511,19 +511,19 @@ fn classify_command( Ok(ClassifiedCommand::Internal(InternalCommand { command, - name_tag: head.tag(), + name_span: head.span().clone(), args, })) } // otherwise, it's an external command - false => Ok(external_command(call, source, name.tagged(head.tag()))), + false => Ok(external_command(call, source, name.tagged(head.span()))), } } // If the command is something else (like a number or a variable), that is currently unsupported. // We might support `$somevar` as a curried command in the future. - call => Err(ShellError::invalid_command(call.head().tag())), + call => Err(ShellError::invalid_command(call.head().span())), } } @@ -540,7 +540,10 @@ fn external_command( .iter() .filter_map(|i| match i { TokenNode::Whitespace(_) => None, - other => Some(other.as_external_arg(source).tagged(other.tag())), + other => Some(Tagged::from_simple_spanned_item( + other.as_external_arg(source), + other.span(), + )), }) .collect(), None => vec![], @@ -550,7 +553,7 @@ fn external_command( ClassifiedCommand::External(ExternalCommand { name: name.to_string(), - name_tag: tag, + name_span: tag.span, args: arg_list_strings, }) } diff --git a/src/commands/cd.rs b/src/commands/cd.rs index a3f5a8d89b..a84e66fce9 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -10,7 +10,8 @@ impl WholeStreamCommand for CD { } fn signature(&self) -> Signature { - Signature::build("cd").optional("directory", SyntaxShape::Path) + Signature::build("cd") + .optional("directory", SyntaxType::Path) } fn usage(&self) -> &str { diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 4d15bce122..277a925341 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -86,7 +86,7 @@ pub(crate) enum ClassifiedCommand { pub(crate) struct InternalCommand { pub(crate) command: Arc, - pub(crate) name_tag: Tag, + pub(crate) name_span: Span, pub(crate) args: hir::Call, } @@ -108,7 +108,7 @@ impl InternalCommand { let result = context.run_command( self.command, - self.name_tag.clone(), + self.name_span.clone(), context.source_map.clone(), self.args, &source, @@ -133,18 +133,21 @@ impl InternalCommand { match value { Tagged { item: Value::Primitive(Primitive::String(cmd)), - tag, + .. } => { context.shell_manager.insert_at_current(Box::new( HelpShell::for_command( - Value::string(cmd).tagged(tag), - &context.registry(), + Tagged::from_simple_spanned_item( + Value::string(cmd), + Span::unknown(), + ), + &context.registry().clone(), )?, )); } _ => { context.shell_manager.insert_at_current(Box::new( - HelpShell::index(&context.registry())?, + HelpShell::index(&context.registry().clone())?, )); } } @@ -186,7 +189,7 @@ impl InternalCommand { pub(crate) struct ExternalCommand { pub(crate) name: String, - pub(crate) name_tag: Tag, + pub(crate) name_span: Span, pub(crate) args: Vec>, } @@ -205,7 +208,7 @@ impl ExternalCommand { ) -> Result { let stdin = input.stdin; let inputs: Vec> = input.objects.into_vec().await; - let name_tag = self.name_tag.clone(); + let name_span = self.name_span.clone(); trace!(target: "nu::run::external", "-> {}", self.name); trace!(target: "nu::run::external", "inputs = {:?}", inputs); @@ -224,17 +227,17 @@ impl ExternalCommand { for i in &inputs { if i.as_string().is_err() { - let mut tag = None; + let mut span = None; for arg in &self.args { if arg.item.contains("$it") { - tag = Some(arg.tag()); + span = Some(arg.span()); } } - if let Some(tag) = tag { + if let Some(span) = span { return Err(ShellError::labeled_error( "External $it needs string data", "given row instead of string data", - tag, + span, )); } else { return Err(ShellError::string("Error: $it needs string data")); @@ -311,7 +314,9 @@ impl ExternalCommand { let stdout = popen.stdout.take().unwrap(); let file = futures::io::AllowStdIo::new(stdout); let stream = Framed::new(file, LinesCodec {}); - let stream = stream.map(move |line| Value::string(line.unwrap()).tagged(name_tag)); + let stream = stream.map(move |line| { + Tagged::from_simple_spanned_item(Value::string(line.unwrap()), name_span) + }); Ok(ClassifiedInputStream::from_input_stream( stream.boxed() as BoxStream<'static, Tagged> )) diff --git a/src/commands/clip.rs b/src/commands/clip.rs index 2ef5bfac1d..9bf7fa9f3a 100644 --- a/src/commands/clip.rs +++ b/src/commands/clip.rs @@ -51,7 +51,7 @@ pub mod clipboard { Ok(OutputStream::from(stream)) } - async fn inner_clip(input: Vec>, name: Tag) -> OutputStream { + async fn inner_clip(input: Vec>, name: Span) -> OutputStream { let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap(); let mut new_copy_data = String::new(); diff --git a/src/commands/command.rs b/src/commands/command.rs index 99352a7b18..6be179895b 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -18,7 +18,7 @@ pub struct UnevaluatedCallInfo { pub args: hir::Call, pub source: Text, pub source_map: SourceMap, - pub name_tag: Tag, + pub name_span: Span, } impl ToDebug for UnevaluatedCallInfo { @@ -38,7 +38,7 @@ impl UnevaluatedCallInfo { Ok(CallInfo { args, source_map: self.source_map, - name_tag: self.name_tag, + name_span: self.name_span, }) } @@ -74,7 +74,7 @@ impl UnevaluatedCallInfo { pub struct CallInfo { pub args: registry::EvaluatedArgs, pub source_map: SourceMap, - pub name_tag: Tag, + pub name_span: Span, } impl CallInfo { @@ -89,7 +89,7 @@ impl CallInfo { args: T::deserialize(&mut deserializer)?, context: RunnablePerItemContext { shell_manager: shell_manager.clone(), - name: self.name_tag, + name: self.name_span, }, callback, }) @@ -158,7 +158,7 @@ impl CommandArgs { let host = self.host.clone(); let args = self.evaluate_once(registry)?; let (input, args) = args.split(); - let name_tag = args.call_info.name_tag; + let name_span = args.call_info.name_span; let mut deserializer = ConfigDeserializer::from_call_info(args.call_info); Ok(RunnableArgs { @@ -167,7 +167,7 @@ impl CommandArgs { input, commands: registry.clone(), shell_manager, - name: name_tag, + name: name_span, source_map, host, }, @@ -191,7 +191,7 @@ impl CommandArgs { let host = self.host.clone(); let args = self.evaluate_once(registry)?; let (input, args) = args.split(); - let name_tag = args.call_info.name_tag; + let name_span = args.call_info.name_span; let mut deserializer = ConfigDeserializer::from_call_info(args.call_info); Ok(RunnableRawArgs { @@ -200,7 +200,7 @@ impl CommandArgs { input, commands: registry.clone(), shell_manager, - name: name_tag, + name: name_span, source_map, host, }, @@ -212,7 +212,7 @@ impl CommandArgs { pub struct RunnablePerItemContext { pub shell_manager: ShellManager, - pub name: Tag, + pub name: Span, } impl RunnablePerItemContext { @@ -227,7 +227,7 @@ pub struct RunnableContext { pub host: Arc>, pub commands: CommandRegistry, pub source_map: SourceMap, - pub name: Tag, + pub name: Span, } impl RunnableContext { @@ -311,8 +311,8 @@ impl EvaluatedWholeStreamCommandArgs { } } - pub fn name_tag(&self) -> Tag { - self.args.call_info.name_tag + pub fn name_span(&self) -> Span { + self.args.call_info.name_span } pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) { @@ -471,6 +471,12 @@ impl ReturnSuccess { pub fn action(input: CommandAction) -> ReturnValue { Ok(ReturnSuccess::Action(input)) } + + pub fn spanned_value(input: Value, span: Span) -> ReturnValue { + Ok(ReturnSuccess::Value(Tagged::from_simple_spanned_item( + input, span, + ))) + } } pub trait WholeStreamCommand: Send + Sync { diff --git a/src/commands/config.rs b/src/commands/config.rs index 3b36c88fad..97b2a948e9 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,7 +1,7 @@ use crate::commands::WholeStreamCommand; use crate::data::{config, Value}; use crate::errors::ShellError; -use crate::parser::hir::SyntaxShape; +use crate::parser::hir::SyntaxType; use crate::parser::registry::{self}; use crate::prelude::*; use std::iter::FromIterator; @@ -26,10 +26,10 @@ impl WholeStreamCommand for Config { fn signature(&self) -> Signature { Signature::build("config") - .named("load", SyntaxShape::Path) - .named("set", SyntaxShape::Any) - .named("get", SyntaxShape::Any) - .named("remove", SyntaxShape::Any) + .named("load", SyntaxType::Path) + .named("set", SyntaxType::Any) + .named("get", SyntaxType::Any) + .named("remove", SyntaxType::Any) .switch("clear") .switch("path") } @@ -96,21 +96,41 @@ pub fn config( config::write(&result, &configuration)?; - return Ok(stream![Value::Row(result.into()).tagged(value.tag())].from_input_stream()); + return Ok(stream![Tagged::from_simple_spanned_item( + Value::Row(result.into()), + value.span() + )] + .from_input_stream()); } - if let Tagged { item: true, tag } = clear { + if let Tagged { + item: true, + tag: Tag { span, .. }, + } = clear + { result.clear(); config::write(&result, &configuration)?; - return Ok(stream![Value::Row(result.into()).tagged(tag)].from_input_stream()); + return Ok(stream![Tagged::from_simple_spanned_item( + Value::Row(result.into()), + span + )] + .from_input_stream()); } - if let Tagged { item: true, tag } = path { + if let Tagged { + item: true, + tag: Tag { span, .. }, + } = path + { let path = config::default_path_for(&configuration)?; - return Ok(stream![Value::Primitive(Primitive::Path(path)).tagged(tag)].from_input_stream()); + return Ok(stream![Tagged::from_simple_spanned_item( + Value::Primitive(Primitive::Path(path)), + span + )] + .from_input_stream()); } if let Some(v) = remove { @@ -126,9 +146,9 @@ pub fn config( ))); } - let obj = VecDeque::from_iter(vec![Value::Row(result.into()).tagged(v.tag())]); + let obj = VecDeque::from_iter(vec![Value::Row(result.into()).simple_spanned(v.span())]); return Ok(obj.from_input_stream()); } - return Ok(vec![Value::Row(result.into()).tagged(name)].into()); + return Ok(vec![Value::Row(result.into()).simple_spanned(name)].into()); } diff --git a/src/commands/cp.rs b/src/commands/cp.rs index bf20c74ce9..491e18b1aa 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -1,6 +1,6 @@ use crate::commands::command::RunnablePerItemContext; use crate::errors::ShellError; -use crate::parser::hir::SyntaxShape; +use crate::parser::hir::SyntaxType; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; use std::path::PathBuf; @@ -21,9 +21,9 @@ impl PerItemCommand for Cpy { fn signature(&self) -> Signature { Signature::build("cp") - .required("src", SyntaxShape::Pattern) - .required("dst", SyntaxShape::Path) - .named("file", SyntaxShape::Any) + .required("src", SyntaxType::Pattern) + .required("dst", SyntaxType::Path) + .named("file", SyntaxType::Any) .switch("recursive") } diff --git a/src/commands/date.rs b/src/commands/date.rs index 6df9e27209..7d3307fe58 100644 --- a/src/commands/date.rs +++ b/src/commands/date.rs @@ -1,5 +1,5 @@ -use crate::data::{Dictionary, Value}; use crate::errors::ShellError; +use crate::data::{Dictionary, Value}; use crate::prelude::*; use chrono::{DateTime, Local, Utc}; @@ -33,40 +33,58 @@ impl WholeStreamCommand for Date { } } -pub fn date_to_value(dt: DateTime, tag: Tag) -> Tagged +pub fn date_to_value(dt: DateTime, span: Span) -> Tagged where T::Offset: Display, { let mut indexmap = IndexMap::new(); - indexmap.insert("year".to_string(), Value::int(dt.year()).tagged(tag)); - indexmap.insert("month".to_string(), Value::int(dt.month()).tagged(tag)); - indexmap.insert("day".to_string(), Value::int(dt.day()).tagged(tag)); - indexmap.insert("hour".to_string(), Value::int(dt.hour()).tagged(tag)); - indexmap.insert("minute".to_string(), Value::int(dt.minute()).tagged(tag)); - indexmap.insert("second".to_string(), Value::int(dt.second()).tagged(tag)); + indexmap.insert( + "year".to_string(), + Tagged::from_simple_spanned_item(Value::int(dt.year()), span), + ); + indexmap.insert( + "month".to_string(), + Tagged::from_simple_spanned_item(Value::int(dt.month()), span), + ); + indexmap.insert( + "day".to_string(), + Tagged::from_simple_spanned_item(Value::int(dt.day()), span), + ); + indexmap.insert( + "hour".to_string(), + Tagged::from_simple_spanned_item(Value::int(dt.hour()), span), + ); + indexmap.insert( + "minute".to_string(), + Tagged::from_simple_spanned_item(Value::int(dt.minute()), span), + ); + indexmap.insert( + "second".to_string(), + Tagged::from_simple_spanned_item(Value::int(dt.second()), span), + ); let tz = dt.offset(); indexmap.insert( "timezone".to_string(), - Value::string(format!("{}", tz)).tagged(tag), + Tagged::from_simple_spanned_item(Value::string(format!("{}", tz)), span), ); - Value::Row(Dictionary::from(indexmap)).tagged(tag) + Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span) } pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let mut date_out = VecDeque::new(); - let tag = args.call_info.name_tag; + let span = args.call_info.name_span; let value = if args.has("utc") { let utc: DateTime = Utc::now(); - date_to_value(utc, tag) + date_to_value(utc, span) } else { let local: DateTime = Local::now(); - date_to_value(local, tag) + date_to_value(local, span) }; date_out.push_back(value); diff --git a/src/commands/echo.rs b/src/commands/echo.rs index 453041bbcd..f464630de7 100644 --- a/src/commands/echo.rs +++ b/src/commands/echo.rs @@ -12,7 +12,7 @@ impl PerItemCommand for Echo { } fn signature(&self) -> Signature { - Signature::build("echo").rest(SyntaxShape::Any) + Signature::build("echo").rest(SyntaxType::Any) } fn usage(&self) -> &str { @@ -35,7 +35,7 @@ fn run( _registry: &CommandRegistry, _raw_args: &RawCommandArgs, ) -> Result { - let name = call_info.name_tag; + let name = call_info.name_span; let mut output = String::new(); @@ -57,7 +57,7 @@ fn run( return Err(ShellError::labeled_error( "Expect a string from pipeline", "not a string-compatible value", - i.tag(), + i.span(), )); } } @@ -65,7 +65,7 @@ fn run( } let stream = VecDeque::from(vec![Ok(ReturnSuccess::Value( - Value::string(output).tagged(name), + Value::string(output).simple_spanned(name), ))]); Ok(stream.to_output_stream()) diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 4148d03c5f..7b42793e12 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -14,7 +14,7 @@ impl PerItemCommand for Enter { } fn signature(&self) -> registry::Signature { - Signature::build("enter").required("location", SyntaxShape::Block) + Signature::build("enter").required("location", SyntaxType::Block) } fn usage(&self) -> &str { @@ -70,7 +70,7 @@ impl PerItemCommand for Enter { crate::commands::open::fetch( &full_path, &location_clone, - Tag::unknown(), + Span::unknown(), ) .await.unwrap(); @@ -103,7 +103,7 @@ impl PerItemCommand for Enter { }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_tag: raw_args.call_info.name_tag, + name_span: raw_args.call_info.name_span, }, }; let mut result = converter.run( diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index 07ef0c3e0f..e87ddf8347 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -2,13 +2,14 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; use crate::data::Value; use crate::errors::ShellError; -use crate::parser::hir::SyntaxShape; +use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; use mime::Mime; use std::path::PathBuf; use std::str::FromStr; use surf::mime; +use uuid::Uuid; pub struct Fetch; impl PerItemCommand for Fetch { @@ -18,7 +19,7 @@ impl PerItemCommand for Fetch { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxShape::Path) + .required("path", SyntaxType::Path) .switch("raw") } @@ -51,14 +52,14 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_tag = path.tag(); + let path_span = path.span(); let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); let stream = async_stream_block! { - let result = fetch(&path_str, path_tag).await; + let result = fetch(&path_str, path_span).await; if let Err(e) = result { yield Err(e); @@ -98,7 +99,7 @@ fn run( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_tag: raw_args.call_info.name_tag, + name_span: raw_args.call_info.name_span, } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); @@ -129,13 +130,13 @@ fn run( pub async fn fetch( location: &str, - tag: Tag, + span: Span, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { if let Err(_) = url::Url::parse(location) { return Err(ShellError::labeled_error( "Incomplete or incorrect url", "expected a full url", - tag, + span, )); } @@ -151,10 +152,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( @@ -163,10 +167,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { @@ -174,13 +181,16 @@ pub async fn fetch( ShellError::labeled_error( "Could not load binary file", "could not load", - tag, + span, ) })?; Ok(( None, Value::Binary(buf), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )) } @@ -190,10 +200,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load svg from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::IMAGE, image_ty) => { @@ -201,13 +214,16 @@ pub async fn fetch( ShellError::labeled_error( "Could not load image file", "could not load", - tag, + span, ) })?; Ok(( Some(image_ty.to_string()), Value::Binary(buf), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )) } @@ -217,10 +233,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { @@ -241,17 +260,23 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )) } (ty, sub_ty) => Ok(( None, Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), } @@ -259,7 +284,10 @@ pub async fn fetch( None => Ok(( None, Value::string(format!("No content type found")), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), }, @@ -267,7 +295,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "URL could not be opened", "url not found", - tag, + span, )); } } diff --git a/src/commands/first.rs b/src/commands/first.rs index 77c9a4f695..6381d5def6 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -17,7 +17,7 @@ impl WholeStreamCommand for First { fn signature(&self) -> Signature { Signature::build("first") - .required("amount", SyntaxShape::Literal) + .required("amount", SyntaxType::Literal) } fn usage(&self) -> &str { diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index b39754a196..e2f5421bd9 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::errors::ExpectedRange; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use bson::{decode_document, spec::BinarySubtype, Bson}; use std::str::FromStr; @@ -198,7 +198,7 @@ pub fn from_bson_bytes_to_value( fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let tag = args.name_tag(); + let span = args.name_span(); let input = args.input; let stream = async_stream_block! { @@ -208,24 +208,24 @@ fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result - match from_bson_bytes_to_value(vb, tag) { + match from_bson_bytes_to_value(vb, span) { Ok(x) => yield ReturnSuccess::value(x), Err(_) => { yield Err(ShellError::labeled_error_with_secondary( "Could not parse as BSON", "input cannot be parsed as BSON", - tag, + span, "value originates from here", - value_tag, + value_tag.span, )) } } _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + span, "value originates from here", - value_tag, + value_tag.span, )), } diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index 68296eb11b..c872e77360 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -86,7 +86,7 @@ fn from_csv( }: FromCSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_tag = name; + let name_span = name; let stream = async_stream_block! { let values: Vec> = input.values.collect().await; @@ -105,15 +105,15 @@ fn from_csv( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_tag, + name_span, "value originates from here", - value_tag, + value_tag.span, )), } } - match from_csv_string_to_value(concat_string, skip_headers, name_tag) { + match from_csv_string_to_value(concat_string, skip_headers, name_span) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -126,9 +126,9 @@ fn from_csv( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as CSV", "input cannot be parsed as CSV", - name_tag, + name_span, "value originates from here", - last_tag, + last_tag.span, )) } , } diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index 8409cf8489..0e128a22c4 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -64,7 +64,7 @@ pub fn from_ini_string_to_value( fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let tag = args.name_tag(); + let span = args.name_span(); let input = args.input; let stream = async_stream_block! { @@ -84,15 +84,15 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + span, "value originates from here", - value_tag, + value_tag.span, )), } } - match from_ini_string_to_value(concat_string, tag) { + match from_ini_string_to_value(concat_string, span) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -105,9 +105,9 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result Result { - let name_tag = name; + let name_span = name; let stream = async_stream_block! { let values: Vec> = input.values.collect().await; @@ -91,9 +91,9 @@ fn from_json( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_tag, + name_span, "value originates from here", - value_tag, + value_tag.span, )), } @@ -106,7 +106,7 @@ fn from_json( continue; } - match from_json_string_to_value(json_str.to_string(), name_tag) { + match from_json_string_to_value(json_str.to_string(), name_span) { Ok(x) => yield ReturnSuccess::value(x), Err(_) => { @@ -114,15 +114,15 @@ fn from_json( yield Err(ShellError::labeled_error_with_secondary( "Could nnot parse as JSON", "input cannot be parsed as JSON", - name_tag, + name_span, "value originates from here", - last_tag)) + last_tag.span)) } } } } } else { - match from_json_string_to_value(concat_string, name_tag) { + match from_json_string_to_value(concat_string, name_span) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { @@ -137,9 +137,9 @@ fn from_json( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as JSON", "input cannot be parsed as JSON", - name_tag, + name_span, "value originates from here", - last_tag)) + last_tag.span)) } } } diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index 1b04a59fd0..c19c4fef10 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -128,7 +128,7 @@ pub fn from_sqlite_bytes_to_value( fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let tag = args.name_tag(); + let span = args.name_span(); let input = args.input; let stream = async_stream_block! { @@ -138,7 +138,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result - match from_sqlite_bytes_to_value(vb, tag) { + match from_sqlite_bytes_to_value(vb, span) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -151,18 +151,18 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + span, "value originates from here", - value_tag, + value_tag.span, )), } diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index 29db38a77e..c1338cb01f 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -68,7 +68,7 @@ pub fn from_toml( registry: &CommandRegistry, ) -> Result { let args = args.evaluate_once(registry)?; - let tag = args.name_tag(); + let span = args.name_span(); let input = args.input; let stream = async_stream_block! { @@ -88,15 +88,15 @@ pub fn from_toml( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + span, "value originates from here", - value_tag, + value_tag.span, )), } } - match from_toml_string_to_value(concat_string, tag) { + match from_toml_string_to_value(concat_string, span) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -109,9 +109,9 @@ pub fn from_toml( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as TOML", "input cannot be parsed as TOML", - tag, + span, "value originates from here", - last_tag, + last_tag.span, )) } , } diff --git a/src/commands/from_tsv.rs b/src/commands/from_tsv.rs index 66f070a5df..92696c1aaf 100644 --- a/src/commands/from_tsv.rs +++ b/src/commands/from_tsv.rs @@ -87,7 +87,7 @@ fn from_tsv( }: FromTSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_tag = name; + let name_span = name; let stream = async_stream_block! { let values: Vec> = input.values.collect().await; @@ -106,15 +106,15 @@ fn from_tsv( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_tag, + name_span, "value originates from here", - value_tag, + value_tag.span, )), } } - match from_tsv_string_to_value(concat_string, skip_headers, name_tag) { + match from_tsv_string_to_value(concat_string, skip_headers, name_span) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -127,9 +127,9 @@ fn from_tsv( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as TSV", "input cannot be parsed as TSV", - name_tag, + name_span, "value originates from here", - last_tag, + last_tag.span, )) } , } diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index f80d428f43..2c3f94cddc 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -83,7 +83,7 @@ pub fn from_xml_string_to_value( fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let tag = args.name_tag(); + let span = args.name_span(); let input = args.input; let stream = async_stream_block! { @@ -103,15 +103,15 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + span, "value originates from here", - value_tag, + value_tag.span, )), } } - match from_xml_string_to_value(concat_string, tag) { + match from_xml_string_to_value(concat_string, span) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -124,9 +124,9 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result Result { let args = args.evaluate_once(registry)?; - let tag = args.name_tag(); + let span = args.name_span(); let input = args.input; let stream = async_stream_block! { @@ -117,15 +117,15 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + span, "value originates from here", - value_tag, + value_tag.span, )), } } - match from_yaml_string_to_value(concat_string, tag) { + match from_yaml_string_to_value(concat_string, span) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -138,9 +138,9 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result Signature { - Signature::build("get").rest(SyntaxShape::Member) + Signature::build("get").rest(SyntaxType::Member) } fn usage(&self) -> &str { @@ -47,7 +47,7 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result registry::Signature { - Signature::build("help").rest(SyntaxShape::Any) + Signature::build("help").rest(SyntaxType::Any) } fn usage(&self) -> &str { @@ -27,11 +27,11 @@ impl PerItemCommand for Help { _raw_args: &RawCommandArgs, _input: Tagged, ) -> Result { - let tag = call_info.name_tag; + let span = call_info.name_span; if call_info.args.len() == 0 { return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( - Value::nothing().tagged(tag), + Tagged::from_simple_spanned_item(Value::nothing(), span), )))] .into()); } diff --git a/src/commands/last.rs b/src/commands/last.rs index fd7a3ecea2..1f9cc62a7f 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Last { } fn signature(&self) -> Signature { - Signature::build("last").required("amount", SyntaxShape::Number) + Signature::build("last").required("amount", SyntaxType::Number) } fn usage(&self) -> &str { diff --git a/src/commands/lines.rs b/src/commands/lines.rs index d2a9cdffd1..15a467224f 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::data::{Primitive, Value}; use crate::errors::ShellError; +use crate::data::{Primitive, Value}; use crate::prelude::*; use log::trace; @@ -32,7 +32,7 @@ impl WholeStreamCommand for Lines { fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let tag = args.name_tag(); + let span = args.name_span(); let input = args.input; let input: InputStream = trace_stream!(target: "nu::trace_stream::lines", "input" = input); @@ -58,9 +58,9 @@ fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result>, -} - impl WholeStreamCommand for LS { fn name(&self) -> &str { "ls" } fn signature(&self) -> Signature { - Signature::build("ls").optional("path", SyntaxShape::Pattern) + Signature::build("ls").optional("path", SyntaxType::Pattern) } fn usage(&self) -> &str { @@ -28,11 +22,12 @@ impl WholeStreamCommand for LS { args: CommandArgs, registry: &CommandRegistry, ) -> Result { - args.process(registry, ls)?.run() - // ls(args, registry) + ls(args, registry) } } -fn ls(LsArgs { path }: LsArgs, context: RunnableContext) -> Result { - context.shell_manager.ls(path, context.name) +fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result { + let shell_manager = args.shell_manager.clone(); + let args = args.evaluate_once(registry)?; + shell_manager.ls(args) } diff --git a/src/commands/mkdir.rs b/src/commands/mkdir.rs index 8bf8a97d4a..9dec9a3142 100644 --- a/src/commands/mkdir.rs +++ b/src/commands/mkdir.rs @@ -17,7 +17,7 @@ impl PerItemCommand for Mkdir { } fn signature(&self) -> Signature { - Signature::build("mkdir").rest(SyntaxShape::Path) + Signature::build("mkdir").rest(SyntaxType::Path) } fn usage(&self) -> &str { diff --git a/src/commands/mv.rs b/src/commands/mv.rs index 2ace1fa05f..130e5996e8 100644 --- a/src/commands/mv.rs +++ b/src/commands/mv.rs @@ -1,6 +1,6 @@ use crate::commands::command::RunnablePerItemContext; use crate::errors::ShellError; -use crate::parser::hir::SyntaxShape; +use crate::parser::hir::SyntaxType; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; use std::path::PathBuf; @@ -20,9 +20,9 @@ impl PerItemCommand for Move { fn signature(&self) -> Signature { Signature::build("mv") - .required("source", SyntaxShape::Pattern) - .required("destination", SyntaxShape::Path) - .named("file", SyntaxShape::Any) + .required("source", SyntaxType::Path) + .required("destination", SyntaxType::Path) + .named("file", SyntaxType::Any) } fn usage(&self) -> &str { diff --git a/src/commands/nth.rs b/src/commands/nth.rs index bf397e1bcf..98ab6a10a9 100644 --- a/src/commands/nth.rs +++ b/src/commands/nth.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Nth { } fn signature(&self) -> Signature { - Signature::build("nth").required("amount", SyntaxShape::Any) + Signature::build("nth").required("amount", SyntaxType::Any) } fn usage(&self) -> &str { diff --git a/src/commands/open.rs b/src/commands/open.rs index 57f035ac63..32a99c6f17 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -2,10 +2,11 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; use crate::data::Value; use crate::errors::ShellError; -use crate::parser::hir::SyntaxShape; +use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; use std::path::{Path, PathBuf}; +use uuid::Uuid; pub struct Open; impl PerItemCommand for Open { @@ -15,7 +16,7 @@ impl PerItemCommand for Open { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxShape::Path) + .required("path", SyntaxType::Path) .switch("raw") } @@ -52,7 +53,7 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_span = path.tag(); + let path_span = path.span(); let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); @@ -99,7 +100,7 @@ fn run( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_tag: raw_args.call_info.name_tag, + name_span: raw_args.call_info.name_span, } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); @@ -131,7 +132,7 @@ fn run( pub async fn fetch( cwd: &PathBuf, location: &str, - tag: Tag, + span: Span, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { let mut cwd = cwd.clone(); @@ -143,7 +144,10 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => { @@ -159,13 +163,19 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, Value::Binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -173,7 +183,10 @@ pub async fn fetch( Ok(( None, Value::Binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )) } @@ -188,13 +201,19 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, Value::Binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -202,7 +221,10 @@ pub async fn fetch( Ok(( None, Value::Binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )) } @@ -210,7 +232,10 @@ pub async fn fetch( _ => Ok(( None, Value::Binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -220,7 +245,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "File could not be opened", "file not found", - tag, + span, )); } } @@ -228,7 +253,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "File could not be opened", "file not found", - tag, + span, )); } } diff --git a/src/commands/pick.rs b/src/commands/pick.rs index 2fe21dbb49..bc5f8df401 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(SyntaxShape::Any) + Signature::build("pick").rest(SyntaxType::Any) } fn usage(&self) -> &str { diff --git a/src/commands/post.rs b/src/commands/post.rs index 88717a4667..8790c929a7 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -1,8 +1,8 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; -use crate::data::Value; use crate::errors::ShellError; -use crate::parser::hir::SyntaxShape; +use crate::data::Value; +use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; use base64::encode; @@ -10,7 +10,7 @@ use mime::Mime; use std::path::PathBuf; use std::str::FromStr; use surf::mime; - +use uuid::Uuid; pub struct Post; impl PerItemCommand for Post { @@ -20,10 +20,10 @@ impl PerItemCommand for Post { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxShape::Any) - .required("body", SyntaxShape::Any) - .named("user", SyntaxShape::Any) - .named("password", SyntaxShape::Any) + .required("path", SyntaxType::Any) + .required("body", SyntaxType::Any) + .named("user", SyntaxType::Any) + .named("password", SyntaxType::Any) .switch("raw") } @@ -63,7 +63,7 @@ fn run( file => file.clone(), }; let path_str = path.as_string()?; - let path_span = path.tag(); + let path_span = path.span(); let has_raw = call_info.args.has("raw"); let user = call_info.args.get("user").map(|x| x.as_string().unwrap()); let password = call_info @@ -109,7 +109,7 @@ fn run( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_tag: raw_args.call_info.name_tag, + name_span: raw_args.call_info.name_span, } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); @@ -143,7 +143,7 @@ pub async fn post( body: &Tagged, user: Option, password: Option, - tag: Tag, + span: Span, registry: &CommandRegistry, raw_args: &RawCommandArgs, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { @@ -189,7 +189,7 @@ pub async fn post( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_tag: raw_args.call_info.name_tag, + name_span: raw_args.call_info.name_span, }, }; let mut result = converter.run( @@ -211,7 +211,7 @@ pub async fn post( return Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", - *tag, + span, )); } } @@ -227,7 +227,7 @@ pub async fn post( return Err(ShellError::labeled_error( "Could not automatically convert table", "needs manual conversion", - *tag, + tag.span, )); } } @@ -243,10 +243,13 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( @@ -255,10 +258,13 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { @@ -266,13 +272,16 @@ pub async fn post( ShellError::labeled_error( "Could not load binary file", "could not load", - tag, + span, ) })?; Ok(( None, Value::Binary(buf), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )) } @@ -281,13 +290,16 @@ pub async fn post( ShellError::labeled_error( "Could not load image file", "could not load", - tag, + span, ) })?; Ok(( Some(image_ty.to_string()), Value::Binary(buf), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )) } @@ -297,10 +309,13 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { @@ -321,10 +336,13 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )) } @@ -334,7 +352,10 @@ pub async fn post( "Not yet supported MIME type: {} {}", ty, sub_ty )), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), } @@ -342,7 +363,10 @@ pub async fn post( None => Ok(( None, Value::string(format!("No content type found")), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), }, @@ -350,7 +374,7 @@ pub async fn post( return Err(ShellError::labeled_error( "URL could not be opened", "url not found", - tag, + span, )); } } @@ -358,7 +382,7 @@ pub async fn post( Err(ShellError::labeled_error( "Expected a url", "needs a url", - tag, + span, )) } } diff --git a/src/commands/reject.rs b/src/commands/reject.rs index ea09bd7495..49dbc4dd36 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Reject { } fn signature(&self) -> Signature { - Signature::build("reject").rest(SyntaxShape::Member) + Signature::build("reject").rest(SyntaxType::Member) } fn usage(&self) -> &str { diff --git a/src/commands/rm.rs b/src/commands/rm.rs index ac5aeaae7d..60bb5c6e4a 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -1,6 +1,6 @@ use crate::commands::command::RunnablePerItemContext; use crate::errors::ShellError; -use crate::parser::hir::SyntaxShape; +use crate::parser::hir::SyntaxType; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; use std::path::PathBuf; @@ -20,7 +20,7 @@ impl PerItemCommand for Remove { fn signature(&self) -> Signature { Signature::build("rm") - .required("path", SyntaxShape::Pattern) + .required("path", SyntaxType::Path) .switch("recursive") } diff --git a/src/commands/save.rs b/src/commands/save.rs index 66d9732d61..26bf8cb16d 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; pub struct Save; macro_rules! process_string { - ($input:ident, $name_tag:ident) => {{ + ($input:ident, $name_span:ident) => {{ let mut result_string = String::new(); for res in $input { match res { @@ -21,7 +21,7 @@ macro_rules! process_string { yield core::task::Poll::Ready(Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", - $name_tag, + $name_span, ))); } } @@ -31,7 +31,7 @@ macro_rules! process_string { } macro_rules! process_string_return_success { - ($result_vec:ident, $name_tag:ident) => {{ + ($result_vec:ident, $name_span:ident) => {{ let mut result_string = String::new(); for res in $result_vec { match res { @@ -45,7 +45,7 @@ macro_rules! process_string_return_success { yield core::task::Poll::Ready(Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during text save", - $name_tag, + $name_span, ))); } } @@ -55,7 +55,7 @@ macro_rules! process_string_return_success { } macro_rules! process_binary_return_success { - ($result_vec:ident, $name_tag:ident) => {{ + ($result_vec:ident, $name_span:ident) => {{ let mut result_binary: Vec = Vec::new(); for res in $result_vec { match res { @@ -71,7 +71,7 @@ macro_rules! process_binary_return_success { yield core::task::Poll::Ready(Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during binary save", - $name_tag, + $name_span, ))); } } @@ -93,7 +93,7 @@ impl WholeStreamCommand for Save { fn signature(&self) -> Signature { Signature::build("save") - .optional("path", SyntaxShape::Path) + .optional("path", SyntaxType::Path) .switch("raw") } @@ -127,7 +127,7 @@ fn save( raw_args: RawCommandArgs, ) -> Result { let mut full_path = PathBuf::from(shell_manager.path()); - let name_tag = name; + let name_span = name; let source_map = source_map.clone(); let stream = async_stream_block! { @@ -145,7 +145,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - name_tag, + name_span, )); } }, @@ -153,7 +153,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - name_tag, + name_span, )); } } @@ -161,7 +161,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - name_tag, + name_span, )); } } else { @@ -185,21 +185,21 @@ fn save( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_tag: raw_args.call_info.name_tag, + name_span: raw_args.call_info.name_span, } }; let mut result = converter.run(new_args.with_input(input), ®istry); let result_vec: Vec> = result.drain_vec().await; if converter.is_binary() { - process_binary_return_success!(result_vec, name_tag) + process_binary_return_success!(result_vec, name_span) } else { - process_string_return_success!(result_vec, name_tag) + process_string_return_success!(result_vec, name_span) } } else { - process_string!(input, name_tag) + process_string!(input, name_span) } } else { - process_string!(input, name_tag) + process_string!(input, name_span) } } else { Ok(string_from(&input).into_bytes()) diff --git a/src/commands/shells.rs b/src/commands/shells.rs index 2aee2c8564..856c23e7be 100644 --- a/src/commands/shells.rs +++ b/src/commands/shells.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::data::TaggedDictBuilder; use crate::errors::ShellError; +use crate::data::TaggedDictBuilder; use crate::prelude::*; pub struct Shells; @@ -29,10 +29,10 @@ impl WholeStreamCommand for Shells { fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result { let mut shells_out = VecDeque::new(); - let tag = args.call_info.name_tag; + let span = args.call_info.name_span; for (index, shell) in args.shell_manager.shells.lock().unwrap().iter().enumerate() { - let mut dict = TaggedDictBuilder::new(tag); + let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(span)); if index == args.shell_manager.current_shell { dict.insert(" ", "X".to_string()); diff --git a/src/commands/size.rs b/src/commands/size.rs index 51f8d22c1d..d8383efcf8 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::data::{TaggedDictBuilder, Value}; use crate::errors::ShellError; +use crate::data::{TaggedDictBuilder, Value}; use crate::prelude::*; pub struct Size; @@ -29,7 +29,7 @@ impl WholeStreamCommand for Size { fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result { let input = args.input; - let tag = args.call_info.name_tag; + let span = args.call_info.name_span; Ok(input .values .map(move |v| match v.item { @@ -37,9 +37,9 @@ fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + span, "value originates from here", - v.tag(), + v.span(), )), }) .to_output_stream()) @@ -71,7 +71,7 @@ fn count(contents: &str, tag: impl Into) -> Tagged { } let mut dict = TaggedDictBuilder::new(tag); - //TODO: add back in name when we have it in the tag + //TODO: add back in name when we have it in the span //dict.insert("name", Value::string(name)); dict.insert("lines", Value::int(lines)); dict.insert("words", Value::int(words)); diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index 041caf300e..90ba24b996 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for SkipWhile { fn signature(&self) -> Signature { Signature::build("skip-while") - .required("condition", SyntaxShape::Block) + .required("condition", SyntaxType::Block) .filter() } diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index 8058b7889e..edce33b963 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -15,7 +15,7 @@ impl WholeStreamCommand for SortBy { } fn signature(&self) -> Signature { - Signature::build("sort-by").rest(SyntaxShape::String) + Signature::build("sort-by").rest(SyntaxType::String) } fn usage(&self) -> &str { diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index 00e2609f26..2df02cac16 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::errors::ShellError; +use crate::data::{Primitive, TaggedDictBuilder, Value}; use crate::prelude::*; use log::trace; @@ -21,9 +21,9 @@ impl WholeStreamCommand for SplitColumn { fn signature(&self) -> Signature { Signature::build("split-column") - .required("separator", SyntaxShape::Any) + .required("separator", SyntaxType::Any) .switch("collapse-empty") - .rest(SyntaxShape::Member) + .rest(SyntaxType::Member) } fn usage(&self) -> &str { @@ -40,11 +40,7 @@ impl WholeStreamCommand for SplitColumn { } fn split_column( - SplitColumnArgs { - separator, - rest, - collapse_empty, - }: SplitColumnArgs, + SplitColumnArgs { separator, rest, collapse_empty}: SplitColumnArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { Ok(input @@ -96,7 +92,7 @@ fn split_column( "requires string input", name, "value originates from here", - v.tag(), + v.span(), )), }) .to_output_stream()) diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index e70e5cfa84..d4f3824a80 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::data::{Primitive, Value}; use crate::errors::ShellError; +use crate::data::{Primitive, Value}; use crate::prelude::*; use log::trace; @@ -17,7 +17,8 @@ impl WholeStreamCommand for SplitRow { } fn signature(&self) -> Signature { - Signature::build("split-row").required("separator", SyntaxShape::Any) + Signature::build("split-row") + .required("separator", SyntaxType::Any) } fn usage(&self) -> &str { @@ -62,7 +63,7 @@ fn split_row( "requires string input", name, "value originates from here", - v.tag(), + v.span(), ))); result } diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 2b45105c2d..3f112482ca 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::data::{TaggedDictBuilder, Value}; use crate::errors::ShellError; +use crate::data::{TaggedDictBuilder, Value}; use crate::prelude::*; pub struct Tags; @@ -36,7 +36,7 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result Result { Ok(Bson::Document(doc)) } -fn shell_encode_document(writer: &mut Vec, doc: Document, tag: Tag) -> Result<(), ShellError> { +fn shell_encode_document( + writer: &mut Vec, + doc: Document, + span: Span, +) -> Result<(), ShellError> { match encode_document(writer, &doc) { Err(e) => Err(ShellError::labeled_error( format!("Failed to encode document due to: {:?}", e), "requires BSON-compatible document", - tag, + span, )), _ => Ok(()), } } -fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result, ShellError> { +fn bson_value_to_bytes(bson: Bson, span: Span) -> Result, ShellError> { let mut out = Vec::new(); match bson { Bson::Array(a) => { for v in a.into_iter() { match v { - Bson::Document(d) => shell_encode_document(&mut out, d, tag)?, + Bson::Document(d) => shell_encode_document(&mut out, d, span)?, _ => { return Err(ShellError::labeled_error( format!("All top level values must be Documents, got {:?}", v), "requires BSON-compatible document", - tag, + span, )) } } } } - Bson::Document(d) => shell_encode_document(&mut out, d, tag)?, + Bson::Document(d) => shell_encode_document(&mut out, d, span)?, _ => { return Err(ShellError::labeled_error( format!("All top level values must be Documents, got {:?}", bson), "requires BSON-compatible document", - tag, + span, )) } } @@ -232,7 +236,7 @@ fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result, ShellError> { fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_tag = args.name_tag(); + let name_span = args.name_span(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -248,23 +252,23 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { - match bson_value_to_bytes(bson_value, name_tag) { + match bson_value_to_bytes(bson_value, name_span) { Ok(x) => yield ReturnSuccess::value( - Value::Binary(x).tagged(name_tag), + Value::Binary(x).simple_spanned(name_span), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with BSON-compatible structure.tag() from pipeline", + "Expected a table with BSON-compatible structure.span() from pipeline", "requires BSON-compatible input", - name_tag, + name_span, "originates from here".to_string(), - value.tag(), + value.span(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with BSON-compatible structure from pipeline", "requires BSON-compatible input", - name_tag)) + name_span)) } } }; diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index fd77fdcb6f..615e49cbf8 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -134,7 +134,7 @@ fn to_csv( ToCSVArgs { headerless }: ToCSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_tag = name; + let name_span = name; let stream = async_stream_block! { let input: Vec> = input.values.collect().await; @@ -155,15 +155,15 @@ fn to_csv( } else { x }; - yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag)) + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span)) } _ => { yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with CSV-compatible structure.tag() from pipeline", + "Expected a table with CSV-compatible structure.span() from pipeline", "requires CSV-compatible input", - name_tag, + name_span, "originates from here".to_string(), - value.tag(), + value.span(), )) } } diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index c6b5d9c016..35c03af32d 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -80,7 +80,7 @@ fn json_list(input: &Vec>) -> Result, Shell fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_tag = args.name_tag(); + let name_span = args.name_span(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -98,21 +98,21 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { match serde_json::to_string(&json_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).tagged(name_tag), + Value::Primitive(Primitive::String(x)).simple_spanned(name_span), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with JSON-compatible structure.tag() from pipeline", + "Expected a table with JSON-compatible structure.span() from pipeline", "requires JSON-compatible input", - name_tag, + name_span, "originates from here".to_string(), - value.tag(), + value.span(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with JSON-compatible structure from pipeline", "requires JSON-compatible input", - name_tag)) + name_span)) } } }; diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index 580dba96bf..0fd392f345 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -200,7 +200,7 @@ fn sqlite_input_stream_to_bytes( fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_tag = args.name_tag(); + let name_span = args.name_span(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -208,9 +208,9 @@ fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result yield ReturnSuccess::value(out), _ => { yield Err(ShellError::labeled_error( - "Expected a table with SQLite-compatible structure.tag() from pipeline", + "Expected a table with SQLite-compatible structure.span() from pipeline", "requires SQLite-compatible input", - name_tag, + name_span, )) }, } diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index cf8f2b11f3..e18e152363 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -75,7 +75,7 @@ fn collect_values(input: &Vec>) -> Result, ShellE fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_tag = args.name_tag(); + let name_span = args.name_span(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -93,21 +93,21 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { match toml::to_string(&toml_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).tagged(name_tag), + Value::Primitive(Primitive::String(x)).simple_spanned(name_span), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with TOML-compatible structure.tag() from pipeline", + "Expected a table with TOML-compatible structure.span() from pipeline", "requires TOML-compatible input", - name_tag, + name_span, "originates from here".to_string(), - value.tag(), + value.span(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with TOML-compatible structure from pipeline", "requires TOML-compatible input", - name_tag)) + name_span)) } } }; diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index 7bce174d01..a0cbf1c1bd 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -133,7 +133,7 @@ fn to_tsv( ToTSVArgs { headerless }: ToTSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_tag = name; + let name_span = name; let stream = async_stream_block! { let input: Vec> = input.values.collect().await; @@ -154,15 +154,15 @@ fn to_tsv( } else { x }; - yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag)) + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span)) } _ => { yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with TSV-compatible structure.tag() from pipeline", + "Expected a table with TSV-compatible structure.span() from pipeline", "requires TSV-compatible input", - name_tag, + name_span, "originates from here".to_string(), - value.tag(), + value.span(), )) } } diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index fc4412da01..915827252d 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -76,7 +76,7 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result Result { let args = args.evaluate_once(registry)?; - let name_tag = args.name_tag(); + let name_span = args.name_span(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -94,21 +94,21 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result { match serde_yaml::to_string(&yaml_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).tagged(name_tag), + Value::Primitive(Primitive::String(x)).simple_spanned(name_span), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with YAML-compatible structure.tag() from pipeline", + "Expected a table with YAML-compatible structure.span() from pipeline", "requires YAML-compatible input", - name_tag, + name_span, "originates from here".to_string(), - value.tag(), + value.span(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with YAML-compatible structure from pipeline", "requires YAML-compatible input", - name_tag)) + name_span)) } } }; diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 11ed025394..865f6b50bb 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::data::Value; use crate::errors::ShellError; +use crate::data::Value; use crate::prelude::*; pub struct Trim; @@ -34,7 +34,7 @@ fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result Result { let args = args.evaluate_once(registry)?; - let tag = args.call_info.name_tag; + let span = args.call_info.name_span; let mut indexmap = IndexMap::new(); indexmap.insert( "version".to_string(), - Value::string(clap::crate_version!()).tagged(tag), + Tagged::from_simple_spanned_item(Value::string(clap::crate_version!()), span), ); - let value = Value::Row(Dictionary::from(indexmap)).tagged(tag); + let value = Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span); Ok(OutputStream::one(value)) } diff --git a/src/commands/where_.rs b/src/commands/where_.rs index 673c6dda84..b09c341b4a 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,6 +1,6 @@ use crate::commands::PerItemCommand; use crate::errors::ShellError; -use crate::parser::hir::SyntaxShape; +use crate::parser::hir::SyntaxType; use crate::parser::registry; use crate::prelude::*; @@ -12,7 +12,8 @@ impl PerItemCommand for Where { } fn signature(&self) -> registry::Signature { - Signature::build("where").required("condition", SyntaxShape::Block) + Signature::build("where") + .required("condition", SyntaxType::Block) } fn usage(&self) -> &str { @@ -42,14 +43,16 @@ impl PerItemCommand for Where { VecDeque::new() } } - Err(e) => return Err(e), + Err(e) => { + return Err(e) + } } } Tagged { tag, .. } => { return Err(ShellError::labeled_error( "Expected a condition", "where needs a condition", - *tag, + tag.span, )) } }; diff --git a/src/commands/which_.rs b/src/commands/which_.rs index 905515848c..06c93f63f5 100644 --- a/src/commands/which_.rs +++ b/src/commands/which_.rs @@ -1,5 +1,5 @@ -use crate::data::Value; use crate::errors::ShellError; +use crate::data::Value; use crate::prelude::*; use crate::commands::WholeStreamCommand; @@ -13,7 +13,8 @@ impl WholeStreamCommand for Which { } fn signature(&self) -> Signature { - Signature::build("which").required("name", SyntaxShape::Any) + Signature::build("which") + .required("name", SyntaxType::Any) } fn usage(&self) -> &str { @@ -33,7 +34,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result 0 { @@ -52,7 +53,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result Result( &mut self, command: Arc, - name_tag: Tag, + name_span: Span, source_map: SourceMap, args: hir::Call, source: &Text, input: InputStream, ) -> OutputStream { - let command_args = self.command_args(args, input, source, source_map, name_tag); + let command_args = self.command_args(args, input, source, source_map, name_span); command.run(command_args, self.registry()) } @@ -135,13 +135,13 @@ impl Context { args: hir::Call, source: &Text, source_map: SourceMap, - name_tag: Tag, + name_span: Span, ) -> UnevaluatedCallInfo { UnevaluatedCallInfo { args, source: source.clone(), source_map, - name_tag, + name_span, } } @@ -151,12 +151,12 @@ impl Context { input: InputStream, source: &Text, source_map: SourceMap, - name_tag: Tag, + name_span: Span, ) -> CommandArgs { CommandArgs { host: self.host.clone(), shell_manager: self.shell_manager.clone(), - call_info: self.call_info(args, source, source_map, name_tag), + call_info: self.call_info(args, source, source_map, name_span), input, } } diff --git a/src/data/base.rs b/src/data/base.rs index fdd5542dc8..aa491ebdcb 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -145,7 +145,7 @@ pub struct Operation { pub struct Block { pub(crate) expressions: Vec, pub(crate) source: Text, - pub(crate) tag: Tag, + pub(crate) span: Span, } impl Block { @@ -153,7 +153,7 @@ impl Block { let scope = Scope::new(value.clone()); if self.expressions.len() == 0 { - return Ok(Value::nothing().tagged(self.tag)); + return Ok(Value::nothing().simple_spanned(self.span)); } let mut last = None; @@ -247,7 +247,7 @@ impl std::convert::TryFrom<&Tagged> for Block { Value::Block(block) => Ok(block.clone()), v => Err(ShellError::type_error( "Block", - value.copy_tag(v.type_name()), + value.copy_span(v.type_name()), )), } } @@ -263,7 +263,7 @@ impl std::convert::TryFrom<&Tagged> for i64 { } v => Err(ShellError::type_error( "Integer", - value.copy_tag(v.type_name()), + value.copy_span(v.type_name()), )), } } @@ -277,7 +277,7 @@ impl std::convert::TryFrom<&Tagged> for String { Value::Primitive(Primitive::String(s)) => Ok(s.clone()), v => Err(ShellError::type_error( "String", - value.copy_tag(v.type_name()), + value.copy_span(v.type_name()), )), } } @@ -291,7 +291,7 @@ impl std::convert::TryFrom<&Tagged> for Vec { Value::Binary(b) => Ok(b.clone()), v => Err(ShellError::type_error( "Binary", - value.copy_tag(v.type_name()), + value.copy_span(v.type_name()), )), } } @@ -305,7 +305,7 @@ impl<'a> std::convert::TryFrom<&'a Tagged> for &'a crate::data::Dictionar Value::Row(d) => Ok(d), v => Err(ShellError::type_error( "Dictionary", - value.copy_tag(v.type_name()), + value.copy_span(v.type_name()), )), } } @@ -327,7 +327,7 @@ impl std::convert::TryFrom>> for Switch { Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present), v => Err(ShellError::type_error( "Boolean", - value.copy_tag(v.type_name()), + value.copy_span(v.type_name()), )), }, } @@ -639,7 +639,7 @@ impl Tagged { Value::Primitive(Primitive::Path(path)) => Ok(path.clone()), other => Err(ShellError::type_error( "Path", - other.type_name().tagged(self.tag()), + other.type_name().tagged(self.span()), )), } } diff --git a/src/data/config.rs b/src/data/config.rs index 6b4d1383f5..08296c09a6 100644 --- a/src/data/config.rs +++ b/src/data/config.rs @@ -50,7 +50,7 @@ pub fn default_path_for(file: &Option) -> Result { } pub fn read( - tag: impl Into, + span: impl Into, at: &Option, ) -> Result>, ShellError> { let filename = default_path()?; @@ -64,15 +64,15 @@ pub fn read( trace!("config file = {}", filename.display()); - let tag = tag.into(); + let span = span.into(); let contents = fs::read_to_string(filename) - .map(|v| v.tagged(tag)) + .map(|v| v.simple_spanned(span)) .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?; let parsed: toml::Value = toml::from_str(&contents) .map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?; - let value = convert_toml_value_to_nu_value(&parsed, tag); + let value = convert_toml_value_to_nu_value(&parsed, Tag::unknown_origin(span)); let tag = value.tag(); match value.item { Value::Row(Dictionary { entries }) => Ok(entries), @@ -83,8 +83,8 @@ pub fn read( } } -pub(crate) fn config(tag: impl Into) -> Result>, ShellError> { - read(tag, &None) +pub(crate) fn config(span: impl Into) -> Result>, ShellError> { + read(span, &None) } pub fn write( diff --git a/src/data/into.rs b/src/data/into.rs index 3d764fa0c1..749ab0601f 100644 --- a/src/data/into.rs +++ b/src/data/into.rs @@ -15,8 +15,8 @@ impl From for Value { impl> Tagged { pub fn into_tagged_value(self) -> Tagged { - let value_tag = self.tag(); + let value_span = self.span(); let value = self.item.into(); - value.tagged(value_tag) + value.simple_spanned(value_span) } } diff --git a/src/data/meta.rs b/src/data/meta.rs index d472a8add9..f1d2b6713d 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -5,7 +5,6 @@ use derive_new::new; use getset::Getters; use serde::Deserialize; use serde::Serialize; -use std::path::{Path, PathBuf}; use uuid::Uuid; #[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] @@ -14,15 +13,9 @@ pub struct Tagged { pub item: T, } -impl HasTag for Tagged { - fn tag(&self) -> Tag { - self.tag - } -} - -impl AsRef for Tagged { - fn as_ref(&self) -> &Path { - self.item.as_ref() +impl HasSpan for Tagged { + fn span(&self) -> Span { + self.tag.span } } @@ -31,6 +24,10 @@ pub trait TaggedItem: Sized { Tagged::from_item(self, tag.into()) } + fn simple_spanned(self, span: impl Into) -> Tagged { + Tagged::from_simple_spanned_item(self, span.into()) + } + // For now, this is a temporary facility. In many cases, there are other useful spans that we // could be using, such as the original source spans of JSON or Toml files, but we don't yet // have the infrastructure to make that work. @@ -56,8 +53,14 @@ impl std::ops::Deref for Tagged { } impl Tagged { - pub fn with_tag(self, tag: impl Into) -> Tagged { - Tagged::from_item(self.item, tag) + pub fn spanned(self, span: impl Into) -> Tagged { + Tagged::from_item( + self.item, + Tag { + span: span.into(), + origin: None, + }, + ) } pub fn from_item(item: T, tag: impl Into) -> Tagged { @@ -67,26 +70,41 @@ impl Tagged { } } + pub fn from_simple_spanned_item(item: T, span: impl Into) -> Tagged { + Tagged::from_item( + item, + Tag { + span: span.into(), + origin: None, + }, + ) + } + pub fn map(self, input: impl FnOnce(T) -> U) -> Tagged { let tag = self.tag(); let mapped = input(self.item); - Tagged::from_item(mapped, tag) + Tagged::from_item(mapped, tag.clone()) } - pub(crate) fn copy_tag(&self, output: U) -> Tagged { - Tagged::from_item(output, self.tag()) + pub(crate) fn copy_span(&self, output: U) -> Tagged { + let span = self.span(); + + Tagged::from_simple_spanned_item(output, span) } pub fn source(&self, source: &Text) -> Text { - Text::from(self.tag().slice(source)) + Text::from(self.span().slice(source)) + } + + pub fn span(&self) -> Span { + self.tag.span } pub fn tag(&self) -> Tag { self.tag } - // TODO: This should not be optional pub fn origin(&self) -> Option { self.tag.origin } @@ -108,14 +126,20 @@ impl Tagged { } } -impl From<&Tag> for Tag { - fn from(input: &Tag) -> Tag { +impl From<&Tagged> for Span { + fn from(input: &Tagged) -> Span { + input.span() + } +} + +impl From<&Span> for Span { + fn from(input: &Span) -> Span { *input } } -impl From> for Span { - fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Span { +impl From> for Span { + fn from(input: nom5_locate::LocatedSpan<&str>) -> Span { Span { start: input.offset, end: input.offset + input.fragment.len(), @@ -123,18 +147,8 @@ impl From> for Span { } } -impl - From<( - nom_locate::LocatedSpanEx, - nom_locate::LocatedSpanEx, - )> for Span -{ - fn from( - input: ( - nom_locate::LocatedSpanEx, - nom_locate::LocatedSpanEx, - ), - ) -> Span { +impl From<(nom5_locate::LocatedSpan, nom5_locate::LocatedSpan)> for Span { + fn from(input: (nom5_locate::LocatedSpan, nom5_locate::LocatedSpan)) -> Span { Span { start: input.0.offset, end: input.1.offset, @@ -183,36 +197,6 @@ impl From<&Span> for Tag { } } -impl From<(usize, usize, Uuid)> for Tag { - fn from((start, end, origin): (usize, usize, Uuid)) -> Self { - Tag { - origin: Some(origin), - span: Span { start, end }, - } - } -} - -impl From<(usize, usize, Option)> for Tag { - fn from((start, end, origin): (usize, usize, Option)) -> Self { - Tag { - origin, - span: Span { start, end }, - } - } -} - -impl From> for Tag { - fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Tag { - Tag { - origin: Some(input.extra), - span: Span { - start: input.offset, - end: input.offset + input.fragment.len(), - }, - } - } -} - impl From for Span { fn from(tag: Tag) -> Self { tag.span @@ -230,36 +214,12 @@ impl Tag { Tag { origin: None, span } } - pub fn unknown_span(origin: Uuid) -> Tag { - Tag { - origin: Some(origin), - span: Span::unknown(), - } - } - pub fn unknown() -> Tag { Tag { origin: None, span: Span::unknown(), } } - - pub fn until(&self, other: impl Into) -> Tag { - let other = other.into(); - debug_assert!(self.origin == other.origin, "Can only merge two tags with the same origin"); - - Tag { - span: Span { - start: self.span.start, - end: other.span.end - }, - origin: self.origin - } - } - - pub fn slice<'a>(&self, source: &'a str) -> &'a str { - self.span.slice(source) - } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)] @@ -324,33 +284,3 @@ impl language_reporting::ReportingSpan for Span { self.end } } - -impl language_reporting::ReportingSpan for Tag { - fn with_start(&self, start: usize) -> Self { - Tag { - span: Span { - start, - end: self.span.end, - }, - origin: self.origin, - } - } - - fn with_end(&self, end: usize) -> Self { - Tag { - span: Span { - start: self.span.start, - end, - }, - origin: self.origin, - } - } - - fn start(&self) -> usize { - self.span.start - } - - fn end(&self) -> usize { - self.span.end - } -} diff --git a/src/errors.rs b/src/errors.rs index 3dfa644d4c..d97435d226 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -14,22 +14,20 @@ pub enum Description { impl Description { pub fn from(value: Tagged>) -> Description { + let value_span = value.span(); let value_tag = value.tag(); - match value_tag { - Tag { - span: crate::data::meta::Span { start: 0, end: 0 }, - .. - } => Description::Synthetic(value.item.into()), + match value_span { + Span { start: 0, end: 0 } => Description::Synthetic(value.item.into()), _ => Description::Source(Tagged::from_item(value.item.into(), value_tag)), } } } impl Description { - fn into_label(self) -> Result, String> { + fn into_label(self) -> Result, String> { match self { - Description::Source(s) => Ok(Label::new_primary(s.tag()).with_message(s.item)), + Description::Source(s) => Ok(Label::new_primary(s.span()).with_message(s.item)), Description::Synthetic(s) => Err(s), } } @@ -83,7 +81,7 @@ impl ShellError { ) -> ShellError { ProximateShellError::RangeError { kind: expected.into(), - actual_kind: actual.copy_tag(format!("{:?}", actual.item)), + actual_kind: actual.copy_span(format!("{:?}", actual.item)), operation, } .start() @@ -118,9 +116,9 @@ impl ShellError { ProximateShellError::MissingProperty { subpath, expr }.start() } - pub(crate) fn missing_value(tag: Option, reason: impl Into) -> ShellError { + pub(crate) fn missing_value(span: Option, reason: impl Into) -> ShellError { ProximateShellError::MissingValue { - tag, + span, reason: reason.into(), } .start() @@ -129,31 +127,28 @@ impl ShellError { pub(crate) fn argument_error( command: impl Into, kind: ArgumentError, - tag: Tag, + span: Span, ) -> ShellError { ProximateShellError::ArgumentError { command: command.into(), error: kind, - tag, + span, } .start() } - pub(crate) fn invalid_external_word(tag: Tag) -> ShellError { + pub(crate) fn invalid_external_word(span: Span) -> ShellError { ProximateShellError::ArgumentError { command: "Invalid argument to Nu command (did you mean to call an external command?)" .into(), error: ArgumentError::InvalidExternalWord, - tag, + span, } .start() } pub(crate) fn parse_error( - error: nom::Err<( - nom_locate::LocatedSpanEx<&str, uuid::Uuid>, - nom::error::ErrorKind, - )>, + error: nom::Err<(nom5_locate::LocatedSpan<&str>, nom::error::ErrorKind)>, ) -> ShellError { use language_reporting::*; @@ -169,34 +164,34 @@ impl ShellError { } nom::Err::Failure(span) | nom::Err::Error(span) => { let diagnostic = Diagnostic::new(Severity::Error, format!("Parse Error")) - .with_label(Label::new_primary(Tag::from(span.0))); + .with_label(Label::new_primary(Span::from(span.0))); ShellError::diagnostic(diagnostic) } } } - pub(crate) fn diagnostic(diagnostic: Diagnostic) -> ShellError { + pub(crate) fn diagnostic(diagnostic: Diagnostic) -> ShellError { ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start() } - pub(crate) fn to_diagnostic(self) -> Diagnostic { + pub(crate) fn to_diagnostic(self) -> Diagnostic { match self.error { ProximateShellError::String(StringError { title, .. }) => { Diagnostic::new(Severity::Error, title) } ProximateShellError::InvalidCommand { command } => { Diagnostic::new(Severity::Error, "Invalid command") - .with_label(Label::new_primary(command)) + .with_label(Label::new_primary(command.span)) } - ProximateShellError::MissingValue { tag, reason } => { + ProximateShellError::MissingValue { span, reason } => { let mut d = Diagnostic::new( Severity::Bug, format!("Internal Error (missing value) :: {}", reason), ); - if let Some(tag) = tag { - d = d.with_label(Label::new_primary(tag)); + if let Some(span) = span { + d = d.with_label(Label::new_primary(span)); } d @@ -204,12 +199,12 @@ impl ShellError { ProximateShellError::ArgumentError { command, error, - tag, + span, } => match error { ArgumentError::InvalidExternalWord => Diagnostic::new( Severity::Error, format!("Invalid bare word for Nu command (did you intend to invoke an external command?)")) - .with_label(Label::new_primary(tag)), + .with_label(Label::new_primary(span)), ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new( Severity::Error, format!( @@ -219,7 +214,7 @@ impl ShellError { Color::Black.bold().paint(name) ), ) - .with_label(Label::new_primary(tag)), + .with_label(Label::new_primary(span)), ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new( Severity::Error, format!( @@ -229,7 +224,7 @@ impl ShellError { ), ) .with_label( - Label::new_primary(tag).with_message(format!("requires {} parameter", name)), + Label::new_primary(span).with_message(format!("requires {} parameter", name)), ), ArgumentError::MissingValueForName(name) => Diagnostic::new( Severity::Error, @@ -240,17 +235,17 @@ impl ShellError { Color::Black.bold().paint(name) ), ) - .with_label(Label::new_primary(tag)), + .with_label(Label::new_primary(span)), }, ProximateShellError::TypeError { expected, actual: Tagged { item: Some(actual), - tag, + tag: Tag { span, .. }, }, } => Diagnostic::new(Severity::Error, "Type Error").with_label( - Label::new_primary(tag) + Label::new_primary(span) .with_message(format!("Expected {}, found {}", expected, actual)), ), @@ -259,10 +254,10 @@ impl ShellError { actual: Tagged { item: None, - tag + tag: Tag { span, .. }, }, } => Diagnostic::new(Severity::Error, "Type Error") - .with_label(Label::new_primary(tag).with_message(expected)), + .with_label(Label::new_primary(span).with_message(expected)), ProximateShellError::RangeError { kind, @@ -270,10 +265,10 @@ impl ShellError { actual_kind: Tagged { item, - tag + tag: Tag { span, .. }, }, } => Diagnostic::new(Severity::Error, "Range Error").with_label( - Label::new_primary(tag).with_message(format!( + Label::new_primary(span).with_message(format!( "Expected to convert {} to {} while {}, but it was out of range", item, kind.desc(), @@ -284,11 +279,11 @@ impl ShellError { ProximateShellError::SyntaxError { problem: Tagged { - tag, + tag: Tag { span, .. }, .. }, } => Diagnostic::new(Severity::Error, "Syntax Error") - .with_label(Label::new_primary(tag).with_message("Unexpected external command")), + .with_label(Label::new_primary(span).with_message("Unexpected external command")), ProximateShellError::MissingProperty { subpath, expr } => { let subpath = subpath.into_label(); @@ -311,8 +306,8 @@ impl ShellError { ProximateShellError::Diagnostic(diag) => diag.diagnostic, ProximateShellError::CoerceError { left, right } => { Diagnostic::new(Severity::Error, "Coercion error") - .with_label(Label::new_primary(left.tag()).with_message(left.item)) - .with_label(Label::new_secondary(right.tag()).with_message(right.item)) + .with_label(Label::new_primary(left.span()).with_message(left.item)) + .with_label(Label::new_secondary(right.span()).with_message(right.item)) } } } @@ -320,29 +315,26 @@ impl ShellError { pub fn labeled_error( msg: impl Into, label: impl Into, - tag: impl Into, + span: impl Into, ) -> ShellError { ShellError::diagnostic( Diagnostic::new(Severity::Error, msg.into()) - .with_label(Label::new_primary(tag.into()).with_message(label.into())), + .with_label(Label::new_primary(span.into()).with_message(label.into())), ) } pub fn labeled_error_with_secondary( msg: impl Into, primary_label: impl Into, - primary_span: impl Into, + primary_span: Span, secondary_label: impl Into, - secondary_span: impl Into, + secondary_span: Span, ) -> ShellError { ShellError::diagnostic( Diagnostic::new_error(msg.into()) + .with_label(Label::new_primary(primary_span).with_message(primary_label.into())) .with_label( - Label::new_primary(primary_span.into()).with_message(primary_label.into()), - ) - .with_label( - Label::new_secondary(secondary_span.into()) - .with_message(secondary_label.into()), + Label::new_secondary(secondary_span).with_message(secondary_label.into()), ), ) } @@ -417,13 +409,13 @@ pub enum ProximateShellError { expr: Description, }, MissingValue { - tag: Option, + span: Option, reason: String, }, ArgumentError { command: String, error: ArgumentError, - tag: Tag, + span: Span, }, RangeError { kind: ExpectedRange, @@ -455,7 +447,7 @@ impl ToDebug for ProximateShellError { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ShellDiagnostic { - pub(crate) diagnostic: Diagnostic, + pub(crate) diagnostic: Diagnostic, } impl PartialEq for ShellDiagnostic { diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index b9db82b546..52edf69818 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -38,13 +38,13 @@ pub(crate) fn evaluate_baseline_expr( source: &Text, ) -> Result, ShellError> { match &expr.item { - RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_tag(literal), source)), + RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(literal), source)), RawExpression::ExternalWord => Err(ShellError::argument_error( "Invalid external word", ArgumentError::InvalidExternalWord, - expr.tag(), + expr.span(), )), - RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.tag())), + 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), @@ -53,10 +53,13 @@ pub(crate) fn evaluate_baseline_expr( let right = evaluate_baseline_expr(binary.right(), registry, scope, source)?; match left.compare(binary.op(), &*right) { - Ok(result) => Ok(Value::boolean(result).tagged(expr.tag())), + Ok(result) => Ok(Tagged::from_simple_spanned_item( + Value::boolean(result), + expr.span(), + )), Err((left_type, right_type)) => Err(ShellError::coerce_error( - binary.left().copy_tag(left_type), - binary.right().copy_tag(right_type), + binary.left().copy_span(left_type), + binary.right().copy_span(right_type), )), } } @@ -68,14 +71,12 @@ pub(crate) fn evaluate_baseline_expr( exprs.push(expr); } - Ok(Value::Table(exprs).tagged(expr.tag())) - } - RawExpression::Block(block) => { - Ok( - Value::Block(Block::new(block.clone(), source.clone(), expr.tag())) - .tagged(expr.tag()), - ) + Ok(Value::Table(exprs).tagged(Tag::unknown_origin(expr.span()))) } + RawExpression::Block(block) => Ok(Tagged::from_simple_spanned_item( + Value::Block(Block::new(block.clone(), source.clone(), expr.span())), + expr.span(), + )), RawExpression::Path(path) => { let value = evaluate_baseline_expr(path.head(), registry, scope, source)?; let mut item = value; @@ -91,12 +92,18 @@ pub(crate) fn evaluate_baseline_expr( )) } Some(next) => { - item = next.clone().item.tagged(expr.tag()); + item = Tagged::from_simple_spanned_item( + next.clone().item, + (expr.span().start, name.span().end), + ) } }; } - Ok(item.item().clone().tagged(expr.tag())) + Ok(Tagged::from_simple_spanned_item( + item.item().clone(), + expr.span(), + )) } RawExpression::Boolean(_boolean) => unimplemented!(), } @@ -106,9 +113,9 @@ fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged int.into(), hir::Literal::Size(int, unit) => unit.compute(int), - hir::Literal::String(tag) => Value::string(tag.slice(source)), - hir::Literal::GlobPattern => Value::pattern(literal.tag().slice(source)), - hir::Literal::Bare => Value::string(literal.tag().slice(source)), + hir::Literal::String(span) => Value::string(span.slice(source)), + hir::Literal::GlobPattern => Value::pattern(literal.span().slice(source)), + hir::Literal::Bare => Value::string(literal.span().slice(source)), }; literal.map(|_| result) @@ -120,12 +127,12 @@ fn evaluate_reference( source: &Text, ) -> Result, ShellError> { match name { - hir::Variable::It(tag) => Ok(scope.it.item.clone().tagged(*tag)), - hir::Variable::Other(tag) => Ok(scope + hir::Variable::It(span) => Ok(scope.it.item.clone().simple_spanned(span)), + hir::Variable::Other(span) => Ok(scope .vars - .get(tag.slice(source)) + .get(span.slice(source)) .map(|v| v.clone()) - .unwrap_or_else(|| Value::nothing().tagged(*tag))), + .unwrap_or_else(|| Value::nothing().simple_spanned(span))), } } @@ -135,6 +142,6 @@ fn evaluate_external( _source: &Text, ) -> Result, ShellError> { Err(ShellError::syntax_error( - "Unexpected external command".tagged(*external.name()), + "Unexpected external command".tagged(external.name()), )) } diff --git a/src/format/table.rs b/src/format/table.rs index 286be222c3..717e3c4a27 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -204,7 +204,7 @@ impl RenderView for TableView { let mut table = Table::new(); - let table_mode = crate::data::config::config(Tag::unknown())? + let table_mode = crate::data::config::config(Span::unknown())? .get("table_mode") .map(|s| match s.as_string().unwrap().as_ref() { "light" => TableMode::Light, diff --git a/src/lib.rs b/src/lib.rs index 60546e3854..a18343df9b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ mod utils; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::context::{SourceMap, SpanSource}; pub use crate::env::host::BasicHost; -pub use crate::parser::hir::SyntaxShape; +pub use crate::parser::hir::SyntaxType; pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder; pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; @@ -31,7 +31,7 @@ pub use cli::cli; pub use data::config::{APP_INFO, config_path}; pub use data::base::{Primitive, Value}; pub use data::dict::{Dictionary, TaggedDictBuilder}; -pub use data::meta::{Tag, Tagged, TaggedItem}; +pub use data::meta::{Span, Tag, Tagged, TaggedItem}; pub use errors::{CoerceInto, ShellError}; pub use num_traits::cast::ToPrimitive; pub use parser::parse::text::Text; diff --git a/src/parser.rs b/src/parser.rs index 3b3ac1a136..2fd891efb0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -21,10 +21,10 @@ pub(crate) use parse::unit::Unit; pub(crate) use parse_command::parse_command; pub(crate) use registry::CommandRegistry; -pub fn parse(input: &str, origin: uuid::Uuid) -> Result { +pub fn parse(input: &str) -> Result { let _ = pretty_env_logger::try_init(); - match pipeline(nom_input(input, origin)) { + match pipeline(nom_input(input)) { Ok((_rest, val)) => Ok(val), Err(err) => Err(ShellError::parse_error(err)), } diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs index f9b9146e50..d5427766b5 100644 --- a/src/parser/deserializer.rs +++ b/src/parser/deserializer.rs @@ -37,7 +37,7 @@ impl<'de> ConfigDeserializer<'de> { let value: Option> = if name == "rest" { let positional = self.call.args.slice_from(self.position); self.position += positional.len(); - Some(Value::Table(positional).tagged_unknown()) // TODO: correct tag + Some(Value::Table(positional).tagged_unknown()) // TODO: correct span } else { if self.call.args.has(name) { self.call.args.get(name).map(|x| x.clone()) @@ -52,7 +52,9 @@ impl<'de> ConfigDeserializer<'de> { self.stack.push(DeserializerItem { key_struct_field: Some((name.to_string(), name)), - val: value.unwrap_or_else(|| Value::nothing().tagged(self.call.name_tag)), + val: value.unwrap_or_else(|| { + Value::nothing().tagged(Tag::unknown_origin(self.call.name_span)) + }), }); Ok(()) diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 96eb7272a6..90bb38796a 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -25,7 +25,7 @@ pub(crate) use self::external_command::ExternalCommand; pub(crate) use self::named::NamedArguments; pub(crate) use self::path::Path; -pub use self::baseline_parse_tokens::SyntaxShape; +pub use self::baseline_parse_tokens::SyntaxType; pub fn path(head: impl Into, tail: Vec>>) -> Path { Path::new( @@ -131,57 +131,72 @@ impl RawExpression { pub type Expression = Tagged; impl Expression { - pub(crate) fn number(i: impl Into, tag: impl Into) -> Expression { - RawExpression::Literal(Literal::Number(i.into())).tagged(tag.into()) + pub(crate) fn number(i: impl Into, span: impl Into) -> Expression { + Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Number(i.into())), span) } pub(crate) fn size( i: impl Into, unit: impl Into, - tag: impl Into, + span: impl Into, ) -> Expression { - RawExpression::Literal(Literal::Size(i.into(), unit.into())).tagged(tag.into()) + Tagged::from_simple_spanned_item( + RawExpression::Literal(Literal::Size(i.into(), unit.into())), + span, + ) } pub(crate) fn synthetic_string(s: impl Into) -> Expression { RawExpression::Synthetic(Synthetic::String(s.into())).tagged_unknown() } - pub(crate) fn string(inner: impl Into, outer: impl Into) -> Expression { - RawExpression::Literal(Literal::String(inner.into())).tagged(outer.into()) + pub(crate) fn string(inner: impl Into, outer: impl Into) -> Expression { + Tagged::from_simple_spanned_item( + RawExpression::Literal(Literal::String(inner.into())), + outer.into(), + ) } - pub(crate) fn file_path(path: impl Into, outer: impl Into) -> Expression { - RawExpression::FilePath(path.into()).tagged(outer) + pub(crate) fn file_path(path: impl Into, outer: impl Into) -> Expression { + Tagged::from_simple_spanned_item(RawExpression::FilePath(path.into()), outer.into()) } - pub(crate) fn bare(tag: impl Into) -> Expression { - RawExpression::Literal(Literal::Bare).tagged(tag) + pub(crate) fn bare(span: impl Into) -> Expression { + Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into()) } pub(crate) fn pattern(tag: impl Into) -> Expression { RawExpression::Literal(Literal::GlobPattern).tagged(tag.into()) } - pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { - RawExpression::Variable(Variable::Other(inner.into())).tagged(outer) + pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { + Tagged::from_simple_spanned_item( + RawExpression::Variable(Variable::Other(inner.into())), + outer.into(), + ) } - pub(crate) fn external_command(inner: impl Into, outer: impl Into) -> Expression { - RawExpression::ExternalCommand(ExternalCommand::new(inner.into())).tagged(outer) + pub(crate) fn external_command(inner: impl Into, outer: impl Into) -> Expression { + Tagged::from_simple_spanned_item( + RawExpression::ExternalCommand(ExternalCommand::new(inner.into())), + outer.into(), + ) } - pub(crate) fn it_variable(inner: impl Into, outer: impl Into) -> Expression { - RawExpression::Variable(Variable::It(inner.into())).tagged(outer) + pub(crate) fn it_variable(inner: impl Into, outer: impl Into) -> Expression { + Tagged::from_simple_spanned_item( + RawExpression::Variable(Variable::It(inner.into())), + outer.into(), + ) } } impl ToDebug for Expression { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { match self.item() { - RawExpression::Literal(l) => l.tagged(self.tag()).fmt_debug(f, source), + RawExpression::Literal(l) => l.tagged(self.span()).fmt_debug(f, source), RawExpression::FilePath(p) => write!(f, "{}", p.display()), - RawExpression::ExternalWord => write!(f, "{}", self.tag().slice(source)), + RawExpression::ExternalWord => write!(f, "{}", self.span().slice(source)), 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)), @@ -227,7 +242,7 @@ impl From> for Expression { pub enum Literal { Number(Number), Size(Number, Unit), - String(Tag), + String(Span), GlobPattern, Bare, } @@ -237,9 +252,9 @@ impl ToDebug for Tagged<&Literal> { match self.item() { Literal::Number(number) => write!(f, "{:?}", *number), Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), - Literal::String(tag) => write!(f, "{}", tag.slice(source)), - Literal::GlobPattern => write!(f, "{}", self.tag().slice(source)), - Literal::Bare => write!(f, "{}", self.tag().slice(source)), + Literal::String(span) => write!(f, "{}", span.slice(source)), + Literal::GlobPattern => write!(f, "{}", self.span().slice(source)), + Literal::Bare => write!(f, "{}", self.span().slice(source)), } } } @@ -258,6 +273,6 @@ impl Literal { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Variable { - It(Tag), - Other(Tag), + It(Span), + Other(Span), } diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index 267494f27c..5248bde5f9 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -10,19 +10,19 @@ pub fn baseline_parse_single_token( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()), + RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), RawToken::Size(int, unit) => { - hir::Expression::size(int.to_number(source), unit, token.tag()) + hir::Expression::size(int.to_number(source), unit, token.span()) } - RawToken::String(tag) => hir::Expression::string(tag, token.tag()), - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) + RawToken::String(span) => hir::Expression::string(span, token.span()), + RawToken::Variable(span) if span.slice(source) == "it" => { + hir::Expression::it_variable(span, token.span()) } - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::GlobPattern => hir::Expression::pattern(token.tag()), - RawToken::Bare => hir::Expression::bare(token.tag()), + RawToken::Variable(span) => hir::Expression::variable(span, token.span()), + RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), + RawToken::GlobPattern => hir::Expression::pattern(token.span()), + RawToken::Bare => hir::Expression::bare(token.span()), }) } @@ -31,24 +31,24 @@ pub fn baseline_parse_token_as_number( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) + RawToken::Variable(span) if span.slice(source) == "it" => { + hir::Expression::it_variable(span, token.span()) } - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()), + RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), + RawToken::Variable(span) => hir::Expression::variable(span, token.span()), + RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), RawToken::Size(number, unit) => { - hir::Expression::size(number.to_number(source), unit, token.tag()) + hir::Expression::size(number.to_number(source), unit, token.span()) } - RawToken::Bare => hir::Expression::bare(token.tag()), + RawToken::Bare => hir::Expression::bare(token.span()), RawToken::GlobPattern => { return Err(ShellError::type_error( "Number", "glob pattern".to_string().tagged(token.tag()), )) } - RawToken::String(tag) => hir::Expression::string(tag, token.tag()), + RawToken::String(span) => hir::Expression::string(span, token.span()), }) } @@ -57,22 +57,22 @@ pub fn baseline_parse_token_as_string( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) + RawToken::Variable(span) if span.slice(source) == "it" => { + hir::Expression::it_variable(span, token.span()) } - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::Number(_) => hir::Expression::bare(token.tag()), - RawToken::Size(_, _) => hir::Expression::bare(token.tag()), - RawToken::Bare => hir::Expression::bare(token.tag()), + RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), + RawToken::Variable(span) => hir::Expression::variable(span, token.span()), + RawToken::Number(_) => hir::Expression::bare(token.span()), + RawToken::Size(_, _) => hir::Expression::bare(token.span()), + RawToken::Bare => hir::Expression::bare(token.span()), RawToken::GlobPattern => { return Err(ShellError::type_error( "String", "glob pattern".tagged(token.tag()), )) } - RawToken::String(tag) => hir::Expression::string(tag, token.tag()), + RawToken::String(span) => hir::Expression::string(span, token.span()), }) } @@ -82,25 +82,26 @@ pub fn baseline_parse_token_as_path( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) - } - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::Number(_) => hir::Expression::bare(token.tag()), - RawToken::Size(_, _) => hir::Expression::bare(token.tag()), - RawToken::Bare => { - hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag()) + RawToken::Variable(span) if span.slice(source) == "it" => { + hir::Expression::it_variable(span, token.span()) } + RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), + RawToken::Variable(span) => hir::Expression::variable(span, token.span()), + RawToken::Number(_) => 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::GlobPattern => { return Err(ShellError::type_error( "Path", "glob pattern".tagged(token.tag()), )) } - RawToken::String(tag) => { - hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag()) + RawToken::String(span) => { + hir::Expression::file_path(expand_path(span.slice(source), context), token.span()) } }) } @@ -111,24 +112,25 @@ pub fn baseline_parse_token_as_pattern( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) + RawToken::Variable(span) if span.slice(source) == "it" => { + hir::Expression::it_variable(span, token.span()) } RawToken::ExternalCommand(_) => { return Err(ShellError::syntax_error( "Invalid external command".to_string().tagged(token.tag()), )) } - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::Number(_) => hir::Expression::bare(token.tag()), - RawToken::Size(_, _) => hir::Expression::bare(token.tag()), - RawToken::GlobPattern => hir::Expression::pattern(token.tag()), - RawToken::Bare => { - hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag()) - } - RawToken::String(tag) => { - hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag()) + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), + RawToken::Variable(span) => hir::Expression::variable(span, token.span()), + RawToken::Number(_) => hir::Expression::bare(token.span()), + RawToken::Size(_, _) => hir::Expression::bare(token.span()), + RawToken::GlobPattern => hir::Expression::pattern(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()) } }) } diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index 8413bd07e1..ac2c703d3a 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -8,7 +8,7 @@ use crate::parser::{ }, DelimitedNode, Delimiter, PathNode, RawToken, TokenNode, }; -use crate::{Tag, Tagged, TaggedItem, Text}; +use crate::{Span, Tag, Tagged, TaggedItem, Text}; use derive_new::new; use log::trace; use serde::{Deserialize, Serialize}; @@ -17,7 +17,7 @@ pub fn baseline_parse_tokens( token_nodes: &mut TokensIterator<'_>, context: &Context, source: &Text, - syntax_type: SyntaxShape, + syntax_type: SyntaxType, ) -> Result, ShellError> { let mut exprs: Vec = vec![]; @@ -34,7 +34,7 @@ pub fn baseline_parse_tokens( } #[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub enum SyntaxShape { +pub enum SyntaxType { Any, List, Literal, @@ -49,21 +49,21 @@ pub enum SyntaxShape { Boolean, } -impl std::fmt::Display for SyntaxShape { +impl std::fmt::Display for SyntaxType { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - SyntaxShape::Any => write!(f, "Any"), - SyntaxShape::List => write!(f, "List"), - SyntaxShape::Literal => write!(f, "Literal"), - SyntaxShape::String => write!(f, "String"), - SyntaxShape::Member => write!(f, "Member"), - SyntaxShape::Variable => write!(f, "Variable"), - SyntaxShape::Number => write!(f, "Number"), - SyntaxShape::Path => write!(f, "Path"), - SyntaxShape::Pattern => write!(f, "Pattern"), - SyntaxShape::Binary => write!(f, "Binary"), - SyntaxShape::Block => write!(f, "Block"), - SyntaxShape::Boolean => write!(f, "Boolean"), + SyntaxType::Any => write!(f, "Any"), + SyntaxType::List => write!(f, "List"), + SyntaxType::Literal => write!(f, "Literal"), + SyntaxType::String => write!(f, "String"), + SyntaxType::Member => write!(f, "Member"), + SyntaxType::Variable => write!(f, "Variable"), + SyntaxType::Number => write!(f, "Number"), + SyntaxType::Path => write!(f, "Path"), + SyntaxType::Pattern => write!(f, "Pattern"), + SyntaxType::Binary => write!(f, "Binary"), + SyntaxType::Block => write!(f, "Block"), + SyntaxType::Boolean => write!(f, "Boolean"), } } } @@ -72,7 +72,7 @@ pub fn baseline_parse_next_expr( tokens: &mut TokensIterator, context: &Context, source: &Text, - syntax_type: SyntaxShape, + syntax_type: SyntaxType, ) -> Result { let next = tokens .next() @@ -81,69 +81,69 @@ pub fn baseline_parse_next_expr( trace!(target: "nu::parser::parse_one_expr", "syntax_type={:?}, token={:?}", syntax_type, next); match (syntax_type, next) { - (SyntaxShape::Path, TokenNode::Token(token)) => { + (SyntaxType::Path, TokenNode::Token(token)) => { return baseline_parse_token_as_path(token, context, source) } - (SyntaxShape::Path, token) => { + (SyntaxType::Path, token) => { return Err(ShellError::type_error( "Path", - token.type_name().tagged(token.tag()), + token.type_name().simple_spanned(token.span()), )) } - (SyntaxShape::Pattern, TokenNode::Token(token)) => { + (SyntaxType::Pattern, TokenNode::Token(token)) => { return baseline_parse_token_as_pattern(token, context, source) } - (SyntaxShape::Pattern, token) => { + (SyntaxType::Pattern, token) => { return Err(ShellError::type_error( "Path", - token.type_name().tagged(token.tag()), + token.type_name().simple_spanned(token.span()), )) } - (SyntaxShape::String, TokenNode::Token(token)) => { + (SyntaxType::String, TokenNode::Token(token)) => { return baseline_parse_token_as_string(token, source); } - (SyntaxShape::String, token) => { + (SyntaxType::String, token) => { return Err(ShellError::type_error( "String", - token.type_name().tagged(token.tag()), + token.type_name().simple_spanned(token.span()), )) } - (SyntaxShape::Number, TokenNode::Token(token)) => { + (SyntaxType::Number, TokenNode::Token(token)) => { return Ok(baseline_parse_token_as_number(token, source)?); } - (SyntaxShape::Number, token) => { + (SyntaxType::Number, token) => { return Err(ShellError::type_error( "Numeric", - token.type_name().tagged(token.tag()), + token.type_name().simple_spanned(token.span()), )) } // TODO: More legit member processing - (SyntaxShape::Member, TokenNode::Token(token)) => { + (SyntaxType::Member, TokenNode::Token(token)) => { return baseline_parse_token_as_string(token, source); } - (SyntaxShape::Member, token) => { + (SyntaxType::Member, token) => { return Err(ShellError::type_error( "member", - token.type_name().tagged(token.tag()), + token.type_name().simple_spanned(token.span()), )) } - (SyntaxShape::Any, _) => {} - (SyntaxShape::List, _) => {} - (SyntaxShape::Literal, _) => {} - (SyntaxShape::Variable, _) => {} - (SyntaxShape::Binary, _) => {} - (SyntaxShape::Block, _) => {} - (SyntaxShape::Boolean, _) => {} + (SyntaxType::Any, _) => {} + (SyntaxType::List, _) => {} + (SyntaxType::Literal, _) => {} + (SyntaxType::Variable, _) => {} + (SyntaxType::Binary, _) => {} + (SyntaxType::Block, _) => {} + (SyntaxType::Boolean, _) => {} }; let first = baseline_parse_semantic_token(next, context, source)?; @@ -162,7 +162,7 @@ pub fn baseline_parse_next_expr( return Err(ShellError::labeled_error( "Expected something after an operator", "operator", - op.tag(), + op.span(), )) } Some(token) => baseline_parse_semantic_token(token, context, source)?, @@ -171,66 +171,75 @@ pub fn baseline_parse_next_expr( // We definitely have a binary expression here -- let's see if we should coerce it into a block match syntax_type { - SyntaxShape::Any => { - let tag = first.tag().until(second.tag()); + SyntaxType::Any => { + let span = (first.span().start, second.span().end); let binary = hir::Binary::new(first, op, second); let binary = hir::RawExpression::Binary(Box::new(binary)); - let binary = binary.tagged(tag); + let binary = Tagged::from_simple_spanned_item(binary, span); Ok(binary) } - SyntaxShape::Block => { - let tag = first.tag().until(second.tag()); + SyntaxType::Block => { + let span = (first.span().start, second.span().end); let path: Tagged = match first { Tagged { item: hir::RawExpression::Literal(hir::Literal::Bare), - tag, + tag: Tag { span, .. }, } => { - let string = tag.slice(source).to_string().tagged(tag); + let string = + Tagged::from_simple_spanned_item(span.slice(source).to_string(), span); let path = hir::Path::new( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Tag::unknown())) - .tagged(Tag::unknown()), + Tagged::from_simple_spanned_item( + // TODO: Deal with synthetic nodes that have no representation at all in source + hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), + (0, 0), + ), vec![string], ); let path = hir::RawExpression::Path(Box::new(path)); - path.tagged(first.tag()) + Tagged::from_simple_spanned_item(path, first.span()) } Tagged { item: hir::RawExpression::Literal(hir::Literal::String(inner)), - tag, + tag: Tag { span, .. }, } => { - let string = inner.slice(source).to_string().tagged(tag); + let string = + Tagged::from_simple_spanned_item(inner.slice(source).to_string(), span); let path = hir::Path::new( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Tag::unknown())) - .tagged_unknown(), + Tagged::from_simple_spanned_item( + // TODO: Deal with synthetic nodes that have no representation at all in source + hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), + (0, 0), + ), vec![string], ); let path = hir::RawExpression::Path(Box::new(path)); - path.tagged(first.tag()) + Tagged::from_simple_spanned_item(path, first.span()) } Tagged { item: hir::RawExpression::Variable(..), .. } => first, - Tagged { tag, item } => { + Tagged { + tag: Tag { span, .. }, + item, + } => { return Err(ShellError::labeled_error( "The first part of an un-braced block must be a column name", item.type_name(), - tag, + span, )) } }; let binary = hir::Binary::new(path, op, second); let binary = hir::RawExpression::Binary(Box::new(binary)); - let binary = binary.tagged(tag); + let binary = Tagged::from_simple_spanned_item(binary, span); let block = hir::RawExpression::Block(vec![binary]); - let block = block.tagged(tag); + let block = Tagged::from_simple_spanned_item(block, span); Ok(block) } @@ -256,11 +265,11 @@ pub fn baseline_parse_semantic_token( "Unexpected operator".tagged(op.tag), )), TokenNode::Flag(flag) => Err(ShellError::syntax_error("Unexpected flag".tagged(flag.tag))), - TokenNode::Member(tag) => Err(ShellError::syntax_error( - "BUG: Top-level member".tagged(*tag), + TokenNode::Member(span) => Err(ShellError::syntax_error( + "BUG: Top-level member".tagged(span), )), - TokenNode::Whitespace(tag) => Err(ShellError::syntax_error( - "BUG: Whitespace found during parse".tagged(*tag), + TokenNode::Whitespace(span) => Err(ShellError::syntax_error( + "BUG: Whitespace found during parse".tagged(span), )), TokenNode::Error(error) => Err(*error.item.clone()), TokenNode::Path(path) => baseline_parse_path(path, context, source), @@ -279,11 +288,11 @@ pub fn baseline_parse_delimited( &mut TokensIterator::new(children), context, source, - SyntaxShape::Any, + SyntaxType::Any, )?; let expr = hir::RawExpression::Block(exprs); - Ok(expr.tagged(token.tag())) + Ok(Tagged::from_simple_spanned_item(expr, token.span())) } Delimiter::Paren => unimplemented!(), Delimiter::Square => { @@ -292,11 +301,11 @@ pub fn baseline_parse_delimited( &mut TokensIterator::new(children), context, source, - SyntaxShape::Any, + SyntaxType::Any, )?; let expr = hir::RawExpression::List(exprs); - Ok(expr.tagged(token.tag())) + Ok(expr.tagged(Tag::unknown_origin(token.span()))) } } } @@ -313,8 +322,8 @@ pub fn baseline_parse_path( for part in token.tail() { let string = match part { TokenNode::Token(token) => match token.item() { - RawToken::Bare => token.tag().slice(source), - RawToken::String(tag) => tag.slice(source), + RawToken::Bare => token.span().slice(source), + RawToken::String(span) => span.slice(source), RawToken::Number(_) | RawToken::Size(..) | RawToken::Variable(_) @@ -323,26 +332,26 @@ pub fn baseline_parse_path( | RawToken::ExternalWord => { return Err(ShellError::type_error( "String", - token.type_name().tagged(part.tag()), + token.type_name().simple_spanned(part), )) } }, - TokenNode::Member(tag) => tag.slice(source), + TokenNode::Member(span) => span.slice(source), // TODO: Make this impossible other => { return Err(ShellError::syntax_error( - format!("{} in path", other.type_name()).tagged(other.tag()), + format!("{} in path", other.type_name()).tagged(other.span()), )) } } .to_string(); - tail.push(string.tagged(part.tag())); + tail.push(string.simple_spanned(part)); } - Ok(hir::path(head, tail).tagged(token.tag()).into()) + Ok(hir::path(head, tail).simple_spanned(token).into()) } #[derive(Debug, new)] diff --git a/src/parser/hir/external_command.rs b/src/parser/hir/external_command.rs index 28865330d5..8511cce1e0 100644 --- a/src/parser/hir/external_command.rs +++ b/src/parser/hir/external_command.rs @@ -9,7 +9,7 @@ use std::fmt; )] #[get = "pub(crate)"] pub struct ExternalCommand { - name: Tag, + name: Span, } impl ToDebug for ExternalCommand { diff --git a/src/parser/hir/named.rs b/src/parser/hir/named.rs index 838f643be5..96d5132fb8 100644 --- a/src/parser/hir/named.rs +++ b/src/parser/hir/named.rs @@ -1,6 +1,7 @@ use crate::parser::hir::Expression; use crate::parser::Flag; use crate::prelude::*; +use crate::Span; use derive_new::new; use indexmap::IndexMap; use log::trace; @@ -10,7 +11,7 @@ use std::fmt; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum NamedValue { AbsentSwitch, - PresentSwitch(Tag), + PresentSwitch(Span), AbsentValue, Value(Expression), } @@ -26,7 +27,7 @@ impl ToDebug for NamedArguments { for (name, value) in &self.named { match value { NamedValue::AbsentSwitch => continue, - NamedValue::PresentSwitch(tag) => write!(f, " --{}", tag.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/files.rs b/src/parser/parse/files.rs index 6cedb1e99c..173da54a80 100644 --- a/src/parser/parse/files.rs +++ b/src/parser/parse/files.rs @@ -1,7 +1,6 @@ -use crate::Tag; +use crate::Span; use derive_new::new; use language_reporting::{FileName, Location}; -use uuid::Uuid; #[derive(new, Debug, Clone)] pub struct Files { @@ -9,30 +8,26 @@ pub struct Files { } impl language_reporting::ReportingFiles for Files { - type Span = Tag; - type FileId = Uuid; + type Span = Span; + type FileId = usize; fn byte_span( &self, - file: Self::FileId, + _file: Self::FileId, from_index: usize, to_index: usize, ) -> Option { - Some(Tag::from((from_index, to_index, file))) + Some(Span::from((from_index, to_index))) } - - fn file_id(&self, tag: Self::Span) -> Self::FileId { - tag.origin.unwrap() + fn file_id(&self, _span: Self::Span) -> Self::FileId { + 0 } - fn file_name(&self, _file: Self::FileId) -> FileName { FileName::Verbatim(format!("shell")) } - fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option { unimplemented!("byte_index") } - fn location(&self, _file: Self::FileId, byte_index: usize) -> Option { let source = &self.snippet; let mut seen_lines = 0; @@ -56,15 +51,14 @@ impl language_reporting::ReportingFiles for Files { None } } - - fn line_span(&self, file: Self::FileId, lineno: usize) -> Option { + fn line_span(&self, _file: Self::FileId, lineno: usize) -> Option { let source = &self.snippet; let mut seen_lines = 0; let mut seen_bytes = 0; for (pos, _) in source.match_indices('\n') { if seen_lines == lineno { - return Some(Tag::from((seen_bytes, pos, file))); + return Some(Span::from((seen_bytes, pos))); } else { seen_lines += 1; seen_bytes = pos + 1; @@ -72,18 +66,17 @@ impl language_reporting::ReportingFiles for Files { } if seen_lines == 0 { - Some(Tag::from((0, self.snippet.len() - 1, file))) + Some(Span::from((0, self.snippet.len() - 1))) } else { None } } - - fn source(&self, tag: Self::Span) -> Option { - if tag.span.start > tag.span.end { + fn source(&self, span: Self::Span) -> Option { + if span.start > span.end { return None; - } else if tag.span.end >= self.snippet.len() { + } else if span.end >= self.snippet.len() { return None; } - Some(tag.slice(&self.snippet).to_string()) + Some(self.snippet[span.start..span.end].to_string()) } } diff --git a/src/parser/parse/flag.rs b/src/parser/parse/flag.rs index 09d1e86337..096d69879f 100644 --- a/src/parser/parse/flag.rs +++ b/src/parser/parse/flag.rs @@ -1,4 +1,4 @@ -use crate::Tag; +use crate::Span; use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; @@ -13,5 +13,5 @@ pub enum FlagKind { #[get = "pub(crate)"] pub struct Flag { kind: FlagKind, - name: Tag, + name: Span, } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index f30eb2c11b..0be05af062 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -5,7 +5,7 @@ use crate::parser::parse::{ tokens::*, unit::*, }; use crate::prelude::*; -use crate::{Tag, Tagged}; +use crate::{Span, Tagged}; use nom; use nom::branch::*; use nom::bytes::complete::*; @@ -18,16 +18,15 @@ use log::trace; use nom::dbg; use nom::*; use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice}; -use nom_locate::{position, LocatedSpanEx}; +use nom5_locate::{position, LocatedSpan}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::str::FromStr; -use uuid::Uuid; -pub type NomSpan<'a> = LocatedSpanEx<&'a str, Uuid>; +pub type NomSpan<'a> = LocatedSpan<&'a str>; -pub fn nom_input(s: &str, origin: Uuid) -> NomSpan<'_> { - LocatedSpanEx::new_extra(s, origin) +pub fn nom_input(s: &str) -> NomSpan<'_> { + LocatedSpan::new(s) } macro_rules! operator { @@ -39,7 +38,7 @@ macro_rules! operator { Ok(( input, - TokenTreeBuilder::tagged_op(tag.fragment, (start, end, input.extra)), + TokenTreeBuilder::spanned_op(tag.fragment, (start, end)), )) } }; @@ -160,14 +159,14 @@ pub fn raw_number(input: NomSpan) -> IResult> { Ok((input, dot)) => input, // it's just an integer - Err(_) => return Ok((input, RawNumber::int((start, input.offset, input.extra)))), + Err(_) => return Ok((input, RawNumber::int((start, input.offset)))), }; let (input, tail) = digit1(input)?; let end = input.offset; - Ok((input, RawNumber::decimal((start, end, input.extra)))) + Ok((input, RawNumber::decimal((start, end)))) }) } @@ -190,7 +189,7 @@ pub fn dq_string(input: NomSpan) -> IResult { let end = input.offset; Ok(( input, - TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), + TokenTreeBuilder::spanned_string((start1, end1), (start, end)), )) }) } @@ -207,7 +206,7 @@ pub fn sq_string(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), + TokenTreeBuilder::spanned_string((start1, end1), (start, end)), )) }) } @@ -227,7 +226,7 @@ pub fn external(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_external(bare, (start, end, input.extra)), + TokenTreeBuilder::spanned_external(bare, (start, end)), )) }) } @@ -251,10 +250,7 @@ pub fn pattern(input: NomSpan) -> IResult { let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_pattern((start, end, input.extra)), - )) + Ok((input, TokenTreeBuilder::spanned_pattern((start, end)))) }) } @@ -267,7 +263,7 @@ pub fn bare(input: NomSpan) -> IResult { let next_char = &input.fragment.chars().nth(0); if let Some(next_char) = next_char { - if is_external_word_char(*next_char) || is_glob_specific_char(*next_char) { + if is_external_word_char(*next_char) || *next_char == '*' { return Err(nom::Err::Error(nom::error::make_error( input, nom::error::ErrorKind::TakeWhile1, @@ -277,10 +273,7 @@ pub fn bare(input: NomSpan) -> IResult { let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_bare((start, end, input.extra)), - )) + Ok((input, TokenTreeBuilder::spanned_bare((start, end)))) }) } @@ -290,10 +283,7 @@ pub fn external_word(input: NomSpan) -> IResult { let (input, _) = take_while1(is_external_word_char)(input)?; let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_external_word((start, end, input.extra)), - )) + Ok((input, TokenTreeBuilder::spanned_external_word((start, end)))) }) } @@ -306,7 +296,7 @@ pub fn var(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_var(bare.tag(), (start, end, input.extra)), + TokenTreeBuilder::spanned_var(bare.span(), (start, end)), )) }) } @@ -319,10 +309,7 @@ pub fn member(input: NomSpan) -> IResult { let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_member((start, end, input.extra)), - )) + Ok((input, TokenTreeBuilder::spanned_member((start, end)))) }) } @@ -335,7 +322,7 @@ pub fn flag(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_flag(bare.tag(), (start, end, input.extra)), + TokenTreeBuilder::spanned_flag(bare.span(), (start, end)), )) }) } @@ -349,7 +336,7 @@ pub fn shorthand(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_shorthand(bare.tag(), (start, end, input.extra)), + TokenTreeBuilder::spanned_shorthand(bare.span(), (start, end)), )) }) } @@ -382,7 +369,7 @@ pub fn raw_unit(input: NomSpan) -> IResult> { Ok(( input, - Unit::from(unit.fragment).tagged((start, end, input.extra)), + Tagged::from_simple_spanned_item(Unit::from(unit.fragment), (start, end)), )) }) } @@ -402,7 +389,7 @@ pub fn size(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_size((number.item, *size), (start, end, input.extra)), + TokenTreeBuilder::spanned_size((number.item, *size), (start, end)), )) } else { let end = input.offset; @@ -414,7 +401,7 @@ pub fn size(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_number(number.item, number.tag), + TokenTreeBuilder::spanned_number(number.item, number.tag), )) } }) @@ -468,18 +455,18 @@ fn make_token_list( let mut nodes = vec![]; if let Some(sp_left) = sp_left { - nodes.push(TokenNode::Whitespace(Tag::from(sp_left))); + nodes.push(TokenNode::Whitespace(Span::from(sp_left))); } nodes.push(first); for (ws, token) in list { - nodes.push(TokenNode::Whitespace(Tag::from(ws))); + nodes.push(TokenNode::Whitespace(Span::from(ws))); nodes.push(token); } if let Some(sp_right) = sp_right { - nodes.push(TokenNode::Whitespace(Tag::from(sp_right))); + nodes.push(TokenNode::Whitespace(Span::from(sp_right))); } nodes @@ -491,10 +478,7 @@ pub fn whitespace(input: NomSpan) -> IResult { let (input, ws1) = space1(input)?; let right = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_ws((left, right, input.extra)), - )) + Ok((input, TokenTreeBuilder::spanned_ws((left, right)))) }) } @@ -524,7 +508,7 @@ pub fn delimited_paren(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_parens(items, (left, right, input.extra)), + TokenTreeBuilder::spanned_parens(items, (left, right)), )) }) } @@ -555,7 +539,7 @@ pub fn delimited_square(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_square(items, (left, right, input.extra)), + TokenTreeBuilder::spanned_square(items, (left, right)), )) }) } @@ -572,10 +556,7 @@ pub fn delimited_brace(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_brace( - items.unwrap_or_else(|| vec![]), - (left, right, input.extra), - ), + TokenTreeBuilder::spanned_brace(items.unwrap_or_else(|| vec![]), (left, right)), )) }) } @@ -586,10 +567,7 @@ pub fn raw_call(input: NomSpan) -> IResult> { let (input, items) = token_list(input)?; let right = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_call(items, (left, right, input.extra)), - )) + Ok((input, TokenTreeBuilder::spanned_call(items, (left, right)))) }) } @@ -603,7 +581,7 @@ pub fn path(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_path((head, tail), (left, right, input.extra)), + TokenTreeBuilder::spanned_path((head, tail), (left, right)), )) }) } @@ -650,9 +628,9 @@ pub fn pipeline(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_pipeline( - (make_call_list(head, items), tail.map(Tag::from)), - (start, end, input.extra), + TokenTreeBuilder::spanned_pipeline( + (make_call_list(head, items), tail.map(Span::from)), + (start, end), ), )) }) @@ -665,17 +643,17 @@ fn make_call_list( let mut out = vec![]; if let Some(head) = head { - let el = PipelineElement::new(None, head.0.map(Tag::from), head.1, head.2.map(Tag::from)); + let el = PipelineElement::new(None, head.0.map(Span::from), head.1, head.2.map(Span::from)); out.push(el); } for (pipe, ws1, call, ws2) in items { let el = PipelineElement::new( - Some(pipe).map(Tag::from), - ws1.map(Tag::from), + Some(pipe).map(Span::from), + ws1.map(Span::from), call, - ws2.map(Tag::from), + ws2.map(Span::from), ); out.push(el); @@ -701,17 +679,12 @@ fn is_external_word_char(c: char) -> bool { } } -/// These characters appear in globs and not bare words -fn is_glob_specific_char(c: char) -> bool { - c == '*' || c == '?' -} - fn is_start_glob_char(c: char) -> bool { - is_start_bare_char(c) || is_glob_specific_char(c) + is_start_bare_char(c) || c == '*' } fn is_glob_char(c: char) -> bool { - is_bare_char(c) || is_glob_specific_char(c) + is_bare_char(c) || c == '*' } fn is_start_bare_char(c: char) -> bool { @@ -806,7 +779,7 @@ mod tests { macro_rules! equal_tokens { ($source:tt -> $tokens:expr) => { let result = apply(pipeline, "pipeline", $source); - let (expected_tree, expected_source) = TokenTreeBuilder::build(uuid::Uuid::nil(), $tokens); + let (expected_tree, expected_source) = TokenTreeBuilder::build($tokens); if result != expected_tree { let debug_result = format!("{}", result.debug($source)); @@ -845,12 +818,12 @@ mod tests { fn test_integer() { assert_leaf! { parsers [ size ] - "123" -> 0..3 { Number(RawNumber::int((0, 3, test_uuid())).item) } + "123" -> 0..3 { Number(RawNumber::int((0, 3)).item) } } assert_leaf! { parsers [ size ] - "-123" -> 0..4 { Number(RawNumber::int((0, 4, test_uuid())).item) } + "-123" -> 0..4 { Number(RawNumber::int((0, 4)).item) } } } @@ -858,12 +831,12 @@ mod tests { fn test_size() { assert_leaf! { parsers [ size ] - "123MB" -> 0..5 { Size(RawNumber::int((0, 3, test_uuid())).item, Unit::MB) } + "123MB" -> 0..5 { Size(RawNumber::int((0, 3)).item, Unit::MB) } } assert_leaf! { parsers [ size ] - "10GB" -> 0..4 { Size(RawNumber::int((0, 2, test_uuid())).item, Unit::GB) } + "10GB" -> 0..4 { Size(RawNumber::int((0, 2)).item, Unit::GB) } } } @@ -901,12 +874,12 @@ mod tests { fn test_string() { assert_leaf! { parsers [ string dq_string ] - r#""hello world""# -> 0..13 { String(tag(1, 12)) } + r#""hello world""# -> 0..13 { String(span(1, 12)) } } assert_leaf! { parsers [ string sq_string ] - r"'hello world'" -> 0..13 { String(tag(1, 12)) } + r"'hello world'" -> 0..13 { String(span(1, 12)) } } } @@ -958,12 +931,12 @@ mod tests { fn test_variable() { assert_leaf! { parsers [ var ] - "$it" -> 0..3 { Variable(tag(1, 3)) } + "$it" -> 0..3 { Variable(span(1, 3)) } } assert_leaf! { parsers [ var ] - "$name" -> 0..5 { Variable(tag(1, 5)) } + "$name" -> 0..5 { Variable(span(1, 5)) } } } @@ -971,7 +944,7 @@ mod tests { fn test_external() { assert_leaf! { parsers [ external ] - "^ls" -> 0..3 { ExternalCommand(tag(1, 3)) } + "^ls" -> 0..3 { ExternalCommand(span(1, 3)) } } } @@ -1287,7 +1260,7 @@ mod tests { desc: &str, string: &str, ) -> T { - match f(NomSpan::new_extra(string, uuid::Uuid::nil())) { + match f(NomSpan::new(string)) { Ok(v) => v.1, Err(other) => { println!("{:?}", other); @@ -1297,46 +1270,44 @@ mod tests { } } - fn tag(left: usize, right: usize) -> Tag { - Tag::from((left, right, uuid::Uuid::nil())) + fn span(left: usize, right: usize) -> Span { + Span::from((left, right)) } fn delimited( - delimiter: Tagged, + delimiter: Delimiter, children: Vec, left: usize, right: usize, ) -> TokenNode { - let node = DelimitedNode::new(*delimiter, children); - let spanned = node.tagged((left, right, delimiter.tag.origin)); + let node = DelimitedNode::new(delimiter, children); + let spanned = Tagged::from_simple_spanned_item(node, (left, right)); TokenNode::Delimited(spanned) } fn path(head: TokenNode, tail: Vec, left: usize, right: usize) -> TokenNode { - let tag = head.tag(); - let node = PathNode::new( Box::new(head), tail.into_iter().map(TokenNode::Token).collect(), ); - let spanned = node.tagged((left, right, tag.origin)); + let spanned = Tagged::from_simple_spanned_item(node, (left, right)); TokenNode::Path(spanned) } + fn leaf_token(token: RawToken, left: usize, right: usize) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right))) + } + fn token(token: RawToken, left: usize, right: usize) -> TokenNode { - TokenNode::Token(token.tagged((left, right, uuid::Uuid::nil()))) + TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right))) } fn build(block: CurriedNode) -> T { - let mut builder = TokenTreeBuilder::new(uuid::Uuid::nil()); + let mut builder = TokenTreeBuilder::new(); block(&mut builder) } fn build_token(block: CurriedToken) -> TokenNode { - TokenTreeBuilder::build(uuid::Uuid::nil(), block).0 - } - - fn test_uuid() -> uuid::Uuid { - uuid::Uuid::nil() + TokenTreeBuilder::build(block).0 } } diff --git a/src/parser/parse/pipeline.rs b/src/parser/parse/pipeline.rs index 42bbe23a18..64a899c179 100644 --- a/src/parser/parse/pipeline.rs +++ b/src/parser/parse/pipeline.rs @@ -1,6 +1,6 @@ use crate::parser::CallNode; use crate::traits::ToDebug; -use crate::{Tag, Tagged}; +use crate::{Span, Tagged}; use derive_new::new; use getset::Getters; use std::fmt; @@ -8,7 +8,7 @@ use std::fmt; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)] pub struct Pipeline { pub(crate) parts: Vec, - pub(crate) post_ws: Option, + pub(crate) post_ws: Option, } impl ToDebug for Pipeline { @@ -27,11 +27,11 @@ impl ToDebug for Pipeline { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] pub struct PipelineElement { - pub pipe: Option, - pub pre_ws: Option, + pub pipe: Option, + pub pre_ws: Option, #[get = "pub(crate)"] call: Tagged, - pub post_ws: Option, + pub post_ws: Option, } impl ToDebug for PipelineElement { diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index e0072360e8..f69c176e97 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -1,7 +1,7 @@ use crate::errors::ShellError; use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*}; use crate::traits::ToDebug; -use crate::{Tag, Tagged, Text}; +use crate::{Span, Tagged, Text}; use derive_new::new; use enum_utils::FromStr; use getset::Getters; @@ -16,8 +16,8 @@ pub enum TokenNode { Pipeline(Tagged), Operator(Tagged), Flag(Tagged), - Member(Tag), - Whitespace(Tag), + Member(Span), + Whitespace(Span), Error(Tagged>), Path(Tagged), @@ -78,31 +78,31 @@ impl fmt::Debug for DebugTokenNode<'_> { ) } TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)), - TokenNode::Error(s) => write!(f, " for {:?}", s.tag().slice(self.source)), - rest => write!(f, "{}", rest.tag().slice(self.source)), + TokenNode::Error(s) => write!(f, " for {:?}", s.span().slice(self.source)), + rest => write!(f, "{}", rest.span().slice(self.source)), } } } -impl From<&TokenNode> for Tag { - fn from(token: &TokenNode) -> Tag { - token.tag() +impl From<&TokenNode> for Span { + fn from(token: &TokenNode) -> Span { + token.span() } } impl TokenNode { - pub fn tag(&self) -> Tag { + pub fn span(&self) -> Span { match self { - TokenNode::Token(t) => t.tag(), - TokenNode::Call(s) => s.tag(), - TokenNode::Delimited(s) => s.tag(), - TokenNode::Pipeline(s) => s.tag(), - TokenNode::Operator(s) => s.tag(), - TokenNode::Flag(s) => s.tag(), + TokenNode::Token(t) => t.span(), + TokenNode::Call(s) => s.span(), + TokenNode::Delimited(s) => s.span(), + TokenNode::Pipeline(s) => s.span(), + TokenNode::Operator(s) => s.span(), + TokenNode::Flag(s) => s.span(), TokenNode::Member(s) => *s, TokenNode::Whitespace(s) => *s, - TokenNode::Error(s) => s.tag(), - TokenNode::Path(s) => s.tag(), + TokenNode::Error(s) => s.span(), + TokenNode::Path(s) => s.span(), } } @@ -127,11 +127,11 @@ impl TokenNode { } pub fn as_external_arg(&self, source: &Text) -> String { - self.tag().slice(source).to_string() + self.span().slice(source).to_string() } pub fn source<'a>(&self, source: &'a Text) -> &'a str { - self.tag().slice(source) + self.span().slice(source) } pub fn is_bare(&self) -> bool { @@ -154,12 +154,12 @@ impl TokenNode { } } - pub fn expect_external(&self) -> Tag { + pub fn expect_external(&self) -> Span { match self { TokenNode::Token(Tagged { - item: RawToken::ExternalCommand(tag), + item: RawToken::ExternalCommand(span), .. - }) => *tag, + }) => *span, _ => panic!("Only call expect_external if you checked is_external first"), } } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index 1ed8383f4c..ae1b344c44 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -7,8 +7,8 @@ use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, PathNode, Token use crate::parser::parse::tokens::{RawNumber, RawToken}; use crate::parser::parse::unit::Unit; use crate::parser::CallNode; +use crate::Span; use derive_new::new; -use uuid::Uuid; #[derive(new)] pub struct TokenTreeBuilder { @@ -17,16 +17,14 @@ pub struct TokenTreeBuilder { #[new(default)] output: String, - - origin: Uuid, } pub type CurriedToken = Box TokenNode + 'static>; pub type CurriedCall = Box Tagged + 'static>; impl TokenTreeBuilder { - pub fn build(origin: Uuid, block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { - let mut builder = TokenTreeBuilder::new(origin); + pub fn build(block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { + let mut builder = TokenTreeBuilder::new(); let node = block(&mut builder); (node, builder.output) } @@ -54,37 +52,50 @@ impl TokenTreeBuilder { .expect("A pipeline must contain at least one element"); let pipe = None; - let pre_tag = pre.map(|pre| b.consume_tag(&pre)); + let pre_span = pre.map(|pre| b.consume(&pre)); let call = call(b); - let post_tag = post.map(|post| b.consume_tag(&post)); + let post_span = post.map(|post| b.consume(&post)); - out.push(PipelineElement::new(pipe, pre_tag, call, post_tag)); + out.push(PipelineElement::new( + pipe, + pre_span.map(Span::from), + call, + post_span.map(Span::from), + )); loop { match input.next() { None => break, Some((pre, call, post)) => { - let pipe = Some(b.consume_tag("|")); - let pre_span = pre.map(|pre| b.consume_tag(&pre)); + let pipe = Some(Span::from(b.consume("|"))); + let pre_span = pre.map(|pre| b.consume(&pre)); let call = call(b); - let post_span = post.map(|post| b.consume_tag(&post)); + let post_span = post.map(|post| b.consume(&post)); - out.push(PipelineElement::new(pipe, pre_span, call, post_span)); + out.push(PipelineElement::new( + pipe, + pre_span.map(Span::from), + call, + post_span.map(Span::from), + )); } } } let end = b.pos; - TokenTreeBuilder::tagged_pipeline((out, None), (start, end, b.origin)) + TokenTreeBuilder::spanned_pipeline((out, None), (start, end)) }) } - pub fn tagged_pipeline( - input: (Vec, Option), - tag: impl Into, + pub fn spanned_pipeline( + input: (Vec, Option), + span: impl Into, ) -> TokenNode { - TokenNode::Pipeline(Pipeline::new(input.0, input.1.into()).tagged(tag.into())) + TokenNode::Pipeline(Tagged::from_simple_spanned_item( + Pipeline::new(input.0, input.1.into()), + span, + )) } pub fn op(input: impl Into) -> CurriedToken { @@ -95,12 +106,12 @@ impl TokenTreeBuilder { b.pos = end; - TokenTreeBuilder::tagged_op(input, (start, end, b.origin)) + TokenTreeBuilder::spanned_op(input, (start, end)) }) } - pub fn tagged_op(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Operator(input.into().tagged(tag.into())) + pub fn spanned_op(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Operator(Tagged::from_simple_spanned_item(input.into(), span.into())) } pub fn string(input: impl Into) -> CurriedToken { @@ -112,15 +123,15 @@ impl TokenTreeBuilder { let (_, end) = b.consume("\""); b.pos = end; - TokenTreeBuilder::tagged_string( - (inner_start, inner_end, b.origin), - (start, end, b.origin), - ) + TokenTreeBuilder::spanned_string((inner_start, inner_end), (start, end)) }) } - pub fn tagged_string(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::String(input.into()).tagged(tag.into())) + pub fn spanned_string(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::String(input.into()), + span.into(), + )) } pub fn bare(input: impl Into) -> CurriedToken { @@ -130,12 +141,15 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_bare((start, end, b.origin)) + TokenTreeBuilder::spanned_bare((start, end)) }) } - pub fn tagged_bare(tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::Bare.tagged(tag.into())) + pub fn spanned_bare(input: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::Bare, + input.into(), + )) } pub fn pattern(input: impl Into) -> CurriedToken { @@ -145,12 +159,15 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_pattern((start, end, b.origin)) + TokenTreeBuilder::spanned_pattern((start, end)) }) } - pub fn tagged_pattern(input: impl Into) -> TokenNode { - TokenNode::Token(RawToken::GlobPattern.tagged(input.into())) + pub fn spanned_pattern(input: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::Bare, + input.into(), + )) } pub fn external_word(input: impl Into) -> CurriedToken { @@ -160,16 +177,22 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_external_word((start, end, b.origin)) + TokenTreeBuilder::spanned_external_word((start, end)) }) } - pub fn tagged_external_word(input: impl Into) -> TokenNode { - TokenNode::Token(RawToken::ExternalWord.tagged(input.into())) + pub fn spanned_external_word(input: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::ExternalWord, + input.into(), + )) } - pub fn tagged_external(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::ExternalCommand(input.into()).tagged(tag.into())) + pub fn spanned_external(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::ExternalCommand(input.into()), + span.into(), + )) } pub fn int(input: impl Into) -> CurriedToken { @@ -179,10 +202,7 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&int.to_string()); b.pos = end; - TokenTreeBuilder::tagged_number( - RawNumber::Int((start, end, b.origin).into()), - (start, end, b.origin), - ) + TokenTreeBuilder::spanned_number(RawNumber::Int((start, end).into()), (start, end)) }) } @@ -193,15 +213,15 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&decimal.to_string()); b.pos = end; - TokenTreeBuilder::tagged_number( - RawNumber::Decimal((start, end, b.origin).into()), - (start, end, b.origin), - ) + TokenTreeBuilder::spanned_number(RawNumber::Decimal((start, end).into()), (start, end)) }) } - pub fn tagged_number(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::Number(input.into()).tagged(tag.into())) + pub fn spanned_number(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::Number(input.into()), + span.into(), + )) } pub fn size(int: impl Into, unit: impl Into) -> CurriedToken { @@ -213,20 +233,23 @@ impl TokenTreeBuilder { let (_, end_unit) = b.consume(unit.as_str()); b.pos = end_unit; - TokenTreeBuilder::tagged_size( - (RawNumber::Int((start_int, end_int, b.origin).into()), unit), - (start_int, end_unit, b.origin), + TokenTreeBuilder::spanned_size( + (RawNumber::Int((start_int, end_int).into()), unit), + (start_int, end_unit), ) }) } - pub fn tagged_size( + pub fn spanned_size( input: (impl Into, impl Into), - tag: impl Into, + span: impl Into, ) -> TokenNode { let (int, unit) = (input.0.into(), input.1.into()); - TokenNode::Token(RawToken::Size(int, unit).tagged(tag.into())) + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::Size(int, unit), + span, + )) } pub fn path(head: CurriedToken, tail: Vec) -> CurriedToken { @@ -244,12 +267,15 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::tagged_path((head, output), (start, end, b.origin)) + TokenTreeBuilder::spanned_path((head, output), (start, end)) }) } - pub fn tagged_path(input: (TokenNode, Vec), tag: impl Into) -> TokenNode { - TokenNode::Path(PathNode::new(Box::new(input.0), input.1).tagged(tag.into())) + pub fn spanned_path(input: (TokenNode, Vec), span: impl Into) -> TokenNode { + TokenNode::Path(Tagged::from_simple_spanned_item( + PathNode::new(Box::new(input.0), input.1), + span, + )) } pub fn var(input: impl Into) -> CurriedToken { @@ -259,12 +285,15 @@ impl TokenTreeBuilder { let (start, _) = b.consume("$"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::tagged_var((inner_start, end, b.origin), (start, end, b.origin)) + TokenTreeBuilder::spanned_var((inner_start, end), (start, end)) }) } - pub fn tagged_var(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::Variable(input.into()).tagged(tag.into())) + pub fn spanned_var(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Token(Tagged::from_simple_spanned_item( + RawToken::Variable(input.into()), + span.into(), + )) } pub fn flag(input: impl Into) -> CurriedToken { @@ -274,12 +303,15 @@ impl TokenTreeBuilder { let (start, _) = b.consume("--"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::tagged_flag((inner_start, end, b.origin), (start, end, b.origin)) + TokenTreeBuilder::spanned_flag((inner_start, end), (start, end)) }) } - pub fn tagged_flag(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Flag(Flag::new(FlagKind::Longhand, input.into()).tagged(tag.into())) + pub fn spanned_flag(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Flag(Tagged::from_simple_spanned_item( + Flag::new(FlagKind::Longhand, input.into()), + span.into(), + )) } pub fn shorthand(input: impl Into) -> CurriedToken { @@ -289,12 +321,15 @@ impl TokenTreeBuilder { let (start, _) = b.consume("-"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::tagged_shorthand((inner_start, end, b.origin), (start, end, b.origin)) + TokenTreeBuilder::spanned_shorthand((inner_start, end), (start, end)) }) } - pub fn tagged_shorthand(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Flag(Flag::new(FlagKind::Shorthand, input.into()).tagged(tag.into())) + pub fn spanned_shorthand(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Flag(Tagged::from_simple_spanned_item( + Flag::new(FlagKind::Shorthand, input.into()), + span.into(), + )) } pub fn member(input: impl Into) -> CurriedToken { @@ -302,12 +337,12 @@ impl TokenTreeBuilder { Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::tagged_member((start, end, b.origin)) + TokenTreeBuilder::spanned_member((start, end)) }) } - pub fn tagged_member(tag: impl Into) -> TokenNode { - TokenNode::Member(tag.into()) + pub fn spanned_member(span: impl Into) -> TokenNode { + TokenNode::Member(span.into()) } pub fn call(head: CurriedToken, input: Vec) -> CurriedCall { @@ -323,11 +358,11 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::tagged_call(nodes, (start, end, b.origin)) + TokenTreeBuilder::spanned_call(nodes, (start, end)) }) } - pub fn tagged_call(input: Vec, tag: impl Into) -> Tagged { + pub fn spanned_call(input: Vec, span: impl Into) -> Tagged { if input.len() == 0 { panic!("BUG: spanned call (TODO)") } @@ -337,7 +372,7 @@ impl TokenTreeBuilder { let head = input.next().unwrap(); let tail = input.collect(); - CallNode::new(Box::new(head), tail).tagged(tag.into()) + Tagged::from_simple_spanned_item(CallNode::new(Box::new(head), tail), span) } pub fn parens(input: Vec) -> CurriedToken { @@ -350,12 +385,15 @@ impl TokenTreeBuilder { let (_, end) = b.consume(")"); - TokenTreeBuilder::tagged_parens(output, (start, end, b.origin)) + TokenTreeBuilder::spanned_parens(output, (start, end)) }) } - pub fn tagged_parens(input: impl Into>, tag: impl Into) -> TokenNode { - TokenNode::Delimited(DelimitedNode::new(Delimiter::Paren, input.into()).tagged(tag.into())) + pub fn spanned_parens(input: impl Into>, span: impl Into) -> TokenNode { + TokenNode::Delimited(Tagged::from_simple_spanned_item( + DelimitedNode::new(Delimiter::Paren, input.into()), + span, + )) } pub fn square(input: Vec) -> CurriedToken { @@ -368,12 +406,15 @@ impl TokenTreeBuilder { let (_, end) = b.consume("]"); - TokenTreeBuilder::tagged_square(output, (start, end, b.origin)) + TokenTreeBuilder::spanned_square(output, (start, end)) }) } - pub fn tagged_square(input: impl Into>, tag: impl Into) -> TokenNode { - TokenNode::Delimited(DelimitedNode::new(Delimiter::Square, input.into()).tagged(tag.into())) + pub fn spanned_square(input: impl Into>, span: impl Into) -> TokenNode { + TokenNode::Delimited(Tagged::from_simple_spanned_item( + DelimitedNode::new(Delimiter::Square, input.into()), + span, + )) } pub fn braced(input: Vec) -> CurriedToken { @@ -386,18 +427,21 @@ impl TokenTreeBuilder { let (_, end) = b.consume(" }"); - TokenTreeBuilder::tagged_brace(output, (start, end, b.origin)) + TokenTreeBuilder::spanned_brace(output, (start, end)) }) } - pub fn tagged_brace(input: impl Into>, tag: impl Into) -> TokenNode { - TokenNode::Delimited(DelimitedNode::new(Delimiter::Brace, input.into()).tagged(tag.into())) + pub fn spanned_brace(input: impl Into>, span: impl Into) -> TokenNode { + TokenNode::Delimited(Tagged::from_simple_spanned_item( + DelimitedNode::new(Delimiter::Brace, input.into()), + span, + )) } pub fn sp() -> CurriedToken { Box::new(|b| { let (start, end) = b.consume(" "); - TokenNode::Whitespace(Tag::from((start, end, b.origin))) + TokenNode::Whitespace(Span::from((start, end))) }) } @@ -406,12 +450,14 @@ impl TokenTreeBuilder { Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::tagged_ws((start, end, b.origin)) + TokenTreeBuilder::spanned_ws((start, end)) }) } - pub fn tagged_ws(tag: impl Into) -> TokenNode { - TokenNode::Whitespace(tag.into()) + pub fn spanned_ws(span: impl Into) -> TokenNode { + let span = span.into(); + + TokenNode::Whitespace(span.into()) } fn consume(&mut self, input: &str) -> (usize, usize) { @@ -420,11 +466,4 @@ impl TokenTreeBuilder { self.output.push_str(input); (start, self.pos) } - - fn consume_tag(&mut self, input: &str) -> Tag { - let start = self.pos; - self.pos += input.len(); - self.output.push_str(input); - (start, self.pos, self.origin).into() - } } diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index d796a8fcb7..b599852499 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -1,6 +1,6 @@ use crate::parser::parse::unit::*; use crate::prelude::*; -use crate::{Tagged, Text}; +use crate::{Span, Tagged, Text}; use std::fmt; use std::str::FromStr; @@ -8,9 +8,9 @@ use std::str::FromStr; pub enum RawToken { Number(RawNumber), Size(RawNumber, Unit), - String(Tag), - Variable(Tag), - ExternalCommand(Tag), + String(Span), + Variable(Span), + ExternalCommand(Span), ExternalWord, GlobPattern, Bare, @@ -18,28 +18,28 @@ pub enum RawToken { #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum RawNumber { - Int(Tag), - Decimal(Tag), + Int(Span), + Decimal(Span), } impl RawNumber { - pub fn int(tag: impl Into) -> Tagged { - let tag = tag.into(); + pub fn int(span: impl Into) -> Tagged { + let span = span.into(); - RawNumber::Int(tag).tagged(tag) + RawNumber::Int(span).tagged(span) } - pub fn decimal(tag: impl Into) -> Tagged { - let tag = tag.into(); + pub fn decimal(span: impl Into) -> Tagged { + let span = span.into(); - RawNumber::Decimal(tag).tagged(tag) + RawNumber::Decimal(span).tagged(span) } pub(crate) fn to_number(self, source: &Text) -> Number { match self { - RawNumber::Int(tag) => Number::Int(BigInt::from_str(tag.slice(source)).unwrap()), - RawNumber::Decimal(tag) => { - Number::Decimal(BigDecimal::from_str(tag.slice(source)).unwrap()) + RawNumber::Int(span) => Number::Int(BigInt::from_str(span.slice(source)).unwrap()), + RawNumber::Decimal(span) => { + Number::Decimal(BigDecimal::from_str(span.slice(source)).unwrap()) } } } @@ -78,6 +78,6 @@ pub struct DebugToken<'a> { impl fmt::Debug for DebugToken<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.node.tag().slice(self.source)) + write!(f, "{}", self.node.span().slice(self.source)) } } diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index 36ba82f8e5..e0fc9d86fc 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -7,7 +7,7 @@ use crate::parser::{ Flag, RawToken, TokenNode, }; use crate::traits::ToDebug; -use crate::{Tag, Tagged, TaggedItem, Text}; +use crate::{Span, Tag, Tagged, Text}; use log::trace; pub fn parse_command( @@ -33,7 +33,7 @@ pub fn parse_command( .collect() }); - match parse_command_tail(&config, context, children, source, call.tag())? { + 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)), } @@ -49,9 +49,12 @@ fn parse_command_head(head: &TokenNode) -> Result { ) => Ok(spanned.map(|_| hir::RawExpression::Literal(hir::Literal::Bare))), TokenNode::Token(Tagged { - item: RawToken::String(inner_tag), - tag, - }) => Ok(hir::RawExpression::Literal(hir::Literal::String(*inner_tag)).tagged(*tag)), + item: RawToken::String(inner_span), + tag: Tag { span, origin: None }, + }) => Ok(Tagged::from_simple_spanned_item( + hir::RawExpression::Literal(hir::Literal::String(*inner_span)), + *span, + )), other => Err(ShellError::unexpected(&format!( "command head -> {:?}", @@ -65,7 +68,7 @@ fn parse_command_tail( context: &Context, tail: Option>, source: &Text, - command_tag: Tag, + command_span: Span, ) -> Result>, Option)>, ShellError> { let tail = &mut match &tail { None => hir::TokensIterator::new(&[]), @@ -86,7 +89,7 @@ fn parse_command_tail( named.insert_switch(name, flag); } NamedType::Mandatory(syntax_type) => { - match extract_mandatory(config, name, tail, source, command_tag) { + match extract_mandatory(config, name, tail, source, command_span) { Err(err) => return Err(err), // produce a correct diagnostic Ok((pos, flag)) => { tail.move_to(pos); @@ -95,7 +98,7 @@ fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), - flag.tag(), + flag.span(), )); } @@ -116,7 +119,7 @@ fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), - flag.tag(), + flag.span(), )); } @@ -147,7 +150,7 @@ fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryPositional(arg.name().to_string()), - command_tag, + command_span, )); } } @@ -205,7 +208,7 @@ fn extract_mandatory( name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Text, - tag: Tag, + span: Span, ) -> Result<(usize, Tagged), ShellError> { let flag = tokens.extract(|t| t.as_flag(name, source)); @@ -213,7 +216,7 @@ fn extract_mandatory( None => Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryFlag(name.to_string()), - tag, + span, )), Some((pos, flag)) => { diff --git a/src/parser/registry.rs b/src/parser/registry.rs index e199be192b..7112d91118 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -1,7 +1,7 @@ // TODO: Temporary redirect pub(crate) use crate::context::CommandRegistry; use crate::evaluate::{evaluate_baseline_expr, Scope}; -use crate::parser::{hir, hir::SyntaxShape, parse_command, CallNode}; +use crate::parser::{hir, hir::SyntaxType, parse_command, CallNode}; use crate::prelude::*; use derive_new::new; use indexmap::IndexMap; @@ -12,35 +12,35 @@ use std::fmt; #[derive(Debug, Serialize, Deserialize, Clone)] pub enum NamedType { Switch, - Mandatory(SyntaxShape), - Optional(SyntaxShape), + Mandatory(SyntaxType), + Optional(SyntaxType), } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum PositionalType { - Mandatory(String, SyntaxShape), - Optional(String, SyntaxShape), + Mandatory(String, SyntaxType), + Optional(String, SyntaxType), } impl PositionalType { - pub fn mandatory(name: &str, ty: SyntaxShape) -> PositionalType { + pub fn mandatory(name: &str, ty: SyntaxType) -> PositionalType { PositionalType::Mandatory(name.to_string(), ty) } pub fn mandatory_any(name: &str) -> PositionalType { - PositionalType::Mandatory(name.to_string(), SyntaxShape::Any) + PositionalType::Mandatory(name.to_string(), SyntaxType::Any) } pub fn mandatory_block(name: &str) -> PositionalType { - PositionalType::Mandatory(name.to_string(), SyntaxShape::Block) + PositionalType::Mandatory(name.to_string(), SyntaxType::Block) } - pub fn optional(name: &str, ty: SyntaxShape) -> PositionalType { + 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(), SyntaxShape::Any) + PositionalType::Optional(name.to_string(), SyntaxType::Any) } pub(crate) fn name(&self) -> &str { @@ -50,7 +50,7 @@ impl PositionalType { } } - pub(crate) fn syntax_type(&self) -> SyntaxShape { + pub(crate) fn syntax_type(&self) -> SyntaxType { match *self { PositionalType::Mandatory(_, t) => t, PositionalType::Optional(_, t) => t, @@ -66,7 +66,7 @@ pub struct Signature { #[new(default)] pub positional: Vec, #[new(value = "None")] - pub rest_positional: Option, + pub rest_positional: Option, #[new(default)] pub named: IndexMap, #[new(value = "false")] @@ -83,21 +83,21 @@ impl Signature { self } - pub fn required(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn required(mut self, name: impl Into, ty: impl Into) -> Signature { self.positional .push(PositionalType::Mandatory(name.into(), ty.into())); self } - pub fn optional(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn optional(mut self, name: impl Into, ty: impl Into) -> Signature { self.positional .push(PositionalType::Optional(name.into(), ty.into())); self } - pub fn named(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn named(mut self, name: impl Into, ty: impl Into) -> Signature { self.named .insert(name.into(), NamedType::Optional(ty.into())); @@ -107,7 +107,7 @@ impl Signature { pub fn required_named( mut self, name: impl Into, - ty: impl Into, + ty: impl Into, ) -> Signature { self.named .insert(name.into(), NamedType::Mandatory(ty.into())); @@ -126,7 +126,7 @@ impl Signature { self } - pub fn rest(mut self, ty: SyntaxShape) -> Signature { + pub fn rest(mut self, ty: SyntaxType) -> Signature { self.rest_positional = Some(ty); self } @@ -312,10 +312,10 @@ pub(crate) fn evaluate_args( for (name, value) in n.named.iter() { match value { - hir::named::NamedValue::PresentSwitch(tag) => { + hir::named::NamedValue::PresentSwitch(span) => { results.insert( name.clone(), - Value::boolean(true).tagged(*tag), + Tagged::from_simple_spanned_item(Value::boolean(true), *span), ); } hir::named::NamedValue::Value(expr) => { diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 03e1d42828..744003cd74 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tagged, Value, + SyntaxType, Tagged, Value, }; struct Add { @@ -44,9 +44,9 @@ impl Plugin for Add { fn config(&mut self) -> Result { Ok(Signature::build("add") .desc("Add a new field to the table.") - .required("Field", SyntaxShape::String) - .required("Value", SyntaxShape::String) - .rest(SyntaxShape::String) + .required("Field", SyntaxType::String) + .required("Value", SyntaxType::String) + .rest(SyntaxType::String) .filter()) } diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index db116fedf5..aeda4ba09b 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tagged, Value, + SyntaxType, Tagged, Value, }; struct Edit { @@ -43,8 +43,8 @@ impl Plugin for Edit { fn config(&mut self) -> Result { Ok(Signature::build("edit") .desc("Edit an existing column to have a new value.") - .required("Field", SyntaxShape::String) - .required("Value", SyntaxShape::String) + .required("Field", SyntaxType::String) + .required("Value", SyntaxType::String) .filter()) } diff --git a/src/plugins/embed.rs b/src/plugins/embed.rs index 646db80918..95140aa609 100644 --- a/src/plugins/embed.rs +++ b/src/plugins/embed.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tag, Tagged, TaggedDictBuilder, Value, + SyntaxType, Tag, Tagged, TaggedDictBuilder, Value, }; struct Embed { @@ -37,8 +37,8 @@ impl Plugin for Embed { fn config(&mut self) -> Result { Ok(Signature::build("embed") .desc("Embeds a new field to the table.") - .required("Field", SyntaxShape::String) - .rest(SyntaxShape::String) + .required("Field", SyntaxType::String) + .rest(SyntaxType::String) .filter()) } diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 4422195be8..d75da41428 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tagged, TaggedItem, Value, + SyntaxType, Tagged, TaggedItem, Value, }; enum Action { @@ -120,7 +120,7 @@ impl Plugin for Inc { .switch("major") .switch("minor") .switch("patch") - .rest(SyntaxShape::String) + .rest(SyntaxType::String) .filter()) } @@ -181,20 +181,18 @@ mod tests { use super::{Inc, SemVerAction}; use indexmap::IndexMap; use nu::{ - CallInfo, EvaluatedArgs, Plugin, ReturnSuccess, SourceMap, Tag, Tagged, TaggedDictBuilder, - TaggedItem, Value, + CallInfo, EvaluatedArgs, Plugin, ReturnSuccess, SourceMap, Span, Tag, Tagged, + TaggedDictBuilder, TaggedItem, Value, }; struct CallStub { - origin: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } impl CallStub { - fn new(origin: uuid::Uuid) -> CallStub { + fn new() -> CallStub { CallStub { - origin, positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -203,14 +201,14 @@ mod tests { fn with_long_flag(&mut self, name: &str) -> &mut Self { self.flags.insert( name.to_string(), - Value::boolean(true).tagged(Tag::unknown()), + Value::boolean(true).simple_spanned(Span::unknown()), ); self } fn with_parameter(&mut self, name: &str) -> &mut Self { self.positionals - .push(Value::string(name.to_string()).tagged(Tag::unknown_span(self.origin))); + .push(Value::string(name.to_string()).simple_spanned(Span::unknown())); self } @@ -218,7 +216,7 @@ mod tests { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), source_map: SourceMap::new(), - name_tag: Tag::unknown_span(self.origin), + name_span: Span::unknown(), } } } @@ -245,7 +243,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new(test_uuid()).with_long_flag("major").create()) + .begin_filter(CallStub::new().with_long_flag("major").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -255,7 +253,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new(test_uuid()).with_long_flag("minor").create()) + .begin_filter(CallStub::new().with_long_flag("minor").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -265,7 +263,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new(test_uuid()).with_long_flag("patch").create()) + .begin_filter(CallStub::new().with_long_flag("patch").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -276,7 +274,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new(test_uuid()) + CallStub::new() .with_long_flag("major") .with_long_flag("minor") .create(), @@ -290,11 +288,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter( - CallStub::new(test_uuid()) - .with_parameter("package.version") - .create() - ) + .begin_filter(CallStub::new().with_parameter("package.version").create()) .is_ok()); assert_eq!(plugin.field, Some("package.version".to_string())); @@ -327,7 +321,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new(test_uuid()) + CallStub::new() .with_long_flag("major") .with_parameter("version") .create() @@ -355,7 +349,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new(test_uuid()) + CallStub::new() .with_long_flag("minor") .with_parameter("version") .create() @@ -384,7 +378,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new(test_uuid()) + CallStub::new() .with_long_flag("patch") .with_parameter(&field) .create() @@ -405,8 +399,4 @@ mod tests { _ => {} } } - - fn test_uuid() -> uuid::Uuid { - uuid::Uuid::nil() - } } diff --git a/src/plugins/ps.rs b/src/plugins/ps.rs index 342bd5eb32..0f06167bdf 100644 --- a/src/plugins/ps.rs +++ b/src/plugins/ps.rs @@ -40,7 +40,7 @@ async fn ps(tag: Tag) -> Vec> { let mut output = vec![]; while let Some(res) = processes.next().await { if let Ok((process, usage)) = res { - let mut dict = TaggedDictBuilder::new(tag); + let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(tag.span)); dict.insert("pid", Value::int(process.pid())); if let Ok(name) = process.name().await { dict.insert("name", Value::string(name)); @@ -64,7 +64,7 @@ impl Plugin for Ps { } fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(ps(Tag::unknown_origin(callinfo.name_tag))) + Ok(block_on(ps(Tag::unknown_origin(callinfo.name_span))) .into_iter() .map(ReturnSuccess::value) .collect()) diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs index efd3231525..cb259e99b9 100644 --- a/src/plugins/skip.rs +++ b/src/plugins/skip.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, CoerceInto, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, - Signature, SyntaxShape, Tagged, TaggedItem, Value, + Signature, SyntaxType, Tagged, TaggedItem, Value, }; struct Skip { @@ -15,9 +15,9 @@ impl Skip { impl Plugin for Skip { fn config(&mut self) -> Result { - Ok(Signature::build("skip") + Ok(Signature::build("skip") .desc("Skip a number of rows") - .rest(SyntaxShape::Number) + .rest(SyntaxType::Number) .filter()) } fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { @@ -34,7 +34,7 @@ impl Plugin for Skip { return Err(ShellError::labeled_error( "Unrecognized type in params", "expected an integer", - arg.tag(), + arg.span(), )) } } diff --git a/src/plugins/str.rs b/src/plugins/str.rs index c9e82ab15c..99700449b4 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tagged, Value, + SyntaxType, Tagged, Value, }; use regex::Regex; @@ -174,7 +174,7 @@ impl Plugin for Str { .switch("to-int") .switch("replace") .switch("find-replace") - .rest(SyntaxShape::Member) + .rest(SyntaxType::Member) .filter()) } @@ -261,7 +261,7 @@ mod tests { use super::{Action, ReplaceAction, Str}; use indexmap::IndexMap; use nu::{ - CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Tag, Tagged, + CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Span, Tag, Tagged, TaggedDictBuilder, TaggedItem, Value, }; use num_bigint::BigInt; @@ -277,7 +277,6 @@ mod tests { } struct CallStub { - origin: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } @@ -285,7 +284,6 @@ mod tests { impl CallStub { fn new() -> CallStub { CallStub { - origin: uuid::Uuid::nil(), positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -294,14 +292,14 @@ mod tests { fn with_long_flag(&mut self, name: &str) -> &mut Self { self.flags.insert( name.to_string(), - Value::boolean(true).tagged(Tag::unknown()), + Value::boolean(true).simple_spanned(Span::unknown()), ); self } fn with_parameter(&mut self, name: &str) -> &mut Self { self.positionals - .push(Value::string(name.to_string()).tagged(Tag::unknown())); + .push(Value::string(name.to_string()).simple_spanned(Span::unknown())); self } @@ -309,7 +307,7 @@ mod tests { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), source_map: SourceMap::new(), - name_tag: Tag::unknown_span(self.origin), + name_span: Span::unknown(), } } } diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs index ffb39cb90b..32ecd7a9ce 100644 --- a/src/plugins/sum.rs +++ b/src/plugins/sum.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - Tagged, TaggedItem, Value, + Tag, Tagged, Value, }; struct Sum { @@ -18,10 +18,11 @@ impl Sum { match &self.total { Some(Tagged { item: Value::Primitive(Primitive::Int(j)), - tag, + tag: Tag { span, .. }, }) => { //TODO: handle overflow - self.total = Some(Value::int(i + j).tagged(*tag)); + self.total = + Some(Tagged::from_simple_spanned_item(Value::int(i + j), span)); Ok(()) } None => { @@ -37,10 +38,11 @@ impl Sum { match self.total { Some(Tagged { item: Value::Primitive(Primitive::Bytes(j)), - tag, + tag: Tag { span, .. }, }) => { //TODO: handle overflow - self.total = Some(Value::bytes(b + j).tagged(tag)); + self.total = + Some(Tagged::from_simple_spanned_item(Value::bytes(b + j), span)); Ok(()) } None => { diff --git a/src/plugins/sys.rs b/src/plugins/sys.rs index 8030a86b88..db7de6e625 100644 --- a/src/plugins/sys.rs +++ b/src/plugins/sys.rs @@ -315,7 +315,7 @@ impl Plugin for Sys { } fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(sysinfo(Tag::unknown_origin(callinfo.name_tag))) + Ok(block_on(sysinfo(Tag::unknown_origin(callinfo.name_span))) .into_iter() .map(ReturnSuccess::value) .collect()) diff --git a/src/prelude.rs b/src/prelude.rs index d58e7989a6..a491aff5de 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -62,7 +62,7 @@ pub(crate) use crate::data::{Primitive, Value}; pub(crate) use crate::env::host::handle_unexpected; pub(crate) use crate::env::Host; pub(crate) use crate::errors::{CoerceInto, ShellError}; -pub(crate) use crate::parser::hir::SyntaxShape; +pub(crate) use crate::parser::hir::SyntaxType; pub(crate) use crate::parser::parse::parser::Number; pub(crate) use crate::parser::registry::Signature; pub(crate) use crate::shell::filesystem_shell::FilesystemShell; @@ -70,7 +70,8 @@ pub(crate) use crate::shell::help_shell::HelpShell; pub(crate) use crate::shell::shell_manager::ShellManager; pub(crate) use crate::shell::value_shell::ValueShell; pub(crate) use crate::stream::{InputStream, OutputStream}; -pub(crate) use crate::traits::{HasTag, ToDebug}; +pub(crate) use crate::traits::{HasSpan, ToDebug}; +pub(crate) use crate::Span; pub(crate) use crate::Text; pub(crate) use bigdecimal::BigDecimal; pub(crate) use futures::stream::BoxStream; diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 1d26fa1630..16cbf513f8 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -81,27 +81,23 @@ impl Shell for FilesystemShell { dirs::home_dir() } - fn ls( - &self, - pattern: Option>, - command_tag: Tag, - ) -> Result { + fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let cwd = self.path(); let mut full_path = PathBuf::from(self.path()); - match &pattern { - Some(value) => full_path.push((*value).as_ref()), + match &args.nth(0) { + Some(value) => full_path.push(Path::new(&value.as_path()?)), _ => {} } let entries: Vec<_> = match glob::glob(&full_path.to_string_lossy()) { Ok(files) => files.collect(), Err(_) => { - if let Some(source) = pattern { + if let Some(source) = args.nth(0) { return Err(ShellError::labeled_error( "Invalid pattern", "Invalid pattern", - source.tag(), + source.span(), )); } else { return Err(ShellError::string("Invalid pattern.")); @@ -118,17 +114,17 @@ impl Shell for FilesystemShell { let entries = std::fs::read_dir(&entry); let entries = match entries { Err(e) => { - if let Some(s) = pattern { + if let Some(s) = args.nth(0) { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - s.tag(), + s.span(), )); } else { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - command_tag, + args.name_span(), )); } } @@ -142,7 +138,11 @@ impl Shell for FilesystemShell { } else { Path::new(&filepath) }; - let value = dir_entry_dict(filename, &entry.metadata()?, command_tag)?; + let value = dir_entry_dict( + filename, + &entry.metadata()?, + Tag::unknown_origin(args.call_info.name_span), + )?; shell_entries.push_back(ReturnSuccess::value(value)) } return Ok(shell_entries.to_output_stream()); @@ -159,7 +159,11 @@ impl Shell for FilesystemShell { Path::new(&entry) }; let metadata = std::fs::metadata(&entry)?; - let value = dir_entry_dict(filename, &metadata, command_tag)?; + let value = dir_entry_dict( + filename, + &metadata, + Tag::unknown_origin(args.call_info.name_span), + )?; shell_entries.push_back(ReturnSuccess::value(value)) } } @@ -175,7 +179,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Can not change to home directory", "can not go to home", - args.call_info.name_tag, + args.call_info.name_span, )) } }, @@ -193,7 +197,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Can not change to directory", "directory not found", - v.tag().clone(), + v.span().clone(), )) } } @@ -203,7 +207,8 @@ impl Shell for FilesystemShell { let mut stream = VecDeque::new(); - stream.push_back(ReturnSuccess::change_cwd( + stream.push_back( + ReturnSuccess::change_cwd( path.to_string_lossy().to_string(), )); @@ -217,10 +222,10 @@ impl Shell for FilesystemShell { dst, recursive, }: CopyArgs, - name: Tag, + name: Span, path: &str, ) -> Result { - let name_tag = name; + let name_span = name; let mut source = PathBuf::from(path); let mut destination = PathBuf::from(path); @@ -275,7 +280,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_tag, + name_span, )); } Ok(o) => o, @@ -291,7 +296,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_tag, + name_span, )); } Ok(o) => o, @@ -327,7 +332,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_tag, + name_span, )); } Ok(o) => o, @@ -341,7 +346,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_tag, + name_span, )); } Ok(o) => o, @@ -355,7 +360,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Copy aborted. Not a valid path", "Copy aborted. Not a valid path", - name_tag, + name_span, )) } } @@ -365,7 +370,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_tag, + name_span, )); } Ok(o) => o, @@ -401,7 +406,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_tag, + name_span, )); } Ok(o) => o, @@ -415,7 +420,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_tag, + name_span, )); } Ok(o) => o, @@ -448,7 +453,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Copy aborted. Not a valid path", "Copy aborted. Not a valid path", - name_tag, + name_span, )) } } @@ -475,7 +480,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Copy aborted. Not a valid destination", "Copy aborted. Not a valid destination", - name_tag, + name_span, )) } } @@ -484,7 +489,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Copy aborted. (Does {:?} exist?)", destination_file_name), format!("Copy aborted. (Does {:?} exist?)", destination_file_name), - dst.tag(), + &dst.span(), )); } } @@ -495,7 +500,7 @@ impl Shell for FilesystemShell { fn mkdir( &self, MkdirArgs { rest: directories }: MkdirArgs, - name: Tag, + name: Span, path: &str, ) -> Result { let full_path = PathBuf::from(path); @@ -520,7 +525,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( reason.to_string(), reason.to_string(), - dir.tag(), + dir.span(), )) } Ok(_) => {} @@ -533,10 +538,10 @@ impl Shell for FilesystemShell { fn mv( &self, MoveArgs { src, dst }: MoveArgs, - name: Tag, + name: Span, path: &str, ) -> Result { - let name_tag = name; + let name_span = name; let mut source = PathBuf::from(path); let mut destination = PathBuf::from(path); @@ -562,7 +567,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Rename aborted. Not a valid destination", "Rename aborted. Not a valid destination", - dst.tag(), + dst.span(), )) } } @@ -576,7 +581,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Rename aborted. Not a valid entry name", "Rename aborted. Not a valid entry name", - name_tag, + name_span, )) } }; @@ -588,7 +593,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Rename aborted. {:}", e.to_string()), format!("Rename aborted. {:}", e.to_string()), - name_tag, + name_span, )) } }; @@ -612,7 +617,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_tag, + name_span, )); } Ok(o) => o, @@ -635,7 +640,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_tag, + name_span, )); } Ok(o) => o, @@ -657,7 +662,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_tag, + name_span, )); } Ok(o) => o, @@ -710,7 +715,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_tag, + name_span, )); } Ok(o) => o, @@ -734,7 +739,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_tag, + name_span, )); } Ok(o) => o, @@ -757,7 +762,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_tag, + name_span, )); } Ok(o) => o, @@ -789,7 +794,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Rename aborted. Not a valid entry name", "Rename aborted. Not a valid entry name", - name_tag, + name_span, )) } }; @@ -813,7 +818,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_tag, + name_span, )); } Ok(o) => o, @@ -825,7 +830,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Rename aborted. (Does {:?} exist?)", destination_file_name), format!("Rename aborted. (Does {:?} exist?)", destination_file_name), - dst.tag(), + dst.span(), )); } } @@ -836,16 +841,16 @@ impl Shell for FilesystemShell { fn rm( &self, RemoveArgs { target, recursive }: RemoveArgs, - name: Tag, + name: Span, path: &str, ) -> Result { - let name_tag = name; + let name_span = name; if target.item.to_str() == Some(".") || target.item.to_str() == Some("..") { return Err(ShellError::labeled_error( "Remove aborted. \".\" or \"..\" may not be removed.", "Remove aborted. \".\" or \"..\" may not be removed.", - target.tag(), + target.span(), )); } @@ -877,7 +882,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("{:?} is a directory. Try using \"--recursive\".", file), format!("{:?} is a directory. Try using \"--recursive\".", file), - target.tag(), + target.span(), )); } } @@ -894,7 +899,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Remove aborted. Not a valid path", "Remove aborted. Not a valid path", - name_tag, + name_span, )) } } @@ -914,7 +919,7 @@ impl Shell for FilesystemShell { "Directory {:?} found somewhere inside. Try using \"--recursive\".", path_file_name ), - target.tag(), + target.span(), )); } @@ -928,7 +933,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Remove aborted. {:}", e.to_string()), format!("Remove aborted. {:}", e.to_string()), - name_tag, + name_span, )) } } @@ -949,7 +954,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "unable to show current directory", "pwd command failed", - args.call_info.name_tag, + args.call_info.name_span, )); } }; @@ -957,7 +962,7 @@ impl Shell for FilesystemShell { let mut stream = VecDeque::new(); stream.push_back(ReturnSuccess::value( Value::Primitive(Primitive::String(p.to_string_lossy().to_string())) - .tagged(args.call_info.name_tag), + .simple_spanned(args.call_info.name_span), )); Ok(stream.into()) diff --git a/src/shell/help_shell.rs b/src/shell/help_shell.rs index 25f1b9c428..35c939d7d4 100644 --- a/src/shell/help_shell.rs +++ b/src/shell/help_shell.rs @@ -126,11 +126,7 @@ impl Shell for HelpShell { self.path = path.clone(); } - fn ls( - &self, - _pattern: Option>, - _command_tag: Tag, - ) -> Result { + fn ls(&self, _args: EvaluatedWholeStreamCommandArgs) -> Result { Ok(self .commands() .map(|x| ReturnSuccess::value(x)) @@ -165,19 +161,24 @@ impl Shell for HelpShell { Ok(stream.into()) } - fn cp(&self, _args: CopyArgs, _name: Tag, _path: &str) -> Result { + fn cp(&self, _args: CopyArgs, _name: Span, _path: &str) -> Result { Ok(OutputStream::empty()) } - fn mv(&self, _args: MoveArgs, _name: Tag, _path: &str) -> Result { + fn mv(&self, _args: MoveArgs, _name: Span, _path: &str) -> Result { Ok(OutputStream::empty()) } - fn mkdir(&self, _args: MkdirArgs, _name: Tag, _path: &str) -> Result { + fn mkdir( + &self, + _args: MkdirArgs, + _name: Span, + _path: &str, + ) -> Result { Ok(OutputStream::empty()) } - fn rm(&self, _args: RemoveArgs, _name: Tag, _path: &str) -> Result { + fn rm(&self, _args: RemoveArgs, _name: Span, _path: &str) -> Result { Ok(OutputStream::empty()) } diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 6fb4544352..16802657db 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -66,7 +66,7 @@ impl Highlighter for Helper { } fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { - let tokens = crate::parser::pipeline(nom_input(line, uuid::Uuid::nil())); + let tokens = crate::parser::pipeline(nom_input(line)); match tokens { Err(_) => Cow::Borrowed(line), @@ -106,47 +106,47 @@ impl Highlighter for Helper { fn paint_token_node(token_node: &TokenNode, line: &str) -> String { let styled = match token_node { - TokenNode::Call(..) => Color::Cyan.bold().paint(token_node.tag().slice(line)), - TokenNode::Whitespace(..) => Color::White.normal().paint(token_node.tag().slice(line)), - TokenNode::Flag(..) => Color::Black.bold().paint(token_node.tag().slice(line)), - TokenNode::Member(..) => Color::Yellow.bold().paint(token_node.tag().slice(line)), - TokenNode::Path(..) => Color::Green.bold().paint(token_node.tag().slice(line)), - TokenNode::Error(..) => Color::Red.bold().paint(token_node.tag().slice(line)), - TokenNode::Delimited(..) => Color::White.paint(token_node.tag().slice(line)), - TokenNode::Operator(..) => Color::White.normal().paint(token_node.tag().slice(line)), - TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.tag().slice(line)), + TokenNode::Call(..) => Color::Cyan.bold().paint(token_node.span().slice(line)), + TokenNode::Whitespace(..) => Color::White.normal().paint(token_node.span().slice(line)), + TokenNode::Flag(..) => Color::Black.bold().paint(token_node.span().slice(line)), + TokenNode::Member(..) => Color::Yellow.bold().paint(token_node.span().slice(line)), + TokenNode::Path(..) => Color::Green.bold().paint(token_node.span().slice(line)), + TokenNode::Error(..) => Color::Red.bold().paint(token_node.span().slice(line)), + TokenNode::Delimited(..) => Color::White.paint(token_node.span().slice(line)), + TokenNode::Operator(..) => Color::White.normal().paint(token_node.span().slice(line)), + TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { item: RawToken::Number(..), .. - }) => Color::Purple.bold().paint(token_node.tag().slice(line)), + }) => Color::Purple.bold().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { item: RawToken::Size(..), .. - }) => Color::Purple.bold().paint(token_node.tag().slice(line)), + }) => Color::Purple.bold().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { item: RawToken::GlobPattern, .. - }) => Color::Cyan.normal().paint(token_node.tag().slice(line)), + }) => Color::Cyan.normal().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { item: RawToken::String(..), .. - }) => Color::Green.normal().paint(token_node.tag().slice(line)), + }) => Color::Green.normal().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { item: RawToken::Variable(..), .. - }) => Color::Yellow.bold().paint(token_node.tag().slice(line)), + }) => Color::Yellow.bold().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { item: RawToken::Bare, .. - }) => Color::Green.normal().paint(token_node.tag().slice(line)), + }) => Color::Green.normal().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { item: RawToken::ExternalCommand(..), .. - }) => Color::Cyan.bold().paint(token_node.tag().slice(line)), + }) => Color::Cyan.bold().paint(token_node.span().slice(line)), TokenNode::Token(Tagged { item: RawToken::ExternalWord, .. - }) => Color::Black.bold().paint(token_node.tag().slice(line)), + }) => Color::Black.bold().paint(token_node.span().slice(line)), }; styled.to_string() @@ -166,7 +166,7 @@ fn paint_pipeline_element(pipeline_element: &PipelineElement, line: &str) -> Str styled.push_str( &Color::Cyan .bold() - .paint(pipeline_element.call().head().tag().slice(line)) + .paint(pipeline_element.call().head().span().slice(line)) .to_string(), ); diff --git a/src/shell/shell.rs b/src/shell/shell.rs index c567e474a3..549aa79d22 100644 --- a/src/shell/shell.rs +++ b/src/shell/shell.rs @@ -13,16 +13,12 @@ pub trait Shell: std::fmt::Debug { fn name(&self, source_map: &SourceMap) -> String; fn homedir(&self) -> Option; - fn ls( - &self, - pattern: Option>, - command_tag: Tag, - ) -> Result; + fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; - fn cp(&self, args: CopyArgs, name: Tag, path: &str) -> Result; - fn mkdir(&self, args: MkdirArgs, name: Tag, path: &str) -> Result; - fn mv(&self, args: MoveArgs, name: Tag, path: &str) -> Result; - fn rm(&self, args: RemoveArgs, name: Tag, path: &str) -> Result; + fn cp(&self, args: CopyArgs, name: Span, path: &str) -> Result; + fn mkdir(&self, args: MkdirArgs, name: Span, path: &str) -> Result; + fn mv(&self, args: MoveArgs, name: Span, path: &str) -> Result; + fn rm(&self, args: RemoveArgs, name: Span, path: &str) -> Result; fn path(&self) -> String; fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn set_path(&mut self, path: String); diff --git a/src/shell/shell_manager.rs b/src/shell/shell_manager.rs index c4c42367ed..53984c9509 100644 --- a/src/shell/shell_manager.rs +++ b/src/shell/shell_manager.rs @@ -115,14 +115,10 @@ impl ShellManager { env[self.current_shell].homedir() } - pub fn ls( - &self, - path: Option>, - command_tag: Tag, - ) -> Result { + pub fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let env = self.shells.lock().unwrap(); - env[self.current_shell].ls(path, command_tag) + env[self.current_shell].ls(args) } pub fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 175e232e7b..010c7ae08e 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -87,15 +87,13 @@ impl Shell for ValueShell { Some(PathBuf::from("/")) } - fn ls( - &self, - target: Option>, - command_name: Tag, - ) -> Result { + fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let mut full_path = PathBuf::from(self.path()); - match &target { - Some(value) => full_path.push(value.as_ref()), + let target = args.nth(0); + + match target { + Some(value) => full_path.push(Path::new(&value.as_path()?)), _ => {} } @@ -103,18 +101,18 @@ impl Shell for ValueShell { value_system.walk_decorate(&self.value)?; if !value_system.exists(&full_path) { - if let Some(target) = &target { + if let Some(target) = target { return Err(ShellError::labeled_error( "Can not list entries inside", "No such path exists", - target.tag(), + target.span(), )); } return Err(ShellError::labeled_error( "Can not list entries inside", "No such path exists", - command_name, + args.call_info.name_span, )); } @@ -159,14 +157,14 @@ impl Shell for ValueShell { return Err(ShellError::labeled_error( "Can not change to path inside", "No such path exists", - destination.tag(), + destination.span(), )); } return Err(ShellError::labeled_error( "Can not change to path inside", "No such path exists", - args.call_info.name_tag, + args.call_info.name_span, )); } @@ -175,7 +173,7 @@ impl Shell for ValueShell { Ok(stream.into()) } - fn cp(&self, _args: CopyArgs, name: Tag, _path: &str) -> Result { + fn cp(&self, _args: CopyArgs, name: Span, _path: &str) -> Result { Err(ShellError::labeled_error( "cp not currently supported on values", "not currently supported", @@ -183,7 +181,7 @@ impl Shell for ValueShell { )) } - fn mv(&self, _args: MoveArgs, name: Tag, _path: &str) -> Result { + fn mv(&self, _args: MoveArgs, name: Span, _path: &str) -> Result { Err(ShellError::labeled_error( "mv not currently supported on values", "not currently supported", @@ -191,7 +189,7 @@ impl Shell for ValueShell { )) } - fn mkdir(&self, _args: MkdirArgs, name: Tag, _path: &str) -> Result { + fn mkdir(&self, _args: MkdirArgs, name: Span, _path: &str) -> Result { Err(ShellError::labeled_error( "mkdir not currently supported on values", "not currently supported", @@ -199,7 +197,7 @@ impl Shell for ValueShell { )) } - fn rm(&self, _args: RemoveArgs, name: Tag, _path: &str) -> Result { + fn rm(&self, _args: RemoveArgs, name: Span, _path: &str) -> Result { Err(ShellError::labeled_error( "rm not currently supported on values", "not currently supported", @@ -215,7 +213,7 @@ impl Shell for ValueShell { let mut stream = VecDeque::new(); stream.push_back(ReturnSuccess::value(Tagged::from_item( Value::string(self.path()), - args.call_info.name_tag, + args.call_info.name_span, ))); Ok(stream.into()) } diff --git a/src/traits.rs b/src/traits.rs index 677d019ad8..5b022c444f 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -12,8 +12,8 @@ impl fmt::Display for Debuggable<'_, T> { } } -pub trait HasTag { - fn tag(&self) -> Tag; +pub trait HasSpan { + fn span(&self) -> Span; } pub trait ToDebug: Sized { From 127381497ca9e35796c046628a58d334350db77b Mon Sep 17 00:00:00 2001 From: Maximilian Roos Date: Wed, 11 Sep 2019 10:36:50 -0400 Subject: [PATCH 069/342] run rustfmt --- src/commands/cd.rs | 3 +- src/commands/date.rs | 2 +- src/commands/exit.rs | 3 +- src/commands/first.rs | 3 +- src/commands/from_bson.rs | 2 +- src/commands/get.rs | 2 +- src/commands/help.rs | 2 +- src/commands/lines.rs | 2 +- src/commands/pick.rs | 2 +- src/commands/post.rs | 2 +- src/commands/reject.rs | 2 +- src/commands/save.rs | 2 +- src/commands/shells.rs | 2 +- src/commands/size.rs | 2 +- src/commands/split_column.rs | 8 +++-- src/commands/split_row.rs | 5 ++- src/commands/tags.rs | 2 +- src/commands/trim.rs | 2 +- src/commands/version.rs | 2 +- src/commands/where_.rs | 7 ++-- src/commands/which_.rs | 5 ++- src/lib.rs | 2 +- src/parser/parse/operator.rs | 1 - src/plugins/skip.rs | 2 +- src/shell.rs | 2 +- src/shell/filesystem_shell.rs | 3 +- tests/command_cd_tests.rs | 60 ++++++++++++++++------------------- tests/command_config_test.rs | 57 +++++++++++++++------------------ tests/command_ls_tests.rs | 31 ++++++++---------- tests/command_mv_tests.rs | 20 +++--------- tests/command_rm_tests.rs | 29 +++++++---------- tests/external_tests.rs | 4 +-- tests/filter_inc_tests.rs | 54 ++++++++++++++----------------- tests/filter_str_tests.rs | 35 +++++++++----------- 34 files changed, 159 insertions(+), 203 deletions(-) diff --git a/src/commands/cd.rs b/src/commands/cd.rs index a84e66fce9..92875c2733 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -10,8 +10,7 @@ impl WholeStreamCommand for CD { } fn signature(&self) -> Signature { - Signature::build("cd") - .optional("directory", SyntaxType::Path) + Signature::build("cd").optional("directory", SyntaxType::Path) } fn usage(&self) -> &str { diff --git a/src/commands/date.rs b/src/commands/date.rs index 7d3307fe58..2d16ec6c55 100644 --- a/src/commands/date.rs +++ b/src/commands/date.rs @@ -1,5 +1,5 @@ -use crate::errors::ShellError; use crate::data::{Dictionary, Value}; +use crate::errors::ShellError; use crate::prelude::*; use chrono::{DateTime, Local, Utc}; diff --git a/src/commands/exit.rs b/src/commands/exit.rs index feed8f7c4f..8a382d8b7d 100644 --- a/src/commands/exit.rs +++ b/src/commands/exit.rs @@ -11,8 +11,7 @@ impl WholeStreamCommand for Exit { } fn signature(&self) -> Signature { - Signature::build("exit") - .switch("now") + Signature::build("exit").switch("now") } fn usage(&self) -> &str { diff --git a/src/commands/first.rs b/src/commands/first.rs index 6381d5def6..a3575b7fea 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -16,8 +16,7 @@ impl WholeStreamCommand for First { } fn signature(&self) -> Signature { - Signature::build("first") - .required("amount", SyntaxType::Literal) + Signature::build("first").required("amount", SyntaxType::Literal) } fn usage(&self) -> &str { diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index e2f5421bd9..12f92965a1 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ExpectedRange; use crate::data::{Primitive, TaggedDictBuilder, Value}; +use crate::errors::ExpectedRange; use crate::prelude::*; use bson::{decode_document, spec::BinarySubtype, Bson}; use std::str::FromStr; diff --git a/src/commands/get.rs b/src/commands/get.rs index 2f9ed8a32a..7f9c4f521f 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::Value; +use crate::errors::ShellError; use crate::prelude::*; pub struct Get; diff --git a/src/commands/help.rs b/src/commands/help.rs index 571868a02a..f8d9b5c2fe 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -1,7 +1,7 @@ use crate::commands::command::CommandAction; use crate::commands::PerItemCommand; -use crate::errors::ShellError; use crate::data::{command_dict, TaggedDictBuilder}; +use crate::errors::ShellError; use crate::parser::registry; use crate::prelude::*; diff --git a/src/commands/lines.rs b/src/commands/lines.rs index 15a467224f..196da8802f 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{Primitive, Value}; +use crate::errors::ShellError; use crate::prelude::*; use log::trace; diff --git a/src/commands/pick.rs b/src/commands/pick.rs index bc5f8df401..1dce172664 100644 --- a/src/commands/pick.rs +++ b/src/commands/pick.rs @@ -1,7 +1,7 @@ use crate::commands::WholeStreamCommand; use crate::context::CommandRegistry; -use crate::errors::ShellError; use crate::data::base::select_fields; +use crate::errors::ShellError; use crate::prelude::*; #[derive(Deserialize)] diff --git a/src/commands/post.rs b/src/commands/post.rs index 8790c929a7..e1001b98d2 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -1,7 +1,7 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; -use crate::errors::ShellError; use crate::data::Value; +use crate::errors::ShellError; use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; diff --git a/src/commands/reject.rs b/src/commands/reject.rs index 49dbc4dd36..3ad7de9fa6 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::base::reject_fields; +use crate::errors::ShellError; use crate::prelude::*; #[derive(Deserialize)] diff --git a/src/commands/save.rs b/src/commands/save.rs index 26bf8cb16d..2fd26defeb 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -1,6 +1,6 @@ use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand}; -use crate::errors::ShellError; use crate::data::Value; +use crate::errors::ShellError; use crate::prelude::*; use std::path::{Path, PathBuf}; diff --git a/src/commands/shells.rs b/src/commands/shells.rs index 856c23e7be..078040412b 100644 --- a/src/commands/shells.rs +++ b/src/commands/shells.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::TaggedDictBuilder; +use crate::errors::ShellError; use crate::prelude::*; pub struct Shells; diff --git a/src/commands/size.rs b/src/commands/size.rs index d8383efcf8..43829d524e 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{TaggedDictBuilder, Value}; +use crate::errors::ShellError; use crate::prelude::*; pub struct Size; diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index 2df02cac16..d057a3dd21 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{Primitive, TaggedDictBuilder, Value}; +use crate::errors::ShellError; use crate::prelude::*; use log::trace; @@ -40,7 +40,11 @@ impl WholeStreamCommand for SplitColumn { } fn split_column( - SplitColumnArgs { separator, rest, collapse_empty}: SplitColumnArgs, + SplitColumnArgs { + separator, + rest, + collapse_empty, + }: SplitColumnArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { Ok(input diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index d4f3824a80..bec18099a0 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{Primitive, Value}; +use crate::errors::ShellError; use crate::prelude::*; use log::trace; @@ -17,8 +17,7 @@ impl WholeStreamCommand for SplitRow { } fn signature(&self) -> Signature { - Signature::build("split-row") - .required("separator", SyntaxType::Any) + Signature::build("split-row").required("separator", SyntaxType::Any) } fn usage(&self) -> &str { diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 3f112482ca..179700a9e3 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{TaggedDictBuilder, Value}; +use crate::errors::ShellError; use crate::prelude::*; pub struct Tags; diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 865f6b50bb..00a978e83c 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::Value; +use crate::errors::ShellError; use crate::prelude::*; pub struct Trim; diff --git a/src/commands/version.rs b/src/commands/version.rs index a7a3578193..703c20be7a 100644 --- a/src/commands/version.rs +++ b/src/commands/version.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; use crate::data::{Dictionary, Value}; +use crate::errors::ShellError; use crate::parser::registry::Signature; use crate::prelude::*; use indexmap::IndexMap; diff --git a/src/commands/where_.rs b/src/commands/where_.rs index b09c341b4a..2111e0687c 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -12,8 +12,7 @@ impl PerItemCommand for Where { } fn signature(&self) -> registry::Signature { - Signature::build("where") - .required("condition", SyntaxType::Block) + Signature::build("where").required("condition", SyntaxType::Block) } fn usage(&self) -> &str { @@ -43,9 +42,7 @@ impl PerItemCommand for Where { VecDeque::new() } } - Err(e) => { - return Err(e) - } + Err(e) => return Err(e), } } Tagged { tag, .. } => { diff --git a/src/commands/which_.rs b/src/commands/which_.rs index 06c93f63f5..eea9d2becf 100644 --- a/src/commands/which_.rs +++ b/src/commands/which_.rs @@ -1,5 +1,5 @@ -use crate::errors::ShellError; use crate::data::Value; +use crate::errors::ShellError; use crate::prelude::*; use crate::commands::WholeStreamCommand; @@ -13,8 +13,7 @@ impl WholeStreamCommand for Which { } fn signature(&self) -> Signature { - Signature::build("which") - .required("name", SyntaxType::Any) + Signature::build("which").required("name", SyntaxType::Any) } fn usage(&self) -> &str { diff --git a/src/lib.rs b/src/lib.rs index a18343df9b..9ee1e9d09b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,8 +28,8 @@ pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder; pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; pub use cli::cli; -pub use data::config::{APP_INFO, config_path}; pub use data::base::{Primitive, Value}; +pub use data::config::{config_path, APP_INFO}; pub use data::dict::{Dictionary, TaggedDictBuilder}; pub use data::meta::{Span, Tag, Tagged, TaggedItem}; pub use errors::{CoerceInto, ShellError}; diff --git a/src/parser/parse/operator.rs b/src/parser/parse/operator.rs index 1942b6012c..82a04ed796 100644 --- a/src/parser/parse/operator.rs +++ b/src/parser/parse/operator.rs @@ -20,7 +20,6 @@ impl ToDebug for Operator { } impl Operator { - pub fn print(&self) -> String { self.as_str().to_string() } diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs index cb259e99b9..135dbbd5db 100644 --- a/src/plugins/skip.rs +++ b/src/plugins/skip.rs @@ -15,7 +15,7 @@ impl Skip { impl Plugin for Skip { fn config(&mut self) -> Result { - Ok(Signature::build("skip") + Ok(Signature::build("skip") .desc("Skip a number of rows") .rest(SyntaxType::Number) .filter()) diff --git a/src/shell.rs b/src/shell.rs index caa1443ac7..14ec1c6755 100644 --- a/src/shell.rs +++ b/src/shell.rs @@ -1,9 +1,9 @@ pub(crate) mod completer; pub(crate) mod filesystem_shell; +pub(crate) mod help_shell; pub(crate) mod helper; pub(crate) mod shell; pub(crate) mod shell_manager; pub(crate) mod value_shell; -pub(crate) mod help_shell; pub(crate) use helper::Helper; diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 16cbf513f8..e746f41aaa 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -207,8 +207,7 @@ impl Shell for FilesystemShell { let mut stream = VecDeque::new(); - stream.push_back( - ReturnSuccess::change_cwd( + stream.push_back(ReturnSuccess::change_cwd( path.to_string_lossy().to_string(), )); diff --git a/tests/command_cd_tests.rs b/tests/command_cd_tests.rs index b9ad8ca12d..e0cc60867f 100644 --- a/tests/command_cd_tests.rs +++ b/tests/command_cd_tests.rs @@ -31,7 +31,7 @@ fn filesystem_change_from_current_directory_using_absolute_path() { ); assert_eq!(PathBuf::from(actual), dirs.formats()); - }) + }) } #[test] @@ -113,29 +113,30 @@ fn filesystem_change_to_a_directory_containing_spaces() { "# ); - assert_eq!(PathBuf::from(actual), dirs.test().join("robalino turner katz")); + assert_eq!( + PathBuf::from(actual), + dirs.test().join("robalino turner katz") + ); }) } #[test] fn filesystem_directory_not_found() { let actual = nu_error!( - cwd: "tests/fixtures", - "cd dir_that_does_not_exist" + cwd: "tests/fixtures", + "cd dir_that_does_not_exist" ); assert!(actual.contains("dir_that_does_not_exist")); assert!(actual.contains("directory not found")); } - #[test] fn valuesystem_change_from_current_path_using_relative_path() { Playground::setup("cd_test_8", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [[bin]] path = "src/plugins/turner.rs" @@ -144,7 +145,7 @@ fn valuesystem_change_from_current_path_using_relative_path() { [[bin]] path = "src/plugins/katz.rs" - "# + "#, )]); let actual = nu!( @@ -164,10 +165,9 @@ fn valuesystem_change_from_current_path_using_relative_path() { #[test] fn valuesystem_change_from_current_path_using_absolute_path() { Playground::setup("cd_test_9", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [dependencies] turner-ts = "0.1.1" robalino-tkd = "0.0.1" @@ -178,7 +178,7 @@ fn valuesystem_change_from_current_path_using_absolute_path() { [[bin]] path = "src/plugins/bbq.rs" - "# + "#, )]); let actual = nu!( @@ -193,16 +193,15 @@ fn valuesystem_change_from_current_path_using_absolute_path() { ); assert_eq!(PathBuf::from(actual), PathBuf::from("/dependencies")); - }) + }) } #[test] fn valuesystem_switch_back_to_previous_working_path() { Playground::setup("cd_test_10", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [dependencies] turner-ts = "0.1.1" robalino-tkd = "0.0.1" @@ -214,7 +213,7 @@ fn valuesystem_switch_back_to_previous_working_path() { [[bin]] path = "src/plugins/bbq.rs" - "# + "#, )]); let actual = nu!( @@ -267,7 +266,6 @@ fn valuesystem_change_from_current_path_using_relative_path_and_dash() { }) } - #[test] fn valuesystem_change_current_path_to_parent_path() { Playground::setup("cd_test_12", |dirs, sandbox| { @@ -298,13 +296,12 @@ fn valuesystem_change_current_path_to_parent_path() { #[test] fn valuesystem_change_to_home_directory() { Playground::setup("cd_test_13", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [paquete] el = "pollo loco" - "# + "#, )]); let actual = nu!( @@ -325,13 +322,12 @@ fn valuesystem_change_to_home_directory() { #[test] fn valuesystem_change_to_a_path_containing_spaces() { Playground::setup("cd_test_14", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" ["pa que te"] el = "pollo loco" - "# + "#, )]); let actual = nu!( diff --git a/tests/command_config_test.rs b/tests/command_config_test.rs index 41038ddb12..3321f85dc1 100644 --- a/tests/command_config_test.rs +++ b/tests/command_config_test.rs @@ -7,25 +7,23 @@ use std::path::PathBuf; #[test] fn has_default_configuration_file() { - let expected = "config.toml"; + let expected = "config.toml"; Playground::setup("config_test_1", |dirs, _| { - nu!(cwd: dirs.root(), "config"); assert_eq!( - dirs.config_path().join(expected), - nu::config_path().unwrap().join(expected) + dirs.config_path().join(expected), + nu::config_path().unwrap().join(expected) ); }) } #[test] fn shows_path_of_configuration_file() { - let expected = "config.toml"; + let expected = "config.toml"; Playground::setup("config_test_2", |dirs, _| { - let actual = nu!( cwd: dirs.test(), "config --path | echo $it" @@ -38,14 +36,13 @@ fn shows_path_of_configuration_file() { #[test] fn use_different_configuration() { Playground::setup("config_test_3", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "test_3.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "test_3.toml", + r#" caballero_1 = "Andrés N. Robalino" caballero_2 = "Jonathan Turner" caballero_3 = "Yehuda katz" - "# + "#, )]); let actual = nu!( @@ -57,20 +54,19 @@ fn use_different_configuration() { assert_eq!(actual, "Andrés N. Robalino"); }); - h::delete_file_at(nu::config_path().unwrap().join("test_3.toml")); + h::delete_file_at(nu::config_path().unwrap().join("test_3.toml")); } #[test] fn sets_configuration_value() { Playground::setup("config_test_4", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "test_4.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "test_4.toml", + r#" caballero_1 = "Andrés N. Robalino" caballero_2 = "Jonathan Turner" caballero_3 = "Yehuda katz" - "# + "#, )]); nu!( @@ -79,27 +75,26 @@ fn sets_configuration_value() { ); let actual = nu!( - cwd: dirs.root(), - r#"open "{}/test_4.toml" | get caballero_4 | echo $it"#, - dirs.config_path() + cwd: dirs.root(), + r#"open "{}/test_4.toml" | get caballero_4 | echo $it"#, + dirs.config_path() ); assert_eq!(actual, "jonas"); }); - h::delete_file_at(nu::config_path().unwrap().join("test_4.toml")); + h::delete_file_at(nu::config_path().unwrap().join("test_4.toml")); } #[test] fn removes_configuration_value() { Playground::setup("config_test_5", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "test_5.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "test_5.toml", + r#" caballeros = [1, 1, 1] podershell = [1, 1, 1] - "# + "#, )]); nu!( @@ -108,13 +103,13 @@ fn removes_configuration_value() { ); let actual = nu_error!( - cwd: dirs.root(), - r#"open "{}/test_5.toml" | get podershell | echo $it"#, - dirs.config_path() + cwd: dirs.root(), + r#"open "{}/test_5.toml" | get podershell | echo $it"#, + dirs.config_path() ); assert!(actual.contains("table missing column")); }); - h::delete_file_at(nu::config_path().unwrap().join("test_5.toml")); -} \ No newline at end of file + h::delete_file_at(nu::config_path().unwrap().join("test_5.toml")); +} diff --git a/tests/command_ls_tests.rs b/tests/command_ls_tests.rs index 8ec1d035dc..f6f5f39f86 100644 --- a/tests/command_ls_tests.rs +++ b/tests/command_ls_tests.rs @@ -6,11 +6,10 @@ use helpers::{Playground, Stub::*}; #[test] fn ls_lists_regular_files() { Playground::setup("ls_test_1", |dirs, sandbox| { - sandbox - .with_files(vec![ - EmptyFile("yehuda.10.txt"), - EmptyFile("jonathan.10.txt"), - EmptyFile("andres.10.txt"), + sandbox.with_files(vec![ + EmptyFile("yehuda.10.txt"), + EmptyFile("jonathan.10.txt"), + EmptyFile("andres.10.txt"), ]); let actual = nu!( @@ -34,12 +33,11 @@ fn ls_lists_regular_files() { #[test] fn ls_lists_regular_files_using_asterisk_wildcard() { Playground::setup("ls_test_2", |dirs, sandbox| { - sandbox - .with_files(vec![ - EmptyFile("los.1.txt"), - EmptyFile("tres.1.txt"), - EmptyFile("amigos.1.txt"), - EmptyFile("arepas.1.clu"), + sandbox.with_files(vec![ + EmptyFile("los.1.txt"), + EmptyFile("tres.1.txt"), + EmptyFile("amigos.1.txt"), + EmptyFile("arepas.1.clu"), ]); let actual = nu!( @@ -63,12 +61,11 @@ fn ls_lists_regular_files_using_asterisk_wildcard() { #[test] fn ls_lists_regular_files_using_question_mark_wildcard() { Playground::setup("ls_test_3", |dirs, sandbox| { - sandbox - .with_files(vec![ - EmptyFile("yehuda.10.txt"), - EmptyFile("jonathan.10.txt"), - EmptyFile("andres.10.txt"), - EmptyFile("chicken_not_to_be_picked_up.100.txt"), + sandbox.with_files(vec![ + EmptyFile("yehuda.10.txt"), + EmptyFile("jonathan.10.txt"), + EmptyFile("andres.10.txt"), + EmptyFile("chicken_not_to_be_picked_up.100.txt"), ]); let actual = nu!( diff --git a/tests/command_mv_tests.rs b/tests/command_mv_tests.rs index 61ce842759..1d01e23329 100644 --- a/tests/command_mv_tests.rs +++ b/tests/command_mv_tests.rs @@ -26,11 +26,7 @@ fn moves_a_file() { #[test] fn overwrites_if_moving_to_existing_file() { Playground::setup("mv_test_2", |dirs, sandbox| { - sandbox - .with_files(vec![ - EmptyFile("andres.txt"), - EmptyFile("jonathan.txt") - ]); + sandbox.with_files(vec![EmptyFile("andres.txt"), EmptyFile("jonathan.txt")]); let original = dirs.test().join("andres.txt"); let expected = dirs.test().join("jonathan.txt"); @@ -142,7 +138,7 @@ fn moves_using_path_with_wildcard() { EmptyFile("sgml_description.json"), EmptyFile("sample.ini"), EmptyFile("utf16.ini"), - EmptyFile("yehuda.ini") + EmptyFile("yehuda.ini"), ]) .mkdir("work_dir") .mkdir("expected"); @@ -150,10 +146,7 @@ fn moves_using_path_with_wildcard() { let work_dir = dirs.test().join("work_dir"); let expected = dirs.test().join("expected"); - nu!( - cwd: work_dir, - "mv ../originals/*.ini ../expected" - ); + nu!(cwd: work_dir, "mv ../originals/*.ini ../expected"); assert!(h::files_exist_at( vec!["yehuda.ini", "jonathan.ini", "sample.ini", "andres.ini",], @@ -170,7 +163,7 @@ fn moves_using_a_glob() { .with_files(vec![ EmptyFile("arepa.txt"), EmptyFile("empanada.txt"), - EmptyFile("taquiza.txt") + EmptyFile("taquiza.txt"), ]) .mkdir("work_dir") .mkdir("expected"); @@ -179,10 +172,7 @@ fn moves_using_a_glob() { let work_dir = dirs.test().join("work_dir"); let expected = dirs.test().join("expected"); - nu!( - cwd: work_dir, - "mv ../meals/* ../expected" - ); + nu!(cwd: work_dir, "mv ../meals/* ../expected"); assert!(meal_dir.exists()); assert!(h::files_exist_at( diff --git a/tests/command_rm_tests.rs b/tests/command_rm_tests.rs index 568219e170..9317b586a3 100644 --- a/tests/command_rm_tests.rs +++ b/tests/command_rm_tests.rs @@ -6,9 +6,7 @@ use helpers::{Playground, Stub::*}; #[test] fn rm_removes_a_file() { Playground::setup("rm_test_1", |dirs, sandbox| { - sandbox - .with_files(vec![EmptyFile("i_will_be_deleted.txt") - ]); + sandbox.with_files(vec![EmptyFile("i_will_be_deleted.txt")]); nu!( cwd: dirs.root(), @@ -29,7 +27,7 @@ fn rm_removes_files_with_wildcard() { .with_files(vec![ EmptyFile("cli.rs"), EmptyFile("lib.rs"), - EmptyFile("prelude.rs") + EmptyFile("prelude.rs"), ]) .within("src/parser") .with_files(vec![EmptyFile("parse.rs"), EmptyFile("parser.rs")]) @@ -38,8 +36,8 @@ fn rm_removes_files_with_wildcard() { .within("src/parser/hir") .with_files(vec![ EmptyFile("baseline_parse.rs"), - EmptyFile("baseline_parse_tokens.rs") - ]); + EmptyFile("baseline_parse_tokens.rs"), + ]); nu!( cwd: dirs.test(), @@ -70,7 +68,7 @@ fn rm_removes_deeply_nested_directories_with_wildcard_and_recursive_flag() { .with_files(vec![ EmptyFile("cli.rs"), EmptyFile("lib.rs"), - EmptyFile("prelude.rs") + EmptyFile("prelude.rs"), ]) .within("src/parser") .with_files(vec![EmptyFile("parse.rs"), EmptyFile("parser.rs")]) @@ -79,8 +77,8 @@ fn rm_removes_deeply_nested_directories_with_wildcard_and_recursive_flag() { .within("src/parser/hir") .with_files(vec![ EmptyFile("baseline_parse.rs"), - EmptyFile("baseline_parse_tokens.rs") - ]); + EmptyFile("baseline_parse_tokens.rs"), + ]); nu!( cwd: dirs.test(), @@ -109,11 +107,10 @@ fn rm_removes_directory_contents_without_recursive_flag_if_empty() { #[test] fn rm_removes_directory_contents_with_recursive_flag() { Playground::setup("rm_test_5", |dirs, sandbox| { - sandbox - .with_files(vec![ - EmptyFile("yehuda.txt"), - EmptyFile("jonathan.txt"), - EmptyFile("andres.txt") + sandbox.with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), ]); nu!( @@ -128,9 +125,7 @@ fn rm_removes_directory_contents_with_recursive_flag() { #[test] fn rm_errors_if_attempting_to_delete_a_directory_with_content_without_recursive_flag() { Playground::setup("rm_test_6", |dirs, sandbox| { - sandbox - .with_files(vec![EmptyFile("some_empty_file.txt") - ]); + sandbox.with_files(vec![EmptyFile("some_empty_file.txt")]); let actual = nu_error!( cwd: dirs.root(), diff --git a/tests/external_tests.rs b/tests/external_tests.rs index 7aabd592db..0d810acac1 100644 --- a/tests/external_tests.rs +++ b/tests/external_tests.rs @@ -3,8 +3,8 @@ mod helpers; #[test] fn external_command() { let actual = nu!( - cwd: "tests/fixtures", - "echo 1" + cwd: "tests/fixtures", + "echo 1" ); assert!(actual.contains("1")); diff --git a/tests/filter_inc_tests.rs b/tests/filter_inc_tests.rs index 9ef0b311e6..658e24308b 100644 --- a/tests/filter_inc_tests.rs +++ b/tests/filter_inc_tests.rs @@ -15,13 +15,12 @@ fn can_only_apply_one() { #[test] fn by_one_with_field_passed() { Playground::setup("plugin_inc_test_1", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [package] edition = "2018" - "# + "#, )]); let actual = nu!( @@ -36,13 +35,12 @@ fn by_one_with_field_passed() { #[test] fn by_one_with_no_field_passed() { Playground::setup("plugin_inc_test_2", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [package] contributors = "2" - "# + "#, )]); let actual = nu!( @@ -57,13 +55,12 @@ fn by_one_with_no_field_passed() { #[test] fn semversion_major_inc() { Playground::setup("plugin_inc_test_3", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [package] version = "0.1.3" - "# + "#, )]); let actual = nu!( @@ -78,13 +75,12 @@ fn semversion_major_inc() { #[test] fn semversion_minor_inc() { Playground::setup("plugin_inc_test_4", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [package] version = "0.1.3" - "# + "#, )]); let actual = nu!( @@ -99,13 +95,12 @@ fn semversion_minor_inc() { #[test] fn semversion_patch_inc() { Playground::setup("plugin_inc_test_5", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [package] version = "0.1.3" - "# + "#, )]); let actual = nu!( @@ -120,13 +115,12 @@ fn semversion_patch_inc() { #[test] fn semversion_without_passing_field() { Playground::setup("plugin_inc_test_6", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [package] version = "0.1.3" - "# + "#, )]); let actual = nu!( diff --git a/tests/filter_str_tests.rs b/tests/filter_str_tests.rs index 03c9d63532..e26a18850e 100644 --- a/tests/filter_str_tests.rs +++ b/tests/filter_str_tests.rs @@ -1,7 +1,7 @@ mod helpers; -use helpers as h; use h::{Playground, Stub::*}; +use helpers as h; #[test] fn can_only_apply_one() { @@ -39,10 +39,9 @@ fn acts_without_passing_field() { #[test] fn downcases() { Playground::setup("plugin_str_test_2", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [dependency] name = "LIGHT" "#, @@ -60,10 +59,9 @@ fn downcases() { #[test] fn upcases() { Playground::setup("plugin_str_test_3", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [package] name = "nushell" "#, @@ -98,10 +96,9 @@ fn converts_to_int() { #[test] fn replaces() { Playground::setup("plugin_str_test_4", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [package] name = "nushell" "#, @@ -124,10 +121,9 @@ fn replaces() { #[test] fn find_and_replaces() { Playground::setup("plugin_str_test_5", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( - "sample.toml", - r#" + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" [fortune.teller] phone = "1-800-KATZ" "#, @@ -150,10 +146,9 @@ fn find_and_replaces() { #[test] fn find_and_replaces_without_passing_field() { Playground::setup("plugin_str_test_6", |dirs, sandbox| { - sandbox - .with_files(vec![FileWithContent( + sandbox.with_files(vec![FileWithContent( "sample.toml", - r#" + r#" [fortune.teller] phone = "1-800-KATZ" "#, From 9dc58247e59b2bf3c42386798f8c89a921dd8aa1 Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Mon, 9 Sep 2019 16:45:55 +0700 Subject: [PATCH 070/342] Fix wrong patch on glibc-busybox because distroless/cc doesn't contain libz --- docker/Package.glibc-busybox.Dockerfile | 13 ++++++++----- docker/Package.glibc-distroless.Dockerfile | 5 ++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docker/Package.glibc-busybox.Dockerfile b/docker/Package.glibc-busybox.Dockerfile index bdd4c18dd0..6191b5f04c 100644 --- a/docker/Package.glibc-busybox.Dockerfile +++ b/docker/Package.glibc-busybox.Dockerfile @@ -1,12 +1,15 @@ ARG base -FROM gcr.io/distroless/cc AS patch +FROM debian:stable-slim AS patch FROM ${base} ARG artifact COPY ${artifact} /bin/ -COPY --from=patch /lib/x86_64-linux-gnu/libz* /lib/x86_64-linux-gnu/ -COPY --from=patch /lib/x86_64-linux-gnu/libdl* /lib/x86_64-linux-gnu/ -COPY --from=patch /lib/x86_64-linux-gnu/librt* /lib/x86_64-linux-gnu/ -COPY --from=patch /lib/x86_64-linux-gnu/libgcc_s* /lib/x86_64-linux-gnu/ +COPY --from=patch \ + /lib/x86_64-linux-gnu/libz.so.1 \ + /lib/x86_64-linux-gnu/libdl.so.2 \ + /lib/x86_64-linux-gnu/librt.so.1 \ + /lib/x86_64-linux-gnu/libgcc_s.so.1 \ + /lib/x86_64-linux-gnu/ + ENTRYPOINT ["/bin/nu"] \ No newline at end of file diff --git a/docker/Package.glibc-distroless.Dockerfile b/docker/Package.glibc-distroless.Dockerfile index 250146f88e..42768fc08c 100644 --- a/docker/Package.glibc-distroless.Dockerfile +++ b/docker/Package.glibc-distroless.Dockerfile @@ -5,5 +5,8 @@ FROM ${base} ARG artifact COPY ${artifact} /bin/ -COPY --from=patch /lib/x86_64-linux-gnu/libz.so.1 /lib/x86_64-linux-gnu/libz.so.1 +COPY --from=patch \ + /lib/x86_64-linux-gnu/libz.so.1 \ + /lib/x86_64-linux-gnu/ + ENTRYPOINT ["/bin/nu"] \ No newline at end of file From dbefbcb0465ed4ea1412c3b39a3085167be7011f Mon Sep 17 00:00:00 2001 From: Maximilian Roos Date: Wed, 11 Sep 2019 13:06:59 -0400 Subject: [PATCH 071/342] CI --- .azure/azure-pipelines.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index b384e8df38..21bbf26095 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -19,7 +19,10 @@ steps: curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` export PATH=$HOME/.cargo/bin:$PATH rustc -Vv + cargo install rustfmt echo "##vso[task.prependpath]$HOME/.cargo/bin" displayName: Install Rust - bash: RUSTFLAGS="-D warnings" cargo test --all-features displayName: Run tests + - bash: cargo fmt + displayName: Lint From 5ca075e38b330d3201c04694b75333c7c964eb33 Mon Sep 17 00:00:00 2001 From: Maximilian Roos Date: Wed, 11 Sep 2019 13:14:31 -0400 Subject: [PATCH 072/342] already installed in CI --- .azure/azure-pipelines.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 21bbf26095..5a87327166 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -19,7 +19,6 @@ steps: curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` export PATH=$HOME/.cargo/bin:$PATH rustc -Vv - cargo install rustfmt echo "##vso[task.prependpath]$HOME/.cargo/bin" displayName: Install Rust - bash: RUSTFLAGS="-D warnings" cargo test --all-features From 1f3f3d3105c9c02542f0aa9fe44d9475428988ca Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Wed, 11 Sep 2019 13:44:23 -0400 Subject: [PATCH 073/342] adding missing docker dependencies openssl and pkgconfig Signed-off-by: Vanessa Sochat --- docker/Dockerfile | 2 ++ docker/Dockerfile.nu-base | 1 + 2 files changed, 3 insertions(+) diff --git a/docker/Dockerfile b/docker/Dockerfile index fa089e77d2..c4dafe3306 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,5 +2,7 @@ ARG FROMTAG=latest FROM quay.io/nushell/nu-base:${FROMTAG} as base FROM rust:1.37-slim COPY --from=base /usr/local/bin/nu /usr/local/bin/nu +RUN apt-get update && \ + apt-get install -y pkg-config libssl-dev ENTRYPOINT ["nu"] CMD ["-l", "info"] diff --git a/docker/Dockerfile.nu-base b/docker/Dockerfile.nu-base index faba0213a5..08cb0a8920 100644 --- a/docker/Dockerfile.nu-base +++ b/docker/Dockerfile.nu-base @@ -7,6 +7,7 @@ ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y libssl-dev \ libxcb-composite0-dev \ libx11-dev \ + libssl-dev \ pkg-config \ curl From 206998a41a61d576018a9ef15462923e178cf221 Mon Sep 17 00:00:00 2001 From: Maximilian Roos Date: Wed, 11 Sep 2019 13:49:16 -0400 Subject: [PATCH 074/342] install correct version --- .azure/azure-pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 5a87327166..851259b43e 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -20,8 +20,9 @@ steps: export PATH=$HOME/.cargo/bin:$PATH rustc -Vv echo "##vso[task.prependpath]$HOME/.cargo/bin" + rustup component add rustfmt --toolchain `cat rust-toolchain` displayName: Install Rust - bash: RUSTFLAGS="-D warnings" cargo test --all-features displayName: Run tests - - bash: cargo fmt + - bash: cargo fmt --all -- --check displayName: Lint From b35549adacf139848cee2f44f71c6104469b3531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 11 Sep 2019 22:20:42 -0500 Subject: [PATCH 075/342] Removes regex crate dependency. --- Cargo.lock | 1 - Cargo.toml | 1 - src/cli.rs | 88 ++++++++------ src/errors.rs | 10 -- src/plugins/str.rs | 236 +------------------------------------- tests/filter_str_tests.rs | 79 +------------ 6 files changed, 61 insertions(+), 354 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 08fcb94a0b..07ddf93483 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1542,7 +1542,6 @@ dependencies = [ "prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "ptree 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rawkey 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index f9e13c9348..b5256b8404 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,6 @@ unicode-xid = "0.2.0" serde_ini = "0.2.0" subprocess = "0.1.18" mime = "0.3.13" -regex = "1.2.1" pretty-hex = "0.1.0" hex = "0.3.2" tempfile = "3.1.0" diff --git a/src/cli.rs b/src/cli.rs index ec8c7085c6..6d02119074 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -15,7 +15,6 @@ use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode}; use crate::prelude::*; use log::{debug, trace}; -use regex::Regex; use rustyline::error::ReadlineError; use rustyline::{self, config::Configurer, config::EditMode, ColorMode, Config, Editor}; use std::env; @@ -98,38 +97,12 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel result } -fn load_plugins_in_dir(path: &std::path::PathBuf, context: &mut Context) -> Result<(), ShellError> { - let re_bin = Regex::new(r"^nu_plugin_[A-Za-z_]+$")?; - let re_exe = Regex::new(r"^nu_plugin_[A-Za-z_]+\.(exe|bat)$")?; +fn search_paths() -> Vec { + let mut search_paths = Vec::new(); - 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)?; - } - } - } - _ => {} - } - Ok(()) -} - -fn load_plugins(context: &mut Context) -> Result<(), ShellError> { match env::var_os("PATH") { Some(paths) => { - for path in env::split_paths(&paths) { - let _ = load_plugins_in_dir(&path, context); - } + search_paths = env::split_paths(&paths).collect::>(); } None => println!("PATH is not defined in the environment."), } @@ -140,7 +113,7 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { let mut path = std::path::PathBuf::from("."); path.push("target"); path.push("debug"); - let _ = load_plugins_in_dir(&path, context); + search_paths.push(path); } #[cfg(not(debug_assertions))] @@ -150,7 +123,58 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { path.push("target"); path.push("release"); - let _ = load_plugins_in_dir(&path, context); + search_paths.push(path); + } + + search_paths +} + +fn load_plugins(context: &mut Context) -> Result<(), ShellError> { + let opts = glob::MatchOptions { + case_sensitive: false, + require_literal_separator: false, + require_literal_leading_dot: false, + }; + + for path in search_paths() { + let pattern = path.join("nu_plugin_[a-z][a-z]*").clone(); + + trace!("Trying {:?}", pattern.display()); + + match glob::glob_with(&pattern.to_string_lossy(), opts) { + Err(_) => {} + Ok(binaries) => { + for bin in binaries.filter_map(Result::ok) { + if !bin.is_file() { + continue; + } + + trace!("Found {:?}", bin.display()); + + let bin_name = bin.file_name().unwrap().to_string_lossy(); + + let is_valid_name = bin_name + .chars() + .all(|c| c.is_ascii_alphabetic() || c == '_'); + + let is_executable = { + #[cfg(windows)] + { + bin_name.ends_with(".exe") || bin_name.ends_with(".bat") + } + + #[cfg(not(windows))] + { + true + } + }; + + if is_valid_name && is_executable { + load_plugin(&bin, context)?; + } + } + } + } } Ok(()) diff --git a/src/errors.rs b/src/errors.rs index d97435d226..579576e710 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -555,16 +555,6 @@ impl std::convert::From for ShellError { } } -impl std::convert::From for ShellError { - fn from(input: regex::Error) -> ShellError { - ProximateShellError::String(StringError { - title: format!("{:?}", input), - error: Value::nothing(), - }) - .start() - } -} - impl std::convert::From> for ShellError { fn from(input: Box) -> ShellError { ProximateShellError::String(StringError { diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 99700449b4..60b0146ef5 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -2,20 +2,12 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, SyntaxType, Tagged, Value, }; -use regex::Regex; #[derive(Debug, Eq, PartialEq)] enum Action { Downcase, Upcase, ToInteger, - Replace(ReplaceAction), -} - -#[derive(Debug, Eq, PartialEq)] -enum ReplaceAction { - Direct, - FindAndReplace, } struct Str { @@ -45,41 +37,12 @@ impl Str { Err(_) => Value::string(input), }, }, - Some(Action::Replace(ref mode)) => match mode { - ReplaceAction::Direct => Value::string(self.first_param()), - ReplaceAction::FindAndReplace => { - let regex = Regex::new(self.first_param()); - - match regex { - Ok(re) => Value::string(re.replace(input, self.second_param()).to_owned()), - Err(_) => Value::string(input), - } - } - }, None => Value::string(input), }; Ok(applied) } - fn did_supply_field(&self) -> bool { - self.field.is_some() - } - - fn first_param(&self) -> &str { - let idx = if self.did_supply_field() { 1 } else { 0 }; - self.get_param(idx) - } - - fn second_param(&self) -> &str { - let idx = if self.did_supply_field() { 2 } else { 1 }; - self.get_param(idx) - } - - fn get_param(&self, idx: usize) -> &str { - self.params.as_ref().unwrap().get(idx).unwrap().as_str() - } - fn for_field(&mut self, field: &str) { self.field = Some(String::from(field)); } @@ -92,14 +55,6 @@ impl Str { self.error = Some(message.to_string()); } - fn for_replace(&mut self, mode: ReplaceAction) { - if self.permit() { - self.action = Some(Action::Replace(mode)); - } else { - self.log_error("can only apply one"); - } - } - fn for_to_int(&mut self) { if self.permit() { self.action = Some(Action::ToInteger); @@ -125,7 +80,7 @@ impl Str { } pub fn usage() -> &'static str { - "Usage: str field [--downcase|--upcase|--to-int|--replace|--find-replace]" + "Usage: str field [--downcase|--upcase|--to-int]" } } @@ -172,8 +127,6 @@ impl Plugin for Str { .switch("downcase") .switch("upcase") .switch("to-int") - .switch("replace") - .switch("find-replace") .rest(SyntaxType::Member) .filter()) } @@ -190,12 +143,6 @@ impl Plugin for Str { if args.has("to-int") { self.for_to_int(); } - if args.has("replace") { - self.for_replace(ReplaceAction::Direct); - } - if args.has("find-replace") { - self.for_replace(ReplaceAction::FindAndReplace); - } if let Some(possible_field) = args.nth(0) { match possible_field { @@ -203,16 +150,6 @@ impl Plugin for Str { item: Value::Primitive(Primitive::String(s)), .. } => match self.action { - Some(Action::Replace(ReplaceAction::Direct)) => { - if args.len() == 2 { - self.for_field(&s); - } - } - Some(Action::Replace(ReplaceAction::FindAndReplace)) => { - if args.len() == 3 { - self.for_field(&s); - } - } Some(Action::Downcase) | Some(Action::Upcase) | Some(Action::ToInteger) @@ -258,7 +195,7 @@ fn main() { #[cfg(test)] mod tests { - use super::{Action, ReplaceAction, Str}; + use super::{Action, Str}; use indexmap::IndexMap; use nu::{ CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Span, Tag, Tagged, @@ -266,16 +203,6 @@ mod tests { }; use num_bigint::BigInt; - impl Str { - fn replace_with(&mut self, value: &str) { - self.params.as_mut().unwrap().push(value.to_string()); - } - - fn find_with(&mut self, search: &str) { - self.params.as_mut().unwrap().push(search.to_string()); - } - } - struct CallStub { positionals: Vec>, flags: IndexMap>, @@ -328,7 +255,7 @@ mod tests { let configured = plugin.config().unwrap(); - for action_flag in &["downcase", "upcase", "to-int", "replace", "find-replace"] { + for action_flag in &["downcase", "upcase", "to-int"] { assert!(configured.named.get(*action_flag).is_some()); } } @@ -362,33 +289,6 @@ mod tests { .is_ok()); assert_eq!(plugin.action.unwrap(), Action::ToInteger); } - - #[test] - fn str_plugin_accepts_replace() { - let mut plugin = Str::new(); - - assert!(plugin - .begin_filter(CallStub::new().with_long_flag("replace").create()) - .is_ok()); - assert_eq!( - plugin.action.unwrap(), - Action::Replace(ReplaceAction::Direct) - ); - } - - #[test] - fn str_plugin_accepts_find_replace() { - let mut plugin = Str::new(); - - assert!(plugin - .begin_filter(CallStub::new().with_long_flag("find-replace").create()) - .is_ok()); - assert_eq!( - plugin.action.unwrap(), - Action::Replace(ReplaceAction::FindAndReplace) - ); - } - #[test] fn str_plugin_accepts_field() { let mut plugin = Str::new(); @@ -441,26 +341,6 @@ mod tests { assert_eq!(strutils.apply("9999").unwrap(), Value::int(9999 as i64)); } - #[test] - fn str_replace() { - let mut strutils = Str::new(); - strutils.for_replace(ReplaceAction::Direct); - strutils.replace_with("robalino"); - assert_eq!(strutils.apply("andres").unwrap(), Value::string("robalino")); - } - - #[test] - fn str_find_replace() { - let mut strutils = Str::new(); - strutils.for_replace(ReplaceAction::FindAndReplace); - strutils.find_with(r"kittens"); - strutils.replace_with("jotandrehuda"); - assert_eq!( - strutils.apply("wykittens").unwrap(), - Value::string("wyjotandrehuda") - ); - } - #[test] fn str_plugin_applies_upcase_with_field() { let mut plugin = Str::new(); @@ -604,114 +484,4 @@ mod tests { _ => {} } } - - #[test] - fn str_plugin_applies_replace_with_field() { - let mut plugin = Str::new(); - - assert!(plugin - .begin_filter( - CallStub::new() - .with_parameter("rustconf") - .with_parameter("22nd August 2019") - .with_long_flag("replace") - .create() - ) - .is_ok()); - - let subject = structured_sample_record("rustconf", "1st January 1970"); - let output = plugin.filter(subject).unwrap(); - - match output[0].as_ref().unwrap() { - ReturnSuccess::Value(Tagged { - item: Value::Row(o), - .. - }) => assert_eq!( - *o.get_data(&String::from("rustconf")).borrow(), - Value::string(String::from("22nd August 2019")) - ), - _ => {} - } - } - - #[test] - fn str_plugin_applies_replace_without_field() { - let mut plugin = Str::new(); - - assert!(plugin - .begin_filter( - CallStub::new() - .with_parameter("22nd August 2019") - .with_long_flag("replace") - .create() - ) - .is_ok()); - - let subject = unstructured_sample_record("1st January 1970"); - let output = plugin.filter(subject).unwrap(); - - match output[0].as_ref().unwrap() { - ReturnSuccess::Value(Tagged { - item: Value::Primitive(Primitive::String(s)), - .. - }) => assert_eq!(*s, String::from("22nd August 2019")), - _ => {} - } - } - - #[test] - fn str_plugin_applies_find_replace_with_field() { - let mut plugin = Str::new(); - - assert!(plugin - .begin_filter( - CallStub::new() - .with_parameter("staff") - .with_parameter("kittens") - .with_parameter("jotandrehuda") - .with_long_flag("find-replace") - .create() - ) - .is_ok()); - - let subject = structured_sample_record("staff", "wykittens"); - let output = plugin.filter(subject).unwrap(); - - match output[0].as_ref().unwrap() { - ReturnSuccess::Value(Tagged { - item: Value::Row(o), - .. - }) => assert_eq!( - *o.get_data(&String::from("staff")).borrow(), - Value::string(String::from("wyjotandrehuda")) - ), - _ => {} - } - } - - #[test] - fn str_plugin_applies_find_replace_without_field() { - let mut plugin = Str::new(); - - assert!(plugin - .begin_filter( - CallStub::new() - .with_parameter("kittens") - .with_parameter("jotandrehuda") - .with_long_flag("find-replace") - .create() - ) - .is_ok()); - - let subject = unstructured_sample_record("wykittens"); - let output = plugin.filter(subject).unwrap(); - - match output[0].as_ref().unwrap() { - ReturnSuccess::Value(Tagged { - item: Value::Primitive(Primitive::String(s)), - .. - }) => assert_eq!(*s, String::from("wyjotandrehuda")), - _ => {} - } - } } diff --git a/tests/filter_str_tests.rs b/tests/filter_str_tests.rs index e26a18850e..55563c2fa0 100644 --- a/tests/filter_str_tests.rs +++ b/tests/filter_str_tests.rs @@ -11,7 +11,7 @@ fn can_only_apply_one() { ); assert!( - actual.contains("Usage: str field [--downcase|--upcase|--to-int|--replace|--find-replace]") + actual.contains("Usage: str field [--downcase|--upcase|--to-int") ); } @@ -91,79 +91,4 @@ fn converts_to_int() { )); assert_eq!(actual, "2509000000"); -} - -#[test] -fn replaces() { - Playground::setup("plugin_str_test_4", |dirs, sandbox| { - sandbox.with_files(vec![FileWithContent( - "sample.toml", - r#" - [package] - name = "nushell" - "#, - )]); - - let actual = nu!( - cwd: dirs.test(), h::pipeline( - r#" - open sample.toml - | str package.name --replace wykittenshell - | get package.name - | echo $it - "# - )); - - assert_eq!(actual, "wykittenshell"); - }) -} - -#[test] -fn find_and_replaces() { - Playground::setup("plugin_str_test_5", |dirs, sandbox| { - sandbox.with_files(vec![FileWithContent( - "sample.toml", - r#" - [fortune.teller] - phone = "1-800-KATZ" - "#, - )]); - - let actual = nu!( - cwd: dirs.test(), h::pipeline( - r#" - open sample.toml - | str fortune.teller.phone --find-replace KATZ "5289" - | get fortune.teller.phone - | echo $it - "# - )); - - assert_eq!(actual, "1-800-5289"); - }) -} - -#[test] -fn find_and_replaces_without_passing_field() { - Playground::setup("plugin_str_test_6", |dirs, sandbox| { - sandbox.with_files(vec![FileWithContent( - "sample.toml", - r#" - [fortune.teller] - phone = "1-800-KATZ" - "#, - )]); - - let actual = nu!( - cwd: dirs.test(), h::pipeline( - r#" - open sample.toml - | get fortune.teller.phone - | str --find-replace KATZ "5289" - | echo $it - "# - )); - - assert_eq!(actual, "1-800-5289"); - }) -} +} \ No newline at end of file From c57c0eb371935a00884d91ffad73161db6d0f43b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 11 Sep 2019 22:34:47 -0500 Subject: [PATCH 076/342] pass lint checks. --- src/cli.rs | 30 +++++++++++++++++++++++------- tests/filter_str_tests.rs | 6 ++---- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 6d02119074..461524f49d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -122,7 +122,6 @@ fn search_paths() -> Vec { let mut path = std::path::PathBuf::from("."); path.push("target"); path.push("release"); - search_paths.push(path); } @@ -137,9 +136,17 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { }; for path in search_paths() { - let pattern = path.join("nu_plugin_[a-z][a-z]*").clone(); + let mut pattern = path.to_path_buf(); - trace!("Trying {:?}", pattern.display()); + #[cfg(windows)] + { + pattern.push(std::path::Path::new("nu_plugin_[a-z]*.[a-z]*")); + } + + #[cfg(not(windows))] + { + pattern.push(std::path::Path::new("nu_plugin_[a-z]*")); + } match glob::glob_with(&pattern.to_string_lossy(), opts) { Err(_) => {} @@ -149,9 +156,16 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { continue; } - trace!("Found {:?}", bin.display()); - - let bin_name = bin.file_name().unwrap().to_string_lossy(); + let bin_name = { + if let Some(name) = bin.file_name() { + match name.to_str() { + Some(raw) => raw, + None => continue, + } + } else { + continue; + } + }; let is_valid_name = bin_name .chars() @@ -160,7 +174,8 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { let is_executable = { #[cfg(windows)] { - bin_name.ends_with(".exe") || bin_name.ends_with(".bat") + bin.ends_with(std::path::Path::new(".exe")) + || bin.ends_with(std::path::Path::new(".bat")) } #[cfg(not(windows))] @@ -170,6 +185,7 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { }; if is_valid_name && is_executable { + trace!("Trying {:?}", bin.display()); load_plugin(&bin, context)?; } } diff --git a/tests/filter_str_tests.rs b/tests/filter_str_tests.rs index 55563c2fa0..9f92186fa6 100644 --- a/tests/filter_str_tests.rs +++ b/tests/filter_str_tests.rs @@ -10,9 +10,7 @@ fn can_only_apply_one() { "open caco3_plastics.csv | first 1 | str origin --downcase --upcase" ); - assert!( - actual.contains("Usage: str field [--downcase|--upcase|--to-int") - ); + assert!(actual.contains("Usage: str field [--downcase|--upcase|--to-int")); } #[test] @@ -91,4 +89,4 @@ fn converts_to_int() { )); assert_eq!(actual, "2509000000"); -} \ No newline at end of file +} From e4ed8c94addcd983996ac8d96af6d4d9a6b13171 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Thu, 12 Sep 2019 02:11:38 -0500 Subject: [PATCH 077/342] dot character is valid in Windows plugin binaries. --- src/cli.rs | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 461524f49d..c3b3eaeb5a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -138,15 +138,7 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { for path in search_paths() { let mut pattern = path.to_path_buf(); - #[cfg(windows)] - { - pattern.push(std::path::Path::new("nu_plugin_[a-z]*.[a-z]*")); - } - - #[cfg(not(windows))] - { - pattern.push(std::path::Path::new("nu_plugin_[a-z]*")); - } + pattern.push(std::path::Path::new("nu_plugin_[a-z]*")); match glob::glob_with(&pattern.to_string_lossy(), opts) { Err(_) => {} @@ -167,15 +159,26 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { } }; - let is_valid_name = bin_name - .chars() - .all(|c| c.is_ascii_alphabetic() || c == '_'); + let is_valid_name = { + #[cfg(windows)] + { + bin_name + .chars() + .all(|c| c.is_ascii_alphabetic() || c == '_' || c == '.') + } + + #[cfg(not(windows))] + { + bin_name + .chars() + .all(|c| c.is_ascii_alphabetic() || c == '_') + } + }; let is_executable = { #[cfg(windows)] { - bin.ends_with(std::path::Path::new(".exe")) - || bin.ends_with(std::path::Path::new(".bat")) + bin_name.ends_with(".exe") || bin_name.ends_with(".bat") } #[cfg(not(windows))] From 7838dac68947f5f68258ca496f23bd2c2654cdb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Thu, 12 Sep 2019 05:22:58 -0500 Subject: [PATCH 078/342] first and get coverage. --- src/commands/get.rs | 18 ++++++- tests/commands_test.rs | 113 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 2 deletions(-) diff --git a/src/commands/get.rs b/src/commands/get.rs index 7f9c4f521f..19b5463d8b 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -7,6 +7,7 @@ pub struct Get; #[derive(Deserialize)] pub struct GetArgs { + member: Tagged, rest: Vec>, } @@ -16,7 +17,9 @@ impl WholeStreamCommand for Get { } fn signature(&self) -> Signature { - Signature::build("get").rest(SyntaxType::Member) + Signature::build("get") + .required("member", SyntaxType::Member) + .rest(SyntaxType::Member) } fn usage(&self) -> &str { @@ -63,13 +66,24 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result Result { let stream = input .values .map(move |item| { let mut result = VecDeque::new(); + + let member = vec![member.clone()]; + + let fields = vec![&member, &fields] + .into_iter() + .flatten() + .collect::>>(); + for field in &fields { match get_member(field, &item) { Ok(Tagged { diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 5188aee0aa..30636eafca 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -3,6 +3,119 @@ mod helpers; use helpers as h; use helpers::{Playground, Stub::*}; +#[test] +fn first_gets_first_rows_by_amount() { + Playground::setup("first_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("los.1.txt"), + EmptyFile("tres.1.txt"), + EmptyFile("amigos.1.txt"), + EmptyFile("arepas.1.clu"), + ]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + ls + | get name + | first 2 + | split-column "." + | get Column2 + | str --to-int + | sum + | echo $it + "# + )); + + assert_eq!(actual, "2"); + }) +} + +#[test] +fn first_requires_an_amount() { + Playground::setup("first_test_2", |dirs, _| { + let actual = nu_error!( + cwd: dirs.test(), "ls | first" + ); + + assert!(actual.contains("requires amount parameter")); + }) +} + +#[test] +fn get() { + Playground::setup("get_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + nu_party_venue = "zion" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + open sample.toml + | get nu_party_venue + | echo $it + "# + )); + + assert_eq!(actual, "zion"); + }) +} + +#[test] +fn get_more_than_one_member() { + Playground::setup("get_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [[fortune_tellers]] + name = "Andrés N. Robalino" + arepas = 1 + broken_builds = 0 + + [[fortune_tellers]] + name = "Jonathan Turner" + arepas = 1 + broken_builds = 1 + + [[fortune_tellers]] + name = "Yehuda Katz" + arepas = 1 + broken_builds = 1 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + open sample.toml + | get fortune_tellers + | get arepas broken_builds + | sum + | echo $it + "# + )); + + assert_eq!(actual, "5"); + }) +} + +#[test] +fn get_requires_at_least_one_member() { + Playground::setup("first_test_3", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("andres.txt")]); + + let actual = nu_error!( + cwd: dirs.test(), "ls | get" + ); + + assert!(actual.contains("requires member parameter")); + }) +} + #[test] fn lines() { let actual = nu!( From c2eefece0e2e7f49d0fc29cebe6739e76234559f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Thu, 12 Sep 2019 06:12:19 -0500 Subject: [PATCH 079/342] Remove warnings. --- src/commands/get.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/get.rs b/src/commands/get.rs index 19b5463d8b..2de6c7a018 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -67,7 +67,7 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result Date: Thu, 12 Sep 2019 09:12:53 -0400 Subject: [PATCH 080/342] changing base image to use updated libssl, adding tests to run --help for nu Signed-off-by: Vanessa Sochat --- .circleci/config.yml | 20 ++++++++++++++++++++ docker/Dockerfile | 9 +++++---- docker/Dockerfile.nu-base | 2 -- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0093753215..1595472977 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -52,6 +52,11 @@ workflows: command: | DOCKER_TAG=$(docker run quay.io/nushell/nu --version | cut -d' ' -f2) echo "Version that would be used for Docker tag is v${DOCKER_TAG}" + - run: + name: Test Executable + command: | + docker run --rm quay.io/nushell/nu-base --help + docker run --rm quay.io/nushell/nu --help # workflow publishes to Docker Hub, with each job having different triggers build_with_deploy: @@ -77,6 +82,11 @@ workflows: name: Build Multistage (smaller) container command: | docker build -f docker/Dockerfile -t quay.io/nushell/nu . + - run: + name: Test Executable + command: | + docker run --rm quay.io/nushell/nu --help + docker run --rm quay.io/nushell/nu-base --help - run: name: Publish Docker Tag with Nushell Version command: | @@ -109,6 +119,11 @@ workflows: name: Build Multistage (smaller) container command: | docker build --build-arg FROMTAG=devel -f docker/Dockerfile -t quay.io/nushell/nu:devel . + - run: + name: Test Executable + command: | + docker run --rm quay.io/nushell/nu:devel --help + docker run --rm quay.io/nushell/nu-base:devel --help - run: name: Publish Development Docker Tags command: | @@ -138,6 +153,11 @@ workflows: name: Build Multistage (smaller) container command: | docker build -f docker/Dockerfile -t quay.io/nushell/nu:nightly . + - run: + name: Test Executable + command: | + docker run --rm quay.io/nushell/nu:nightly --help + docker run --rm quay.io/nushell/nu-base:nightly --help - run: name: Publish Nightly Nushell Containers command: | diff --git a/docker/Dockerfile b/docker/Dockerfile index c4dafe3306..ffb1e5377d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,8 +1,9 @@ -ARG FROMTAG=latest +ARG FROMTAG=latest FROM quay.io/nushell/nu-base:${FROMTAG} as base -FROM rust:1.37-slim +FROM ubuntu:18.04 COPY --from=base /usr/local/bin/nu /usr/local/bin/nu -RUN apt-get update && \ - apt-get install -y pkg-config libssl-dev +ENV DEBIAN_FRONTEND noninteractive +RUN apt-get update && apt-get install -y libssl-dev \ + pkg-config ENTRYPOINT ["nu"] CMD ["-l", "info"] diff --git a/docker/Dockerfile.nu-base b/docker/Dockerfile.nu-base index 08cb0a8920..68b393b420 100644 --- a/docker/Dockerfile.nu-base +++ b/docker/Dockerfile.nu-base @@ -6,8 +6,6 @@ FROM ubuntu:16.04 ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y libssl-dev \ libxcb-composite0-dev \ - libx11-dev \ - libssl-dev \ pkg-config \ curl From a215997dcdc9d9123c6cff086b1dfb34bfea4a45 Mon Sep 17 00:00:00 2001 From: Jan Koprowski Date: Tue, 10 Sep 2019 18:40:14 +0200 Subject: [PATCH 081/342] Introduce debian packaging --- .gitignore | 7 +++++++ debian/changelog | 5 +++++ debian/compat | 1 + debian/control | 18 ++++++++++++++++++ debian/copyright | 32 ++++++++++++++++++++++++++++++++ debian/install | 1 + debian/postinst | 8 ++++++++ debian/postrm | 17 +++++++++++++++++ debian/rules | 25 +++++++++++++++++++++++++ debian/source/format | 1 + docker/Dockerfile.bionic | 23 +++++++++++++++++++++++ 11 files changed, 138 insertions(+) create mode 100644 debian/changelog create mode 100644 debian/compat create mode 100644 debian/control create mode 100644 debian/copyright create mode 100644 debian/install create mode 100644 debian/postinst create mode 100644 debian/postrm create mode 100755 debian/rules create mode 100644 debian/source/format create mode 100644 docker/Dockerfile.bionic diff --git a/.gitignore b/.gitignore index 2026921b61..d8aedd6cec 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,10 @@ **/*.rs.bk history.txt tests/fixtures/nuplayground + +# Debian/Ubuntu +debian/.debhelper/ +debian/debhelper-build-stamp +debian/files +debian/nu.substvars +debian/nu/ diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000000..d6f8273939 --- /dev/null +++ b/debian/changelog @@ -0,0 +1,5 @@ +nu (0.2.0-1) unstable; urgency=low + + * Initial release + + -- Jan Koprowski Wed, 04 Sep 2019 21:38:44 +0200 diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000000..f599e28b8a --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +10 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000000..50c156c8da --- /dev/null +++ b/debian/control @@ -0,0 +1,18 @@ +Source: nu +Section: shells +Priority: optional +Maintainer: Jan Koprowski +Build-Depends: debhelper (>= 10) +Standards-Version: 4.1.2 +Homepage: https://github.com/nushell/nushell +Vcs-Git: https://github.com/nushell/nushell.git +Vcs-Browser: https://github.com/nushell/nushell + +Package: nu +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: A modern shell for the GitHub era + The goal of this project is to take the Unix + philosophy of shells, where pipes connect simple + commands together, and bring it to the modern + style of development. diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000000..81ce9e5e34 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,32 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ +Upstream-Name: nu +Source: https://github.com/nushell/nushell + +Files: * +Copyright: 2019 Yehuda Katz + 2019 Jonathan Turner +License: MIT + +Files: debian/* +Copyright: 2019 Yehuda Katz + 2019 Jonathan Turner +License: MIT + +License: MIT + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + . + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + . + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/debian/install b/debian/install new file mode 100644 index 0000000000..eca0e05134 --- /dev/null +++ b/debian/install @@ -0,0 +1 @@ +target/release/nu usr/bin diff --git a/debian/postinst b/debian/postinst new file mode 100644 index 0000000000..861d76811d --- /dev/null +++ b/debian/postinst @@ -0,0 +1,8 @@ +#! /bin/bash + +if [ "$1" = configure ] && which add-shell >/dev/null +then + add-shell /usr/bin/nu +fi + +exit 0 diff --git a/debian/postrm b/debian/postrm new file mode 100644 index 0000000000..1e4655c7be --- /dev/null +++ b/debian/postrm @@ -0,0 +1,17 @@ +#!/bin/sh + +set -e + +case "$1" in + upgrade|failed-upgrade|abort-install|abort-upgrade) + ;; + remove|purge|disappear) + if which remove-shell >/dev/null && [ -f /etc/shells ]; then + remove-shell /usr/bin/nu + fi + ;; + *) + echo "postrm called with unknown argument \`$1'" >&2 + exit 1 + ;; +esac diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000000..e1c367c123 --- /dev/null +++ b/debian/rules @@ -0,0 +1,25 @@ +#!/usr/bin/make -f +# See debhelper(7) (uncomment to enable) +# output every command that modifies files on the build system. +#export DH_VERBOSE = 1 + + +# see FEATURE AREAS in dpkg-buildflags(1) +#export DEB_BUILD_MAINT_OPTIONS = hardening=+all + +# see ENVIRONMENT in dpkg-buildflags(1) +# package maintainers to append CFLAGS +#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic +# package maintainers to append LDFLAGS +#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed + + +%: + dh $@ + + +# dh_make generated override targets +# This is example for Cmake (See https://bugs.debian.org/641051 ) +#override_dh_auto_configure: +# dh_auto_configure -- # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) + diff --git a/debian/source/format b/debian/source/format new file mode 100644 index 0000000000..163aaf8d82 --- /dev/null +++ b/debian/source/format @@ -0,0 +1 @@ +3.0 (quilt) diff --git a/docker/Dockerfile.bionic b/docker/Dockerfile.bionic new file mode 100644 index 0000000000..5d5bafc48c --- /dev/null +++ b/docker/Dockerfile.bionic @@ -0,0 +1,23 @@ +FROM ubuntu:18.04 + +# docker build -f docker/Dockerfile.bionic . + +ENV DEBIAN_FRONTEND noninteractive +RUN apt-get update && apt-get install -y libssl-dev \ + libxcb-composite0-dev \ + libx11-dev \ + pkg-config \ + curl \ + devscripts \ + debhelper + +WORKDIR /code +COPY ./rust-toolchain ./rust-toolchain +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` +ENV PATH=/root/.cargo/bin:$PATH +COPY . /code +RUN rustc -Vv && cargo build --release +RUN debuild -b -us -uc -i +RUN dpkg -i ../nu_0.2.0-1_amd64.deb +RUN chsh -s /usr/bin/nu +RUN echo 'ls | get name | echo $it' | /usr/bin/nu From 189877e4dde9e2f0b7a0921955583c7eda6e22aa Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 13 Sep 2019 06:29:16 +1200 Subject: [PATCH 082/342] Improve help and make binary a primitive --- src/commands/autoview.rs | 4 +- src/commands/fetch.rs | 4 +- src/commands/from_bson.rs | 6 +- src/commands/from_sqlite.rs | 4 +- src/commands/help.rs | 125 ++++++++++++++++++++++++++++-------- src/commands/nth.rs | 2 +- src/commands/open.rs | 10 +-- src/commands/post.rs | 8 +-- src/commands/save.rs | 4 +- src/commands/to_bson.rs | 4 +- src/commands/to_json.rs | 2 +- src/commands/to_sqlite.rs | 4 +- src/commands/to_toml.rs | 2 +- src/commands/to_yaml.rs | 2 +- src/data/base.rs | 18 +++--- src/format/generic.rs | 5 -- src/plugins/binaryview.rs | 6 +- 17 files changed, 140 insertions(+), 70 deletions(-) diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index adce1df402..6a8d61eeea 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -39,7 +39,7 @@ pub fn autoview( if input.len() > 0 { if let Tagged { - item: Value::Binary(_), + item: Value::Primitive(Primitive::Binary(_)), .. } = input[0usize] { @@ -50,7 +50,7 @@ pub fn autoview( } else { for i in input { match i.item { - Value::Binary(b) => { + Value::Primitive(Primitive::Binary(b)) => { use pretty_hex::*; println!("{:?}", b.hex_dump()); } diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index e87ddf8347..f2ddc737dc 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -186,7 +186,7 @@ pub async fn fetch( })?; Ok(( None, - Value::Binary(buf), + Value::Primitive(Primitive::Binary(buf)), Tag { span, origin: Some(Uuid::new_v4()), @@ -219,7 +219,7 @@ pub async fn fetch( })?; Ok(( Some(image_ty.to_string()), - Value::Binary(buf), + Value::Primitive(Primitive::Binary(buf)), Tag { span, origin: Some(Uuid::new_v4()), diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index e2f5421bd9..c5f742057e 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -1,6 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::errors::ExpectedRange; use crate::data::{Primitive, TaggedDictBuilder, Value}; +use crate::errors::ExpectedRange; use crate::prelude::*; use bson::{decode_document, spec::BinarySubtype, Bson}; use std::str::FromStr; @@ -122,7 +122,7 @@ fn convert_bson_value_to_nu_value( ); collected.insert_tagged( "$binary".to_string(), - Value::Binary(bytes.to_owned()).tagged(tag), + Value::Primitive(Primitive::Binary(bytes.to_owned())).tagged(tag), ); collected.into_tagged_value() } @@ -207,7 +207,7 @@ fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result + Value::Primitive(Primitive::Binary(vb)) => match from_bson_bytes_to_value(vb, span) { Ok(x) => yield ReturnSuccess::value(x), Err(_) => { diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index c19c4fef10..a56ecd3058 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -106,7 +106,7 @@ fn convert_sqlite_value_to_nu_value(value: ValueRef, tag: impl Into + Clone // this unwrap is safe because we know the ValueRef is Text. Value::Primitive(Primitive::String(t.as_str().unwrap().to_string())).tagged(tag) } - ValueRef::Blob(u) => Value::Binary(u.to_owned()).tagged(tag), + ValueRef::Blob(u) => Value::binary(u.to_owned()).tagged(tag), } } @@ -137,7 +137,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result + Value::Primitive(Primitive::Binary(vb)) => match from_sqlite_bytes_to_value(vb, span) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { diff --git a/src/commands/help.rs b/src/commands/help.rs index 571868a02a..bcb2158f4d 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -1,8 +1,7 @@ -use crate::commands::command::CommandAction; use crate::commands::PerItemCommand; -use crate::errors::ShellError; use crate::data::{command_dict, TaggedDictBuilder}; -use crate::parser::registry; +use crate::errors::ShellError; +use crate::parser::registry::{self, NamedType, PositionalType}; use crate::prelude::*; pub struct Help; @@ -29,44 +28,116 @@ impl PerItemCommand for Help { ) -> Result { let span = call_info.name_span; - if call_info.args.len() == 0 { - return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( - Tagged::from_simple_spanned_item(Value::nothing(), span), - )))] - .into()); - } - - match call_info.args.expect_nth(0)? { - Tagged { + match call_info.args.nth(0) { + Some(Tagged { item: Value::Primitive(Primitive::String(document)), tag, - } => { + }) => { + let mut help = VecDeque::new(); if document == "commands" { - let mut specs = VecDeque::new(); - - for cmd in registry.names() { - let mut spec = TaggedDictBuilder::new(tag.clone()); + let mut sorted_names = registry.names(); + sorted_names.sort(); + for cmd in sorted_names { + let mut short_desc = TaggedDictBuilder::new(tag.clone()); let value = command_dict(registry.get_command(&cmd).unwrap(), tag.clone()); - spec.insert("name", cmd); - spec.insert( + short_desc.insert("name", cmd); + short_desc.insert( "description", value.get_data_by_key("usage").unwrap().as_string().unwrap(), ); - spec.insert_tagged("details", value); - specs.push_back(ReturnSuccess::value(spec.into_tagged_value())); + help.push_back(ReturnSuccess::value(short_desc.into_tagged_value())); } + } else { + if let Some(command) = registry.get_command(document) { + let mut long_desc = String::new(); - return Ok(specs.to_output_stream()); + long_desc.push_str(&command.usage()); + long_desc.push_str("\n"); + + let signature = command.signature(); + + let mut one_liner = String::new(); + one_liner.push_str(&signature.name); + one_liner.push_str(" "); + if signature.named.len() > 0 { + one_liner.push_str("{flags} "); + } + + for positional in signature.positional { + match positional { + PositionalType::Mandatory(name, _m) => { + one_liner.push_str(&format!("<{}> ", name)); + } + PositionalType::Optional(name, _o) => { + one_liner.push_str(&format!("({}) ", name)); + } + } + } + + if signature.rest_positional.is_some() { + one_liner.push_str(" ...args"); + } + long_desc.push_str(&format!("\nUsage:\n > {}\n", one_liner)); + + if signature.named.len() > 0 { + long_desc.push_str("\nflags:\n"); + for (flag, ty) in signature.named { + match ty { + NamedType::Switch => { + long_desc.push_str(&format!(" --{}\n", flag)); + } + NamedType::Mandatory(m) => { + long_desc.push_str(&format!( + " --{} <{}> (required parameter)\n", + flag, m + )); + } + NamedType::Optional(o) => { + long_desc.push_str(&format!(" --{} <{}>\n", flag, o)); + } + } + } + } + + // pub struct Signature { + // pub name: String, + // #[new(default)] + // pub usage: String, + // #[new(default)] + // pub positional: Vec, + // #[new(value = "None")] + // pub rest_positional: Option, + // #[new(default)] + // pub named: IndexMap, + // #[new(value = "false")] + // pub is_filter: bool, + // } + + help.push_back(ReturnSuccess::value( + Value::string(long_desc).tagged(tag.clone()), + )); + } } - Ok(OutputStream::empty()) + Ok(help.to_output_stream()) + } + _ => { + let msg = r#"Welcome to Nushell. +Here are some tips to help you get started. +* help commands - list all available commands +* help - display help about a particular command +You can also learn more at http://book.nushell.sh"#; + + let mut output_stream = VecDeque::new(); + + output_stream.push_back(ReturnSuccess::value( + Value::string(msg).simple_spanned(span), + )); + + Ok(output_stream.to_output_stream()) } - x => Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( - x.clone(), - )))] - .into()), } } } diff --git a/src/commands/nth.rs b/src/commands/nth.rs index 98ab6a10a9..554034d8e1 100644 --- a/src/commands/nth.rs +++ b/src/commands/nth.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Nth { } fn signature(&self) -> Signature { - Signature::build("nth").required("amount", SyntaxType::Any) + Signature::build("nth").required("row number", SyntaxType::Any) } fn usage(&self) -> &str { diff --git a/src/commands/open.rs b/src/commands/open.rs index 32a99c6f17..232399a390 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -171,7 +171,7 @@ pub async fn fetch( )), Err(_) => Ok(( None, - Value::Binary(bytes), + Value::Primitive(Primitive::Binary(bytes)), Tag { span, origin: Some(Uuid::new_v4()), @@ -182,7 +182,7 @@ pub async fn fetch( } else { Ok(( None, - Value::Binary(bytes), + Value::Primitive(Primitive::Binary(bytes)), Tag { span, origin: Some(Uuid::new_v4()), @@ -209,7 +209,7 @@ pub async fn fetch( )), Err(_) => Ok(( None, - Value::Binary(bytes), + Value::Primitive(Primitive::Binary(bytes)), Tag { span, origin: Some(Uuid::new_v4()), @@ -220,7 +220,7 @@ pub async fn fetch( } else { Ok(( None, - Value::Binary(bytes), + Value::Primitive(Primitive::Binary(bytes)), Tag { span, origin: Some(Uuid::new_v4()), @@ -231,7 +231,7 @@ pub async fn fetch( } _ => Ok(( None, - Value::Binary(bytes), + Value::Primitive(Primitive::Binary(bytes)), Tag { span, origin: Some(Uuid::new_v4()), diff --git a/src/commands/post.rs b/src/commands/post.rs index 8790c929a7..e72ed175ac 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -1,7 +1,7 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; -use crate::errors::ShellError; use crate::data::Value; +use crate::errors::ShellError; use crate::parser::hir::SyntaxType; use crate::parser::registry::Signature; use crate::prelude::*; @@ -167,7 +167,7 @@ pub async fn post( s.await } Tagged { - item: Value::Binary(b), + item: Value::Primitive(Primitive::Binary(b)), .. } => { let mut s = surf::post(location).body_bytes(b); @@ -277,7 +277,7 @@ pub async fn post( })?; Ok(( None, - Value::Binary(buf), + Value::Primitive(Primitive::Binary(buf)), Tag { span, origin: Some(Uuid::new_v4()), @@ -295,7 +295,7 @@ pub async fn post( })?; Ok(( Some(image_ty.to_string()), - Value::Binary(buf), + Value::Primitive(Primitive::Binary(buf)), Tag { span, origin: Some(Uuid::new_v4()), diff --git a/src/commands/save.rs b/src/commands/save.rs index 26bf8cb16d..77bd4433d8 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -1,6 +1,6 @@ use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand}; -use crate::errors::ShellError; use crate::data::Value; +use crate::errors::ShellError; use crate::prelude::*; use std::path::{Path, PathBuf}; @@ -60,7 +60,7 @@ macro_rules! process_binary_return_success { for res in $result_vec { match res { Ok(ReturnSuccess::Value(Tagged { - item: Value::Binary(b), + item: Value::Primitive(Primitive::Binary(b)), .. })) => { for u in b.into_iter() { diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index a77bebeacc..e1cd3a108f 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -58,7 +58,7 @@ pub fn value_to_bson_value(v: &Tagged) -> Result { .collect::>()?, ), Value::Block(_) => Bson::Null, - Value::Binary(b) => Bson::Binary(BinarySubtype::Generic, b.clone()), + Value::Primitive(Primitive::Binary(b)) => Bson::Binary(BinarySubtype::Generic, b.clone()), Value::Row(o) => object_value_to_bson(o)?, }) } @@ -254,7 +254,7 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { match bson_value_to_bytes(bson_value, name_span) { Ok(x) => yield ReturnSuccess::value( - Value::Binary(x).simple_spanned(name_span), + Value::Primitive(Primitive::Binary(x)).simple_spanned(name_span), ), _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a table with BSON-compatible structure.span() from pipeline", diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 35c03af32d..aa0e720dbf 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -51,7 +51,7 @@ pub fn value_to_json_value(v: &Tagged) -> Result serde_json::Value::Array(json_list(l)?), Value::Block(_) => serde_json::Value::Null, - Value::Binary(b) => serde_json::Value::Array( + Value::Primitive(Primitive::Binary(b)) => serde_json::Value::Array( b.iter() .map(|x| { serde_json::Value::Number(serde_json::Number::from_f64(*x as f64).unwrap()) diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index 0fd392f345..e4eef72c24 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -85,7 +85,6 @@ fn get_columns(rows: &Vec>) -> Result { fn nu_value_to_sqlite_string(v: Value) -> String { match v { - Value::Binary(u) => format!("x'{}'", encode(u)), Value::Primitive(p) => match p { Primitive::Nothing => "NULL".into(), Primitive::Int(i) => format!("{}", i), @@ -97,6 +96,7 @@ fn nu_value_to_sqlite_string(v: Value) -> String { Primitive::Boolean(_) => "0".into(), Primitive::Date(d) => format!("'{}'", d), Primitive::Path(p) => format!("'{}'", p.display().to_string().replace("'", "''")), + Primitive::Binary(u) => format!("x'{}'", encode(u)), Primitive::BeginningOfStream => "NULL".into(), Primitive::EndOfStream => "NULL".into(), }, @@ -195,7 +195,7 @@ fn sqlite_input_stream_to_bytes( } let mut out = Vec::new(); tempfile.read_to_end(&mut out)?; - Ok(Value::Binary(out).tagged(tag)) + Ok(Value::binary(out).tagged(tag)) } fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result { diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index e18e152363..6669c94cd6 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -50,7 +50,7 @@ pub fn value_to_toml_value(v: &Tagged) -> Result Value::Table(l) => toml::Value::Array(collect_values(l)?), Value::Block(_) => toml::Value::String("".to_string()), - Value::Binary(b) => { + Value::Primitive(Primitive::Binary(b)) => { toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect()) } Value::Row(o) => { diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index 915827252d..f4aa63ad6a 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -56,7 +56,7 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result serde_yaml::Value::Null, - Value::Binary(b) => serde_yaml::Value::Sequence( + Value::Primitive(Primitive::Binary(b)) => serde_yaml::Value::Sequence( b.iter() .map(|x| serde_yaml::Value::Number(serde_yaml::Number::from(*x))) .collect(), diff --git a/src/data/base.rs b/src/data/base.rs index aa491ebdcb..c80cf409f0 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -24,6 +24,8 @@ pub enum Primitive { Boolean(bool), Date(DateTime), Path(PathBuf), + #[serde(with = "serde_bytes")] + Binary(Vec), // Stream markers (used as bookend markers rather than actual values) BeginningOfStream, @@ -58,6 +60,7 @@ impl Primitive { String(_) => "string", Boolean(_) => "boolean", Date(_) => "date", + Binary(_) => "binary", } .to_string() } @@ -77,6 +80,7 @@ impl Primitive { String(string) => write!(f, "{:?}", string), Boolean(boolean) => write!(f, "{}", boolean), Date(date) => write!(f, "{}", date), + Binary(binary) => write!(f, "{:?}", binary), } } @@ -121,6 +125,7 @@ impl Primitive { (true, Some(_)) => format!("Yes"), (false, Some(_)) => format!("No"), }, + Primitive::Binary(_) => format!(""), Primitive::Date(d) => format!("{}", d.humanize()), } } @@ -175,8 +180,6 @@ impl Block { pub enum Value { Primitive(Primitive), Row(crate::data::Dictionary), - #[serde(with = "serde_bytes")] - Binary(Vec), Table(Vec>), Block(Block), @@ -227,7 +230,6 @@ impl fmt::Debug for ValueDebug<'_> { Value::Row(o) => o.debug(f), Value::Table(l) => debug_list(l).fmt(f), Value::Block(_) => write!(f, "[[block]]"), - Value::Binary(_) => write!(f, "[[binary]]"), } } } @@ -288,7 +290,7 @@ impl std::convert::TryFrom<&Tagged> for Vec { fn try_from(value: &Tagged) -> Result, ShellError> { match value.item() { - Value::Binary(b) => Ok(b.clone()), + Value::Primitive(Primitive::Binary(b)) => Ok(b.clone()), v => Err(ShellError::type_error( "Binary", value.copy_span(v.type_name()), @@ -347,7 +349,6 @@ impl Value { Value::Row(_) => format!("object"), Value::Table(_) => format!("list"), Value::Block(_) => format!("block"), - Value::Binary(_) => format!("binary"), } } @@ -363,7 +364,6 @@ impl Value { .collect(), Value::Block(_) => vec![], Value::Table(_) => vec![], - Value::Binary(_) => vec![], } } @@ -495,7 +495,6 @@ impl Value { Value::Row(o) => o.get_data(desc), Value::Block(_) => MaybeOwned::Owned(Value::nothing()), Value::Table(_) => MaybeOwned::Owned(Value::nothing()), - Value::Binary(_) => MaybeOwned::Owned(Value::nothing()), } } @@ -514,7 +513,6 @@ impl Value { l.len(), if l.len() == 1 { "row" } else { "rows" } ), - Value::Binary(_) => format!(""), } } @@ -602,6 +600,10 @@ impl Value { Value::Primitive(Primitive::Decimal(s.into())) } + pub fn binary(binary: Vec) -> Value { + Value::Primitive(Primitive::Binary(binary)) + } + pub fn number(s: impl Into) -> Value { let num = s.into(); diff --git a/src/format/generic.rs b/src/format/generic.rs index 57726f819c..b6f9e29f26 100644 --- a/src/format/generic.rs +++ b/src/format/generic.rs @@ -35,11 +35,6 @@ impl RenderView for GenericView<'_> { view.render_view(host)?; Ok(()) } - - Value::Binary(_) => { - host.stdout(""); - Ok(()) - } } } } diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index a6b8df8990..3b92177374 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -1,5 +1,7 @@ use crossterm::{cursor, terminal, Attribute, RawScreen}; -use nu::{serve_plugin, CallInfo, Plugin, ShellError, Signature, SpanSource, Tagged, Value}; +use nu::{ + serve_plugin, CallInfo, Plugin, Primitive, ShellError, Signature, SpanSource, Tagged, Value, +}; use pretty_hex::*; struct BinaryView; @@ -21,7 +23,7 @@ impl Plugin for BinaryView { for v in input { let value_origin = v.origin(); match v.item { - Value::Binary(b) => { + Value::Primitive(Primitive::Binary(b)) => { let source = value_origin.and_then(|x| call_info.source_map.get(&x)); let _ = view_binary(&b, source, call_info.args.has("lores")); } From d0d56deaf1f48f8d88cd97798b712a5eaac15e39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Thu, 12 Sep 2019 18:49:29 -0500 Subject: [PATCH 083/342] Permit Nu finding and picking up development plugins if there are any first. --- src/cli.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index c3b3eaeb5a..7b6f6e863e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -113,7 +113,10 @@ fn search_paths() -> Vec { let mut path = std::path::PathBuf::from("."); path.push("target"); path.push("debug"); - search_paths.push(path); + + if path.exists() { + search_paths.push(path); + } } #[cfg(not(debug_assertions))] @@ -122,9 +125,15 @@ fn search_paths() -> Vec { let mut path = std::path::PathBuf::from("."); path.push("target"); path.push("release"); - search_paths.push(path); + + if path.exists() { + search_paths.push(path); + } } + // permit Nu finding and picking up development plugins + // if there are any first. + search_paths.reverse(); search_paths } From b11a4535bdf8f44d93e71f97b3742d9b2f4672bd Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 13 Sep 2019 13:54:17 +1200 Subject: [PATCH 084/342] Bump compiler --- rust-toolchain | 2 +- src/evaluate/evaluator.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/rust-toolchain b/rust-toolchain index e6ae9d2242..8f93bd146e 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2019-08-30 +nightly-2019-09-11 diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 52edf69818..d067a65a65 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -45,7 +45,9 @@ pub(crate) fn evaluate_baseline_expr( expr.span(), )), RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.span())), - RawExpression::Synthetic(hir::Synthetic::String(s)) => Ok(Value::string(s).tagged_unknown()), + 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), RawExpression::Binary(binary) => { From 4b8d0d62eb096ef7f2bd842305b6aae456da4954 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 13 Sep 2019 15:04:14 +1200 Subject: [PATCH 085/342] Update README.md --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 28f5c15656..48c1c155bf 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,13 @@ This project has reached a minimum-viable product level of quality. While contri Nu comes with a set 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. -There is also a [book](https://book.nushell.sh) about Nu, currently in progress. +# Learning more + +There are a few good resources to learn about Nu. First, there is a [book](https://book.nushell.sh) about Nu, currently in progress. The book focuses on using Nu and its core concepts. + +If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-handbook/tree/master/en). + +We also have an active [discord](https://discord.gg/NtAbbGn) and [twitter](https://twitter.com/nu_shell) if you'd like to come chat with us. # Installation From 53cb40d8f66a4a774048c17975897d2971256bfb Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 13 Sep 2019 15:44:21 +1200 Subject: [PATCH 086/342] Add basic 'did you mean' support --- Cargo.lock | 192 ++++++++++++++++++++++++-------------------- Cargo.toml | 19 ++--- src/commands/get.rs | 13 ++- 3 files changed, 125 insertions(+), 99 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 07ddf93483..3fea3453ee 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,7 @@ dependencies = [ [[package]] name = "ansi_term" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -83,7 +83,7 @@ name = "backtrace-sys" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -116,10 +116,10 @@ name = "bigdecimal" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -129,7 +129,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -158,14 +158,14 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "decimal 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -178,7 +178,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -221,8 +221,12 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.38" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "jobserver 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "cfg-if" @@ -231,13 +235,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "chrono" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -246,7 +250,7 @@ name = "chrono-humanize" version = "0.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -299,7 +303,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -448,7 +452,7 @@ dependencies = [ "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -497,7 +501,7 @@ name = "curl-sys" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "libnghttp2-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", @@ -531,11 +535,11 @@ version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "ord_subset 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -788,10 +792,11 @@ dependencies = [ [[package]] name = "futures-timer" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -841,12 +846,12 @@ dependencies = [ [[package]] name = "git2" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "libgit2-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1077,12 +1082,11 @@ dependencies = [ [[package]] name = "image" -version = "0.22.1" +version = "0.22.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1091,10 +1095,10 @@ dependencies = [ [[package]] name = "indexmap" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1165,6 +1169,16 @@ name = "itoa" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "jobserver" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "jpeg-decoder" version = "0.1.15" @@ -1199,7 +1213,7 @@ dependencies = [ "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "render-tree 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1238,10 +1252,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libgit2-sys" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1252,7 +1266,7 @@ name = "libnghttp2-sys" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1261,7 +1275,7 @@ name = "libsqlite3-sys" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1271,7 +1285,7 @@ name = "libz-sys" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1315,11 +1329,6 @@ dependencies = [ "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "lzw" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "macaddr" version = "0.1.1" @@ -1369,18 +1378,15 @@ dependencies = [ [[package]] name = "mime" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] [[package]] name = "mime_guess" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1389,7 +1395,7 @@ name = "miniz-sys" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1406,12 +1412,17 @@ name = "miniz_oxide_c_api" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "miniz_oxide 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "natural" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "neso" version = "0.5.0" @@ -1420,7 +1431,7 @@ dependencies = [ "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1431,7 +1442,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1443,7 +1454,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1495,7 +1506,7 @@ dependencies = [ name = "nu" version = "0.2.0" dependencies = [ - "ansi_term 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "battery 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1503,7 +1514,7 @@ dependencies = [ "bson 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "byte-unit 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1516,23 +1527,24 @@ dependencies = [ "enum-utils 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures-async-stream 0.1.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-timer 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "getset 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", - "git2 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "git2 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "heim 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "image 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)", - "indexmap 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "language-reporting 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", + "natural 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1546,7 +1558,7 @@ dependencies = [ "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1568,12 +1580,13 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1680,7 +1693,7 @@ name = "onig_sys" version = "69.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1695,7 +1708,7 @@ version = "0.9.49" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1783,7 +1796,7 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "line-wrap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1824,7 +1837,7 @@ name = "pretty_env_logger" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)", + "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1868,7 +1881,7 @@ dependencies = [ "directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde-value 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "tint 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2034,7 +2047,7 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2243,7 +2256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.99" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2279,7 +2292,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2287,7 +2300,7 @@ name = "serde_bytes" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2315,7 +2328,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2324,10 +2337,10 @@ name = "serde_json" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "indexmap 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2345,7 +2358,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2356,7 +2369,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2449,9 +2462,9 @@ dependencies = [ "isahc 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2505,7 +2518,7 @@ dependencies = [ "onig 4.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "plist 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2611,7 +2624,7 @@ name = "toml" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2619,7 +2632,7 @@ name = "toml" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2728,7 +2741,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2971,7 +2984,7 @@ dependencies = [ "checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -"checksum ansi_term 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eaa72766c3585a1f812a3387a7e2c6cab780f899c2f43ff6ea06c8d071fcbb36" +"checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" @@ -2994,9 +3007,9 @@ dependencies = [ "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" -"checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" +"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" -"checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" +"checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" "checksum chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2ff48a655fe8d2dae9a39e66af7fd8ff32a879e8c4e27422c25596a8b5e90d" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" "checksum clipboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "25a904646c0340239dcf7c51677b33928bf24fdf424b79a57909c0109075b2e7" @@ -3058,12 +3071,12 @@ dependencies = [ "checksum futures-io-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "ee7de0c1c9ed23f9457b0437fec7663ce64d9cc3c906597e714e529377b5ddd1" "checksum futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "efa8f90c4fb2328e381f8adfd4255b4a2b696f77d1c63a3dee6700b564c4e4b5" "checksum futures-sink-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "e9b65a2481863d1b78e094a07e9c0eed458cc7dc6e72b22b7138b8a67d924859" -"checksum futures-timer 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8f9eb554aa23143abc64ec4d0016f038caf53bb7cbc3d91490835c54edc96550" +"checksum futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "878f1d2fc31355fa02ed2372e741b0c17e58373341e6a122569b4623a14a7d33" "checksum futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "7df53daff1e98cc024bf2720f3ceb0414d96fbb0a94f3cad3a5c3bf3be1d261c" "checksum futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "36552cd31353fd135114510d53b8d120758120c36aa636a9341970f9efb1e4a0" "checksum getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "34f33de6f0ae7c9cb5e574502a562e2b512799e32abb801cd1e79ad952b62b49" "checksum getset 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "117a5b13aecd4e10161bb3feb22dda898e8552836c2391d8e4645d5e703ab866" -"checksum git2 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "327d698f86a7ebdfeb86a4238ccdb004828939d3a3555b6ead679541d14e36c0" +"checksum git2 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39f27186fbb5ec67ece9a56990292bc5aed3c3fc51b9b07b0b52446b1dfb4a82" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum heim 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4e61bf22465c7f49852fd7e6044a395394962a2eaac0b5c1b87b5b0f010b0f48" @@ -3082,8 +3095,8 @@ dependencies = [ "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum image 0.22.1 (registry+https://github.com/rust-lang/crates.io-index)" = "663a975007e0b49903e2e8ac0db2c432c465855f2d65f17883ba1476e85f0b42" -"checksum indexmap 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a4d6d89e0948bf10c08b9ecc8ac5b83f07f857ebe2c0cbe38de15b4e4f510356" +"checksum image 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ee0665404aa0f2ad154021777b785878b0e5b1c1da030455abc3d9ed257c2c67" +"checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3" "checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" "checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" "checksum isahc 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e1b971511b5d8de4a51d4da4bc8e374bf60ce841e91b116f46ae06ae2e2a8e9b" @@ -3091,6 +3104,7 @@ dependencies = [ "checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" +"checksum jobserver 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f74e73053eaf95399bf926e48fc7a2a3ce50bd0eaaa2357d391e95b2dcdd4f10" "checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d" "checksum js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "1efc4f2a556c58e79c5500912e221dd826bec64ff4aabd8ce71ccef6da02d7d4" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" @@ -3100,7 +3114,7 @@ dependencies = [ "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum lexical-core 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b0f90c979adde96d19eb10eb6431ba0c441e2f9e9bdff868b2f6f5114ff519" "checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" -"checksum libgit2-sys 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c2078aec6f4b16d1b89f6a72e4f6eb1e75ffa85312023291e89c6d3087bc8fb" +"checksum libgit2-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a30f8637eb59616ee3b8a00f6adff781ee4ddd8343a615b8238de756060cc1b3" "checksum libnghttp2-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02254d44f4435dd79e695f2c2b83cd06a47919adea30216ceaf0c57ca0a72463" "checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25" "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" @@ -3109,7 +3123,6 @@ dependencies = [ "checksum linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ae91b68aebc4ddb91978b11a1b02ddd8602a05ec19002801c5666000e05e0f83" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum lru-cache 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" -"checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" "checksum macaddr 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ff4752cb15cffb3e68f7dcb22e0818ac871f8c98fb07a634a81f41fb202a09f" "checksum mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "86dd2487cdfea56def77b88438a2c915fb45113c5319bfe7e14306ca4cd0b0e1" "checksum mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" @@ -3117,11 +3130,12 @@ dependencies = [ "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e6bcd6433cff03a4bfc3d9834d504467db1f1cf6d0ea765d37d330249ed629d" "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" -"checksum mime 0.3.13 (registry+https://github.com/rust-lang/crates.io-index)" = "3e27ca21f40a310bd06d9031785f4801710d566c184a6e15bad4f1d9b65f9425" +"checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" "checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" "checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" "checksum miniz_oxide 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe2959c5a0747a8d7a56b4444c252ffd2dda5d452cfd147cdfdda73b1c3ece5b" "checksum miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6c675792957b0d19933816c4e1d56663c341dd9bfa31cb2140ff2267c1d8ecf4" +"checksum natural 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd659d7d6b4554da2c0e7a486d5952b24dfce0e0bac88ab53b270f4efe1010a6" "checksum neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3c31defbcb081163db18437fd88c2a267cb3e26f7bd5e4b186e4b1b38fe8c8" "checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" "checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" @@ -3130,7 +3144,7 @@ dependencies = [ "checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b" "checksum nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4312467f8b28d909344b934207e502212fa5a3adf1bff7428b0b86a666223d" "checksum ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602" -"checksum num-bigint 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "57450397855d951f1a41305e54851b1a7b8f5d2e349543a02a2effe25459f718" +"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "76bd5272412d173d6bf9afdf98db8612bbabc9a7a830b7bfc9c188911716132e" "checksum num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f2885278d5fe2adc2f75ced642d52d879bffaceb5a2e0b1d4309ffdfb239b454" @@ -3210,7 +3224,7 @@ dependencies = [ "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" -"checksum serde 1.0.99 (registry+https://github.com/rust-lang/crates.io-index)" = "fec2851eb56d010dc9a21b89ca53ee75e6528bab60c11e89d38390904982da9f" +"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" "checksum serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0b833c5ad67d52ced5f5938b2980f32a9c1c5ef047f0b4fb3127e7a423c76153" "checksum serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" "checksum serde-value 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7a663f873dedc4eac1a559d4c6bc0d0b2c34dc5ac4702e105014b8281489e44f" diff --git a/Cargo.toml b/Cargo.toml index b5256b8404..44074162fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,14 +15,14 @@ documentation = "https://book.nushell.sh" [dependencies] rustyline = "5.0.2" -chrono = { version = "0.4.7", features = ["serde"] } +chrono = { version = "0.4.9", features = ["serde"] } derive-new = "0.5.8" prettytable-rs = "0.8.0" itertools = "0.8.0" -ansi_term = "0.12.0" +ansi_term = "0.12.1" nom = "5.0.0" dunce = "1.0.0" -indexmap = { version = "1.1.0", features = ["serde-1"] } +indexmap = { version = "1.2.0", features = ["serde-1"] } chrono-humanize = "0.0.11" byte-unit = "3.0.1" base64 = "0.10.1" @@ -34,7 +34,7 @@ term = "0.5.2" bytes = "0.4.12" log = "0.4.8" pretty_env_logger = "0.3.1" -serde = { version = "1.0.99", features = ["derive"] } +serde = { version = "1.0.100", features = ["derive"] } bson = { version = "0.14.0", features = ["decimal128"] } serde_json = "1.0.40" serde-hjson = "0.9.1" @@ -46,7 +46,7 @@ app_dirs = "1.2.1" csv = "1.1" toml = "0.5.3" clap = "2.33.0" -git2 = { version = "0.10.0", default_features = false } +git2 = { version = "0.10.1", default_features = false } dirs = "2.0.2" glob = "0.3.0" ctrlc = "3.1.3" @@ -58,7 +58,7 @@ enum-utils = "0.1.1" unicode-xid = "0.2.0" serde_ini = "0.2.0" subprocess = "0.1.18" -mime = "0.3.13" +mime = "0.3.14" pretty-hex = "0.1.0" hex = "0.3.2" tempfile = "3.1.0" @@ -67,10 +67,11 @@ which = "2.0.1" uuid = {version = "0.7.4", features = [ "v4", "serde" ]} textwrap = {version = "0.11.0", features = ["term_size"]} shellexpand = "1.0.0" -futures-timer = "0.3.0" +futures-timer = "0.4.0" pin-utils = "0.1.0-alpha.4" -num-bigint = { version = "0.2.2", features = ["serde"] } +num-bigint = { version = "0.2.3", features = ["serde"] } bigdecimal = { version = "0.1.0", features = ["serde"] } +natural = "0.3.0" neso = { version = "0.5.0", optional = true } crossterm = { version = "0.10.2", optional = true } @@ -81,7 +82,7 @@ battery = {version = "0.7.4", optional = true } rawkey = {version = "0.1.2", optional = true } clipboard = {version = "0.5", optional = true } ptree = {version = "0.2", optional = true } -image = { version = "0.22.1", default_features = false, features = ["png_codec", "jpeg"], optional = true } +image = { version = "0.22.2", default_features = false, features = ["png_codec", "jpeg"], optional = true } [features] raw-key = ["rawkey", "neso"] diff --git a/src/commands/get.rs b/src/commands/get.rs index 2de6c7a018..f019c2a564 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -47,9 +47,20 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result return Ok(v.clone()), None => { + let possibilities = obj.data_descriptors(); + + let mut possible_matches: Vec<_> = possibilities + .iter() + .map(|x| { + (natural::distance::levenshtein_distance(x, &path.item), x) + }) + .collect(); + + possible_matches.sort(); + return Err(ShellError::labeled_error( "Unknown column", - "table missing column", + format!("did you mean '{}'?", possible_matches[0].1), path.span(), )); } From 074a76c9d42bc89f637f34ed3598002830167f5d Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Fri, 13 Sep 2019 15:48:32 +1200 Subject: [PATCH 087/342] Fix test --- tests/command_config_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/command_config_test.rs b/tests/command_config_test.rs index 3321f85dc1..2dbac01556 100644 --- a/tests/command_config_test.rs +++ b/tests/command_config_test.rs @@ -108,7 +108,7 @@ fn removes_configuration_value() { dirs.config_path() ); - assert!(actual.contains("table missing column")); + assert!(actual.contains("did you mean")); }); h::delete_file_at(nu::config_path().unwrap().join("test_5.toml")); From 579c7ff6d6877bfeeb15cfc192664de4487a8ac2 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Fri, 13 Sep 2019 08:46:52 -0400 Subject: [PATCH 088/342] updating base to 18.04 Signed-off-by: Vanessa Sochat --- docker/Dockerfile.nu-base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.nu-base b/docker/Dockerfile.nu-base index 68b393b420..206bb02369 100644 --- a/docker/Dockerfile.nu-base +++ b/docker/Dockerfile.nu-base @@ -1,4 +1,4 @@ -FROM ubuntu:16.04 +FROM ubuntu:18.04 # docker build -f docker/Dockerfile.nu-base -t nushell/nu-base . # docker run -it nushell/nu-base From 9382a7e64a8f1cf142e196194e586c743f266438 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 14 Sep 2019 05:51:40 +1200 Subject: [PATCH 089/342] Detach externals so they don't freeze while buffering --- src/commands/classified.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 277a925341..2b733c88ef 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -307,10 +307,12 @@ impl ExternalCommand { Ok(ClassifiedInputStream::new()) } StreamNext::External => { + let _ = popen.detach(); let stdout = popen.stdout.take().unwrap(); Ok(ClassifiedInputStream::from_stdout(stdout)) } StreamNext::Internal => { + let _ = popen.detach(); let stdout = popen.stdout.take().unwrap(); let file = futures::io::AllowStdIo::new(stdout); let stream = Framed::new(file, LinesCodec {}); From 4922028d691a5c902c8f50dc71a7c0737d37b0ec Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 14 Sep 2019 10:58:28 +1200 Subject: [PATCH 090/342] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 48c1c155bf..ca3602fd99 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Nu comes with a set of built-in commands (listed below). If a command is unknown There are a few good resources to learn about Nu. First, there is a [book](https://book.nushell.sh) about Nu, currently in progress. The book focuses on using Nu and its core concepts. -If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-handbook/tree/master/en). +If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-handbook/tree/master/en) to help get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. We also have an active [discord](https://discord.gg/NtAbbGn) and [twitter](https://twitter.com/nu_shell) if you'd like to come chat with us. From 417916d2da1af376c3acdb7d8440d92d621600af Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 14 Sep 2019 11:10:08 +1200 Subject: [PATCH 091/342] Switch to rustline git instead of crates.io There have been a few fixes in rustyline we want to help test, so let's switch to their latest master ahead of the next release. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 44074162fc..04307ac7f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ documentation = "https://book.nushell.sh" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rustyline = "5.0.2" +rustyline = { git = "https://github.com/kkawakam/rustyline" } chrono = { version = "0.4.9", features = ["serde"] } derive-new = "0.5.8" prettytable-rs = "0.8.0" From ab915f1c4439dd63fa6c7e2113b58e93b9e218c6 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 14 Sep 2019 11:30:24 -0500 Subject: [PATCH 092/342] Revert "Revert "Migrate most uses of the Span concept to Tag"" This reverts commit bee7c5639cc78d65e6656518627a86009a522500. --- Cargo.lock | 8 +- Cargo.toml | 2 +- src/cli.rs | 25 ++- src/commands/autoview.rs | 5 +- src/commands/cd.rs | 2 +- src/commands/classified.rs | 31 ++-- src/commands/clip.rs | 2 +- src/commands/command.rs | 30 ++-- src/commands/config.rs | 44 ++--- src/commands/cp.rs | 8 +- src/commands/date.rs | 42 ++--- src/commands/echo.rs | 8 +- src/commands/enter.rs | 6 +- src/commands/fetch.rs | 80 +++------ src/commands/first.rs | 2 +- src/commands/from_bson.rs | 12 +- src/commands/from_csv.rs | 12 +- src/commands/from_ini.rs | 12 +- src/commands/from_json.rs | 18 +- src/commands/from_sqlite.rs | 12 +- src/commands/from_toml.rs | 12 +- src/commands/from_tsv.rs | 12 +- src/commands/from_xml.rs | 12 +- src/commands/from_yaml.rs | 12 +- src/commands/get.rs | 8 +- src/commands/help.rs | 21 ++- src/commands/last.rs | 2 +- src/commands/lines.rs | 6 +- src/commands/ls.rs | 17 +- src/commands/mkdir.rs | 2 +- src/commands/mv.rs | 8 +- src/commands/nth.rs | 2 +- src/commands/open.rs | 65 +++---- src/commands/pick.rs | 2 +- src/commands/post.rs | 84 ++++----- src/commands/reject.rs | 2 +- src/commands/rm.rs | 4 +- src/commands/save.rs | 32 ++-- src/commands/shells.rs | 4 +- src/commands/size.rs | 8 +- src/commands/skip_while.rs | 2 +- src/commands/sort_by.rs | 2 +- src/commands/split_column.rs | 6 +- src/commands/split_row.rs | 4 +- src/commands/tags.rs | 2 +- src/commands/to_bson.rs | 32 ++-- src/commands/to_csv.rs | 10 +- src/commands/to_json.rs | 12 +- src/commands/to_sqlite.rs | 6 +- src/commands/to_toml.rs | 12 +- src/commands/to_tsv.rs | 10 +- src/commands/to_yaml.rs | 12 +- src/commands/trim.rs | 2 +- src/commands/version.rs | 6 +- src/commands/where_.rs | 6 +- src/commands/which_.rs | 10 +- src/context.rs | 12 +- src/data/base.rs | 18 +- src/data/config.rs | 12 +- src/data/into.rs | 4 +- src/data/meta.rs | 164 +++++++++++++----- src/errors.rs | 96 +++++----- src/evaluate/evaluator.rs | 53 +++--- src/format/table.rs | 2 +- src/lib.rs | 4 +- src/parser.rs | 4 +- src/parser/deserializer.rs | 6 +- src/parser/hir.rs | 65 +++---- src/parser/hir/baseline_parse.rs | 106 ++++++------ src/parser/hir/baseline_parse_tokens.rs | 161 ++++++++--------- src/parser/hir/external_command.rs | 2 +- src/parser/hir/named.rs | 5 +- src/parser/parse/files.rs | 35 ++-- src/parser/parse/flag.rs | 4 +- src/parser/parse/parser.rs | 157 ++++++++++------- src/parser/parse/pipeline.rs | 10 +- src/parser/parse/token_tree.rs | 44 ++--- src/parser/parse/token_tree_builder.rs | 221 ++++++++++-------------- src/parser/parse/tokens.rs | 32 ++-- src/parser/parse_command.rs | 27 ++- src/parser/registry.rs | 38 ++-- src/plugins/add.rs | 8 +- src/plugins/edit.rs | 6 +- src/plugins/embed.rs | 6 +- src/plugins/inc.rs | 42 +++-- src/plugins/ps.rs | 4 +- src/plugins/skip.rs | 6 +- src/plugins/str.rs | 16 +- src/plugins/sum.rs | 12 +- src/plugins/sys.rs | 2 +- src/prelude.rs | 5 +- src/shell/filesystem_shell.rs | 112 ++++++------ src/shell/help_shell.rs | 19 +- src/shell/helper.rs | 38 ++-- src/shell/shell.rs | 14 +- src/shell/shell_manager.rs | 8 +- src/shell/value_shell.rs | 32 ++-- src/traits.rs | 4 +- 98 files changed, 1178 insertions(+), 1248 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3fea3453ee..063d3402b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1485,8 +1485,8 @@ dependencies = [ ] [[package]] -name = "nom5_locate" -version = "0.1.1" +name = "nom_locate" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1543,7 +1543,7 @@ dependencies = [ "natural 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3142,7 +3142,7 @@ dependencies = [ "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b" -"checksum nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4312467f8b28d909344b934207e502212fa5a3adf1bff7428b0b86a666223d" +"checksum nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f932834fd8e391fc7710e2ba17e8f9f8645d846b55aa63207e17e110a1e1ce35" "checksum ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602" "checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" diff --git a/Cargo.toml b/Cargo.toml index 44074162fc..9f2f22fd89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ ctrlc = "3.1.3" surf = "1.0.2" url = "2.1.0" roxmltree = "0.7.0" -nom5_locate = "0.1.1" +nom_locate = "1.0.0" enum-utils = "0.1.1" unicode-xid = "0.2.0" serde_ini = "0.2.0" diff --git a/src/cli.rs b/src/cli.rs index 7b6f6e863e..a15ba6eedc 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -318,7 +318,7 @@ pub async fn cli() -> Result<(), Box> { context.shell_manager.clone(), ))); - let edit_mode = crate::data::config::config(Span::unknown())? + let edit_mode = crate::data::config::config(Tag::unknown())? .get("edit_mode") .map(|s| match s.as_string().unwrap().as_ref() { "vi" => EditMode::Vi, @@ -396,7 +396,7 @@ async fn process_line(readline: Result, ctx: &mut Context Ok(line) if line.trim() == "" => LineResult::Success(line.clone()), Ok(line) => { - let result = match crate::parser::parse(&line) { + let result = match crate::parser::parse(&line, uuid::Uuid::nil()) { Err(err) => { return LineResult::Error(line.clone(), err); } @@ -418,7 +418,7 @@ async fn process_line(readline: Result, ctx: &mut Context .commands .push(ClassifiedCommand::Internal(InternalCommand { command: whole_stream_command(autoview::Autoview), - name_span: Span::unknown(), + name_tag: Tag::unknown(), args: hir::Call::new( Box::new(hir::Expression::synthetic_string("autoview")), None, @@ -538,10 +538,10 @@ fn classify_command( match call { // If the command starts with `^`, treat it as an external command no matter what call if call.head().is_external() => { - let name_span = call.head().expect_external(); - let name = name_span.slice(source); + let name_tag = call.head().expect_external(); + let name = name_tag.slice(source); - Ok(external_command(call, source, name.tagged(name_span))) + Ok(external_command(call, source, name.tagged(name_tag))) } // Otherwise, if the command is a bare word, we'll need to triage it @@ -563,19 +563,19 @@ fn classify_command( Ok(ClassifiedCommand::Internal(InternalCommand { command, - name_span: head.span().clone(), + name_tag: head.tag(), args, })) } // otherwise, it's an external command - false => Ok(external_command(call, source, name.tagged(head.span()))), + false => Ok(external_command(call, source, name.tagged(head.tag()))), } } // If the command is something else (like a number or a variable), that is currently unsupported. // We might support `$somevar` as a curried command in the future. - call => Err(ShellError::invalid_command(call.head().span())), + call => Err(ShellError::invalid_command(call.head().tag())), } } @@ -592,10 +592,7 @@ fn external_command( .iter() .filter_map(|i| match i { TokenNode::Whitespace(_) => None, - other => Some(Tagged::from_simple_spanned_item( - other.as_external_arg(source), - other.span(), - )), + other => Some(other.as_external_arg(source).tagged(other.tag())), }) .collect(), None => vec![], @@ -605,7 +602,7 @@ fn external_command( ClassifiedCommand::External(ExternalCommand { name: name.to_string(), - name_span: tag.span, + name_tag: tag, args: arg_list_strings, }) } diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 6a8d61eeea..9edc926334 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -113,11 +113,12 @@ fn is_single_origined_text_value(input: &Vec>) -> bool { if let Tagged { item: Value::Primitive(Primitive::String(_)), tag: Tag { - origin: Some(_), .. + origin: Some(origin), + .. }, } = input[0] { - true + origin != uuid::Uuid::nil() } else { false } diff --git a/src/commands/cd.rs b/src/commands/cd.rs index 92875c2733..a3f5a8d89b 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -10,7 +10,7 @@ impl WholeStreamCommand for CD { } fn signature(&self) -> Signature { - Signature::build("cd").optional("directory", SyntaxType::Path) + Signature::build("cd").optional("directory", SyntaxShape::Path) } fn usage(&self) -> &str { diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 2b733c88ef..1a107267df 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -86,7 +86,7 @@ pub(crate) enum ClassifiedCommand { pub(crate) struct InternalCommand { pub(crate) command: Arc, - pub(crate) name_span: Span, + pub(crate) name_tag: Tag, pub(crate) args: hir::Call, } @@ -108,7 +108,7 @@ impl InternalCommand { let result = context.run_command( self.command, - self.name_span.clone(), + self.name_tag.clone(), context.source_map.clone(), self.args, &source, @@ -133,21 +133,18 @@ impl InternalCommand { match value { Tagged { item: Value::Primitive(Primitive::String(cmd)), - .. + tag, } => { context.shell_manager.insert_at_current(Box::new( HelpShell::for_command( - Tagged::from_simple_spanned_item( - Value::string(cmd), - Span::unknown(), - ), - &context.registry().clone(), + Value::string(cmd).tagged(tag), + &context.registry(), )?, )); } _ => { context.shell_manager.insert_at_current(Box::new( - HelpShell::index(&context.registry().clone())?, + HelpShell::index(&context.registry())?, )); } } @@ -189,7 +186,7 @@ impl InternalCommand { pub(crate) struct ExternalCommand { pub(crate) name: String, - pub(crate) name_span: Span, + pub(crate) name_tag: Tag, pub(crate) args: Vec>, } @@ -208,7 +205,7 @@ impl ExternalCommand { ) -> Result { let stdin = input.stdin; let inputs: Vec> = input.objects.into_vec().await; - let name_span = self.name_span.clone(); + let name_tag = self.name_tag.clone(); trace!(target: "nu::run::external", "-> {}", self.name); trace!(target: "nu::run::external", "inputs = {:?}", inputs); @@ -227,17 +224,17 @@ impl ExternalCommand { for i in &inputs { if i.as_string().is_err() { - let mut span = None; + let mut tag = None; for arg in &self.args { if arg.item.contains("$it") { - span = Some(arg.span()); + tag = Some(arg.tag()); } } - if let Some(span) = span { + if let Some(tag) = tag { return Err(ShellError::labeled_error( "External $it needs string data", "given row instead of string data", - span, + tag, )); } else { return Err(ShellError::string("Error: $it needs string data")); @@ -316,9 +313,7 @@ impl ExternalCommand { let stdout = popen.stdout.take().unwrap(); let file = futures::io::AllowStdIo::new(stdout); let stream = Framed::new(file, LinesCodec {}); - let stream = stream.map(move |line| { - Tagged::from_simple_spanned_item(Value::string(line.unwrap()), name_span) - }); + let stream = stream.map(move |line| Value::string(line.unwrap()).tagged(name_tag)); Ok(ClassifiedInputStream::from_input_stream( stream.boxed() as BoxStream<'static, Tagged> )) diff --git a/src/commands/clip.rs b/src/commands/clip.rs index 9bf7fa9f3a..2ef5bfac1d 100644 --- a/src/commands/clip.rs +++ b/src/commands/clip.rs @@ -51,7 +51,7 @@ pub mod clipboard { Ok(OutputStream::from(stream)) } - async fn inner_clip(input: Vec>, name: Span) -> OutputStream { + async fn inner_clip(input: Vec>, name: Tag) -> OutputStream { let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap(); let mut new_copy_data = String::new(); diff --git a/src/commands/command.rs b/src/commands/command.rs index 6be179895b..99352a7b18 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -18,7 +18,7 @@ pub struct UnevaluatedCallInfo { pub args: hir::Call, pub source: Text, pub source_map: SourceMap, - pub name_span: Span, + pub name_tag: Tag, } impl ToDebug for UnevaluatedCallInfo { @@ -38,7 +38,7 @@ impl UnevaluatedCallInfo { Ok(CallInfo { args, source_map: self.source_map, - name_span: self.name_span, + name_tag: self.name_tag, }) } @@ -74,7 +74,7 @@ impl UnevaluatedCallInfo { pub struct CallInfo { pub args: registry::EvaluatedArgs, pub source_map: SourceMap, - pub name_span: Span, + pub name_tag: Tag, } impl CallInfo { @@ -89,7 +89,7 @@ impl CallInfo { args: T::deserialize(&mut deserializer)?, context: RunnablePerItemContext { shell_manager: shell_manager.clone(), - name: self.name_span, + name: self.name_tag, }, callback, }) @@ -158,7 +158,7 @@ impl CommandArgs { let host = self.host.clone(); let args = self.evaluate_once(registry)?; let (input, args) = args.split(); - let name_span = args.call_info.name_span; + let name_tag = args.call_info.name_tag; let mut deserializer = ConfigDeserializer::from_call_info(args.call_info); Ok(RunnableArgs { @@ -167,7 +167,7 @@ impl CommandArgs { input, commands: registry.clone(), shell_manager, - name: name_span, + name: name_tag, source_map, host, }, @@ -191,7 +191,7 @@ impl CommandArgs { let host = self.host.clone(); let args = self.evaluate_once(registry)?; let (input, args) = args.split(); - let name_span = args.call_info.name_span; + let name_tag = args.call_info.name_tag; let mut deserializer = ConfigDeserializer::from_call_info(args.call_info); Ok(RunnableRawArgs { @@ -200,7 +200,7 @@ impl CommandArgs { input, commands: registry.clone(), shell_manager, - name: name_span, + name: name_tag, source_map, host, }, @@ -212,7 +212,7 @@ impl CommandArgs { pub struct RunnablePerItemContext { pub shell_manager: ShellManager, - pub name: Span, + pub name: Tag, } impl RunnablePerItemContext { @@ -227,7 +227,7 @@ pub struct RunnableContext { pub host: Arc>, pub commands: CommandRegistry, pub source_map: SourceMap, - pub name: Span, + pub name: Tag, } impl RunnableContext { @@ -311,8 +311,8 @@ impl EvaluatedWholeStreamCommandArgs { } } - pub fn name_span(&self) -> Span { - self.args.call_info.name_span + pub fn name_tag(&self) -> Tag { + self.args.call_info.name_tag } pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) { @@ -471,12 +471,6 @@ impl ReturnSuccess { pub fn action(input: CommandAction) -> ReturnValue { Ok(ReturnSuccess::Action(input)) } - - pub fn spanned_value(input: Value, span: Span) -> ReturnValue { - Ok(ReturnSuccess::Value(Tagged::from_simple_spanned_item( - input, span, - ))) - } } pub trait WholeStreamCommand: Send + Sync { diff --git a/src/commands/config.rs b/src/commands/config.rs index 97b2a948e9..3b36c88fad 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -1,7 +1,7 @@ use crate::commands::WholeStreamCommand; use crate::data::{config, Value}; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::{self}; use crate::prelude::*; use std::iter::FromIterator; @@ -26,10 +26,10 @@ impl WholeStreamCommand for Config { fn signature(&self) -> Signature { Signature::build("config") - .named("load", SyntaxType::Path) - .named("set", SyntaxType::Any) - .named("get", SyntaxType::Any) - .named("remove", SyntaxType::Any) + .named("load", SyntaxShape::Path) + .named("set", SyntaxShape::Any) + .named("get", SyntaxShape::Any) + .named("remove", SyntaxShape::Any) .switch("clear") .switch("path") } @@ -96,41 +96,21 @@ pub fn config( config::write(&result, &configuration)?; - return Ok(stream![Tagged::from_simple_spanned_item( - Value::Row(result.into()), - value.span() - )] - .from_input_stream()); + return Ok(stream![Value::Row(result.into()).tagged(value.tag())].from_input_stream()); } - if let Tagged { - item: true, - tag: Tag { span, .. }, - } = clear - { + if let Tagged { item: true, tag } = clear { result.clear(); config::write(&result, &configuration)?; - return Ok(stream![Tagged::from_simple_spanned_item( - Value::Row(result.into()), - span - )] - .from_input_stream()); + return Ok(stream![Value::Row(result.into()).tagged(tag)].from_input_stream()); } - if let Tagged { - item: true, - tag: Tag { span, .. }, - } = path - { + if let Tagged { item: true, tag } = path { let path = config::default_path_for(&configuration)?; - return Ok(stream![Tagged::from_simple_spanned_item( - Value::Primitive(Primitive::Path(path)), - span - )] - .from_input_stream()); + return Ok(stream![Value::Primitive(Primitive::Path(path)).tagged(tag)].from_input_stream()); } if let Some(v) = remove { @@ -146,9 +126,9 @@ pub fn config( ))); } - let obj = VecDeque::from_iter(vec![Value::Row(result.into()).simple_spanned(v.span())]); + let obj = VecDeque::from_iter(vec![Value::Row(result.into()).tagged(v.tag())]); return Ok(obj.from_input_stream()); } - return Ok(vec![Value::Row(result.into()).simple_spanned(name)].into()); + return Ok(vec![Value::Row(result.into()).tagged(name)].into()); } diff --git a/src/commands/cp.rs b/src/commands/cp.rs index 491e18b1aa..bf20c74ce9 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -1,6 +1,6 @@ use crate::commands::command::RunnablePerItemContext; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; use std::path::PathBuf; @@ -21,9 +21,9 @@ impl PerItemCommand for Cpy { fn signature(&self) -> Signature { Signature::build("cp") - .required("src", SyntaxType::Pattern) - .required("dst", SyntaxType::Path) - .named("file", SyntaxType::Any) + .required("src", SyntaxShape::Pattern) + .required("dst", SyntaxShape::Path) + .named("file", SyntaxShape::Any) .switch("recursive") } diff --git a/src/commands/date.rs b/src/commands/date.rs index 2d16ec6c55..6df9e27209 100644 --- a/src/commands/date.rs +++ b/src/commands/date.rs @@ -33,58 +33,40 @@ impl WholeStreamCommand for Date { } } -pub fn date_to_value(dt: DateTime, span: Span) -> Tagged +pub fn date_to_value(dt: DateTime, tag: Tag) -> Tagged where T::Offset: Display, { let mut indexmap = IndexMap::new(); - indexmap.insert( - "year".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.year()), span), - ); - indexmap.insert( - "month".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.month()), span), - ); - indexmap.insert( - "day".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.day()), span), - ); - indexmap.insert( - "hour".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.hour()), span), - ); - indexmap.insert( - "minute".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.minute()), span), - ); - indexmap.insert( - "second".to_string(), - Tagged::from_simple_spanned_item(Value::int(dt.second()), span), - ); + indexmap.insert("year".to_string(), Value::int(dt.year()).tagged(tag)); + indexmap.insert("month".to_string(), Value::int(dt.month()).tagged(tag)); + indexmap.insert("day".to_string(), Value::int(dt.day()).tagged(tag)); + indexmap.insert("hour".to_string(), Value::int(dt.hour()).tagged(tag)); + indexmap.insert("minute".to_string(), Value::int(dt.minute()).tagged(tag)); + indexmap.insert("second".to_string(), Value::int(dt.second()).tagged(tag)); let tz = dt.offset(); indexmap.insert( "timezone".to_string(), - Tagged::from_simple_spanned_item(Value::string(format!("{}", tz)), span), + Value::string(format!("{}", tz)).tagged(tag), ); - Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span) + Value::Row(Dictionary::from(indexmap)).tagged(tag) } pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let mut date_out = VecDeque::new(); - let span = args.call_info.name_span; + let tag = args.call_info.name_tag; let value = if args.has("utc") { let utc: DateTime = Utc::now(); - date_to_value(utc, span) + date_to_value(utc, tag) } else { let local: DateTime = Local::now(); - date_to_value(local, span) + date_to_value(local, tag) }; date_out.push_back(value); diff --git a/src/commands/echo.rs b/src/commands/echo.rs index f464630de7..453041bbcd 100644 --- a/src/commands/echo.rs +++ b/src/commands/echo.rs @@ -12,7 +12,7 @@ impl PerItemCommand for Echo { } fn signature(&self) -> Signature { - Signature::build("echo").rest(SyntaxType::Any) + Signature::build("echo").rest(SyntaxShape::Any) } fn usage(&self) -> &str { @@ -35,7 +35,7 @@ fn run( _registry: &CommandRegistry, _raw_args: &RawCommandArgs, ) -> Result { - let name = call_info.name_span; + let name = call_info.name_tag; let mut output = String::new(); @@ -57,7 +57,7 @@ fn run( return Err(ShellError::labeled_error( "Expect a string from pipeline", "not a string-compatible value", - i.span(), + i.tag(), )); } } @@ -65,7 +65,7 @@ fn run( } let stream = VecDeque::from(vec![Ok(ReturnSuccess::Value( - Value::string(output).simple_spanned(name), + Value::string(output).tagged(name), ))]); Ok(stream.to_output_stream()) diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 7b42793e12..4148d03c5f 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -14,7 +14,7 @@ impl PerItemCommand for Enter { } fn signature(&self) -> registry::Signature { - Signature::build("enter").required("location", SyntaxType::Block) + Signature::build("enter").required("location", SyntaxShape::Block) } fn usage(&self) -> &str { @@ -70,7 +70,7 @@ impl PerItemCommand for Enter { crate::commands::open::fetch( &full_path, &location_clone, - Span::unknown(), + Tag::unknown(), ) .await.unwrap(); @@ -103,7 +103,7 @@ impl PerItemCommand for Enter { }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, }, }; let mut result = converter.run( diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index f2ddc737dc..1494423cf5 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -2,14 +2,13 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; use crate::data::Value; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::Signature; use crate::prelude::*; use mime::Mime; use std::path::PathBuf; use std::str::FromStr; use surf::mime; -use uuid::Uuid; pub struct Fetch; impl PerItemCommand for Fetch { @@ -19,7 +18,7 @@ impl PerItemCommand for Fetch { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxType::Path) + .required("path", SyntaxShape::Path) .switch("raw") } @@ -52,14 +51,14 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_span = path.span(); + let path_tag = path.tag(); let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); let stream = async_stream_block! { - let result = fetch(&path_str, path_span).await; + let result = fetch(&path_str, path_tag).await; if let Err(e) = result { yield Err(e); @@ -99,7 +98,7 @@ fn run( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); @@ -130,13 +129,13 @@ fn run( pub async fn fetch( location: &str, - span: Span, + tag: Tag, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { if let Err(_) = url::Url::parse(location) { return Err(ShellError::labeled_error( "Incomplete or incorrect url", "expected a full url", - span, + tag, )); } @@ -152,13 +151,10 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( @@ -167,13 +163,10 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { @@ -181,16 +174,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load binary file", "could not load", - span, + tag, ) })?; Ok(( None, - Value::Primitive(Primitive::Binary(buf)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + Value::binary(buf), + tag, SpanSource::Url(location.to_string()), )) } @@ -200,13 +190,10 @@ pub async fn fetch( ShellError::labeled_error( "Could not load svg from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::IMAGE, image_ty) => { @@ -214,16 +201,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load image file", "could not load", - span, + tag, ) })?; Ok(( Some(image_ty.to_string()), - Value::Primitive(Primitive::Binary(buf)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + Value::binary(buf), + tag, SpanSource::Url(location.to_string()), )) } @@ -233,13 +217,10 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { @@ -260,23 +241,17 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )) } (ty, sub_ty) => Ok(( None, Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), } @@ -284,10 +259,7 @@ pub async fn fetch( None => Ok(( None, Value::string(format!("No content type found")), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), }, @@ -295,7 +267,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "URL could not be opened", "url not found", - span, + tag, )); } } diff --git a/src/commands/first.rs b/src/commands/first.rs index a3575b7fea..e39b5155d0 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for First { } fn signature(&self) -> Signature { - Signature::build("first").required("amount", SyntaxType::Literal) + Signature::build("first").required("amount", SyntaxShape::Literal) } fn usage(&self) -> &str { diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index c5f742057e..a4171e3fec 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -198,7 +198,7 @@ pub fn from_bson_bytes_to_value( fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -208,24 +208,24 @@ fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result - match from_bson_bytes_to_value(vb, span) { + match from_bson_bytes_to_value(vb, tag) { Ok(x) => yield ReturnSuccess::value(x), Err(_) => { yield Err(ShellError::labeled_error_with_secondary( "Could not parse as BSON", "input cannot be parsed as BSON", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )) } } _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index c872e77360..68296eb11b 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -86,7 +86,7 @@ fn from_csv( }: FromCSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let values: Vec> = input.values.collect().await; @@ -105,15 +105,15 @@ fn from_csv( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_span, + name_tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_csv_string_to_value(concat_string, skip_headers, name_span) { + match from_csv_string_to_value(concat_string, skip_headers, name_tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -126,9 +126,9 @@ fn from_csv( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as CSV", "input cannot be parsed as CSV", - name_span, + name_tag, "value originates from here", - last_tag.span, + last_tag, )) } , } diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index 0e128a22c4..8409cf8489 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -64,7 +64,7 @@ pub fn from_ini_string_to_value( fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -84,15 +84,15 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_ini_string_to_value(concat_string, span) { + match from_ini_string_to_value(concat_string, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -105,9 +105,9 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let values: Vec> = input.values.collect().await; @@ -91,9 +91,9 @@ fn from_json( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_span, + name_tag, "value originates from here", - value_tag.span, + value_tag, )), } @@ -106,7 +106,7 @@ fn from_json( continue; } - match from_json_string_to_value(json_str.to_string(), name_span) { + match from_json_string_to_value(json_str.to_string(), name_tag) { Ok(x) => yield ReturnSuccess::value(x), Err(_) => { @@ -114,15 +114,15 @@ fn from_json( yield Err(ShellError::labeled_error_with_secondary( "Could nnot parse as JSON", "input cannot be parsed as JSON", - name_span, + name_tag, "value originates from here", - last_tag.span)) + last_tag)) } } } } } else { - match from_json_string_to_value(concat_string, name_span) { + match from_json_string_to_value(concat_string, name_tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { @@ -137,9 +137,9 @@ fn from_json( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as JSON", "input cannot be parsed as JSON", - name_span, + name_tag, "value originates from here", - last_tag.span)) + last_tag)) } } } diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index a56ecd3058..e880571911 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -128,7 +128,7 @@ pub fn from_sqlite_bytes_to_value( fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -138,7 +138,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result - match from_sqlite_bytes_to_value(vb, span) { + match from_sqlite_bytes_to_value(vb, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -151,18 +151,18 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index c1338cb01f..29db38a77e 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -68,7 +68,7 @@ pub fn from_toml( registry: &CommandRegistry, ) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -88,15 +88,15 @@ pub fn from_toml( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_toml_string_to_value(concat_string, span) { + match from_toml_string_to_value(concat_string, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -109,9 +109,9 @@ pub fn from_toml( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as TOML", "input cannot be parsed as TOML", - span, + tag, "value originates from here", - last_tag.span, + last_tag, )) } , } diff --git a/src/commands/from_tsv.rs b/src/commands/from_tsv.rs index 92696c1aaf..66f070a5df 100644 --- a/src/commands/from_tsv.rs +++ b/src/commands/from_tsv.rs @@ -87,7 +87,7 @@ fn from_tsv( }: FromTSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let values: Vec> = input.values.collect().await; @@ -106,15 +106,15 @@ fn from_tsv( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_span, + name_tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_tsv_string_to_value(concat_string, skip_headers, name_span) { + match from_tsv_string_to_value(concat_string, skip_headers, name_tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -127,9 +127,9 @@ fn from_tsv( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as TSV", "input cannot be parsed as TSV", - name_span, + name_tag, "value originates from here", - last_tag.span, + last_tag, )) } , } diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index 2c3f94cddc..f80d428f43 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -83,7 +83,7 @@ pub fn from_xml_string_to_value( fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -103,15 +103,15 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_xml_string_to_value(concat_string, span) { + match from_xml_string_to_value(concat_string, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -124,9 +124,9 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let stream = async_stream_block! { @@ -117,15 +117,15 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - value_tag.span, + value_tag, )), } } - match from_yaml_string_to_value(concat_string, span) { + match from_yaml_string_to_value(concat_string, tag) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -138,9 +138,9 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result Signature { Signature::build("get") - .required("member", SyntaxType::Member) - .rest(SyntaxType::Member) + .rest(SyntaxShape::Member) + .rest(SyntaxShape::Member) } fn usage(&self) -> &str { @@ -60,8 +60,8 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result registry::Signature { - Signature::build("help").rest(SyntaxType::Any) + Signature::build("help").rest(SyntaxShape::Any) } fn usage(&self) -> &str { @@ -26,13 +26,20 @@ impl PerItemCommand for Help { _raw_args: &RawCommandArgs, _input: Tagged, ) -> Result { - let span = call_info.name_span; + let tag = call_info.name_tag; - match call_info.args.nth(0) { - Some(Tagged { + if call_info.args.len() == 0 { + return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( + Value::nothing().tagged(tag), + )))] + .into()); + } + + match call_info.args.expect_nth(0)? { + Tagged { item: Value::Primitive(Primitive::String(document)), tag, - }) => { + } => { let mut help = VecDeque::new(); if document == "commands" { let mut sorted_names = registry.names(); @@ -120,9 +127,7 @@ You can also learn more at http://book.nushell.sh"#; let mut output_stream = VecDeque::new(); - output_stream.push_back(ReturnSuccess::value( - Value::string(msg).simple_spanned(span), - )); + output_stream.push_back(ReturnSuccess::value(Value::string(msg).tagged(tag))); Ok(output_stream.to_output_stream()) } diff --git a/src/commands/last.rs b/src/commands/last.rs index 1f9cc62a7f..fd7a3ecea2 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Last { } fn signature(&self) -> Signature { - Signature::build("last").required("amount", SyntaxType::Number) + Signature::build("last").required("amount", SyntaxShape::Number) } fn usage(&self) -> &str { diff --git a/src/commands/lines.rs b/src/commands/lines.rs index 196da8802f..d2a9cdffd1 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -32,7 +32,7 @@ impl WholeStreamCommand for Lines { fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let span = args.name_span(); + let tag = args.name_tag(); let input = args.input; let input: InputStream = trace_stream!(target: "nu::trace_stream::lines", "input" = input); @@ -58,9 +58,9 @@ fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result>, +} + impl WholeStreamCommand for LS { fn name(&self) -> &str { "ls" } fn signature(&self) -> Signature { - Signature::build("ls").optional("path", SyntaxType::Pattern) + Signature::build("ls").optional("path", SyntaxShape::Pattern) } fn usage(&self) -> &str { @@ -22,12 +28,11 @@ impl WholeStreamCommand for LS { args: CommandArgs, registry: &CommandRegistry, ) -> Result { - ls(args, registry) + args.process(registry, ls)?.run() + // ls(args, registry) } } -fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result { - let shell_manager = args.shell_manager.clone(); - let args = args.evaluate_once(registry)?; - shell_manager.ls(args) +fn ls(LsArgs { path }: LsArgs, context: RunnableContext) -> Result { + context.shell_manager.ls(path, context.name) } diff --git a/src/commands/mkdir.rs b/src/commands/mkdir.rs index 9dec9a3142..8bf8a97d4a 100644 --- a/src/commands/mkdir.rs +++ b/src/commands/mkdir.rs @@ -17,7 +17,7 @@ impl PerItemCommand for Mkdir { } fn signature(&self) -> Signature { - Signature::build("mkdir").rest(SyntaxType::Path) + Signature::build("mkdir").rest(SyntaxShape::Path) } fn usage(&self) -> &str { diff --git a/src/commands/mv.rs b/src/commands/mv.rs index 130e5996e8..2ace1fa05f 100644 --- a/src/commands/mv.rs +++ b/src/commands/mv.rs @@ -1,6 +1,6 @@ use crate::commands::command::RunnablePerItemContext; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; use std::path::PathBuf; @@ -20,9 +20,9 @@ impl PerItemCommand for Move { fn signature(&self) -> Signature { Signature::build("mv") - .required("source", SyntaxType::Path) - .required("destination", SyntaxType::Path) - .named("file", SyntaxType::Any) + .required("source", SyntaxShape::Pattern) + .required("destination", SyntaxShape::Path) + .named("file", SyntaxShape::Any) } fn usage(&self) -> &str { diff --git a/src/commands/nth.rs b/src/commands/nth.rs index 554034d8e1..bf397e1bcf 100644 --- a/src/commands/nth.rs +++ b/src/commands/nth.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Nth { } fn signature(&self) -> Signature { - Signature::build("nth").required("row number", SyntaxType::Any) + Signature::build("nth").required("amount", SyntaxShape::Any) } fn usage(&self) -> &str { diff --git a/src/commands/open.rs b/src/commands/open.rs index 232399a390..8dae5bd26e 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -2,11 +2,10 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; use crate::data::Value; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::Signature; use crate::prelude::*; use std::path::{Path, PathBuf}; -use uuid::Uuid; pub struct Open; impl PerItemCommand for Open { @@ -16,7 +15,7 @@ impl PerItemCommand for Open { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxType::Path) + .required("path", SyntaxShape::Path) .switch("raw") } @@ -53,7 +52,7 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_span = path.span(); + let path_span = path.tag(); let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); @@ -100,7 +99,7 @@ fn run( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); @@ -132,7 +131,7 @@ fn run( pub async fn fetch( cwd: &PathBuf, location: &str, - span: Span, + tag: Tag, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { let mut cwd = cwd.clone(); @@ -144,10 +143,7 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => { @@ -163,30 +159,21 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, - Value::Primitive(Primitive::Binary(bytes)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + Value::binary(bytes), + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), } } else { Ok(( None, - Value::Primitive(Primitive::Binary(bytes)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + Value::binary(bytes), + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )) } @@ -201,41 +188,29 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, - Value::Primitive(Primitive::Binary(bytes)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + Value::binary(bytes), + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), } } else { Ok(( None, - Value::Primitive(Primitive::Binary(bytes)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + Value::binary(bytes), + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )) } } _ => Ok(( None, - Value::Primitive(Primitive::Binary(bytes)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + Value::binary(bytes), + tag, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -245,7 +220,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "File could not be opened", "file not found", - span, + tag, )); } } @@ -253,7 +228,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "File could not be opened", "file not found", - span, + tag, )); } } diff --git a/src/commands/pick.rs b/src/commands/pick.rs index 1dce172664..605b7f8890 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(SyntaxType::Any) + Signature::build("pick").rest(SyntaxShape::Any) } fn usage(&self) -> &str { diff --git a/src/commands/post.rs b/src/commands/post.rs index e72ed175ac..b9aca99e9c 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -2,7 +2,7 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; use crate::data::Value; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::Signature; use crate::prelude::*; use base64::encode; @@ -10,7 +10,7 @@ use mime::Mime; use std::path::PathBuf; use std::str::FromStr; use surf::mime; -use uuid::Uuid; + pub struct Post; impl PerItemCommand for Post { @@ -20,10 +20,10 @@ impl PerItemCommand for Post { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxType::Any) - .required("body", SyntaxType::Any) - .named("user", SyntaxType::Any) - .named("password", SyntaxType::Any) + .required("path", SyntaxShape::Any) + .required("body", SyntaxShape::Any) + .named("user", SyntaxShape::Any) + .named("password", SyntaxShape::Any) .switch("raw") } @@ -63,7 +63,7 @@ fn run( file => file.clone(), }; let path_str = path.as_string()?; - let path_span = path.span(); + let path_span = path.tag(); let has_raw = call_info.args.has("raw"); let user = call_info.args.get("user").map(|x| x.as_string().unwrap()); let password = call_info @@ -109,7 +109,7 @@ fn run( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, } }; let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); @@ -143,7 +143,7 @@ pub async fn post( body: &Tagged, user: Option, password: Option, - span: Span, + tag: Tag, registry: &CommandRegistry, raw_args: &RawCommandArgs, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { @@ -189,7 +189,7 @@ pub async fn post( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, }, }; let mut result = converter.run( @@ -211,7 +211,7 @@ pub async fn post( return Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", - span, + *tag, )); } } @@ -227,7 +227,7 @@ pub async fn post( return Err(ShellError::labeled_error( "Could not automatically convert table", "needs manual conversion", - tag.span, + *tag, )); } } @@ -243,13 +243,10 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( @@ -258,13 +255,10 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { @@ -272,16 +266,13 @@ pub async fn post( ShellError::labeled_error( "Could not load binary file", "could not load", - span, + tag, ) })?; Ok(( None, - Value::Primitive(Primitive::Binary(buf)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + Value::binary(buf), + tag, SpanSource::Url(location.to_string()), )) } @@ -290,16 +281,13 @@ pub async fn post( ShellError::labeled_error( "Could not load image file", "could not load", - span, + tag, ) })?; Ok(( Some(image_ty.to_string()), - Value::Primitive(Primitive::Binary(buf)), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + Value::binary(buf), + tag, SpanSource::Url(location.to_string()), )) } @@ -309,13 +297,10 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { @@ -336,13 +321,10 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - span, + tag, ) })?), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )) } @@ -352,10 +334,7 @@ pub async fn post( "Not yet supported MIME type: {} {}", ty, sub_ty )), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), } @@ -363,10 +342,7 @@ pub async fn post( None => Ok(( None, Value::string(format!("No content type found")), - Tag { - span, - origin: Some(Uuid::new_v4()), - }, + tag, SpanSource::Url(location.to_string()), )), }, @@ -374,7 +350,7 @@ pub async fn post( return Err(ShellError::labeled_error( "URL could not be opened", "url not found", - span, + tag, )); } } @@ -382,7 +358,7 @@ pub async fn post( Err(ShellError::labeled_error( "Expected a url", "needs a url", - span, + tag, )) } } diff --git a/src/commands/reject.rs b/src/commands/reject.rs index 3ad7de9fa6..3521635233 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Reject { } fn signature(&self) -> Signature { - Signature::build("reject").rest(SyntaxType::Member) + Signature::build("reject").rest(SyntaxShape::Member) } fn usage(&self) -> &str { diff --git a/src/commands/rm.rs b/src/commands/rm.rs index 60bb5c6e4a..ac5aeaae7d 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -1,6 +1,6 @@ use crate::commands::command::RunnablePerItemContext; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry::{CommandRegistry, Signature}; use crate::prelude::*; use std::path::PathBuf; @@ -20,7 +20,7 @@ impl PerItemCommand for Remove { fn signature(&self) -> Signature { Signature::build("rm") - .required("path", SyntaxType::Path) + .required("path", SyntaxShape::Pattern) .switch("recursive") } diff --git a/src/commands/save.rs b/src/commands/save.rs index 77bd4433d8..d7ae75312a 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -7,7 +7,7 @@ use std::path::{Path, PathBuf}; pub struct Save; macro_rules! process_string { - ($input:ident, $name_span:ident) => {{ + ($input:ident, $name_tag:ident) => {{ let mut result_string = String::new(); for res in $input { match res { @@ -21,7 +21,7 @@ macro_rules! process_string { yield core::task::Poll::Ready(Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", - $name_span, + $name_tag, ))); } } @@ -31,7 +31,7 @@ macro_rules! process_string { } macro_rules! process_string_return_success { - ($result_vec:ident, $name_span:ident) => {{ + ($result_vec:ident, $name_tag:ident) => {{ let mut result_string = String::new(); for res in $result_vec { match res { @@ -45,7 +45,7 @@ macro_rules! process_string_return_success { yield core::task::Poll::Ready(Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during text save", - $name_span, + $name_tag, ))); } } @@ -55,7 +55,7 @@ macro_rules! process_string_return_success { } macro_rules! process_binary_return_success { - ($result_vec:ident, $name_span:ident) => {{ + ($result_vec:ident, $name_tag:ident) => {{ let mut result_binary: Vec = Vec::new(); for res in $result_vec { match res { @@ -71,7 +71,7 @@ macro_rules! process_binary_return_success { yield core::task::Poll::Ready(Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during binary save", - $name_span, + $name_tag, ))); } } @@ -93,7 +93,7 @@ impl WholeStreamCommand for Save { fn signature(&self) -> Signature { Signature::build("save") - .optional("path", SyntaxType::Path) + .optional("path", SyntaxShape::Path) .switch("raw") } @@ -127,7 +127,7 @@ fn save( raw_args: RawCommandArgs, ) -> Result { let mut full_path = PathBuf::from(shell_manager.path()); - let name_span = name; + let name_tag = name; let source_map = source_map.clone(); let stream = async_stream_block! { @@ -145,7 +145,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - name_span, + name_tag, )); } }, @@ -153,7 +153,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - name_span, + name_tag, )); } } @@ -161,7 +161,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath", "needs path", - name_span, + name_tag, )); } } else { @@ -185,21 +185,21 @@ fn save( }, source: raw_args.call_info.source, source_map: raw_args.call_info.source_map, - name_span: raw_args.call_info.name_span, + name_tag: raw_args.call_info.name_tag, } }; let mut result = converter.run(new_args.with_input(input), ®istry); let result_vec: Vec> = result.drain_vec().await; if converter.is_binary() { - process_binary_return_success!(result_vec, name_span) + process_binary_return_success!(result_vec, name_tag) } else { - process_string_return_success!(result_vec, name_span) + process_string_return_success!(result_vec, name_tag) } } else { - process_string!(input, name_span) + process_string!(input, name_tag) } } else { - process_string!(input, name_span) + process_string!(input, name_tag) } } else { Ok(string_from(&input).into_bytes()) diff --git a/src/commands/shells.rs b/src/commands/shells.rs index 078040412b..2aee2c8564 100644 --- a/src/commands/shells.rs +++ b/src/commands/shells.rs @@ -29,10 +29,10 @@ impl WholeStreamCommand for Shells { fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result { let mut shells_out = VecDeque::new(); - let span = args.call_info.name_span; + let tag = args.call_info.name_tag; for (index, shell) in args.shell_manager.shells.lock().unwrap().iter().enumerate() { - let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(span)); + let mut dict = TaggedDictBuilder::new(tag); if index == args.shell_manager.current_shell { dict.insert(" ", "X".to_string()); diff --git a/src/commands/size.rs b/src/commands/size.rs index 43829d524e..51f8d22c1d 100644 --- a/src/commands/size.rs +++ b/src/commands/size.rs @@ -29,7 +29,7 @@ impl WholeStreamCommand for Size { fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result { let input = args.input; - let span = args.call_info.name_span; + let tag = args.call_info.name_tag; Ok(input .values .map(move |v| match v.item { @@ -37,9 +37,9 @@ fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - span, + tag, "value originates from here", - v.span(), + v.tag(), )), }) .to_output_stream()) @@ -71,7 +71,7 @@ fn count(contents: &str, tag: impl Into) -> Tagged { } let mut dict = TaggedDictBuilder::new(tag); - //TODO: add back in name when we have it in the span + //TODO: add back in name when we have it in the tag //dict.insert("name", Value::string(name)); dict.insert("lines", Value::int(lines)); dict.insert("words", Value::int(words)); diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index 90ba24b996..041caf300e 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for SkipWhile { fn signature(&self) -> Signature { Signature::build("skip-while") - .required("condition", SyntaxType::Block) + .required("condition", SyntaxShape::Block) .filter() } diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index edce33b963..8058b7889e 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -15,7 +15,7 @@ impl WholeStreamCommand for SortBy { } fn signature(&self) -> Signature { - Signature::build("sort-by").rest(SyntaxType::String) + Signature::build("sort-by").rest(SyntaxShape::String) } fn usage(&self) -> &str { diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index d057a3dd21..00e2609f26 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -21,9 +21,9 @@ impl WholeStreamCommand for SplitColumn { fn signature(&self) -> Signature { Signature::build("split-column") - .required("separator", SyntaxType::Any) + .required("separator", SyntaxShape::Any) .switch("collapse-empty") - .rest(SyntaxType::Member) + .rest(SyntaxShape::Member) } fn usage(&self) -> &str { @@ -96,7 +96,7 @@ fn split_column( "requires string input", name, "value originates from here", - v.span(), + v.tag(), )), }) .to_output_stream()) diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index bec18099a0..e70e5cfa84 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -17,7 +17,7 @@ impl WholeStreamCommand for SplitRow { } fn signature(&self) -> Signature { - Signature::build("split-row").required("separator", SyntaxType::Any) + Signature::build("split-row").required("separator", SyntaxShape::Any) } fn usage(&self) -> &str { @@ -62,7 +62,7 @@ fn split_row( "requires string input", name, "value originates from here", - v.span(), + v.tag(), ))); result } diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 179700a9e3..2b45105c2d 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -36,7 +36,7 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result Result { Ok(Bson::Document(doc)) } -fn shell_encode_document( - writer: &mut Vec, - doc: Document, - span: Span, -) -> Result<(), ShellError> { +fn shell_encode_document(writer: &mut Vec, doc: Document, tag: Tag) -> Result<(), ShellError> { match encode_document(writer, &doc) { Err(e) => Err(ShellError::labeled_error( format!("Failed to encode document due to: {:?}", e), "requires BSON-compatible document", - span, + tag, )), _ => Ok(()), } } -fn bson_value_to_bytes(bson: Bson, span: Span) -> Result, ShellError> { +fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result, ShellError> { let mut out = Vec::new(); match bson { Bson::Array(a) => { for v in a.into_iter() { match v { - Bson::Document(d) => shell_encode_document(&mut out, d, span)?, + Bson::Document(d) => shell_encode_document(&mut out, d, tag)?, _ => { return Err(ShellError::labeled_error( format!("All top level values must be Documents, got {:?}", v), "requires BSON-compatible document", - span, + tag, )) } } } } - Bson::Document(d) => shell_encode_document(&mut out, d, span)?, + Bson::Document(d) => shell_encode_document(&mut out, d, tag)?, _ => { return Err(ShellError::labeled_error( format!("All top level values must be Documents, got {:?}", bson), "requires BSON-compatible document", - span, + tag, )) } } @@ -236,7 +232,7 @@ fn bson_value_to_bytes(bson: Bson, span: Span) -> Result, ShellError> { fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -252,23 +248,23 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { - match bson_value_to_bytes(bson_value, name_span) { + match bson_value_to_bytes(bson_value, name_tag) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::Binary(x)).simple_spanned(name_span), + Value::binary(x).tagged(name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with BSON-compatible structure.span() from pipeline", + "Expected a table with BSON-compatible structure.tag() from pipeline", "requires BSON-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with BSON-compatible structure from pipeline", "requires BSON-compatible input", - name_span)) + name_tag)) } } }; diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 615e49cbf8..fd77fdcb6f 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -134,7 +134,7 @@ fn to_csv( ToCSVArgs { headerless }: ToCSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let input: Vec> = input.values.collect().await; @@ -155,15 +155,15 @@ fn to_csv( } else { x }; - yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span)) + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag)) } _ => { yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with CSV-compatible structure.span() from pipeline", + "Expected a table with CSV-compatible structure.tag() from pipeline", "requires CSV-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )) } } diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index aa0e720dbf..d8aaa96794 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -80,7 +80,7 @@ fn json_list(input: &Vec>) -> Result, Shell fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -98,21 +98,21 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { match serde_json::to_string(&json_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).simple_spanned(name_span), + Value::Primitive(Primitive::String(x)).tagged(name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with JSON-compatible structure.span() from pipeline", + "Expected a table with JSON-compatible structure.tag() from pipeline", "requires JSON-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with JSON-compatible structure from pipeline", "requires JSON-compatible input", - name_span)) + name_tag)) } } }; diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index e4eef72c24..c695667ca0 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -200,7 +200,7 @@ fn sqlite_input_stream_to_bytes( fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -208,9 +208,9 @@ fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result yield ReturnSuccess::value(out), _ => { yield Err(ShellError::labeled_error( - "Expected a table with SQLite-compatible structure.span() from pipeline", + "Expected a table with SQLite-compatible structure.tag() from pipeline", "requires SQLite-compatible input", - name_span, + name_tag, )) }, } diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 6669c94cd6..a30c9d3cf7 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -75,7 +75,7 @@ fn collect_values(input: &Vec>) -> Result, ShellE fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -93,21 +93,21 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { match toml::to_string(&toml_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).simple_spanned(name_span), + Value::Primitive(Primitive::String(x)).tagged(name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with TOML-compatible structure.span() from pipeline", + "Expected a table with TOML-compatible structure.tag() from pipeline", "requires TOML-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with TOML-compatible structure from pipeline", "requires TOML-compatible input", - name_span)) + name_tag)) } } }; diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index a0cbf1c1bd..7bce174d01 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -133,7 +133,7 @@ fn to_tsv( ToTSVArgs { headerless }: ToTSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let name_span = name; + let name_tag = name; let stream = async_stream_block! { let input: Vec> = input.values.collect().await; @@ -154,15 +154,15 @@ fn to_tsv( } else { x }; - yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span)) + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag)) } _ => { yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with TSV-compatible structure.span() from pipeline", + "Expected a table with TSV-compatible structure.tag() from pipeline", "requires TSV-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )) } } diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index f4aa63ad6a..db54af6e89 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -76,7 +76,7 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result Result { let args = args.evaluate_once(registry)?; - let name_span = args.name_span(); + let name_tag = args.name_tag(); let stream = async_stream_block! { let input: Vec> = args.input.values.collect().await; @@ -94,21 +94,21 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result { match serde_yaml::to_string(&yaml_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).simple_spanned(name_span), + Value::Primitive(Primitive::String(x)).tagged(name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( - "Expected a table with YAML-compatible structure.span() from pipeline", + "Expected a table with YAML-compatible structure.tag() from pipeline", "requires YAML-compatible input", - name_span, + name_tag, "originates from here".to_string(), - value.span(), + value.tag(), )), } } _ => yield Err(ShellError::labeled_error( "Expected a table with YAML-compatible structure from pipeline", "requires YAML-compatible input", - name_span)) + name_tag)) } } }; diff --git a/src/commands/trim.rs b/src/commands/trim.rs index 00a978e83c..11ed025394 100644 --- a/src/commands/trim.rs +++ b/src/commands/trim.rs @@ -34,7 +34,7 @@ fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result Result { let args = args.evaluate_once(registry)?; - let span = args.call_info.name_span; + let tag = args.call_info.name_tag; let mut indexmap = IndexMap::new(); indexmap.insert( "version".to_string(), - Tagged::from_simple_spanned_item(Value::string(clap::crate_version!()), span), + Value::string(clap::crate_version!()).tagged(tag), ); - let value = Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span); + let value = Value::Row(Dictionary::from(indexmap)).tagged(tag); Ok(OutputStream::one(value)) } diff --git a/src/commands/where_.rs b/src/commands/where_.rs index 2111e0687c..673c6dda84 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -1,6 +1,6 @@ use crate::commands::PerItemCommand; use crate::errors::ShellError; -use crate::parser::hir::SyntaxType; +use crate::parser::hir::SyntaxShape; use crate::parser::registry; use crate::prelude::*; @@ -12,7 +12,7 @@ impl PerItemCommand for Where { } fn signature(&self) -> registry::Signature { - Signature::build("where").required("condition", SyntaxType::Block) + Signature::build("where").required("condition", SyntaxShape::Block) } fn usage(&self) -> &str { @@ -49,7 +49,7 @@ impl PerItemCommand for Where { return Err(ShellError::labeled_error( "Expected a condition", "where needs a condition", - tag.span, + *tag, )) } }; diff --git a/src/commands/which_.rs b/src/commands/which_.rs index eea9d2becf..905515848c 100644 --- a/src/commands/which_.rs +++ b/src/commands/which_.rs @@ -13,7 +13,7 @@ impl WholeStreamCommand for Which { } fn signature(&self) -> Signature { - Signature::build("which").required("name", SyntaxType::Any) + Signature::build("which").required("name", SyntaxShape::Any) } fn usage(&self) -> &str { @@ -33,7 +33,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result 0 { @@ -52,7 +52,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result Result( &mut self, command: Arc, - name_span: Span, + name_tag: Tag, source_map: SourceMap, args: hir::Call, source: &Text, input: InputStream, ) -> OutputStream { - let command_args = self.command_args(args, input, source, source_map, name_span); + let command_args = self.command_args(args, input, source, source_map, name_tag); command.run(command_args, self.registry()) } @@ -135,13 +135,13 @@ impl Context { args: hir::Call, source: &Text, source_map: SourceMap, - name_span: Span, + name_tag: Tag, ) -> UnevaluatedCallInfo { UnevaluatedCallInfo { args, source: source.clone(), source_map, - name_span, + name_tag, } } @@ -151,12 +151,12 @@ impl Context { input: InputStream, source: &Text, source_map: SourceMap, - name_span: Span, + name_tag: Tag, ) -> CommandArgs { CommandArgs { host: self.host.clone(), shell_manager: self.shell_manager.clone(), - call_info: self.call_info(args, source, source_map, name_span), + call_info: self.call_info(args, source, source_map, name_tag), input, } } diff --git a/src/data/base.rs b/src/data/base.rs index c80cf409f0..8993ac71b1 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -150,7 +150,7 @@ pub struct Operation { pub struct Block { pub(crate) expressions: Vec, pub(crate) source: Text, - pub(crate) span: Span, + pub(crate) tag: Tag, } impl Block { @@ -158,7 +158,7 @@ impl Block { let scope = Scope::new(value.clone()); if self.expressions.len() == 0 { - return Ok(Value::nothing().simple_spanned(self.span)); + return Ok(Value::nothing().tagged(self.tag)); } let mut last = None; @@ -249,7 +249,7 @@ impl std::convert::TryFrom<&Tagged> for Block { Value::Block(block) => Ok(block.clone()), v => Err(ShellError::type_error( "Block", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -265,7 +265,7 @@ impl std::convert::TryFrom<&Tagged> for i64 { } v => Err(ShellError::type_error( "Integer", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -279,7 +279,7 @@ impl std::convert::TryFrom<&Tagged> for String { Value::Primitive(Primitive::String(s)) => Ok(s.clone()), v => Err(ShellError::type_error( "String", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -293,7 +293,7 @@ impl std::convert::TryFrom<&Tagged> for Vec { Value::Primitive(Primitive::Binary(b)) => Ok(b.clone()), v => Err(ShellError::type_error( "Binary", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -307,7 +307,7 @@ impl<'a> std::convert::TryFrom<&'a Tagged> for &'a crate::data::Dictionar Value::Row(d) => Ok(d), v => Err(ShellError::type_error( "Dictionary", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), } } @@ -329,7 +329,7 @@ impl std::convert::TryFrom>> for Switch { Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present), v => Err(ShellError::type_error( "Boolean", - value.copy_span(v.type_name()), + value.copy_tag(v.type_name()), )), }, } @@ -641,7 +641,7 @@ impl Tagged { Value::Primitive(Primitive::Path(path)) => Ok(path.clone()), other => Err(ShellError::type_error( "Path", - other.type_name().tagged(self.span()), + other.type_name().tagged(self.tag()), )), } } diff --git a/src/data/config.rs b/src/data/config.rs index 08296c09a6..6b4d1383f5 100644 --- a/src/data/config.rs +++ b/src/data/config.rs @@ -50,7 +50,7 @@ pub fn default_path_for(file: &Option) -> Result { } pub fn read( - span: impl Into, + tag: impl Into, at: &Option, ) -> Result>, ShellError> { let filename = default_path()?; @@ -64,15 +64,15 @@ pub fn read( trace!("config file = {}", filename.display()); - let span = span.into(); + let tag = tag.into(); let contents = fs::read_to_string(filename) - .map(|v| v.simple_spanned(span)) + .map(|v| v.tagged(tag)) .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?; let parsed: toml::Value = toml::from_str(&contents) .map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?; - let value = convert_toml_value_to_nu_value(&parsed, Tag::unknown_origin(span)); + let value = convert_toml_value_to_nu_value(&parsed, tag); let tag = value.tag(); match value.item { Value::Row(Dictionary { entries }) => Ok(entries), @@ -83,8 +83,8 @@ pub fn read( } } -pub(crate) fn config(span: impl Into) -> Result>, ShellError> { - read(span, &None) +pub(crate) fn config(tag: impl Into) -> Result>, ShellError> { + read(tag, &None) } pub fn write( diff --git a/src/data/into.rs b/src/data/into.rs index 749ab0601f..3d764fa0c1 100644 --- a/src/data/into.rs +++ b/src/data/into.rs @@ -15,8 +15,8 @@ impl From for Value { impl> Tagged { pub fn into_tagged_value(self) -> Tagged { - let value_span = self.span(); + let value_tag = self.tag(); let value = self.item.into(); - value.simple_spanned(value_span) + value.tagged(value_tag) } } diff --git a/src/data/meta.rs b/src/data/meta.rs index f1d2b6713d..d472a8add9 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -5,6 +5,7 @@ use derive_new::new; use getset::Getters; use serde::Deserialize; use serde::Serialize; +use std::path::{Path, PathBuf}; use uuid::Uuid; #[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] @@ -13,9 +14,15 @@ pub struct Tagged { pub item: T, } -impl HasSpan for Tagged { - fn span(&self) -> Span { - self.tag.span +impl HasTag for Tagged { + fn tag(&self) -> Tag { + self.tag + } +} + +impl AsRef for Tagged { + fn as_ref(&self) -> &Path { + self.item.as_ref() } } @@ -24,10 +31,6 @@ pub trait TaggedItem: Sized { Tagged::from_item(self, tag.into()) } - fn simple_spanned(self, span: impl Into) -> Tagged { - Tagged::from_simple_spanned_item(self, span.into()) - } - // For now, this is a temporary facility. In many cases, there are other useful spans that we // could be using, such as the original source spans of JSON or Toml files, but we don't yet // have the infrastructure to make that work. @@ -53,14 +56,8 @@ impl std::ops::Deref for Tagged { } impl Tagged { - pub fn spanned(self, span: impl Into) -> Tagged { - Tagged::from_item( - self.item, - Tag { - span: span.into(), - origin: None, - }, - ) + pub fn with_tag(self, tag: impl Into) -> Tagged { + Tagged::from_item(self.item, tag) } pub fn from_item(item: T, tag: impl Into) -> Tagged { @@ -70,41 +67,26 @@ impl Tagged { } } - pub fn from_simple_spanned_item(item: T, span: impl Into) -> Tagged { - Tagged::from_item( - item, - Tag { - span: span.into(), - origin: None, - }, - ) - } - pub fn map(self, input: impl FnOnce(T) -> U) -> Tagged { let tag = self.tag(); let mapped = input(self.item); - Tagged::from_item(mapped, tag.clone()) + Tagged::from_item(mapped, tag) } - pub(crate) fn copy_span(&self, output: U) -> Tagged { - let span = self.span(); - - Tagged::from_simple_spanned_item(output, span) + pub(crate) fn copy_tag(&self, output: U) -> Tagged { + Tagged::from_item(output, self.tag()) } pub fn source(&self, source: &Text) -> Text { - Text::from(self.span().slice(source)) - } - - pub fn span(&self) -> Span { - self.tag.span + Text::from(self.tag().slice(source)) } pub fn tag(&self) -> Tag { self.tag } + // TODO: This should not be optional pub fn origin(&self) -> Option { self.tag.origin } @@ -126,20 +108,14 @@ impl Tagged { } } -impl From<&Tagged> for Span { - fn from(input: &Tagged) -> Span { - input.span() - } -} - -impl From<&Span> for Span { - fn from(input: &Span) -> Span { +impl From<&Tag> for Tag { + fn from(input: &Tag) -> Tag { *input } } -impl From> for Span { - fn from(input: nom5_locate::LocatedSpan<&str>) -> Span { +impl From> for Span { + fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Span { Span { start: input.offset, end: input.offset + input.fragment.len(), @@ -147,8 +123,18 @@ impl From> for Span { } } -impl From<(nom5_locate::LocatedSpan, nom5_locate::LocatedSpan)> for Span { - fn from(input: (nom5_locate::LocatedSpan, nom5_locate::LocatedSpan)) -> Span { +impl + From<( + nom_locate::LocatedSpanEx, + nom_locate::LocatedSpanEx, + )> for Span +{ + fn from( + input: ( + nom_locate::LocatedSpanEx, + nom_locate::LocatedSpanEx, + ), + ) -> Span { Span { start: input.0.offset, end: input.1.offset, @@ -197,6 +183,36 @@ impl From<&Span> for Tag { } } +impl From<(usize, usize, Uuid)> for Tag { + fn from((start, end, origin): (usize, usize, Uuid)) -> Self { + Tag { + origin: Some(origin), + span: Span { start, end }, + } + } +} + +impl From<(usize, usize, Option)> for Tag { + fn from((start, end, origin): (usize, usize, Option)) -> Self { + Tag { + origin, + span: Span { start, end }, + } + } +} + +impl From> for Tag { + fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Tag { + Tag { + origin: Some(input.extra), + span: Span { + start: input.offset, + end: input.offset + input.fragment.len(), + }, + } + } +} + impl From for Span { fn from(tag: Tag) -> Self { tag.span @@ -214,12 +230,36 @@ impl Tag { Tag { origin: None, span } } + pub fn unknown_span(origin: Uuid) -> Tag { + Tag { + origin: Some(origin), + span: Span::unknown(), + } + } + pub fn unknown() -> Tag { Tag { origin: None, span: Span::unknown(), } } + + pub fn until(&self, other: impl Into) -> Tag { + let other = other.into(); + debug_assert!(self.origin == other.origin, "Can only merge two tags with the same origin"); + + Tag { + span: Span { + start: self.span.start, + end: other.span.end + }, + origin: self.origin + } + } + + pub fn slice<'a>(&self, source: &'a str) -> &'a str { + self.span.slice(source) + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)] @@ -284,3 +324,33 @@ impl language_reporting::ReportingSpan for Span { self.end } } + +impl language_reporting::ReportingSpan for Tag { + fn with_start(&self, start: usize) -> Self { + Tag { + span: Span { + start, + end: self.span.end, + }, + origin: self.origin, + } + } + + fn with_end(&self, end: usize) -> Self { + Tag { + span: Span { + start: self.span.start, + end, + }, + origin: self.origin, + } + } + + fn start(&self) -> usize { + self.span.start + } + + fn end(&self) -> usize { + self.span.end + } +} diff --git a/src/errors.rs b/src/errors.rs index 579576e710..db70e13548 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -14,20 +14,22 @@ pub enum Description { impl Description { pub fn from(value: Tagged>) -> Description { - let value_span = value.span(); let value_tag = value.tag(); - match value_span { - Span { start: 0, end: 0 } => Description::Synthetic(value.item.into()), + match value_tag { + Tag { + span: crate::data::meta::Span { start: 0, end: 0 }, + .. + } => Description::Synthetic(value.item.into()), _ => Description::Source(Tagged::from_item(value.item.into(), value_tag)), } } } impl Description { - fn into_label(self) -> Result, String> { + fn into_label(self) -> Result, String> { match self { - Description::Source(s) => Ok(Label::new_primary(s.span()).with_message(s.item)), + Description::Source(s) => Ok(Label::new_primary(s.tag()).with_message(s.item)), Description::Synthetic(s) => Err(s), } } @@ -81,7 +83,7 @@ impl ShellError { ) -> ShellError { ProximateShellError::RangeError { kind: expected.into(), - actual_kind: actual.copy_span(format!("{:?}", actual.item)), + actual_kind: actual.copy_tag(format!("{:?}", actual.item)), operation, } .start() @@ -116,9 +118,9 @@ impl ShellError { ProximateShellError::MissingProperty { subpath, expr }.start() } - pub(crate) fn missing_value(span: Option, reason: impl Into) -> ShellError { + pub(crate) fn missing_value(tag: Option, reason: impl Into) -> ShellError { ProximateShellError::MissingValue { - span, + tag, reason: reason.into(), } .start() @@ -127,28 +129,31 @@ impl ShellError { pub(crate) fn argument_error( command: impl Into, kind: ArgumentError, - span: Span, + tag: Tag, ) -> ShellError { ProximateShellError::ArgumentError { command: command.into(), error: kind, - span, + tag, } .start() } - pub(crate) fn invalid_external_word(span: Span) -> ShellError { + pub(crate) fn invalid_external_word(tag: Tag) -> ShellError { ProximateShellError::ArgumentError { command: "Invalid argument to Nu command (did you mean to call an external command?)" .into(), error: ArgumentError::InvalidExternalWord, - span, + tag, } .start() } pub(crate) fn parse_error( - error: nom::Err<(nom5_locate::LocatedSpan<&str>, nom::error::ErrorKind)>, + error: nom::Err<( + nom_locate::LocatedSpanEx<&str, uuid::Uuid>, + nom::error::ErrorKind, + )>, ) -> ShellError { use language_reporting::*; @@ -164,34 +169,34 @@ impl ShellError { } nom::Err::Failure(span) | nom::Err::Error(span) => { let diagnostic = Diagnostic::new(Severity::Error, format!("Parse Error")) - .with_label(Label::new_primary(Span::from(span.0))); + .with_label(Label::new_primary(Tag::from(span.0))); ShellError::diagnostic(diagnostic) } } } - pub(crate) fn diagnostic(diagnostic: Diagnostic) -> ShellError { + pub(crate) fn diagnostic(diagnostic: Diagnostic) -> ShellError { ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start() } - pub(crate) fn to_diagnostic(self) -> Diagnostic { + pub(crate) fn to_diagnostic(self) -> Diagnostic { match self.error { ProximateShellError::String(StringError { title, .. }) => { Diagnostic::new(Severity::Error, title) } ProximateShellError::InvalidCommand { command } => { Diagnostic::new(Severity::Error, "Invalid command") - .with_label(Label::new_primary(command.span)) + .with_label(Label::new_primary(command)) } - ProximateShellError::MissingValue { span, reason } => { + ProximateShellError::MissingValue { tag, reason } => { let mut d = Diagnostic::new( Severity::Bug, format!("Internal Error (missing value) :: {}", reason), ); - if let Some(span) = span { - d = d.with_label(Label::new_primary(span)); + if let Some(tag) = tag { + d = d.with_label(Label::new_primary(tag)); } d @@ -199,12 +204,12 @@ impl ShellError { ProximateShellError::ArgumentError { command, error, - span, + tag, } => match error { ArgumentError::InvalidExternalWord => Diagnostic::new( Severity::Error, format!("Invalid bare word for Nu command (did you intend to invoke an external command?)")) - .with_label(Label::new_primary(span)), + .with_label(Label::new_primary(tag)), ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new( Severity::Error, format!( @@ -214,7 +219,7 @@ impl ShellError { Color::Black.bold().paint(name) ), ) - .with_label(Label::new_primary(span)), + .with_label(Label::new_primary(tag)), ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new( Severity::Error, format!( @@ -224,7 +229,7 @@ impl ShellError { ), ) .with_label( - Label::new_primary(span).with_message(format!("requires {} parameter", name)), + Label::new_primary(tag).with_message(format!("requires {} parameter", name)), ), ArgumentError::MissingValueForName(name) => Diagnostic::new( Severity::Error, @@ -235,17 +240,17 @@ impl ShellError { Color::Black.bold().paint(name) ), ) - .with_label(Label::new_primary(span)), + .with_label(Label::new_primary(tag)), }, ProximateShellError::TypeError { expected, actual: Tagged { item: Some(actual), - tag: Tag { span, .. }, + tag, }, } => Diagnostic::new(Severity::Error, "Type Error").with_label( - Label::new_primary(span) + Label::new_primary(tag) .with_message(format!("Expected {}, found {}", expected, actual)), ), @@ -254,10 +259,10 @@ impl ShellError { actual: Tagged { item: None, - tag: Tag { span, .. }, + tag }, } => Diagnostic::new(Severity::Error, "Type Error") - .with_label(Label::new_primary(span).with_message(expected)), + .with_label(Label::new_primary(tag).with_message(expected)), ProximateShellError::RangeError { kind, @@ -265,10 +270,10 @@ impl ShellError { actual_kind: Tagged { item, - tag: Tag { span, .. }, + tag }, } => Diagnostic::new(Severity::Error, "Range Error").with_label( - Label::new_primary(span).with_message(format!( + Label::new_primary(tag).with_message(format!( "Expected to convert {} to {} while {}, but it was out of range", item, kind.desc(), @@ -279,11 +284,11 @@ impl ShellError { ProximateShellError::SyntaxError { problem: Tagged { - tag: Tag { span, .. }, + tag, .. }, } => Diagnostic::new(Severity::Error, "Syntax Error") - .with_label(Label::new_primary(span).with_message("Unexpected external command")), + .with_label(Label::new_primary(tag).with_message("Unexpected external command")), ProximateShellError::MissingProperty { subpath, expr } => { let subpath = subpath.into_label(); @@ -306,8 +311,8 @@ impl ShellError { ProximateShellError::Diagnostic(diag) => diag.diagnostic, ProximateShellError::CoerceError { left, right } => { Diagnostic::new(Severity::Error, "Coercion error") - .with_label(Label::new_primary(left.span()).with_message(left.item)) - .with_label(Label::new_secondary(right.span()).with_message(right.item)) + .with_label(Label::new_primary(left.tag()).with_message(left.item)) + .with_label(Label::new_secondary(right.tag()).with_message(right.item)) } } } @@ -315,26 +320,29 @@ impl ShellError { pub fn labeled_error( msg: impl Into, label: impl Into, - span: impl Into, + tag: impl Into, ) -> ShellError { ShellError::diagnostic( Diagnostic::new(Severity::Error, msg.into()) - .with_label(Label::new_primary(span.into()).with_message(label.into())), + .with_label(Label::new_primary(tag.into()).with_message(label.into())), ) } pub fn labeled_error_with_secondary( msg: impl Into, primary_label: impl Into, - primary_span: Span, + primary_span: impl Into, secondary_label: impl Into, - secondary_span: Span, + secondary_span: impl Into, ) -> ShellError { ShellError::diagnostic( Diagnostic::new_error(msg.into()) - .with_label(Label::new_primary(primary_span).with_message(primary_label.into())) .with_label( - Label::new_secondary(secondary_span).with_message(secondary_label.into()), + Label::new_primary(primary_span.into()).with_message(primary_label.into()), + ) + .with_label( + Label::new_secondary(secondary_span.into()) + .with_message(secondary_label.into()), ), ) } @@ -409,13 +417,13 @@ pub enum ProximateShellError { expr: Description, }, MissingValue { - span: Option, + tag: Option, reason: String, }, ArgumentError { command: String, error: ArgumentError, - span: Span, + tag: Tag, }, RangeError { kind: ExpectedRange, @@ -447,7 +455,7 @@ impl ToDebug for ProximateShellError { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ShellDiagnostic { - pub(crate) diagnostic: Diagnostic, + pub(crate) diagnostic: Diagnostic, } impl PartialEq for ShellDiagnostic { diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index d067a65a65..8228c1035c 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -38,13 +38,13 @@ pub(crate) fn evaluate_baseline_expr( source: &Text, ) -> Result, ShellError> { match &expr.item { - RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(literal), source)), + RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_tag(literal), source)), RawExpression::ExternalWord => Err(ShellError::argument_error( "Invalid external word", ArgumentError::InvalidExternalWord, - expr.span(), + expr.tag(), )), - RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.span())), + RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.tag())), RawExpression::Synthetic(hir::Synthetic::String(s)) => { Ok(Value::string(s).tagged_unknown()) } @@ -55,13 +55,10 @@ pub(crate) fn evaluate_baseline_expr( let right = evaluate_baseline_expr(binary.right(), registry, scope, source)?; match left.compare(binary.op(), &*right) { - Ok(result) => Ok(Tagged::from_simple_spanned_item( - Value::boolean(result), - expr.span(), - )), + Ok(result) => Ok(Value::boolean(result).tagged(expr.tag())), Err((left_type, right_type)) => Err(ShellError::coerce_error( - binary.left().copy_span(left_type), - binary.right().copy_span(right_type), + binary.left().copy_tag(left_type), + binary.right().copy_tag(right_type), )), } } @@ -73,12 +70,14 @@ pub(crate) fn evaluate_baseline_expr( exprs.push(expr); } - Ok(Value::Table(exprs).tagged(Tag::unknown_origin(expr.span()))) + Ok(Value::Table(exprs).tagged(expr.tag())) + } + RawExpression::Block(block) => { + Ok( + Value::Block(Block::new(block.clone(), source.clone(), expr.tag())) + .tagged(expr.tag()), + ) } - RawExpression::Block(block) => Ok(Tagged::from_simple_spanned_item( - Value::Block(Block::new(block.clone(), source.clone(), expr.span())), - expr.span(), - )), RawExpression::Path(path) => { let value = evaluate_baseline_expr(path.head(), registry, scope, source)?; let mut item = value; @@ -94,18 +93,12 @@ pub(crate) fn evaluate_baseline_expr( )) } Some(next) => { - item = Tagged::from_simple_spanned_item( - next.clone().item, - (expr.span().start, name.span().end), - ) + item = next.clone().item.tagged(expr.tag()); } }; } - Ok(Tagged::from_simple_spanned_item( - item.item().clone(), - expr.span(), - )) + Ok(item.item().clone().tagged(expr.tag())) } RawExpression::Boolean(_boolean) => unimplemented!(), } @@ -115,9 +108,9 @@ fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged int.into(), hir::Literal::Size(int, unit) => unit.compute(int), - hir::Literal::String(span) => Value::string(span.slice(source)), - hir::Literal::GlobPattern => Value::pattern(literal.span().slice(source)), - hir::Literal::Bare => Value::string(literal.span().slice(source)), + hir::Literal::String(tag) => Value::string(tag.slice(source)), + hir::Literal::GlobPattern => Value::pattern(literal.tag().slice(source)), + hir::Literal::Bare => Value::string(literal.tag().slice(source)), }; literal.map(|_| result) @@ -129,12 +122,12 @@ fn evaluate_reference( source: &Text, ) -> Result, ShellError> { match name { - hir::Variable::It(span) => Ok(scope.it.item.clone().simple_spanned(span)), - hir::Variable::Other(span) => Ok(scope + hir::Variable::It(tag) => Ok(scope.it.item.clone().tagged(*tag)), + hir::Variable::Other(tag) => Ok(scope .vars - .get(span.slice(source)) + .get(tag.slice(source)) .map(|v| v.clone()) - .unwrap_or_else(|| Value::nothing().simple_spanned(span))), + .unwrap_or_else(|| Value::nothing().tagged(*tag))), } } @@ -144,6 +137,6 @@ fn evaluate_external( _source: &Text, ) -> Result, ShellError> { Err(ShellError::syntax_error( - "Unexpected external command".tagged(external.name()), + "Unexpected external command".tagged(*external.name()), )) } diff --git a/src/format/table.rs b/src/format/table.rs index 717e3c4a27..286be222c3 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -204,7 +204,7 @@ impl RenderView for TableView { let mut table = Table::new(); - let table_mode = crate::data::config::config(Span::unknown())? + let table_mode = crate::data::config::config(Tag::unknown())? .get("table_mode") .map(|s| match s.as_string().unwrap().as_ref() { "light" => TableMode::Light, diff --git a/src/lib.rs b/src/lib.rs index 9ee1e9d09b..1aedc2e11f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -23,7 +23,7 @@ mod utils; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::context::{SourceMap, SpanSource}; pub use crate::env::host::BasicHost; -pub use crate::parser::hir::SyntaxType; +pub use crate::parser::hir::SyntaxShape; pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder; pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; @@ -31,7 +31,7 @@ pub use cli::cli; pub use data::base::{Primitive, Value}; pub use data::config::{config_path, APP_INFO}; pub use data::dict::{Dictionary, TaggedDictBuilder}; -pub use data::meta::{Span, Tag, Tagged, TaggedItem}; +pub use data::meta::{Tag, Tagged, TaggedItem}; pub use errors::{CoerceInto, ShellError}; pub use num_traits::cast::ToPrimitive; pub use parser::parse::text::Text; diff --git a/src/parser.rs b/src/parser.rs index 2fd891efb0..3b3ac1a136 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -21,10 +21,10 @@ pub(crate) use parse::unit::Unit; pub(crate) use parse_command::parse_command; pub(crate) use registry::CommandRegistry; -pub fn parse(input: &str) -> Result { +pub fn parse(input: &str, origin: uuid::Uuid) -> Result { let _ = pretty_env_logger::try_init(); - match pipeline(nom_input(input)) { + match pipeline(nom_input(input, origin)) { Ok((_rest, val)) => Ok(val), Err(err) => Err(ShellError::parse_error(err)), } diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs index d5427766b5..f9b9146e50 100644 --- a/src/parser/deserializer.rs +++ b/src/parser/deserializer.rs @@ -37,7 +37,7 @@ impl<'de> ConfigDeserializer<'de> { let value: Option> = if name == "rest" { let positional = self.call.args.slice_from(self.position); self.position += positional.len(); - Some(Value::Table(positional).tagged_unknown()) // TODO: correct span + Some(Value::Table(positional).tagged_unknown()) // TODO: correct tag } else { if self.call.args.has(name) { self.call.args.get(name).map(|x| x.clone()) @@ -52,9 +52,7 @@ impl<'de> ConfigDeserializer<'de> { self.stack.push(DeserializerItem { key_struct_field: Some((name.to_string(), name)), - val: value.unwrap_or_else(|| { - Value::nothing().tagged(Tag::unknown_origin(self.call.name_span)) - }), + val: value.unwrap_or_else(|| Value::nothing().tagged(self.call.name_tag)), }); Ok(()) diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 90bb38796a..96eb7272a6 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -25,7 +25,7 @@ pub(crate) use self::external_command::ExternalCommand; pub(crate) use self::named::NamedArguments; pub(crate) use self::path::Path; -pub use self::baseline_parse_tokens::SyntaxType; +pub use self::baseline_parse_tokens::SyntaxShape; pub fn path(head: impl Into, tail: Vec>>) -> Path { Path::new( @@ -131,72 +131,57 @@ impl RawExpression { pub type Expression = Tagged; impl Expression { - pub(crate) fn number(i: impl Into, span: impl Into) -> Expression { - Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Number(i.into())), span) + pub(crate) fn number(i: impl Into, tag: impl Into) -> Expression { + RawExpression::Literal(Literal::Number(i.into())).tagged(tag.into()) } pub(crate) fn size( i: impl Into, unit: impl Into, - span: impl Into, + tag: impl Into, ) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::Literal(Literal::Size(i.into(), unit.into())), - span, - ) + RawExpression::Literal(Literal::Size(i.into(), unit.into())).tagged(tag.into()) } pub(crate) fn synthetic_string(s: impl Into) -> Expression { RawExpression::Synthetic(Synthetic::String(s.into())).tagged_unknown() } - pub(crate) fn string(inner: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::Literal(Literal::String(inner.into())), - outer.into(), - ) + pub(crate) fn string(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Literal(Literal::String(inner.into())).tagged(outer.into()) } - pub(crate) fn file_path(path: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item(RawExpression::FilePath(path.into()), outer.into()) + pub(crate) fn file_path(path: impl Into, outer: impl Into) -> Expression { + RawExpression::FilePath(path.into()).tagged(outer) } - pub(crate) fn bare(span: impl Into) -> Expression { - Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into()) + pub(crate) fn bare(tag: impl Into) -> Expression { + RawExpression::Literal(Literal::Bare).tagged(tag) } pub(crate) fn pattern(tag: impl Into) -> Expression { RawExpression::Literal(Literal::GlobPattern).tagged(tag.into()) } - pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::Variable(Variable::Other(inner.into())), - outer.into(), - ) + pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Variable(Variable::Other(inner.into())).tagged(outer) } - pub(crate) fn external_command(inner: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::ExternalCommand(ExternalCommand::new(inner.into())), - outer.into(), - ) + pub(crate) fn external_command(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::ExternalCommand(ExternalCommand::new(inner.into())).tagged(outer) } - pub(crate) fn it_variable(inner: impl Into, outer: impl Into) -> Expression { - Tagged::from_simple_spanned_item( - RawExpression::Variable(Variable::It(inner.into())), - outer.into(), - ) + pub(crate) fn it_variable(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Variable(Variable::It(inner.into())).tagged(outer) } } impl ToDebug for Expression { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { match self.item() { - RawExpression::Literal(l) => l.tagged(self.span()).fmt_debug(f, source), + RawExpression::Literal(l) => l.tagged(self.tag()).fmt_debug(f, source), RawExpression::FilePath(p) => write!(f, "{}", p.display()), - RawExpression::ExternalWord => write!(f, "{}", self.span().slice(source)), + RawExpression::ExternalWord => write!(f, "{}", self.tag().slice(source)), 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)), @@ -242,7 +227,7 @@ impl From> for Expression { pub enum Literal { Number(Number), Size(Number, Unit), - String(Span), + String(Tag), GlobPattern, Bare, } @@ -252,9 +237,9 @@ impl ToDebug for Tagged<&Literal> { match self.item() { Literal::Number(number) => write!(f, "{:?}", *number), Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), - Literal::String(span) => write!(f, "{}", span.slice(source)), - Literal::GlobPattern => write!(f, "{}", self.span().slice(source)), - Literal::Bare => write!(f, "{}", self.span().slice(source)), + Literal::String(tag) => write!(f, "{}", tag.slice(source)), + Literal::GlobPattern => write!(f, "{}", self.tag().slice(source)), + Literal::Bare => write!(f, "{}", self.tag().slice(source)), } } } @@ -273,6 +258,6 @@ impl Literal { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Variable { - It(Span), - Other(Span), + It(Tag), + Other(Tag), } diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index 5248bde5f9..267494f27c 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -10,19 +10,19 @@ pub fn baseline_parse_single_token( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), + RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()), RawToken::Size(int, unit) => { - hir::Expression::size(int.to_number(source), unit, token.span()) + hir::Expression::size(int.to_number(source), unit, token.tag()) } - RawToken::String(span) => hir::Expression::string(span, token.span()), - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::String(tag) => hir::Expression::string(tag, token.tag()), + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) } - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::GlobPattern => hir::Expression::pattern(token.span()), - RawToken::Bare => hir::Expression::bare(token.span()), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::GlobPattern => hir::Expression::pattern(token.tag()), + RawToken::Bare => hir::Expression::bare(token.tag()), }) } @@ -31,24 +31,24 @@ pub fn baseline_parse_token_as_number( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) } - RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()), + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()), RawToken::Size(number, unit) => { - hir::Expression::size(number.to_number(source), unit, token.span()) + hir::Expression::size(number.to_number(source), unit, token.tag()) } - RawToken::Bare => hir::Expression::bare(token.span()), + RawToken::Bare => hir::Expression::bare(token.tag()), RawToken::GlobPattern => { return Err(ShellError::type_error( "Number", "glob pattern".to_string().tagged(token.tag()), )) } - RawToken::String(span) => hir::Expression::string(span, token.span()), + RawToken::String(tag) => hir::Expression::string(tag, token.tag()), }) } @@ -57,22 +57,22 @@ pub fn baseline_parse_token_as_string( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) } - RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Number(_) => hir::Expression::bare(token.span()), - RawToken::Size(_, _) => hir::Expression::bare(token.span()), - RawToken::Bare => hir::Expression::bare(token.span()), + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::Number(_) => hir::Expression::bare(token.tag()), + RawToken::Size(_, _) => hir::Expression::bare(token.tag()), + RawToken::Bare => hir::Expression::bare(token.tag()), RawToken::GlobPattern => { return Err(ShellError::type_error( "String", "glob pattern".tagged(token.tag()), )) } - RawToken::String(span) => hir::Expression::string(span, token.span()), + RawToken::String(tag) => hir::Expression::string(tag, token.tag()), }) } @@ -82,26 +82,25 @@ pub fn baseline_parse_token_as_path( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) + } + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::Number(_) => hir::Expression::bare(token.tag()), + RawToken::Size(_, _) => hir::Expression::bare(token.tag()), + RawToken::Bare => { + hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag()) } - RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Number(_) => 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::GlobPattern => { return Err(ShellError::type_error( "Path", "glob pattern".tagged(token.tag()), )) } - RawToken::String(span) => { - hir::Expression::file_path(expand_path(span.slice(source), context), token.span()) + RawToken::String(tag) => { + hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag()) } }) } @@ -112,25 +111,24 @@ pub fn baseline_parse_token_as_pattern( source: &Text, ) -> Result { Ok(match *token.item() { - RawToken::Variable(span) if span.slice(source) == "it" => { - hir::Expression::it_variable(span, token.span()) + RawToken::Variable(tag) if tag.slice(source) == "it" => { + hir::Expression::it_variable(tag, token.tag()) } RawToken::ExternalCommand(_) => { return Err(ShellError::syntax_error( "Invalid external command".to_string().tagged(token.tag()), )) } - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), - RawToken::Variable(span) => hir::Expression::variable(span, token.span()), - RawToken::Number(_) => hir::Expression::bare(token.span()), - RawToken::Size(_, _) => hir::Expression::bare(token.span()), - RawToken::GlobPattern => hir::Expression::pattern(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()) + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), + RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), + RawToken::Number(_) => hir::Expression::bare(token.tag()), + RawToken::Size(_, _) => hir::Expression::bare(token.tag()), + RawToken::GlobPattern => hir::Expression::pattern(token.tag()), + RawToken::Bare => { + hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag()) + } + RawToken::String(tag) => { + hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag()) } }) } diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs index ac2c703d3a..8413bd07e1 100644 --- a/src/parser/hir/baseline_parse_tokens.rs +++ b/src/parser/hir/baseline_parse_tokens.rs @@ -8,7 +8,7 @@ use crate::parser::{ }, DelimitedNode, Delimiter, PathNode, RawToken, TokenNode, }; -use crate::{Span, Tag, Tagged, TaggedItem, Text}; +use crate::{Tag, Tagged, TaggedItem, Text}; use derive_new::new; use log::trace; use serde::{Deserialize, Serialize}; @@ -17,7 +17,7 @@ pub fn baseline_parse_tokens( token_nodes: &mut TokensIterator<'_>, context: &Context, source: &Text, - syntax_type: SyntaxType, + syntax_type: SyntaxShape, ) -> Result, ShellError> { let mut exprs: Vec = vec![]; @@ -34,7 +34,7 @@ pub fn baseline_parse_tokens( } #[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub enum SyntaxType { +pub enum SyntaxShape { Any, List, Literal, @@ -49,21 +49,21 @@ pub enum SyntaxType { Boolean, } -impl std::fmt::Display for SyntaxType { +impl std::fmt::Display for SyntaxShape { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match self { - SyntaxType::Any => write!(f, "Any"), - SyntaxType::List => write!(f, "List"), - SyntaxType::Literal => write!(f, "Literal"), - SyntaxType::String => write!(f, "String"), - SyntaxType::Member => write!(f, "Member"), - SyntaxType::Variable => write!(f, "Variable"), - SyntaxType::Number => write!(f, "Number"), - SyntaxType::Path => write!(f, "Path"), - SyntaxType::Pattern => write!(f, "Pattern"), - SyntaxType::Binary => write!(f, "Binary"), - SyntaxType::Block => write!(f, "Block"), - SyntaxType::Boolean => write!(f, "Boolean"), + SyntaxShape::Any => write!(f, "Any"), + SyntaxShape::List => write!(f, "List"), + SyntaxShape::Literal => write!(f, "Literal"), + SyntaxShape::String => write!(f, "String"), + SyntaxShape::Member => write!(f, "Member"), + SyntaxShape::Variable => write!(f, "Variable"), + SyntaxShape::Number => write!(f, "Number"), + SyntaxShape::Path => write!(f, "Path"), + SyntaxShape::Pattern => write!(f, "Pattern"), + SyntaxShape::Binary => write!(f, "Binary"), + SyntaxShape::Block => write!(f, "Block"), + SyntaxShape::Boolean => write!(f, "Boolean"), } } } @@ -72,7 +72,7 @@ pub fn baseline_parse_next_expr( tokens: &mut TokensIterator, context: &Context, source: &Text, - syntax_type: SyntaxType, + syntax_type: SyntaxShape, ) -> Result { let next = tokens .next() @@ -81,69 +81,69 @@ pub fn baseline_parse_next_expr( trace!(target: "nu::parser::parse_one_expr", "syntax_type={:?}, token={:?}", syntax_type, next); match (syntax_type, next) { - (SyntaxType::Path, TokenNode::Token(token)) => { + (SyntaxShape::Path, TokenNode::Token(token)) => { return baseline_parse_token_as_path(token, context, source) } - (SyntaxType::Path, token) => { + (SyntaxShape::Path, token) => { return Err(ShellError::type_error( "Path", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } - (SyntaxType::Pattern, TokenNode::Token(token)) => { + (SyntaxShape::Pattern, TokenNode::Token(token)) => { return baseline_parse_token_as_pattern(token, context, source) } - (SyntaxType::Pattern, token) => { + (SyntaxShape::Pattern, token) => { return Err(ShellError::type_error( "Path", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } - (SyntaxType::String, TokenNode::Token(token)) => { + (SyntaxShape::String, TokenNode::Token(token)) => { return baseline_parse_token_as_string(token, source); } - (SyntaxType::String, token) => { + (SyntaxShape::String, token) => { return Err(ShellError::type_error( "String", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } - (SyntaxType::Number, TokenNode::Token(token)) => { + (SyntaxShape::Number, TokenNode::Token(token)) => { return Ok(baseline_parse_token_as_number(token, source)?); } - (SyntaxType::Number, token) => { + (SyntaxShape::Number, token) => { return Err(ShellError::type_error( "Numeric", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } // TODO: More legit member processing - (SyntaxType::Member, TokenNode::Token(token)) => { + (SyntaxShape::Member, TokenNode::Token(token)) => { return baseline_parse_token_as_string(token, source); } - (SyntaxType::Member, token) => { + (SyntaxShape::Member, token) => { return Err(ShellError::type_error( "member", - token.type_name().simple_spanned(token.span()), + token.type_name().tagged(token.tag()), )) } - (SyntaxType::Any, _) => {} - (SyntaxType::List, _) => {} - (SyntaxType::Literal, _) => {} - (SyntaxType::Variable, _) => {} - (SyntaxType::Binary, _) => {} - (SyntaxType::Block, _) => {} - (SyntaxType::Boolean, _) => {} + (SyntaxShape::Any, _) => {} + (SyntaxShape::List, _) => {} + (SyntaxShape::Literal, _) => {} + (SyntaxShape::Variable, _) => {} + (SyntaxShape::Binary, _) => {} + (SyntaxShape::Block, _) => {} + (SyntaxShape::Boolean, _) => {} }; let first = baseline_parse_semantic_token(next, context, source)?; @@ -162,7 +162,7 @@ pub fn baseline_parse_next_expr( return Err(ShellError::labeled_error( "Expected something after an operator", "operator", - op.span(), + op.tag(), )) } Some(token) => baseline_parse_semantic_token(token, context, source)?, @@ -171,75 +171,66 @@ pub fn baseline_parse_next_expr( // We definitely have a binary expression here -- let's see if we should coerce it into a block match syntax_type { - SyntaxType::Any => { - let span = (first.span().start, second.span().end); + SyntaxShape::Any => { + let tag = first.tag().until(second.tag()); let binary = hir::Binary::new(first, op, second); let binary = hir::RawExpression::Binary(Box::new(binary)); - let binary = Tagged::from_simple_spanned_item(binary, span); + let binary = binary.tagged(tag); Ok(binary) } - SyntaxType::Block => { - let span = (first.span().start, second.span().end); + SyntaxShape::Block => { + let tag = first.tag().until(second.tag()); let path: Tagged = match first { Tagged { item: hir::RawExpression::Literal(hir::Literal::Bare), - tag: Tag { span, .. }, + tag, } => { - let string = - Tagged::from_simple_spanned_item(span.slice(source).to_string(), span); + let string = tag.slice(source).to_string().tagged(tag); let path = hir::Path::new( - Tagged::from_simple_spanned_item( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), - (0, 0), - ), + // TODO: Deal with synthetic nodes that have no representation at all in source + hir::RawExpression::Variable(hir::Variable::It(Tag::unknown())) + .tagged(Tag::unknown()), vec![string], ); let path = hir::RawExpression::Path(Box::new(path)); - Tagged::from_simple_spanned_item(path, first.span()) + path.tagged(first.tag()) } Tagged { item: hir::RawExpression::Literal(hir::Literal::String(inner)), - tag: Tag { span, .. }, + tag, } => { - let string = - Tagged::from_simple_spanned_item(inner.slice(source).to_string(), span); + let string = inner.slice(source).to_string().tagged(tag); let path = hir::Path::new( - Tagged::from_simple_spanned_item( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), - (0, 0), - ), + // TODO: Deal with synthetic nodes that have no representation at all in source + hir::RawExpression::Variable(hir::Variable::It(Tag::unknown())) + .tagged_unknown(), vec![string], ); let path = hir::RawExpression::Path(Box::new(path)); - Tagged::from_simple_spanned_item(path, first.span()) + path.tagged(first.tag()) } Tagged { item: hir::RawExpression::Variable(..), .. } => first, - Tagged { - tag: Tag { span, .. }, - item, - } => { + Tagged { tag, item } => { return Err(ShellError::labeled_error( "The first part of an un-braced block must be a column name", item.type_name(), - span, + tag, )) } }; let binary = hir::Binary::new(path, op, second); let binary = hir::RawExpression::Binary(Box::new(binary)); - let binary = Tagged::from_simple_spanned_item(binary, span); + let binary = binary.tagged(tag); let block = hir::RawExpression::Block(vec![binary]); - let block = Tagged::from_simple_spanned_item(block, span); + let block = block.tagged(tag); Ok(block) } @@ -265,11 +256,11 @@ pub fn baseline_parse_semantic_token( "Unexpected operator".tagged(op.tag), )), TokenNode::Flag(flag) => Err(ShellError::syntax_error("Unexpected flag".tagged(flag.tag))), - TokenNode::Member(span) => Err(ShellError::syntax_error( - "BUG: Top-level member".tagged(span), + TokenNode::Member(tag) => Err(ShellError::syntax_error( + "BUG: Top-level member".tagged(*tag), )), - TokenNode::Whitespace(span) => Err(ShellError::syntax_error( - "BUG: Whitespace found during parse".tagged(span), + TokenNode::Whitespace(tag) => Err(ShellError::syntax_error( + "BUG: Whitespace found during parse".tagged(*tag), )), TokenNode::Error(error) => Err(*error.item.clone()), TokenNode::Path(path) => baseline_parse_path(path, context, source), @@ -288,11 +279,11 @@ pub fn baseline_parse_delimited( &mut TokensIterator::new(children), context, source, - SyntaxType::Any, + SyntaxShape::Any, )?; let expr = hir::RawExpression::Block(exprs); - Ok(Tagged::from_simple_spanned_item(expr, token.span())) + Ok(expr.tagged(token.tag())) } Delimiter::Paren => unimplemented!(), Delimiter::Square => { @@ -301,11 +292,11 @@ pub fn baseline_parse_delimited( &mut TokensIterator::new(children), context, source, - SyntaxType::Any, + SyntaxShape::Any, )?; let expr = hir::RawExpression::List(exprs); - Ok(expr.tagged(Tag::unknown_origin(token.span()))) + Ok(expr.tagged(token.tag())) } } } @@ -322,8 +313,8 @@ pub fn baseline_parse_path( for part in token.tail() { let string = match part { TokenNode::Token(token) => match token.item() { - RawToken::Bare => token.span().slice(source), - RawToken::String(span) => span.slice(source), + RawToken::Bare => token.tag().slice(source), + RawToken::String(tag) => tag.slice(source), RawToken::Number(_) | RawToken::Size(..) | RawToken::Variable(_) @@ -332,26 +323,26 @@ pub fn baseline_parse_path( | RawToken::ExternalWord => { return Err(ShellError::type_error( "String", - token.type_name().simple_spanned(part), + token.type_name().tagged(part.tag()), )) } }, - TokenNode::Member(span) => span.slice(source), + TokenNode::Member(tag) => tag.slice(source), // TODO: Make this impossible other => { return Err(ShellError::syntax_error( - format!("{} in path", other.type_name()).tagged(other.span()), + format!("{} in path", other.type_name()).tagged(other.tag()), )) } } .to_string(); - tail.push(string.simple_spanned(part)); + tail.push(string.tagged(part.tag())); } - Ok(hir::path(head, tail).simple_spanned(token).into()) + Ok(hir::path(head, tail).tagged(token.tag()).into()) } #[derive(Debug, new)] diff --git a/src/parser/hir/external_command.rs b/src/parser/hir/external_command.rs index 8511cce1e0..28865330d5 100644 --- a/src/parser/hir/external_command.rs +++ b/src/parser/hir/external_command.rs @@ -9,7 +9,7 @@ use std::fmt; )] #[get = "pub(crate)"] pub struct ExternalCommand { - name: Span, + name: Tag, } impl ToDebug for ExternalCommand { diff --git a/src/parser/hir/named.rs b/src/parser/hir/named.rs index 96d5132fb8..838f643be5 100644 --- a/src/parser/hir/named.rs +++ b/src/parser/hir/named.rs @@ -1,7 +1,6 @@ use crate::parser::hir::Expression; use crate::parser::Flag; use crate::prelude::*; -use crate::Span; use derive_new::new; use indexmap::IndexMap; use log::trace; @@ -11,7 +10,7 @@ use std::fmt; #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] pub enum NamedValue { AbsentSwitch, - PresentSwitch(Span), + PresentSwitch(Tag), AbsentValue, Value(Expression), } @@ -27,7 +26,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(tag) => write!(f, " --{}", tag.slice(source))?, NamedValue::AbsentValue => continue, NamedValue::Value(expr) => write!(f, " --{} {}", name, expr.debug(source))?, } diff --git a/src/parser/parse/files.rs b/src/parser/parse/files.rs index 173da54a80..6cedb1e99c 100644 --- a/src/parser/parse/files.rs +++ b/src/parser/parse/files.rs @@ -1,6 +1,7 @@ -use crate::Span; +use crate::Tag; use derive_new::new; use language_reporting::{FileName, Location}; +use uuid::Uuid; #[derive(new, Debug, Clone)] pub struct Files { @@ -8,26 +9,30 @@ pub struct Files { } impl language_reporting::ReportingFiles for Files { - type Span = Span; - type FileId = usize; + type Span = Tag; + type FileId = Uuid; fn byte_span( &self, - _file: Self::FileId, + file: Self::FileId, from_index: usize, to_index: usize, ) -> Option { - Some(Span::from((from_index, to_index))) + Some(Tag::from((from_index, to_index, file))) } - fn file_id(&self, _span: Self::Span) -> Self::FileId { - 0 + + fn file_id(&self, tag: Self::Span) -> Self::FileId { + tag.origin.unwrap() } + fn file_name(&self, _file: Self::FileId) -> FileName { FileName::Verbatim(format!("shell")) } + fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option { unimplemented!("byte_index") } + fn location(&self, _file: Self::FileId, byte_index: usize) -> Option { let source = &self.snippet; let mut seen_lines = 0; @@ -51,14 +56,15 @@ impl language_reporting::ReportingFiles for Files { None } } - fn line_span(&self, _file: Self::FileId, lineno: usize) -> Option { + + fn line_span(&self, file: Self::FileId, lineno: usize) -> Option { let source = &self.snippet; let mut seen_lines = 0; let mut seen_bytes = 0; for (pos, _) in source.match_indices('\n') { if seen_lines == lineno { - return Some(Span::from((seen_bytes, pos))); + return Some(Tag::from((seen_bytes, pos, file))); } else { seen_lines += 1; seen_bytes = pos + 1; @@ -66,17 +72,18 @@ impl language_reporting::ReportingFiles for Files { } if seen_lines == 0 { - Some(Span::from((0, self.snippet.len() - 1))) + Some(Tag::from((0, self.snippet.len() - 1, file))) } else { None } } - fn source(&self, span: Self::Span) -> Option { - if span.start > span.end { + + fn source(&self, tag: Self::Span) -> Option { + if tag.span.start > tag.span.end { return None; - } else if span.end >= self.snippet.len() { + } else if tag.span.end >= self.snippet.len() { return None; } - Some(self.snippet[span.start..span.end].to_string()) + Some(tag.slice(&self.snippet).to_string()) } } diff --git a/src/parser/parse/flag.rs b/src/parser/parse/flag.rs index 096d69879f..09d1e86337 100644 --- a/src/parser/parse/flag.rs +++ b/src/parser/parse/flag.rs @@ -1,4 +1,4 @@ -use crate::Span; +use crate::Tag; use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; @@ -13,5 +13,5 @@ pub enum FlagKind { #[get = "pub(crate)"] pub struct Flag { kind: FlagKind, - name: Span, + name: Tag, } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 0be05af062..f30eb2c11b 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -5,7 +5,7 @@ use crate::parser::parse::{ tokens::*, unit::*, }; use crate::prelude::*; -use crate::{Span, Tagged}; +use crate::{Tag, Tagged}; use nom; use nom::branch::*; use nom::bytes::complete::*; @@ -18,15 +18,16 @@ use log::trace; use nom::dbg; use nom::*; use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice}; -use nom5_locate::{position, LocatedSpan}; +use nom_locate::{position, LocatedSpanEx}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::str::FromStr; +use uuid::Uuid; -pub type NomSpan<'a> = LocatedSpan<&'a str>; +pub type NomSpan<'a> = LocatedSpanEx<&'a str, Uuid>; -pub fn nom_input(s: &str) -> NomSpan<'_> { - LocatedSpan::new(s) +pub fn nom_input(s: &str, origin: Uuid) -> NomSpan<'_> { + LocatedSpanEx::new_extra(s, origin) } macro_rules! operator { @@ -38,7 +39,7 @@ macro_rules! operator { Ok(( input, - TokenTreeBuilder::spanned_op(tag.fragment, (start, end)), + TokenTreeBuilder::tagged_op(tag.fragment, (start, end, input.extra)), )) } }; @@ -159,14 +160,14 @@ pub fn raw_number(input: NomSpan) -> IResult> { Ok((input, dot)) => input, // it's just an integer - Err(_) => return Ok((input, RawNumber::int((start, input.offset)))), + Err(_) => return Ok((input, RawNumber::int((start, input.offset, input.extra)))), }; let (input, tail) = digit1(input)?; let end = input.offset; - Ok((input, RawNumber::decimal((start, end)))) + Ok((input, RawNumber::decimal((start, end, input.extra)))) }) } @@ -189,7 +190,7 @@ pub fn dq_string(input: NomSpan) -> IResult { let end = input.offset; Ok(( input, - TokenTreeBuilder::spanned_string((start1, end1), (start, end)), + TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), )) }) } @@ -206,7 +207,7 @@ pub fn sq_string(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_string((start1, end1), (start, end)), + TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), )) }) } @@ -226,7 +227,7 @@ pub fn external(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_external(bare, (start, end)), + TokenTreeBuilder::tagged_external(bare, (start, end, input.extra)), )) }) } @@ -250,7 +251,10 @@ pub fn pattern(input: NomSpan) -> IResult { let end = input.offset; - Ok((input, TokenTreeBuilder::spanned_pattern((start, end)))) + Ok(( + input, + TokenTreeBuilder::tagged_pattern((start, end, input.extra)), + )) }) } @@ -263,7 +267,7 @@ pub fn bare(input: NomSpan) -> IResult { let next_char = &input.fragment.chars().nth(0); if let Some(next_char) = next_char { - if is_external_word_char(*next_char) || *next_char == '*' { + if is_external_word_char(*next_char) || is_glob_specific_char(*next_char) { return Err(nom::Err::Error(nom::error::make_error( input, nom::error::ErrorKind::TakeWhile1, @@ -273,7 +277,10 @@ pub fn bare(input: NomSpan) -> IResult { let end = input.offset; - Ok((input, TokenTreeBuilder::spanned_bare((start, end)))) + Ok(( + input, + TokenTreeBuilder::tagged_bare((start, end, input.extra)), + )) }) } @@ -283,7 +290,10 @@ pub fn external_word(input: NomSpan) -> IResult { let (input, _) = take_while1(is_external_word_char)(input)?; let end = input.offset; - Ok((input, TokenTreeBuilder::spanned_external_word((start, end)))) + Ok(( + input, + TokenTreeBuilder::tagged_external_word((start, end, input.extra)), + )) }) } @@ -296,7 +306,7 @@ pub fn var(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_var(bare.span(), (start, end)), + TokenTreeBuilder::tagged_var(bare.tag(), (start, end, input.extra)), )) }) } @@ -309,7 +319,10 @@ pub fn member(input: NomSpan) -> IResult { let end = input.offset; - Ok((input, TokenTreeBuilder::spanned_member((start, end)))) + Ok(( + input, + TokenTreeBuilder::tagged_member((start, end, input.extra)), + )) }) } @@ -322,7 +335,7 @@ pub fn flag(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_flag(bare.span(), (start, end)), + TokenTreeBuilder::tagged_flag(bare.tag(), (start, end, input.extra)), )) }) } @@ -336,7 +349,7 @@ pub fn shorthand(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_shorthand(bare.span(), (start, end)), + TokenTreeBuilder::tagged_shorthand(bare.tag(), (start, end, input.extra)), )) }) } @@ -369,7 +382,7 @@ pub fn raw_unit(input: NomSpan) -> IResult> { Ok(( input, - Tagged::from_simple_spanned_item(Unit::from(unit.fragment), (start, end)), + Unit::from(unit.fragment).tagged((start, end, input.extra)), )) }) } @@ -389,7 +402,7 @@ pub fn size(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_size((number.item, *size), (start, end)), + TokenTreeBuilder::tagged_size((number.item, *size), (start, end, input.extra)), )) } else { let end = input.offset; @@ -401,7 +414,7 @@ pub fn size(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_number(number.item, number.tag), + TokenTreeBuilder::tagged_number(number.item, number.tag), )) } }) @@ -455,18 +468,18 @@ fn make_token_list( let mut nodes = vec![]; if let Some(sp_left) = sp_left { - nodes.push(TokenNode::Whitespace(Span::from(sp_left))); + nodes.push(TokenNode::Whitespace(Tag::from(sp_left))); } nodes.push(first); for (ws, token) in list { - nodes.push(TokenNode::Whitespace(Span::from(ws))); + nodes.push(TokenNode::Whitespace(Tag::from(ws))); nodes.push(token); } if let Some(sp_right) = sp_right { - nodes.push(TokenNode::Whitespace(Span::from(sp_right))); + nodes.push(TokenNode::Whitespace(Tag::from(sp_right))); } nodes @@ -478,7 +491,10 @@ pub fn whitespace(input: NomSpan) -> IResult { let (input, ws1) = space1(input)?; let right = input.offset; - Ok((input, TokenTreeBuilder::spanned_ws((left, right)))) + Ok(( + input, + TokenTreeBuilder::tagged_ws((left, right, input.extra)), + )) }) } @@ -508,7 +524,7 @@ pub fn delimited_paren(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_parens(items, (left, right)), + TokenTreeBuilder::tagged_parens(items, (left, right, input.extra)), )) }) } @@ -539,7 +555,7 @@ pub fn delimited_square(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_square(items, (left, right)), + TokenTreeBuilder::tagged_square(items, (left, right, input.extra)), )) }) } @@ -556,7 +572,10 @@ pub fn delimited_brace(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_brace(items.unwrap_or_else(|| vec![]), (left, right)), + TokenTreeBuilder::tagged_brace( + items.unwrap_or_else(|| vec![]), + (left, right, input.extra), + ), )) }) } @@ -567,7 +586,10 @@ pub fn raw_call(input: NomSpan) -> IResult> { let (input, items) = token_list(input)?; let right = input.offset; - Ok((input, TokenTreeBuilder::spanned_call(items, (left, right)))) + Ok(( + input, + TokenTreeBuilder::tagged_call(items, (left, right, input.extra)), + )) }) } @@ -581,7 +603,7 @@ pub fn path(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_path((head, tail), (left, right)), + TokenTreeBuilder::tagged_path((head, tail), (left, right, input.extra)), )) }) } @@ -628,9 +650,9 @@ pub fn pipeline(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::spanned_pipeline( - (make_call_list(head, items), tail.map(Span::from)), - (start, end), + TokenTreeBuilder::tagged_pipeline( + (make_call_list(head, items), tail.map(Tag::from)), + (start, end, input.extra), ), )) }) @@ -643,17 +665,17 @@ fn make_call_list( let mut out = vec![]; if let Some(head) = head { - let el = PipelineElement::new(None, head.0.map(Span::from), head.1, head.2.map(Span::from)); + let el = PipelineElement::new(None, head.0.map(Tag::from), head.1, head.2.map(Tag::from)); out.push(el); } for (pipe, ws1, call, ws2) in items { let el = PipelineElement::new( - Some(pipe).map(Span::from), - ws1.map(Span::from), + Some(pipe).map(Tag::from), + ws1.map(Tag::from), call, - ws2.map(Span::from), + ws2.map(Tag::from), ); out.push(el); @@ -679,12 +701,17 @@ fn is_external_word_char(c: char) -> bool { } } +/// These characters appear in globs and not bare words +fn is_glob_specific_char(c: char) -> bool { + c == '*' || c == '?' +} + fn is_start_glob_char(c: char) -> bool { - is_start_bare_char(c) || c == '*' + is_start_bare_char(c) || is_glob_specific_char(c) } fn is_glob_char(c: char) -> bool { - is_bare_char(c) || c == '*' + is_bare_char(c) || is_glob_specific_char(c) } fn is_start_bare_char(c: char) -> bool { @@ -779,7 +806,7 @@ mod tests { macro_rules! equal_tokens { ($source:tt -> $tokens:expr) => { let result = apply(pipeline, "pipeline", $source); - let (expected_tree, expected_source) = TokenTreeBuilder::build($tokens); + let (expected_tree, expected_source) = TokenTreeBuilder::build(uuid::Uuid::nil(), $tokens); if result != expected_tree { let debug_result = format!("{}", result.debug($source)); @@ -818,12 +845,12 @@ mod tests { fn test_integer() { assert_leaf! { parsers [ size ] - "123" -> 0..3 { Number(RawNumber::int((0, 3)).item) } + "123" -> 0..3 { Number(RawNumber::int((0, 3, test_uuid())).item) } } assert_leaf! { parsers [ size ] - "-123" -> 0..4 { Number(RawNumber::int((0, 4)).item) } + "-123" -> 0..4 { Number(RawNumber::int((0, 4, test_uuid())).item) } } } @@ -831,12 +858,12 @@ mod tests { fn test_size() { assert_leaf! { parsers [ size ] - "123MB" -> 0..5 { Size(RawNumber::int((0, 3)).item, Unit::MB) } + "123MB" -> 0..5 { Size(RawNumber::int((0, 3, test_uuid())).item, Unit::MB) } } assert_leaf! { parsers [ size ] - "10GB" -> 0..4 { Size(RawNumber::int((0, 2)).item, Unit::GB) } + "10GB" -> 0..4 { Size(RawNumber::int((0, 2, test_uuid())).item, Unit::GB) } } } @@ -874,12 +901,12 @@ mod tests { fn test_string() { assert_leaf! { parsers [ string dq_string ] - r#""hello world""# -> 0..13 { String(span(1, 12)) } + r#""hello world""# -> 0..13 { String(tag(1, 12)) } } assert_leaf! { parsers [ string sq_string ] - r"'hello world'" -> 0..13 { String(span(1, 12)) } + r"'hello world'" -> 0..13 { String(tag(1, 12)) } } } @@ -931,12 +958,12 @@ mod tests { fn test_variable() { assert_leaf! { parsers [ var ] - "$it" -> 0..3 { Variable(span(1, 3)) } + "$it" -> 0..3 { Variable(tag(1, 3)) } } assert_leaf! { parsers [ var ] - "$name" -> 0..5 { Variable(span(1, 5)) } + "$name" -> 0..5 { Variable(tag(1, 5)) } } } @@ -944,7 +971,7 @@ mod tests { fn test_external() { assert_leaf! { parsers [ external ] - "^ls" -> 0..3 { ExternalCommand(span(1, 3)) } + "^ls" -> 0..3 { ExternalCommand(tag(1, 3)) } } } @@ -1260,7 +1287,7 @@ mod tests { desc: &str, string: &str, ) -> T { - match f(NomSpan::new(string)) { + match f(NomSpan::new_extra(string, uuid::Uuid::nil())) { Ok(v) => v.1, Err(other) => { println!("{:?}", other); @@ -1270,44 +1297,46 @@ mod tests { } } - fn span(left: usize, right: usize) -> Span { - Span::from((left, right)) + fn tag(left: usize, right: usize) -> Tag { + Tag::from((left, right, uuid::Uuid::nil())) } fn delimited( - delimiter: Delimiter, + delimiter: Tagged, children: Vec, left: usize, right: usize, ) -> TokenNode { - let node = DelimitedNode::new(delimiter, children); - let spanned = Tagged::from_simple_spanned_item(node, (left, right)); + let node = DelimitedNode::new(*delimiter, children); + let spanned = node.tagged((left, right, delimiter.tag.origin)); TokenNode::Delimited(spanned) } fn path(head: TokenNode, tail: Vec, left: usize, right: usize) -> TokenNode { + let tag = head.tag(); + let node = PathNode::new( Box::new(head), tail.into_iter().map(TokenNode::Token).collect(), ); - let spanned = Tagged::from_simple_spanned_item(node, (left, right)); + let spanned = node.tagged((left, right, tag.origin)); TokenNode::Path(spanned) } - fn leaf_token(token: RawToken, left: usize, right: usize) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right))) - } - fn token(token: RawToken, left: usize, right: usize) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right))) + TokenNode::Token(token.tagged((left, right, uuid::Uuid::nil()))) } fn build(block: CurriedNode) -> T { - let mut builder = TokenTreeBuilder::new(); + let mut builder = TokenTreeBuilder::new(uuid::Uuid::nil()); block(&mut builder) } fn build_token(block: CurriedToken) -> TokenNode { - TokenTreeBuilder::build(block).0 + TokenTreeBuilder::build(uuid::Uuid::nil(), block).0 + } + + fn test_uuid() -> uuid::Uuid { + uuid::Uuid::nil() } } diff --git a/src/parser/parse/pipeline.rs b/src/parser/parse/pipeline.rs index 64a899c179..42bbe23a18 100644 --- a/src/parser/parse/pipeline.rs +++ b/src/parser/parse/pipeline.rs @@ -1,6 +1,6 @@ use crate::parser::CallNode; use crate::traits::ToDebug; -use crate::{Span, Tagged}; +use crate::{Tag, Tagged}; use derive_new::new; use getset::Getters; use std::fmt; @@ -8,7 +8,7 @@ use std::fmt; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)] pub struct Pipeline { pub(crate) parts: Vec, - pub(crate) post_ws: Option, + pub(crate) post_ws: Option, } impl ToDebug for Pipeline { @@ -27,11 +27,11 @@ impl ToDebug for Pipeline { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] pub struct PipelineElement { - pub pipe: Option, - pub pre_ws: Option, + pub pipe: Option, + pub pre_ws: Option, #[get = "pub(crate)"] call: Tagged, - pub post_ws: Option, + pub post_ws: Option, } impl ToDebug for PipelineElement { diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index f69c176e97..e0072360e8 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -1,7 +1,7 @@ use crate::errors::ShellError; use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*}; use crate::traits::ToDebug; -use crate::{Span, Tagged, Text}; +use crate::{Tag, Tagged, Text}; use derive_new::new; use enum_utils::FromStr; use getset::Getters; @@ -16,8 +16,8 @@ pub enum TokenNode { Pipeline(Tagged), Operator(Tagged), Flag(Tagged), - Member(Span), - Whitespace(Span), + Member(Tag), + Whitespace(Tag), Error(Tagged>), Path(Tagged), @@ -78,31 +78,31 @@ impl fmt::Debug for DebugTokenNode<'_> { ) } TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)), - TokenNode::Error(s) => write!(f, " for {:?}", s.span().slice(self.source)), - rest => write!(f, "{}", rest.span().slice(self.source)), + TokenNode::Error(s) => write!(f, " for {:?}", s.tag().slice(self.source)), + rest => write!(f, "{}", rest.tag().slice(self.source)), } } } -impl From<&TokenNode> for Span { - fn from(token: &TokenNode) -> Span { - token.span() +impl From<&TokenNode> for Tag { + fn from(token: &TokenNode) -> Tag { + token.tag() } } impl TokenNode { - pub fn span(&self) -> Span { + pub fn tag(&self) -> Tag { match self { - TokenNode::Token(t) => t.span(), - TokenNode::Call(s) => s.span(), - TokenNode::Delimited(s) => s.span(), - TokenNode::Pipeline(s) => s.span(), - TokenNode::Operator(s) => s.span(), - TokenNode::Flag(s) => s.span(), + TokenNode::Token(t) => t.tag(), + TokenNode::Call(s) => s.tag(), + TokenNode::Delimited(s) => s.tag(), + TokenNode::Pipeline(s) => s.tag(), + TokenNode::Operator(s) => s.tag(), + TokenNode::Flag(s) => s.tag(), TokenNode::Member(s) => *s, TokenNode::Whitespace(s) => *s, - TokenNode::Error(s) => s.span(), - TokenNode::Path(s) => s.span(), + TokenNode::Error(s) => s.tag(), + TokenNode::Path(s) => s.tag(), } } @@ -127,11 +127,11 @@ impl TokenNode { } pub fn as_external_arg(&self, source: &Text) -> String { - self.span().slice(source).to_string() + self.tag().slice(source).to_string() } pub fn source<'a>(&self, source: &'a Text) -> &'a str { - self.span().slice(source) + self.tag().slice(source) } pub fn is_bare(&self) -> bool { @@ -154,12 +154,12 @@ impl TokenNode { } } - pub fn expect_external(&self) -> Span { + pub fn expect_external(&self) -> Tag { match self { TokenNode::Token(Tagged { - item: RawToken::ExternalCommand(span), + item: RawToken::ExternalCommand(tag), .. - }) => *span, + }) => *tag, _ => panic!("Only call expect_external if you checked is_external first"), } } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index ae1b344c44..1ed8383f4c 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -7,8 +7,8 @@ use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, PathNode, Token use crate::parser::parse::tokens::{RawNumber, RawToken}; use crate::parser::parse::unit::Unit; use crate::parser::CallNode; -use crate::Span; use derive_new::new; +use uuid::Uuid; #[derive(new)] pub struct TokenTreeBuilder { @@ -17,14 +17,16 @@ pub struct TokenTreeBuilder { #[new(default)] output: String, + + origin: Uuid, } pub type CurriedToken = Box TokenNode + 'static>; pub type CurriedCall = Box Tagged + 'static>; impl TokenTreeBuilder { - pub fn build(block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { - let mut builder = TokenTreeBuilder::new(); + pub fn build(origin: Uuid, block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { + let mut builder = TokenTreeBuilder::new(origin); let node = block(&mut builder); (node, builder.output) } @@ -52,50 +54,37 @@ impl TokenTreeBuilder { .expect("A pipeline must contain at least one element"); let pipe = None; - let pre_span = pre.map(|pre| b.consume(&pre)); + let pre_tag = pre.map(|pre| b.consume_tag(&pre)); let call = call(b); - let post_span = post.map(|post| b.consume(&post)); + let post_tag = post.map(|post| b.consume_tag(&post)); - out.push(PipelineElement::new( - pipe, - pre_span.map(Span::from), - call, - post_span.map(Span::from), - )); + out.push(PipelineElement::new(pipe, pre_tag, call, post_tag)); loop { match input.next() { None => break, Some((pre, call, post)) => { - let pipe = Some(Span::from(b.consume("|"))); - let pre_span = pre.map(|pre| b.consume(&pre)); + let pipe = Some(b.consume_tag("|")); + let pre_span = pre.map(|pre| b.consume_tag(&pre)); let call = call(b); - let post_span = post.map(|post| b.consume(&post)); + let post_span = post.map(|post| b.consume_tag(&post)); - out.push(PipelineElement::new( - pipe, - pre_span.map(Span::from), - call, - post_span.map(Span::from), - )); + out.push(PipelineElement::new(pipe, pre_span, call, post_span)); } } } let end = b.pos; - TokenTreeBuilder::spanned_pipeline((out, None), (start, end)) + TokenTreeBuilder::tagged_pipeline((out, None), (start, end, b.origin)) }) } - pub fn spanned_pipeline( - input: (Vec, Option), - span: impl Into, + pub fn tagged_pipeline( + input: (Vec, Option), + tag: impl Into, ) -> TokenNode { - TokenNode::Pipeline(Tagged::from_simple_spanned_item( - Pipeline::new(input.0, input.1.into()), - span, - )) + TokenNode::Pipeline(Pipeline::new(input.0, input.1.into()).tagged(tag.into())) } pub fn op(input: impl Into) -> CurriedToken { @@ -106,12 +95,12 @@ impl TokenTreeBuilder { b.pos = end; - TokenTreeBuilder::spanned_op(input, (start, end)) + TokenTreeBuilder::tagged_op(input, (start, end, b.origin)) }) } - pub fn spanned_op(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Operator(Tagged::from_simple_spanned_item(input.into(), span.into())) + pub fn tagged_op(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Operator(input.into().tagged(tag.into())) } pub fn string(input: impl Into) -> CurriedToken { @@ -123,15 +112,15 @@ impl TokenTreeBuilder { let (_, end) = b.consume("\""); b.pos = end; - TokenTreeBuilder::spanned_string((inner_start, inner_end), (start, end)) + TokenTreeBuilder::tagged_string( + (inner_start, inner_end, b.origin), + (start, end, b.origin), + ) }) } - pub fn spanned_string(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::String(input.into()), - span.into(), - )) + pub fn tagged_string(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::String(input.into()).tagged(tag.into())) } pub fn bare(input: impl Into) -> CurriedToken { @@ -141,15 +130,12 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::spanned_bare((start, end)) + TokenTreeBuilder::tagged_bare((start, end, b.origin)) }) } - pub fn spanned_bare(input: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Bare, - input.into(), - )) + pub fn tagged_bare(tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Bare.tagged(tag.into())) } pub fn pattern(input: impl Into) -> CurriedToken { @@ -159,15 +145,12 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::spanned_pattern((start, end)) + TokenTreeBuilder::tagged_pattern((start, end, b.origin)) }) } - pub fn spanned_pattern(input: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Bare, - input.into(), - )) + pub fn tagged_pattern(input: impl Into) -> TokenNode { + TokenNode::Token(RawToken::GlobPattern.tagged(input.into())) } pub fn external_word(input: impl Into) -> CurriedToken { @@ -177,22 +160,16 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::spanned_external_word((start, end)) + TokenTreeBuilder::tagged_external_word((start, end, b.origin)) }) } - pub fn spanned_external_word(input: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::ExternalWord, - input.into(), - )) + pub fn tagged_external_word(input: impl Into) -> TokenNode { + TokenNode::Token(RawToken::ExternalWord.tagged(input.into())) } - pub fn spanned_external(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::ExternalCommand(input.into()), - span.into(), - )) + pub fn tagged_external(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::ExternalCommand(input.into()).tagged(tag.into())) } pub fn int(input: impl Into) -> CurriedToken { @@ -202,7 +179,10 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&int.to_string()); b.pos = end; - TokenTreeBuilder::spanned_number(RawNumber::Int((start, end).into()), (start, end)) + TokenTreeBuilder::tagged_number( + RawNumber::Int((start, end, b.origin).into()), + (start, end, b.origin), + ) }) } @@ -213,15 +193,15 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&decimal.to_string()); b.pos = end; - TokenTreeBuilder::spanned_number(RawNumber::Decimal((start, end).into()), (start, end)) + TokenTreeBuilder::tagged_number( + RawNumber::Decimal((start, end, b.origin).into()), + (start, end, b.origin), + ) }) } - pub fn spanned_number(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Number(input.into()), - span.into(), - )) + pub fn tagged_number(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Number(input.into()).tagged(tag.into())) } pub fn size(int: impl Into, unit: impl Into) -> CurriedToken { @@ -233,23 +213,20 @@ impl TokenTreeBuilder { let (_, end_unit) = b.consume(unit.as_str()); b.pos = end_unit; - TokenTreeBuilder::spanned_size( - (RawNumber::Int((start_int, end_int).into()), unit), - (start_int, end_unit), + TokenTreeBuilder::tagged_size( + (RawNumber::Int((start_int, end_int, b.origin).into()), unit), + (start_int, end_unit, b.origin), ) }) } - pub fn spanned_size( + pub fn tagged_size( input: (impl Into, impl Into), - span: impl Into, + tag: impl Into, ) -> TokenNode { let (int, unit) = (input.0.into(), input.1.into()); - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Size(int, unit), - span, - )) + TokenNode::Token(RawToken::Size(int, unit).tagged(tag.into())) } pub fn path(head: CurriedToken, tail: Vec) -> CurriedToken { @@ -267,15 +244,12 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::spanned_path((head, output), (start, end)) + TokenTreeBuilder::tagged_path((head, output), (start, end, b.origin)) }) } - pub fn spanned_path(input: (TokenNode, Vec), span: impl Into) -> TokenNode { - TokenNode::Path(Tagged::from_simple_spanned_item( - PathNode::new(Box::new(input.0), input.1), - span, - )) + pub fn tagged_path(input: (TokenNode, Vec), tag: impl Into) -> TokenNode { + TokenNode::Path(PathNode::new(Box::new(input.0), input.1).tagged(tag.into())) } pub fn var(input: impl Into) -> CurriedToken { @@ -285,15 +259,12 @@ impl TokenTreeBuilder { let (start, _) = b.consume("$"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::spanned_var((inner_start, end), (start, end)) + TokenTreeBuilder::tagged_var((inner_start, end, b.origin), (start, end, b.origin)) }) } - pub fn spanned_var(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Token(Tagged::from_simple_spanned_item( - RawToken::Variable(input.into()), - span.into(), - )) + pub fn tagged_var(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Variable(input.into()).tagged(tag.into())) } pub fn flag(input: impl Into) -> CurriedToken { @@ -303,15 +274,12 @@ impl TokenTreeBuilder { let (start, _) = b.consume("--"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::spanned_flag((inner_start, end), (start, end)) + TokenTreeBuilder::tagged_flag((inner_start, end, b.origin), (start, end, b.origin)) }) } - pub fn spanned_flag(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Flag(Tagged::from_simple_spanned_item( - Flag::new(FlagKind::Longhand, input.into()), - span.into(), - )) + pub fn tagged_flag(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Flag(Flag::new(FlagKind::Longhand, input.into()).tagged(tag.into())) } pub fn shorthand(input: impl Into) -> CurriedToken { @@ -321,15 +289,12 @@ impl TokenTreeBuilder { let (start, _) = b.consume("-"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::spanned_shorthand((inner_start, end), (start, end)) + TokenTreeBuilder::tagged_shorthand((inner_start, end, b.origin), (start, end, b.origin)) }) } - pub fn spanned_shorthand(input: impl Into, span: impl Into) -> TokenNode { - TokenNode::Flag(Tagged::from_simple_spanned_item( - Flag::new(FlagKind::Shorthand, input.into()), - span.into(), - )) + pub fn tagged_shorthand(input: impl Into, tag: impl Into) -> TokenNode { + TokenNode::Flag(Flag::new(FlagKind::Shorthand, input.into()).tagged(tag.into())) } pub fn member(input: impl Into) -> CurriedToken { @@ -337,12 +302,12 @@ impl TokenTreeBuilder { Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::spanned_member((start, end)) + TokenTreeBuilder::tagged_member((start, end, b.origin)) }) } - pub fn spanned_member(span: impl Into) -> TokenNode { - TokenNode::Member(span.into()) + pub fn tagged_member(tag: impl Into) -> TokenNode { + TokenNode::Member(tag.into()) } pub fn call(head: CurriedToken, input: Vec) -> CurriedCall { @@ -358,11 +323,11 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::spanned_call(nodes, (start, end)) + TokenTreeBuilder::tagged_call(nodes, (start, end, b.origin)) }) } - pub fn spanned_call(input: Vec, span: impl Into) -> Tagged { + pub fn tagged_call(input: Vec, tag: impl Into) -> Tagged { if input.len() == 0 { panic!("BUG: spanned call (TODO)") } @@ -372,7 +337,7 @@ impl TokenTreeBuilder { let head = input.next().unwrap(); let tail = input.collect(); - Tagged::from_simple_spanned_item(CallNode::new(Box::new(head), tail), span) + CallNode::new(Box::new(head), tail).tagged(tag.into()) } pub fn parens(input: Vec) -> CurriedToken { @@ -385,15 +350,12 @@ impl TokenTreeBuilder { let (_, end) = b.consume(")"); - TokenTreeBuilder::spanned_parens(output, (start, end)) + TokenTreeBuilder::tagged_parens(output, (start, end, b.origin)) }) } - pub fn spanned_parens(input: impl Into>, span: impl Into) -> TokenNode { - TokenNode::Delimited(Tagged::from_simple_spanned_item( - DelimitedNode::new(Delimiter::Paren, input.into()), - span, - )) + pub fn tagged_parens(input: impl Into>, tag: impl Into) -> TokenNode { + TokenNode::Delimited(DelimitedNode::new(Delimiter::Paren, input.into()).tagged(tag.into())) } pub fn square(input: Vec) -> CurriedToken { @@ -406,15 +368,12 @@ impl TokenTreeBuilder { let (_, end) = b.consume("]"); - TokenTreeBuilder::spanned_square(output, (start, end)) + TokenTreeBuilder::tagged_square(output, (start, end, b.origin)) }) } - pub fn spanned_square(input: impl Into>, span: impl Into) -> TokenNode { - TokenNode::Delimited(Tagged::from_simple_spanned_item( - DelimitedNode::new(Delimiter::Square, input.into()), - span, - )) + pub fn tagged_square(input: impl Into>, tag: impl Into) -> TokenNode { + TokenNode::Delimited(DelimitedNode::new(Delimiter::Square, input.into()).tagged(tag.into())) } pub fn braced(input: Vec) -> CurriedToken { @@ -427,21 +386,18 @@ impl TokenTreeBuilder { let (_, end) = b.consume(" }"); - TokenTreeBuilder::spanned_brace(output, (start, end)) + TokenTreeBuilder::tagged_brace(output, (start, end, b.origin)) }) } - pub fn spanned_brace(input: impl Into>, span: impl Into) -> TokenNode { - TokenNode::Delimited(Tagged::from_simple_spanned_item( - DelimitedNode::new(Delimiter::Brace, input.into()), - span, - )) + pub fn tagged_brace(input: impl Into>, tag: impl Into) -> TokenNode { + TokenNode::Delimited(DelimitedNode::new(Delimiter::Brace, input.into()).tagged(tag.into())) } pub fn sp() -> CurriedToken { Box::new(|b| { let (start, end) = b.consume(" "); - TokenNode::Whitespace(Span::from((start, end))) + TokenNode::Whitespace(Tag::from((start, end, b.origin))) }) } @@ -450,14 +406,12 @@ impl TokenTreeBuilder { Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::spanned_ws((start, end)) + TokenTreeBuilder::tagged_ws((start, end, b.origin)) }) } - pub fn spanned_ws(span: impl Into) -> TokenNode { - let span = span.into(); - - TokenNode::Whitespace(span.into()) + pub fn tagged_ws(tag: impl Into) -> TokenNode { + TokenNode::Whitespace(tag.into()) } fn consume(&mut self, input: &str) -> (usize, usize) { @@ -466,4 +420,11 @@ impl TokenTreeBuilder { self.output.push_str(input); (start, self.pos) } + + fn consume_tag(&mut self, input: &str) -> Tag { + let start = self.pos; + self.pos += input.len(); + self.output.push_str(input); + (start, self.pos, self.origin).into() + } } diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index b599852499..d796a8fcb7 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -1,6 +1,6 @@ use crate::parser::parse::unit::*; use crate::prelude::*; -use crate::{Span, Tagged, Text}; +use crate::{Tagged, Text}; use std::fmt; use std::str::FromStr; @@ -8,9 +8,9 @@ use std::str::FromStr; pub enum RawToken { Number(RawNumber), Size(RawNumber, Unit), - String(Span), - Variable(Span), - ExternalCommand(Span), + String(Tag), + Variable(Tag), + ExternalCommand(Tag), ExternalWord, GlobPattern, Bare, @@ -18,28 +18,28 @@ pub enum RawToken { #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum RawNumber { - Int(Span), - Decimal(Span), + Int(Tag), + Decimal(Tag), } impl RawNumber { - pub fn int(span: impl Into) -> Tagged { - let span = span.into(); + pub fn int(tag: impl Into) -> Tagged { + let tag = tag.into(); - RawNumber::Int(span).tagged(span) + RawNumber::Int(tag).tagged(tag) } - pub fn decimal(span: impl Into) -> Tagged { - let span = span.into(); + pub fn decimal(tag: impl Into) -> Tagged { + let tag = tag.into(); - RawNumber::Decimal(span).tagged(span) + RawNumber::Decimal(tag).tagged(tag) } pub(crate) fn to_number(self, source: &Text) -> Number { match self { - RawNumber::Int(span) => Number::Int(BigInt::from_str(span.slice(source)).unwrap()), - RawNumber::Decimal(span) => { - Number::Decimal(BigDecimal::from_str(span.slice(source)).unwrap()) + RawNumber::Int(tag) => Number::Int(BigInt::from_str(tag.slice(source)).unwrap()), + RawNumber::Decimal(tag) => { + Number::Decimal(BigDecimal::from_str(tag.slice(source)).unwrap()) } } } @@ -78,6 +78,6 @@ pub struct DebugToken<'a> { impl fmt::Debug for DebugToken<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.node.span().slice(self.source)) + write!(f, "{}", self.node.tag().slice(self.source)) } } diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index e0fc9d86fc..36ba82f8e5 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -7,7 +7,7 @@ use crate::parser::{ Flag, RawToken, TokenNode, }; use crate::traits::ToDebug; -use crate::{Span, Tag, Tagged, Text}; +use crate::{Tag, Tagged, TaggedItem, Text}; use log::trace; pub fn parse_command( @@ -33,7 +33,7 @@ pub fn parse_command( .collect() }); - match parse_command_tail(&config, context, children, source, call.span())? { + match parse_command_tail(&config, context, children, source, call.tag())? { None => Ok(hir::Call::new(Box::new(head), None, None)), Some((positional, named)) => Ok(hir::Call::new(Box::new(head), positional, named)), } @@ -49,12 +49,9 @@ fn parse_command_head(head: &TokenNode) -> Result { ) => Ok(spanned.map(|_| hir::RawExpression::Literal(hir::Literal::Bare))), TokenNode::Token(Tagged { - item: RawToken::String(inner_span), - tag: Tag { span, origin: None }, - }) => Ok(Tagged::from_simple_spanned_item( - hir::RawExpression::Literal(hir::Literal::String(*inner_span)), - *span, - )), + item: RawToken::String(inner_tag), + tag, + }) => Ok(hir::RawExpression::Literal(hir::Literal::String(*inner_tag)).tagged(*tag)), other => Err(ShellError::unexpected(&format!( "command head -> {:?}", @@ -68,7 +65,7 @@ fn parse_command_tail( context: &Context, tail: Option>, source: &Text, - command_span: Span, + command_tag: Tag, ) -> Result>, Option)>, ShellError> { let tail = &mut match &tail { None => hir::TokensIterator::new(&[]), @@ -89,7 +86,7 @@ fn parse_command_tail( named.insert_switch(name, flag); } NamedType::Mandatory(syntax_type) => { - match extract_mandatory(config, name, tail, source, command_span) { + match extract_mandatory(config, name, tail, source, command_tag) { Err(err) => return Err(err), // produce a correct diagnostic Ok((pos, flag)) => { tail.move_to(pos); @@ -98,7 +95,7 @@ fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), - flag.span(), + flag.tag(), )); } @@ -119,7 +116,7 @@ fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), - flag.span(), + flag.tag(), )); } @@ -150,7 +147,7 @@ fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryPositional(arg.name().to_string()), - command_span, + command_tag, )); } } @@ -208,7 +205,7 @@ fn extract_mandatory( name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Text, - span: Span, + tag: Tag, ) -> Result<(usize, Tagged), ShellError> { let flag = tokens.extract(|t| t.as_flag(name, source)); @@ -216,7 +213,7 @@ fn extract_mandatory( None => Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryFlag(name.to_string()), - span, + tag, )), Some((pos, flag)) => { diff --git a/src/parser/registry.rs b/src/parser/registry.rs index 7112d91118..e199be192b 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -1,7 +1,7 @@ // TODO: Temporary redirect pub(crate) use crate::context::CommandRegistry; use crate::evaluate::{evaluate_baseline_expr, Scope}; -use crate::parser::{hir, hir::SyntaxType, parse_command, CallNode}; +use crate::parser::{hir, hir::SyntaxShape, parse_command, CallNode}; use crate::prelude::*; use derive_new::new; use indexmap::IndexMap; @@ -12,35 +12,35 @@ use std::fmt; #[derive(Debug, Serialize, Deserialize, Clone)] pub enum NamedType { Switch, - Mandatory(SyntaxType), - Optional(SyntaxType), + Mandatory(SyntaxShape), + Optional(SyntaxShape), } #[derive(Debug, Clone, Serialize, Deserialize)] pub enum PositionalType { - Mandatory(String, SyntaxType), - Optional(String, SyntaxType), + Mandatory(String, SyntaxShape), + Optional(String, SyntaxShape), } impl PositionalType { - pub fn mandatory(name: &str, ty: SyntaxType) -> PositionalType { + pub fn mandatory(name: &str, ty: SyntaxShape) -> PositionalType { PositionalType::Mandatory(name.to_string(), ty) } pub fn mandatory_any(name: &str) -> PositionalType { - PositionalType::Mandatory(name.to_string(), SyntaxType::Any) + PositionalType::Mandatory(name.to_string(), SyntaxShape::Any) } pub fn mandatory_block(name: &str) -> PositionalType { - PositionalType::Mandatory(name.to_string(), SyntaxType::Block) + PositionalType::Mandatory(name.to_string(), SyntaxShape::Block) } - pub fn optional(name: &str, ty: SyntaxType) -> PositionalType { + pub fn optional(name: &str, ty: SyntaxShape) -> PositionalType { PositionalType::Optional(name.to_string(), ty) } pub fn optional_any(name: &str) -> PositionalType { - PositionalType::Optional(name.to_string(), SyntaxType::Any) + PositionalType::Optional(name.to_string(), SyntaxShape::Any) } pub(crate) fn name(&self) -> &str { @@ -50,7 +50,7 @@ impl PositionalType { } } - pub(crate) fn syntax_type(&self) -> SyntaxType { + pub(crate) fn syntax_type(&self) -> SyntaxShape { match *self { PositionalType::Mandatory(_, t) => t, PositionalType::Optional(_, t) => t, @@ -66,7 +66,7 @@ pub struct Signature { #[new(default)] pub positional: Vec, #[new(value = "None")] - pub rest_positional: Option, + pub rest_positional: Option, #[new(default)] pub named: IndexMap, #[new(value = "false")] @@ -83,21 +83,21 @@ impl Signature { self } - pub fn required(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn required(mut self, name: impl Into, ty: impl Into) -> Signature { self.positional .push(PositionalType::Mandatory(name.into(), ty.into())); self } - pub fn optional(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn optional(mut self, name: impl Into, ty: impl Into) -> Signature { self.positional .push(PositionalType::Optional(name.into(), ty.into())); self } - pub fn named(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn named(mut self, name: impl Into, ty: impl Into) -> Signature { self.named .insert(name.into(), NamedType::Optional(ty.into())); @@ -107,7 +107,7 @@ impl Signature { pub fn required_named( mut self, name: impl Into, - ty: impl Into, + ty: impl Into, ) -> Signature { self.named .insert(name.into(), NamedType::Mandatory(ty.into())); @@ -126,7 +126,7 @@ impl Signature { self } - pub fn rest(mut self, ty: SyntaxType) -> Signature { + pub fn rest(mut self, ty: SyntaxShape) -> Signature { self.rest_positional = Some(ty); self } @@ -312,10 +312,10 @@ pub(crate) fn evaluate_args( for (name, value) in n.named.iter() { match value { - hir::named::NamedValue::PresentSwitch(span) => { + hir::named::NamedValue::PresentSwitch(tag) => { results.insert( name.clone(), - Tagged::from_simple_spanned_item(Value::boolean(true), *span), + Value::boolean(true).tagged(*tag), ); } hir::named::NamedValue::Value(expr) => { diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 744003cd74..03e1d42828 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tagged, Value, + SyntaxShape, Tagged, Value, }; struct Add { @@ -44,9 +44,9 @@ impl Plugin for Add { fn config(&mut self) -> Result { Ok(Signature::build("add") .desc("Add a new field to the table.") - .required("Field", SyntaxType::String) - .required("Value", SyntaxType::String) - .rest(SyntaxType::String) + .required("Field", SyntaxShape::String) + .required("Value", SyntaxShape::String) + .rest(SyntaxShape::String) .filter()) } diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index aeda4ba09b..db116fedf5 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tagged, Value, + SyntaxShape, Tagged, Value, }; struct Edit { @@ -43,8 +43,8 @@ impl Plugin for Edit { fn config(&mut self) -> Result { Ok(Signature::build("edit") .desc("Edit an existing column to have a new value.") - .required("Field", SyntaxType::String) - .required("Value", SyntaxType::String) + .required("Field", SyntaxShape::String) + .required("Value", SyntaxShape::String) .filter()) } diff --git a/src/plugins/embed.rs b/src/plugins/embed.rs index 95140aa609..646db80918 100644 --- a/src/plugins/embed.rs +++ b/src/plugins/embed.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tag, Tagged, TaggedDictBuilder, Value, + SyntaxShape, Tag, Tagged, TaggedDictBuilder, Value, }; struct Embed { @@ -37,8 +37,8 @@ impl Plugin for Embed { fn config(&mut self) -> Result { Ok(Signature::build("embed") .desc("Embeds a new field to the table.") - .required("Field", SyntaxType::String) - .rest(SyntaxType::String) + .required("Field", SyntaxShape::String) + .rest(SyntaxShape::String) .filter()) } diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index d75da41428..4422195be8 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tagged, TaggedItem, Value, + SyntaxShape, Tagged, TaggedItem, Value, }; enum Action { @@ -120,7 +120,7 @@ impl Plugin for Inc { .switch("major") .switch("minor") .switch("patch") - .rest(SyntaxType::String) + .rest(SyntaxShape::String) .filter()) } @@ -181,18 +181,20 @@ mod tests { use super::{Inc, SemVerAction}; use indexmap::IndexMap; use nu::{ - CallInfo, EvaluatedArgs, Plugin, ReturnSuccess, SourceMap, Span, Tag, Tagged, - TaggedDictBuilder, TaggedItem, Value, + CallInfo, EvaluatedArgs, Plugin, ReturnSuccess, SourceMap, Tag, Tagged, TaggedDictBuilder, + TaggedItem, Value, }; struct CallStub { + origin: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } impl CallStub { - fn new() -> CallStub { + fn new(origin: uuid::Uuid) -> CallStub { CallStub { + origin, positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -201,14 +203,14 @@ mod tests { fn with_long_flag(&mut self, name: &str) -> &mut Self { self.flags.insert( name.to_string(), - Value::boolean(true).simple_spanned(Span::unknown()), + Value::boolean(true).tagged(Tag::unknown()), ); self } fn with_parameter(&mut self, name: &str) -> &mut Self { self.positionals - .push(Value::string(name.to_string()).simple_spanned(Span::unknown())); + .push(Value::string(name.to_string()).tagged(Tag::unknown_span(self.origin))); self } @@ -216,7 +218,7 @@ mod tests { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), source_map: SourceMap::new(), - name_span: Span::unknown(), + name_tag: Tag::unknown_span(self.origin), } } } @@ -243,7 +245,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new().with_long_flag("major").create()) + .begin_filter(CallStub::new(test_uuid()).with_long_flag("major").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -253,7 +255,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new().with_long_flag("minor").create()) + .begin_filter(CallStub::new(test_uuid()).with_long_flag("minor").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -263,7 +265,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new().with_long_flag("patch").create()) + .begin_filter(CallStub::new(test_uuid()).with_long_flag("patch").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -274,7 +276,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new() + CallStub::new(test_uuid()) .with_long_flag("major") .with_long_flag("minor") .create(), @@ -288,7 +290,11 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new().with_parameter("package.version").create()) + .begin_filter( + CallStub::new(test_uuid()) + .with_parameter("package.version") + .create() + ) .is_ok()); assert_eq!(plugin.field, Some("package.version".to_string())); @@ -321,7 +327,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new() + CallStub::new(test_uuid()) .with_long_flag("major") .with_parameter("version") .create() @@ -349,7 +355,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new() + CallStub::new(test_uuid()) .with_long_flag("minor") .with_parameter("version") .create() @@ -378,7 +384,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new() + CallStub::new(test_uuid()) .with_long_flag("patch") .with_parameter(&field) .create() @@ -399,4 +405,8 @@ mod tests { _ => {} } } + + fn test_uuid() -> uuid::Uuid { + uuid::Uuid::nil() + } } diff --git a/src/plugins/ps.rs b/src/plugins/ps.rs index 0f06167bdf..1ae9938d34 100644 --- a/src/plugins/ps.rs +++ b/src/plugins/ps.rs @@ -40,7 +40,7 @@ async fn ps(tag: Tag) -> Vec> { let mut output = vec![]; while let Some(res) = processes.next().await { if let Ok((process, usage)) = res { - let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(tag.span)); + let mut dict = TaggedDictBuilder::new(tag); dict.insert("pid", Value::int(process.pid())); if let Ok(name) = process.name().await { dict.insert("name", Value::string(name)); @@ -64,7 +64,7 @@ impl Plugin for Ps { } fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(ps(Tag::unknown_origin(callinfo.name_span))) + Ok(block_on(ps(callinfo.name_tag)) .into_iter() .map(ReturnSuccess::value) .collect()) diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs index 135dbbd5db..efd3231525 100644 --- a/src/plugins/skip.rs +++ b/src/plugins/skip.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, CoerceInto, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, - Signature, SyntaxType, Tagged, TaggedItem, Value, + Signature, SyntaxShape, Tagged, TaggedItem, Value, }; struct Skip { @@ -17,7 +17,7 @@ impl Plugin for Skip { fn config(&mut self) -> Result { Ok(Signature::build("skip") .desc("Skip a number of rows") - .rest(SyntaxType::Number) + .rest(SyntaxShape::Number) .filter()) } fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { @@ -34,7 +34,7 @@ impl Plugin for Skip { return Err(ShellError::labeled_error( "Unrecognized type in params", "expected an integer", - arg.span(), + arg.tag(), )) } } diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 60b0146ef5..ca80a4ed5f 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxType, Tagged, Value, + SyntaxShape, Tagged, Value, }; #[derive(Debug, Eq, PartialEq)] @@ -127,7 +127,9 @@ impl Plugin for Str { .switch("downcase") .switch("upcase") .switch("to-int") - .rest(SyntaxType::Member) + .switch("replace") + .switch("find-replace") + .rest(SyntaxShape::Member) .filter()) } @@ -198,12 +200,13 @@ mod tests { use super::{Action, Str}; use indexmap::IndexMap; use nu::{ - CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Span, Tag, Tagged, + CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Tag, Tagged, TaggedDictBuilder, TaggedItem, Value, }; use num_bigint::BigInt; struct CallStub { + origin: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } @@ -211,6 +214,7 @@ mod tests { impl CallStub { fn new() -> CallStub { CallStub { + origin: uuid::Uuid::nil(), positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -219,14 +223,14 @@ mod tests { fn with_long_flag(&mut self, name: &str) -> &mut Self { self.flags.insert( name.to_string(), - Value::boolean(true).simple_spanned(Span::unknown()), + Value::boolean(true).tagged(Tag::unknown()), ); self } fn with_parameter(&mut self, name: &str) -> &mut Self { self.positionals - .push(Value::string(name.to_string()).simple_spanned(Span::unknown())); + .push(Value::string(name.to_string()).tagged(Tag::unknown())); self } @@ -234,7 +238,7 @@ mod tests { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), source_map: SourceMap::new(), - name_span: Span::unknown(), + name_tag: Tag::unknown_span(self.origin), } } } diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs index 32ecd7a9ce..ffb39cb90b 100644 --- a/src/plugins/sum.rs +++ b/src/plugins/sum.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - Tag, Tagged, Value, + Tagged, TaggedItem, Value, }; struct Sum { @@ -18,11 +18,10 @@ impl Sum { match &self.total { Some(Tagged { item: Value::Primitive(Primitive::Int(j)), - tag: Tag { span, .. }, + tag, }) => { //TODO: handle overflow - self.total = - Some(Tagged::from_simple_spanned_item(Value::int(i + j), span)); + self.total = Some(Value::int(i + j).tagged(*tag)); Ok(()) } None => { @@ -38,11 +37,10 @@ impl Sum { match self.total { Some(Tagged { item: Value::Primitive(Primitive::Bytes(j)), - tag: Tag { span, .. }, + tag, }) => { //TODO: handle overflow - self.total = - Some(Tagged::from_simple_spanned_item(Value::bytes(b + j), span)); + self.total = Some(Value::bytes(b + j).tagged(tag)); Ok(()) } None => { diff --git a/src/plugins/sys.rs b/src/plugins/sys.rs index db7de6e625..1f86b51d7e 100644 --- a/src/plugins/sys.rs +++ b/src/plugins/sys.rs @@ -315,7 +315,7 @@ impl Plugin for Sys { } fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { - Ok(block_on(sysinfo(Tag::unknown_origin(callinfo.name_span))) + Ok(block_on(sysinfo(callinfo.name_tag)) .into_iter() .map(ReturnSuccess::value) .collect()) diff --git a/src/prelude.rs b/src/prelude.rs index a491aff5de..d58e7989a6 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -62,7 +62,7 @@ pub(crate) use crate::data::{Primitive, Value}; pub(crate) use crate::env::host::handle_unexpected; pub(crate) use crate::env::Host; pub(crate) use crate::errors::{CoerceInto, ShellError}; -pub(crate) use crate::parser::hir::SyntaxType; +pub(crate) use crate::parser::hir::SyntaxShape; pub(crate) use crate::parser::parse::parser::Number; pub(crate) use crate::parser::registry::Signature; pub(crate) use crate::shell::filesystem_shell::FilesystemShell; @@ -70,8 +70,7 @@ pub(crate) use crate::shell::help_shell::HelpShell; pub(crate) use crate::shell::shell_manager::ShellManager; pub(crate) use crate::shell::value_shell::ValueShell; pub(crate) use crate::stream::{InputStream, OutputStream}; -pub(crate) use crate::traits::{HasSpan, ToDebug}; -pub(crate) use crate::Span; +pub(crate) use crate::traits::{HasTag, ToDebug}; pub(crate) use crate::Text; pub(crate) use bigdecimal::BigDecimal; pub(crate) use futures::stream::BoxStream; diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index e746f41aaa..1d26fa1630 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -81,23 +81,27 @@ impl Shell for FilesystemShell { dirs::home_dir() } - fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + fn ls( + &self, + pattern: Option>, + command_tag: Tag, + ) -> Result { let cwd = self.path(); let mut full_path = PathBuf::from(self.path()); - match &args.nth(0) { - Some(value) => full_path.push(Path::new(&value.as_path()?)), + match &pattern { + Some(value) => full_path.push((*value).as_ref()), _ => {} } let entries: Vec<_> = match glob::glob(&full_path.to_string_lossy()) { Ok(files) => files.collect(), Err(_) => { - if let Some(source) = args.nth(0) { + if let Some(source) = pattern { return Err(ShellError::labeled_error( "Invalid pattern", "Invalid pattern", - source.span(), + source.tag(), )); } else { return Err(ShellError::string("Invalid pattern.")); @@ -114,17 +118,17 @@ impl Shell for FilesystemShell { let entries = std::fs::read_dir(&entry); let entries = match entries { Err(e) => { - if let Some(s) = args.nth(0) { + if let Some(s) = pattern { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - s.span(), + s.tag(), )); } else { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - args.name_span(), + command_tag, )); } } @@ -138,11 +142,7 @@ impl Shell for FilesystemShell { } else { Path::new(&filepath) }; - let value = dir_entry_dict( - filename, - &entry.metadata()?, - Tag::unknown_origin(args.call_info.name_span), - )?; + let value = dir_entry_dict(filename, &entry.metadata()?, command_tag)?; shell_entries.push_back(ReturnSuccess::value(value)) } return Ok(shell_entries.to_output_stream()); @@ -159,11 +159,7 @@ impl Shell for FilesystemShell { Path::new(&entry) }; let metadata = std::fs::metadata(&entry)?; - let value = dir_entry_dict( - filename, - &metadata, - Tag::unknown_origin(args.call_info.name_span), - )?; + let value = dir_entry_dict(filename, &metadata, command_tag)?; shell_entries.push_back(ReturnSuccess::value(value)) } } @@ -179,7 +175,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Can not change to home directory", "can not go to home", - args.call_info.name_span, + args.call_info.name_tag, )) } }, @@ -197,7 +193,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Can not change to directory", "directory not found", - v.span().clone(), + v.tag().clone(), )) } } @@ -221,10 +217,10 @@ impl Shell for FilesystemShell { dst, recursive, }: CopyArgs, - name: Span, + name: Tag, path: &str, ) -> Result { - let name_span = name; + let name_tag = name; let mut source = PathBuf::from(path); let mut destination = PathBuf::from(path); @@ -279,7 +275,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -295,7 +291,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -331,7 +327,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -345,7 +341,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -359,7 +355,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Copy aborted. Not a valid path", "Copy aborted. Not a valid path", - name_span, + name_tag, )) } } @@ -369,7 +365,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -405,7 +401,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -419,7 +415,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - name_span, + name_tag, )); } Ok(o) => o, @@ -452,7 +448,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Copy aborted. Not a valid path", "Copy aborted. Not a valid path", - name_span, + name_tag, )) } } @@ -479,7 +475,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Copy aborted. Not a valid destination", "Copy aborted. Not a valid destination", - name_span, + name_tag, )) } } @@ -488,7 +484,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Copy aborted. (Does {:?} exist?)", destination_file_name), format!("Copy aborted. (Does {:?} exist?)", destination_file_name), - &dst.span(), + dst.tag(), )); } } @@ -499,7 +495,7 @@ impl Shell for FilesystemShell { fn mkdir( &self, MkdirArgs { rest: directories }: MkdirArgs, - name: Span, + name: Tag, path: &str, ) -> Result { let full_path = PathBuf::from(path); @@ -524,7 +520,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( reason.to_string(), reason.to_string(), - dir.span(), + dir.tag(), )) } Ok(_) => {} @@ -537,10 +533,10 @@ impl Shell for FilesystemShell { fn mv( &self, MoveArgs { src, dst }: MoveArgs, - name: Span, + name: Tag, path: &str, ) -> Result { - let name_span = name; + let name_tag = name; let mut source = PathBuf::from(path); let mut destination = PathBuf::from(path); @@ -566,7 +562,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Rename aborted. Not a valid destination", "Rename aborted. Not a valid destination", - dst.span(), + dst.tag(), )) } } @@ -580,7 +576,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Rename aborted. Not a valid entry name", "Rename aborted. Not a valid entry name", - name_span, + name_tag, )) } }; @@ -592,7 +588,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Rename aborted. {:}", e.to_string()), format!("Rename aborted. {:}", e.to_string()), - name_span, + name_tag, )) } }; @@ -616,7 +612,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -639,7 +635,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -661,7 +657,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -714,7 +710,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -738,7 +734,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -761,7 +757,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -793,7 +789,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Rename aborted. Not a valid entry name", "Rename aborted. Not a valid entry name", - name_span, + name_tag, )) } }; @@ -817,7 +813,7 @@ impl Shell for FilesystemShell { destination_file_name, e.to_string(), ), - name_span, + name_tag, )); } Ok(o) => o, @@ -829,7 +825,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Rename aborted. (Does {:?} exist?)", destination_file_name), format!("Rename aborted. (Does {:?} exist?)", destination_file_name), - dst.span(), + dst.tag(), )); } } @@ -840,16 +836,16 @@ impl Shell for FilesystemShell { fn rm( &self, RemoveArgs { target, recursive }: RemoveArgs, - name: Span, + name: Tag, path: &str, ) -> Result { - let name_span = name; + let name_tag = name; if target.item.to_str() == Some(".") || target.item.to_str() == Some("..") { return Err(ShellError::labeled_error( "Remove aborted. \".\" or \"..\" may not be removed.", "Remove aborted. \".\" or \"..\" may not be removed.", - target.span(), + target.tag(), )); } @@ -881,7 +877,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("{:?} is a directory. Try using \"--recursive\".", file), format!("{:?} is a directory. Try using \"--recursive\".", file), - target.span(), + target.tag(), )); } } @@ -898,7 +894,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Remove aborted. Not a valid path", "Remove aborted. Not a valid path", - name_span, + name_tag, )) } } @@ -918,7 +914,7 @@ impl Shell for FilesystemShell { "Directory {:?} found somewhere inside. Try using \"--recursive\".", path_file_name ), - target.span(), + target.tag(), )); } @@ -932,7 +928,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( format!("Remove aborted. {:}", e.to_string()), format!("Remove aborted. {:}", e.to_string()), - name_span, + name_tag, )) } } @@ -953,7 +949,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "unable to show current directory", "pwd command failed", - args.call_info.name_span, + args.call_info.name_tag, )); } }; @@ -961,7 +957,7 @@ impl Shell for FilesystemShell { let mut stream = VecDeque::new(); stream.push_back(ReturnSuccess::value( Value::Primitive(Primitive::String(p.to_string_lossy().to_string())) - .simple_spanned(args.call_info.name_span), + .tagged(args.call_info.name_tag), )); Ok(stream.into()) diff --git a/src/shell/help_shell.rs b/src/shell/help_shell.rs index 35c939d7d4..25f1b9c428 100644 --- a/src/shell/help_shell.rs +++ b/src/shell/help_shell.rs @@ -126,7 +126,11 @@ impl Shell for HelpShell { self.path = path.clone(); } - fn ls(&self, _args: EvaluatedWholeStreamCommandArgs) -> Result { + fn ls( + &self, + _pattern: Option>, + _command_tag: Tag, + ) -> Result { Ok(self .commands() .map(|x| ReturnSuccess::value(x)) @@ -161,24 +165,19 @@ impl Shell for HelpShell { Ok(stream.into()) } - fn cp(&self, _args: CopyArgs, _name: Span, _path: &str) -> Result { + fn cp(&self, _args: CopyArgs, _name: Tag, _path: &str) -> Result { Ok(OutputStream::empty()) } - fn mv(&self, _args: MoveArgs, _name: Span, _path: &str) -> Result { + fn mv(&self, _args: MoveArgs, _name: Tag, _path: &str) -> Result { Ok(OutputStream::empty()) } - fn mkdir( - &self, - _args: MkdirArgs, - _name: Span, - _path: &str, - ) -> Result { + fn mkdir(&self, _args: MkdirArgs, _name: Tag, _path: &str) -> Result { Ok(OutputStream::empty()) } - fn rm(&self, _args: RemoveArgs, _name: Span, _path: &str) -> Result { + fn rm(&self, _args: RemoveArgs, _name: Tag, _path: &str) -> Result { Ok(OutputStream::empty()) } diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 16802657db..6fb4544352 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -66,7 +66,7 @@ impl Highlighter for Helper { } fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { - let tokens = crate::parser::pipeline(nom_input(line)); + let tokens = crate::parser::pipeline(nom_input(line, uuid::Uuid::nil())); match tokens { Err(_) => Cow::Borrowed(line), @@ -106,47 +106,47 @@ impl Highlighter for Helper { fn paint_token_node(token_node: &TokenNode, line: &str) -> String { let styled = match token_node { - TokenNode::Call(..) => Color::Cyan.bold().paint(token_node.span().slice(line)), - TokenNode::Whitespace(..) => Color::White.normal().paint(token_node.span().slice(line)), - TokenNode::Flag(..) => Color::Black.bold().paint(token_node.span().slice(line)), - TokenNode::Member(..) => Color::Yellow.bold().paint(token_node.span().slice(line)), - TokenNode::Path(..) => Color::Green.bold().paint(token_node.span().slice(line)), - TokenNode::Error(..) => Color::Red.bold().paint(token_node.span().slice(line)), - TokenNode::Delimited(..) => Color::White.paint(token_node.span().slice(line)), - TokenNode::Operator(..) => Color::White.normal().paint(token_node.span().slice(line)), - TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.span().slice(line)), + TokenNode::Call(..) => Color::Cyan.bold().paint(token_node.tag().slice(line)), + TokenNode::Whitespace(..) => Color::White.normal().paint(token_node.tag().slice(line)), + TokenNode::Flag(..) => Color::Black.bold().paint(token_node.tag().slice(line)), + TokenNode::Member(..) => Color::Yellow.bold().paint(token_node.tag().slice(line)), + TokenNode::Path(..) => Color::Green.bold().paint(token_node.tag().slice(line)), + TokenNode::Error(..) => Color::Red.bold().paint(token_node.tag().slice(line)), + TokenNode::Delimited(..) => Color::White.paint(token_node.tag().slice(line)), + TokenNode::Operator(..) => Color::White.normal().paint(token_node.tag().slice(line)), + TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::Number(..), .. - }) => Color::Purple.bold().paint(token_node.span().slice(line)), + }) => Color::Purple.bold().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::Size(..), .. - }) => Color::Purple.bold().paint(token_node.span().slice(line)), + }) => Color::Purple.bold().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::GlobPattern, .. - }) => Color::Cyan.normal().paint(token_node.span().slice(line)), + }) => Color::Cyan.normal().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::String(..), .. - }) => Color::Green.normal().paint(token_node.span().slice(line)), + }) => Color::Green.normal().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::Variable(..), .. - }) => Color::Yellow.bold().paint(token_node.span().slice(line)), + }) => Color::Yellow.bold().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::Bare, .. - }) => Color::Green.normal().paint(token_node.span().slice(line)), + }) => Color::Green.normal().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::ExternalCommand(..), .. - }) => Color::Cyan.bold().paint(token_node.span().slice(line)), + }) => Color::Cyan.bold().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::ExternalWord, .. - }) => Color::Black.bold().paint(token_node.span().slice(line)), + }) => Color::Black.bold().paint(token_node.tag().slice(line)), }; styled.to_string() @@ -166,7 +166,7 @@ fn paint_pipeline_element(pipeline_element: &PipelineElement, line: &str) -> Str styled.push_str( &Color::Cyan .bold() - .paint(pipeline_element.call().head().span().slice(line)) + .paint(pipeline_element.call().head().tag().slice(line)) .to_string(), ); diff --git a/src/shell/shell.rs b/src/shell/shell.rs index 549aa79d22..c567e474a3 100644 --- a/src/shell/shell.rs +++ b/src/shell/shell.rs @@ -13,12 +13,16 @@ pub trait Shell: std::fmt::Debug { fn name(&self, source_map: &SourceMap) -> String; fn homedir(&self) -> Option; - fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; + fn ls( + &self, + pattern: Option>, + command_tag: Tag, + ) -> Result; fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; - fn cp(&self, args: CopyArgs, name: Span, path: &str) -> Result; - fn mkdir(&self, args: MkdirArgs, name: Span, path: &str) -> Result; - fn mv(&self, args: MoveArgs, name: Span, path: &str) -> Result; - fn rm(&self, args: RemoveArgs, name: Span, path: &str) -> Result; + fn cp(&self, args: CopyArgs, name: Tag, path: &str) -> Result; + fn mkdir(&self, args: MkdirArgs, name: Tag, path: &str) -> Result; + fn mv(&self, args: MoveArgs, name: Tag, path: &str) -> Result; + fn rm(&self, args: RemoveArgs, name: Tag, path: &str) -> Result; fn path(&self) -> String; fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn set_path(&mut self, path: String); diff --git a/src/shell/shell_manager.rs b/src/shell/shell_manager.rs index 53984c9509..c4c42367ed 100644 --- a/src/shell/shell_manager.rs +++ b/src/shell/shell_manager.rs @@ -115,10 +115,14 @@ impl ShellManager { env[self.current_shell].homedir() } - pub fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + pub fn ls( + &self, + path: Option>, + command_tag: Tag, + ) -> Result { let env = self.shells.lock().unwrap(); - env[self.current_shell].ls(args) + env[self.current_shell].ls(path, command_tag) } pub fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 010c7ae08e..175e232e7b 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -87,13 +87,15 @@ impl Shell for ValueShell { Some(PathBuf::from("/")) } - fn ls(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { + fn ls( + &self, + target: Option>, + command_name: Tag, + ) -> Result { let mut full_path = PathBuf::from(self.path()); - let target = args.nth(0); - - match target { - Some(value) => full_path.push(Path::new(&value.as_path()?)), + match &target { + Some(value) => full_path.push(value.as_ref()), _ => {} } @@ -101,18 +103,18 @@ impl Shell for ValueShell { value_system.walk_decorate(&self.value)?; if !value_system.exists(&full_path) { - if let Some(target) = target { + if let Some(target) = &target { return Err(ShellError::labeled_error( "Can not list entries inside", "No such path exists", - target.span(), + target.tag(), )); } return Err(ShellError::labeled_error( "Can not list entries inside", "No such path exists", - args.call_info.name_span, + command_name, )); } @@ -157,14 +159,14 @@ impl Shell for ValueShell { return Err(ShellError::labeled_error( "Can not change to path inside", "No such path exists", - destination.span(), + destination.tag(), )); } return Err(ShellError::labeled_error( "Can not change to path inside", "No such path exists", - args.call_info.name_span, + args.call_info.name_tag, )); } @@ -173,7 +175,7 @@ impl Shell for ValueShell { Ok(stream.into()) } - fn cp(&self, _args: CopyArgs, name: Span, _path: &str) -> Result { + fn cp(&self, _args: CopyArgs, name: Tag, _path: &str) -> Result { Err(ShellError::labeled_error( "cp not currently supported on values", "not currently supported", @@ -181,7 +183,7 @@ impl Shell for ValueShell { )) } - fn mv(&self, _args: MoveArgs, name: Span, _path: &str) -> Result { + fn mv(&self, _args: MoveArgs, name: Tag, _path: &str) -> Result { Err(ShellError::labeled_error( "mv not currently supported on values", "not currently supported", @@ -189,7 +191,7 @@ impl Shell for ValueShell { )) } - fn mkdir(&self, _args: MkdirArgs, name: Span, _path: &str) -> Result { + fn mkdir(&self, _args: MkdirArgs, name: Tag, _path: &str) -> Result { Err(ShellError::labeled_error( "mkdir not currently supported on values", "not currently supported", @@ -197,7 +199,7 @@ impl Shell for ValueShell { )) } - fn rm(&self, _args: RemoveArgs, name: Span, _path: &str) -> Result { + fn rm(&self, _args: RemoveArgs, name: Tag, _path: &str) -> Result { Err(ShellError::labeled_error( "rm not currently supported on values", "not currently supported", @@ -213,7 +215,7 @@ impl Shell for ValueShell { let mut stream = VecDeque::new(); stream.push_back(ReturnSuccess::value(Tagged::from_item( Value::string(self.path()), - args.call_info.name_span, + args.call_info.name_tag, ))); Ok(stream.into()) } diff --git a/src/traits.rs b/src/traits.rs index 5b022c444f..677d019ad8 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -12,8 +12,8 @@ impl fmt::Display for Debuggable<'_, T> { } } -pub trait HasSpan { - fn span(&self) -> Span; +pub trait HasTag { + fn tag(&self) -> Tag; } pub trait ToDebug: Sized { From 19767ad551868f2c950fc992178f1fea5f0b24ec Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 14 Sep 2019 11:48:45 -0500 Subject: [PATCH 093/342] Taking another stab at replacing Span with Tag --- src/commands/get.rs | 4 ++-- tests/command_config_test.rs | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/commands/get.rs b/src/commands/get.rs index 1c6d91d3d3..930392e5d6 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -18,7 +18,7 @@ impl WholeStreamCommand for Get { fn signature(&self) -> Signature { Signature::build("get") - .rest(SyntaxShape::Member) + .required("member", SyntaxShape::Member) .rest(SyntaxShape::Member) } @@ -60,7 +60,7 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result Date: Sat, 14 Sep 2019 12:16:52 -0500 Subject: [PATCH 094/342] Fixed lints --- src/data/meta.rs | 9 ++++++--- src/parser/registry.rs | 5 +---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/data/meta.rs b/src/data/meta.rs index d472a8add9..fd1c597c10 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -246,14 +246,17 @@ impl Tag { pub fn until(&self, other: impl Into) -> Tag { let other = other.into(); - debug_assert!(self.origin == other.origin, "Can only merge two tags with the same origin"); + debug_assert!( + self.origin == other.origin, + "Can only merge two tags with the same origin" + ); Tag { span: Span { start: self.span.start, - end: other.span.end + end: other.span.end, }, - origin: self.origin + origin: self.origin, } } diff --git a/src/parser/registry.rs b/src/parser/registry.rs index e199be192b..955a1a04c9 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -313,10 +313,7 @@ pub(crate) fn evaluate_args( for (name, value) in n.named.iter() { match value { hir::named::NamedValue::PresentSwitch(tag) => { - results.insert( - name.clone(), - Value::boolean(true).tagged(*tag), - ); + results.insert(name.clone(), Value::boolean(true).tagged(*tag)); } hir::named::NamedValue::Value(expr) => { results.insert( From 2b88f1eed00b800814bc3f14aacb02a220b22738 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 15 Sep 2019 05:48:24 +1200 Subject: [PATCH 095/342] Serialize bigint/bigdecimal as i64/f64 --- src/cli.rs | 1 + src/data/base.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/cli.rs b/src/cli.rs index 7b6f6e863e..e7ab3ec2e3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -59,6 +59,7 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel let result = match reader.read_line(&mut input) { Ok(count) => { trace!("processing response ({} bytes)", count); + trace!("response: {}", input); let response = serde_json::from_str::>>(&input); match response { diff --git a/src/data/base.rs b/src/data/base.rs index c80cf409f0..1922ee3fc5 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -13,10 +13,64 @@ use std::fmt; use std::path::PathBuf; use std::time::SystemTime; +mod serde_bigint { + use num_traits::cast::FromPrimitive; + use num_traits::cast::ToPrimitive; + + pub fn serialize(big_int: &super::BigInt, serializer: S) -> Result + where + S: serde::Serializer, + { + serde::Serialize::serialize( + &big_int + .to_i64() + .ok_or(serde::ser::Error::custom("expected a i64-sized bignum"))?, + serializer, + ) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let x: i64 = serde::Deserialize::deserialize(deserializer)?; + Ok(super::BigInt::from_i64(x) + .ok_or(serde::de::Error::custom("expected a i64-sized bignum"))?) + } +} + +mod serde_bigdecimal { + use num_traits::cast::FromPrimitive; + use num_traits::cast::ToPrimitive; + + pub fn serialize(big_decimal: &super::BigDecimal, serializer: S) -> Result + where + S: serde::Serializer, + { + serde::Serialize::serialize( + &big_decimal + .to_f64() + .ok_or(serde::ser::Error::custom("expected a f64-sized bignum"))?, + serializer, + ) + } + + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let x: f64 = serde::Deserialize::deserialize(deserializer)?; + Ok(super::BigDecimal::from_f64(x) + .ok_or(serde::de::Error::custom("expected a f64-sized bigdecimal"))?) + } +} + #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize, Serialize)] pub enum Primitive { Nothing, + #[serde(with = "serde_bigint")] Int(BigInt), + #[serde(with = "serde_bigdecimal")] Decimal(BigDecimal), Bytes(u64), String(String), From dc4421c07dd5e67ea30d83f8cb5ad37d68bd9967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Sat, 14 Sep 2019 14:50:26 -0500 Subject: [PATCH 096/342] Str flags no longer supported. --- src/plugins/str.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/plugins/str.rs b/src/plugins/str.rs index ca80a4ed5f..8030cc4bd3 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -127,8 +127,6 @@ impl Plugin for Str { .switch("downcase") .switch("upcase") .switch("to-int") - .switch("replace") - .switch("find-replace") .rest(SyntaxShape::Member) .filter()) } From 91bea7fb2a5c584f03054bfff85879d0c8135d7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Sat, 14 Sep 2019 14:53:31 -0500 Subject: [PATCH 097/342] Assert the column is unknown. did you mean in error messages appear when `get`ing unknown columns. Here we know the column does not exist so we check the exact error message. --- tests/command_config_test.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/command_config_test.rs b/tests/command_config_test.rs index 67c12655b6..dd0f4e0ebb 100644 --- a/tests/command_config_test.rs +++ b/tests/command_config_test.rs @@ -108,8 +108,7 @@ fn removes_configuration_value() { dirs.config_path() ); - println!("{}", actual); - assert!(actual.contains("did you mean")); + assert!(actual.contains("Unknown column")); }); h::delete_file_at(nu::config_path().unwrap().join("test_5.toml")); From 88c1b1dc6f37bd5908a00a63f3bc692883b4ce42 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 15 Sep 2019 13:51:19 +1200 Subject: [PATCH 098/342] Improve default features and don't precompute ls --- Cargo.lock | 6 +-- Cargo.toml | 2 +- src/shell/filesystem_shell.rs | 86 +++++++++++++++++------------------ 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 063d3402b0..06bc5b48be 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1556,7 +1556,7 @@ dependencies = [ "rawkey 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustyline 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustyline 5.0.2 (git+https://github.com/kkawakam/rustyline)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2196,7 +2196,7 @@ dependencies = [ [[package]] name = "rustyline" version = "5.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/kkawakam/rustyline#5e68e972810133a7343b75db30addc98aea63ba0" dependencies = [ "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3216,7 +3216,7 @@ dependencies = [ "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustyline 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8ee0838a6594169a1c5f4bb9af0fe692cc99691941710a8cc6576395ede804e" +"checksum rustyline 5.0.2 (git+https://github.com/kkawakam/rustyline)" = "" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" "checksum safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e133ccc4f4d1cd4f89cc8a7ff618287d56dc7f638b8e38fc32c5fdcadc339dd5" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" diff --git a/Cargo.toml b/Cargo.toml index 0240e076ad..cd3a6e0882 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,12 +85,12 @@ ptree = {version = "0.2", optional = true } image = { version = "0.22.2", default_features = false, features = ["png_codec", "jpeg"], optional = true } [features] +default = ["textview", "sys", "ps"] raw-key = ["rawkey", "neso"] textview = ["syntect", "onig_sys", "crossterm"] binaryview = ["image", "crossterm"] sys = ["heim", "battery"] ps = ["heim"] -all = ["raw-key", "textview", "binaryview", "sys", "ps", "clipboard", "ptree"] [dependencies.rusqlite] version = "0.20.0" diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 1d26fa1630..d28047745b 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -94,8 +94,49 @@ impl Shell for FilesystemShell { _ => {} } - let entries: Vec<_> = match glob::glob(&full_path.to_string_lossy()) { - Ok(files) => files.collect(), + let mut shell_entries = VecDeque::new(); + + //If it's not a glob, try to display the contents of the entry if it's a directory + let lossy_path = full_path.to_string_lossy(); + if !lossy_path.contains("*") && !lossy_path.contains("?") { + let entry = Path::new(&full_path); + if entry.is_dir() { + let entries = std::fs::read_dir(&entry); + let entries = match entries { + Err(e) => { + if let Some(s) = pattern { + return Err(ShellError::labeled_error( + e.to_string(), + e.to_string(), + s.tag(), + )); + } else { + return Err(ShellError::labeled_error( + e.to_string(), + e.to_string(), + command_tag, + )); + } + } + Ok(o) => o, + }; + for entry in entries { + let entry = entry?; + let filepath = entry.path(); + let filename = if let Ok(fname) = filepath.strip_prefix(&cwd) { + fname + } else { + Path::new(&filepath) + }; + let value = dir_entry_dict(filename, &entry.metadata()?, command_tag)?; + shell_entries.push_back(ReturnSuccess::value(value)) + } + return Ok(shell_entries.to_output_stream()); + } + } + + let entries = match glob::glob(&full_path.to_string_lossy()) { + Ok(files) => files, Err(_) => { if let Some(source) = pattern { return Err(ShellError::labeled_error( @@ -109,47 +150,6 @@ impl Shell for FilesystemShell { } }; - let mut shell_entries = VecDeque::new(); - - // If this is a single entry, try to display the contents of the entry if it's a directory - if entries.len() == 1 { - if let Ok(entry) = &entries[0] { - if entry.is_dir() { - let entries = std::fs::read_dir(&entry); - let entries = match entries { - Err(e) => { - if let Some(s) = pattern { - return Err(ShellError::labeled_error( - e.to_string(), - e.to_string(), - s.tag(), - )); - } else { - return Err(ShellError::labeled_error( - e.to_string(), - e.to_string(), - command_tag, - )); - } - } - Ok(o) => o, - }; - for entry in entries { - let entry = entry?; - let filepath = entry.path(); - let filename = if let Ok(fname) = filepath.strip_prefix(&cwd) { - fname - } else { - Path::new(&filepath) - }; - let value = dir_entry_dict(filename, &entry.metadata()?, command_tag)?; - shell_entries.push_back(ReturnSuccess::value(value)) - } - return Ok(shell_entries.to_output_stream()); - } - } - } - // Enumerate the entries from the glob and add each for entry in entries { if let Ok(entry) = entry { From 8a6c70047853d7122c3139ebb4e1df7af621568f Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 16 Sep 2019 06:18:06 +1200 Subject: [PATCH 099/342] Move rustyline to latest stable --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index cd3a6e0882..46fd25c2c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ documentation = "https://book.nushell.sh" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rustyline = { git = "https://github.com/kkawakam/rustyline" } +rustyline = "5.0.3" chrono = { version = "0.4.9", features = ["serde"] } derive-new = "0.5.8" prettytable-rs = "0.8.0" From 17855d37a4274f7a463bf455cbfd8268c1f327a2 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 16 Sep 2019 19:52:58 +1200 Subject: [PATCH 100/342] Add env command --- src/cli.rs | 1 + src/commands.rs | 2 ++ src/commands/env.rs | 68 +++++++++++++++++++++++++++++++++++++++++++++ src/data/dict.rs | 4 +++ 4 files changed, 75 insertions(+) create mode 100644 src/commands/env.rs diff --git a/src/cli.rs b/src/cli.rs index 68c652c772..0bc8856195 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -244,6 +244,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Tags), whole_stream_command(First), whole_stream_command(Last), + whole_stream_command(Env), whole_stream_command(FromCSV), whole_stream_command(FromTSV), whole_stream_command(FromINI), diff --git a/src/commands.rs b/src/commands.rs index c6cc7b7285..af612d5752 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -13,6 +13,7 @@ pub(crate) mod date; pub(crate) mod debug; pub(crate) mod echo; pub(crate) mod enter; +pub(crate) mod env; pub(crate) mod exit; pub(crate) mod fetch; pub(crate) mod first; @@ -78,6 +79,7 @@ pub(crate) use date::Date; pub(crate) use debug::Debug; pub(crate) use echo::Echo; pub(crate) use enter::Enter; +pub(crate) use env::Env; pub(crate) use exit::Exit; pub(crate) use fetch::Fetch; pub(crate) use first::First; diff --git a/src/commands/env.rs b/src/commands/env.rs new file mode 100644 index 0000000000..6fc26507cc --- /dev/null +++ b/src/commands/env.rs @@ -0,0 +1,68 @@ +use crate::data::{Dictionary, Value}; +use crate::errors::ShellError; +use crate::prelude::*; +use crate::TaggedDictBuilder; + +use crate::commands::WholeStreamCommand; +use crate::parser::registry::Signature; +use indexmap::IndexMap; + +pub struct Env; + +impl WholeStreamCommand for Env { + fn name(&self) -> &str { + "env" + } + + fn signature(&self) -> Signature { + Signature::build("env") + } + + fn usage(&self) -> &str { + "Get the current environment." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + env(args, registry) + } +} + +pub fn get_environment(tag: Tag) -> Result, Box> { + let mut indexmap = IndexMap::new(); + + let path = std::env::current_dir()?; + indexmap.insert("cwd".to_string(), Value::path(path).tagged(tag)); + + if let Some(home) = dirs::home_dir() { + indexmap.insert("home".to_string(), Value::path(home).tagged(tag)); + } + + let temp = std::env::temp_dir(); + indexmap.insert("temp".to_string(), Value::path(temp).tagged(tag)); + + let mut dict = TaggedDictBuilder::new(tag); + for v in std::env::vars() { + dict.insert(v.0, Value::string(v.1)); + } + if !dict.is_empty() { + indexmap.insert("vars".to_string(), dict.into_tagged_value()); + } + + Ok(Value::Row(Dictionary::from(indexmap)).tagged(tag)) +} + +pub fn env(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + + let mut env_out = VecDeque::new(); + let tag = args.call_info.name_tag; + + let value = get_environment(tag)?; + env_out.push_back(value); + + Ok(env_out.to_output_stream()) +} diff --git a/src/data/dict.rs b/src/data/dict.rs index eba68a7f8a..c14c86dd90 100644 --- a/src/data/dict.rs +++ b/src/data/dict.rs @@ -169,6 +169,10 @@ impl TaggedDictBuilder { pub fn into_tagged_dict(self) -> Tagged { Dictionary { entries: self.dict }.tagged(self.tag) } + + pub fn is_empty(&self) -> bool { + self.dict.is_empty() + } } impl From for Tagged { From 4ad249694fb3cb5d7744b4244227411f31494eea Mon Sep 17 00:00:00 2001 From: Jan Koprowski Date: Mon, 16 Sep 2019 19:55:53 +0200 Subject: [PATCH 101/342] Base on quay.io/nushell/nu-base:latest image --- docker/packaging/Dockerfile.ubuntu-bionic | 17 +++++++++++++++++ docker/packaging/README.md | 21 +++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 docker/packaging/Dockerfile.ubuntu-bionic create mode 100644 docker/packaging/README.md diff --git a/docker/packaging/Dockerfile.ubuntu-bionic b/docker/packaging/Dockerfile.ubuntu-bionic new file mode 100644 index 0000000000..144f7b421e --- /dev/null +++ b/docker/packaging/Dockerfile.ubuntu-bionic @@ -0,0 +1,17 @@ +# docker build -f docker/packaging/Dockerfile.ubuntu-bionic . + +ARG FROMTAG=latest +FROM quay.io/nushell/nu-base:${FROMTAG} + +RUN apt-get update && apt-get install -y \ + devscripts \ + debhelper + +COPY debian /code/debian + +RUN rustc -Vv && cargo build --release && \ + cp README.md debian/README.Debian && \ + debuild -b -us -uc -i && \ + dpkg -i ../nu_0.2.0-1_amd64.deb && \ + chsh -s /usr/bin/nu && \ + echo 'ls | get name | echo $it' | /usr/bin/nu \ No newline at end of file diff --git a/docker/packaging/README.md b/docker/packaging/README.md new file mode 100644 index 0000000000..8ca442bda3 --- /dev/null +++ b/docker/packaging/README.md @@ -0,0 +1,21 @@ +# Packaging + +This directory contains docker images used for creating packages for different distribution. + +## How to use this docker files? + +Start with: + +`docker build -f docker/packaging/Dockerfile.ubuntu-bionic .` + +after building the image please copy dpkg package from inside: + +`docker cp $(docker ps -q -a | head -n1):/nu_0.2.0-1_amd64.deb .` + +## What should be done + +* We should run sbuild command to create chroot and then install dpkg. +For two reasons. First: we want to use the same tools as Ubuntu package builders +to handle the cornercases. Second: we want to test dpkg requirements. +* File debian/changelog file should be generated based on git history. +* Building package and nu version should be parametrized. \ No newline at end of file From 7fbd6ce232c4f6b6574a3202cd919c6e71b2d374 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 17 Sep 2019 14:09:15 +1200 Subject: [PATCH 102/342] Fix internal paths --- Cargo.lock | 8 ++++---- src/cli.rs | 20 ++++++++++++++---- src/commands/autoview.rs | 6 +++--- src/commands/classified.rs | 2 ++ src/commands/command.rs | 42 ++++++++++---------------------------- src/commands/enter.rs | 1 + src/commands/fetch.rs | 2 +- src/commands/open.rs | 2 +- src/commands/post.rs | 3 ++- src/commands/save.rs | 2 +- src/context.rs | 3 ++- 11 files changed, 44 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 06bc5b48be..cd99727678 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1556,7 +1556,7 @@ dependencies = [ "rawkey 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustyline 5.0.2 (git+https://github.com/kkawakam/rustyline)", + "rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2195,8 +2195,8 @@ dependencies = [ [[package]] name = "rustyline" -version = "5.0.2" -source = "git+https://github.com/kkawakam/rustyline#5e68e972810133a7343b75db30addc98aea63ba0" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3216,7 +3216,7 @@ dependencies = [ "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustyline 5.0.2 (git+https://github.com/kkawakam/rustyline)" = "" +"checksum rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4795e277e6e57dec9df62b515cd4991371daa80e8dc8d80d596e58722b89c417" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" "checksum safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e133ccc4f4d1cd4f89cc8a7ff618287d56dc7f638b8e38fc32c5fdcadc339dd5" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" diff --git a/src/cli.rs b/src/cli.rs index 0bc8856195..531ffc1f54 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -432,6 +432,7 @@ async fn process_line(readline: Result, ctx: &mut Context let mut input = ClassifiedInputStream::new(); let mut iter = pipeline.commands.into_iter().peekable(); + let mut is_first_command = true; loop { let item: Option = iter.next(); @@ -457,20 +458,29 @@ async fn process_line(readline: Result, ctx: &mut Context ( Some(ClassifiedCommand::Internal(left)), Some(ClassifiedCommand::External(_)), - ) => match left.run(ctx, input, Text::from(line)).await { + ) => match left + .run(ctx, input, Text::from(line), is_first_command) + .await + { Ok(val) => ClassifiedInputStream::from_input_stream(val), Err(err) => return LineResult::Error(line.clone(), err), }, (Some(ClassifiedCommand::Internal(left)), Some(_)) => { - match left.run(ctx, input, Text::from(line)).await { + match left + .run(ctx, input, Text::from(line), is_first_command) + .await + { Ok(val) => ClassifiedInputStream::from_input_stream(val), Err(err) => return LineResult::Error(line.clone(), err), } } (Some(ClassifiedCommand::Internal(left)), None) => { - match left.run(ctx, input, Text::from(line)).await { + match left + .run(ctx, input, Text::from(line), is_first_command) + .await + { Ok(val) => ClassifiedInputStream::from_input_stream(val), Err(err) => return LineResult::Error(line.clone(), err), } @@ -497,7 +507,9 @@ async fn process_line(readline: Result, ctx: &mut Context Err(err) => return LineResult::Error(line.clone(), err), } } - } + }; + + is_first_command = false; } LineResult::Success(line.clone()) diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 9edc926334..c135fecd67 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -45,7 +45,7 @@ pub fn autoview( { let binary = context.get_command("binaryview"); if let Some(binary) = binary { - let result = binary.run(raw.with_input(input), &context.commands); + let result = binary.run(raw.with_input(input), &context.commands, false); result.collect::>().await; } else { for i in input { @@ -61,7 +61,7 @@ pub fn autoview( } else if is_single_origined_text_value(&input) { let text = context.get_command("textview"); if let Some(text) = text { - let result = text.run(raw.with_input(input), &context.commands); + let result = text.run(raw.with_input(input), &context.commands, false); result.collect::>().await; } else { for i in input { @@ -84,7 +84,7 @@ pub fn autoview( } } else { let table = context.expect_command("table"); - let result = table.run(raw.with_input(input), &context.commands); + let result = table.run(raw.with_input(input), &context.commands, false); result.collect::>().await; } } diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 1a107267df..b042441403 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -96,6 +96,7 @@ impl InternalCommand { context: &mut Context, input: ClassifiedInputStream, source: Text, + is_first_command: bool, ) -> Result { if log_enabled!(log::Level::Trace) { trace!(target: "nu::run::internal", "->"); @@ -113,6 +114,7 @@ impl InternalCommand { self.args, &source, objects, + is_first_command, ); let result = trace_out_stream!(target: "nu::trace_stream::internal", source: &source, "output" = result); diff --git a/src/commands/command.rs b/src/commands/command.rs index 99352a7b18..8bbf2d7981 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -41,33 +41,6 @@ impl UnevaluatedCallInfo { name_tag: self.name_tag, }) } - - pub fn has_it_or_block(&self) -> bool { - use hir::RawExpression; - use hir::Variable; - - if let Some(positional) = &self.args.positional() { - for pos in positional { - match pos { - Tagged { - item: RawExpression::Variable(Variable::It(_)), - .. - } => { - return true; - } - Tagged { - item: RawExpression::Block(_), - .. - } => { - return true; - } - _ => {} - } - } - } - - false - } } #[derive(Deserialize, Serialize, Debug, Clone)] @@ -556,13 +529,20 @@ impl Command { } } - pub fn run(&self, args: CommandArgs, registry: ®istry::CommandRegistry) -> OutputStream { + pub fn run( + &self, + args: CommandArgs, + registry: ®istry::CommandRegistry, + is_first_command: bool, + ) -> OutputStream { match self { Command::WholeStream(command) => match command.run(args, registry) { Ok(stream) => stream, Err(err) => OutputStream::one(Err(err)), }, - Command::PerItem(command) => self.run_helper(command.clone(), args, registry.clone()), + Command::PerItem(command) => { + self.run_helper(command.clone(), args, registry.clone(), is_first_command) + } } } @@ -571,6 +551,7 @@ impl Command { command: Arc, args: CommandArgs, registry: CommandRegistry, + is_first_command: bool, ) -> OutputStream { let raw_args = RawCommandArgs { host: args.host, @@ -578,7 +559,7 @@ impl Command { call_info: args.call_info, }; - if raw_args.call_info.has_it_or_block() { + if !is_first_command { let out = args .input .values @@ -603,7 +584,6 @@ impl Command { .call_info .evaluate(®istry, &Scope::it_value(nothing.clone())) .unwrap(); - // We don't have an $it or block, so just execute what we have match command .run(&call_info, ®istry, &raw_args, nothing) diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 4148d03c5f..ee19b096ed 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -109,6 +109,7 @@ impl PerItemCommand for Enter { let mut result = converter.run( new_args.with_input(vec![tagged_contents]), ®istry, + false ); let result_vec: Vec> = result.drain_vec().await; diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index 1494423cf5..c9e16cb45a 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -101,7 +101,7 @@ fn run( name_tag: raw_args.call_info.name_tag, } }; - let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); + let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry, false); let result_vec: Vec> = result.drain_vec().await; for res in result_vec { match res { diff --git a/src/commands/open.rs b/src/commands/open.rs index 8dae5bd26e..fa068d63e6 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -102,7 +102,7 @@ fn run( name_tag: raw_args.call_info.name_tag, } }; - let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); + let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry, false); let result_vec: Vec> = result.drain_vec().await; for res in result_vec { match res { diff --git a/src/commands/post.rs b/src/commands/post.rs index b9aca99e9c..f653e6492a 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -112,7 +112,7 @@ fn run( name_tag: raw_args.call_info.name_tag, } }; - let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry); + let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry, false); let result_vec: Vec> = result.drain_vec().await; for res in result_vec { match res { @@ -195,6 +195,7 @@ pub async fn post( let mut result = converter.run( new_args.with_input(vec![item.clone().tagged(tag.clone())]), ®istry, + false, ); let result_vec: Vec> = result.drain_vec().await; diff --git a/src/commands/save.rs b/src/commands/save.rs index d7ae75312a..253045b3f9 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -188,7 +188,7 @@ fn save( name_tag: raw_args.call_info.name_tag, } }; - let mut result = converter.run(new_args.with_input(input), ®istry); + let mut result = converter.run(new_args.with_input(input), ®istry, false); let result_vec: Vec> = result.drain_vec().await; if converter.is_binary() { process_binary_return_success!(result_vec, name_tag) diff --git a/src/context.rs b/src/context.rs index fe68864db1..57dd1a841c 100644 --- a/src/context.rs +++ b/src/context.rs @@ -125,9 +125,10 @@ impl Context { args: hir::Call, source: &Text, input: InputStream, + is_first_command: bool, ) -> OutputStream { let command_args = self.command_args(args, input, source, source_map, name_tag); - command.run(command_args, self.registry()) + command.run(command_args, self.registry(), is_first_command) } fn call_info( From f6b82e4c0c033d19e797f8f22b881ff9ecdb042f Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 17 Sep 2019 19:07:11 +1200 Subject: [PATCH 103/342] Replace vtable with pivot command --- src/cli.rs | 2 +- src/commands.rs | 4 +- src/commands/pivot.rs | 133 +++++++++++++++++++++++++++++++++++++++++ src/commands/vtable.rs | 47 --------------- src/format.rs | 2 - src/format/vtable.rs | 81 ------------------------- 6 files changed, 136 insertions(+), 133 deletions(-) create mode 100644 src/commands/pivot.rs delete mode 100644 src/commands/vtable.rs delete mode 100644 src/format/vtable.rs diff --git a/src/cli.rs b/src/cli.rs index 531ffc1f54..c02b919066 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -270,13 +270,13 @@ pub async fn cli() -> Result<(), Box> { per_item_command(Help), whole_stream_command(Exit), whole_stream_command(Autoview), + whole_stream_command(Pivot), per_item_command(Cpy), whole_stream_command(Date), per_item_command(Mkdir), per_item_command(Move), whole_stream_command(Save), whole_stream_command(Table), - whole_stream_command(VTable), whole_stream_command(Version), whole_stream_command(Which), ]); diff --git a/src/commands.rs b/src/commands.rs index af612d5752..5f9b0e5d5e 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -37,6 +37,7 @@ pub(crate) mod next; pub(crate) mod nth; pub(crate) mod open; pub(crate) mod pick; +pub(crate) mod pivot; pub(crate) mod plugin; pub(crate) mod post; pub(crate) mod prev; @@ -62,7 +63,6 @@ pub(crate) mod to_tsv; pub(crate) mod to_yaml; pub(crate) mod trim; pub(crate) mod version; -pub(crate) mod vtable; pub(crate) mod where_; pub(crate) mod which_; @@ -105,6 +105,7 @@ pub(crate) use next::Next; pub(crate) use nth::Nth; pub(crate) use open::Open; pub(crate) use pick::Pick; +pub(crate) use pivot::Pivot; pub(crate) use post::Post; pub(crate) use prev::Previous; pub(crate) use pwd::PWD; @@ -130,6 +131,5 @@ pub(crate) use to_tsv::ToTSV; pub(crate) use to_yaml::ToYAML; pub(crate) use trim::Trim; pub(crate) use version::Version; -pub(crate) use vtable::VTable; pub(crate) use where_::Where; pub(crate) use which_::Which; diff --git a/src/commands/pivot.rs b/src/commands/pivot.rs new file mode 100644 index 0000000000..0232f2d59e --- /dev/null +++ b/src/commands/pivot.rs @@ -0,0 +1,133 @@ +use crate::commands::WholeStreamCommand; +use crate::errors::ShellError; +use crate::prelude::*; +use crate::TaggedDictBuilder; + +pub struct Pivot; + +#[derive(Deserialize)] +pub struct PivotArgs { + rest: Vec>, + #[serde(rename(deserialize = "header-row"))] + header_row: bool, + #[serde(rename(deserialize = "ignore-titles"))] + ignore_titles: bool, +} + +impl WholeStreamCommand for Pivot { + fn name(&self) -> &str { + "pivot" + } + + fn signature(&self) -> Signature { + Signature::build("pivot") + .switch("header-row") + .switch("ignore-titles") + .rest(SyntaxShape::String) + } + + fn usage(&self) -> &str { + "Pivots the table contents so rows become columns and columns become rows." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, pivot)?.run() + } +} + +fn merge_descriptors(values: &[Tagged]) -> Vec { + let mut ret = vec![]; + for value in values { + for desc in value.data_descriptors() { + if !ret.contains(&desc) { + ret.push(desc); + } + } + } + ret +} + +pub fn pivot(args: PivotArgs, context: RunnableContext) -> Result { + let stream = async_stream_block! { + let input = context.input.into_vec().await; + + let descs = merge_descriptors(&input); + + let mut headers = vec![]; + + if args.rest.len() > 0 && args.header_row { + yield Err(ShellError::labeled_error("Can not provide header names and use header row", "using header row", context.name)); + return; + } + + if args.header_row { + for i in input.clone() { + if let Some(desc) = descs.get(0) { + match i.get_data_by_key(&desc) { + Some(x) => { + if let Ok(s) = x.as_string() { + headers.push(s); + } else { + yield Err(ShellError::labeled_error("Header row needs string headers", "used non-string headers", context.name)); + return; + } + } + _ => { + yield Err(ShellError::labeled_error("Header row is incomplete and can't be used", "using incomplete header row", context.name)); + return; + } + } + } else { + yield Err(ShellError::labeled_error("Header row is incomplete and can't be used", "using incomplete header row", context.name)); + return; + } + } + } else { + for i in 0..input.len()+1 { + if let Some(name) = args.rest.get(i) { + headers.push(name.to_string()) + } else { + headers.push(format!("Column{}", i)); + } + } + } + + let descs: Vec<_> = if args.header_row { + descs.iter().skip(1).collect() + } else { + descs.iter().collect() + }; + + for desc in descs { + let mut column_num: usize = 0; + let mut dict = TaggedDictBuilder::new(context.name); + + if !args.ignore_titles && !args.header_row { + dict.insert(headers[column_num].clone(), Value::string(desc.clone())); + column_num += 1 + } + + for i in input.clone() { + match i.get_data_by_key(&desc) { + Some(x) => { + dict.insert_tagged(headers[column_num].clone(), x.clone()); + } + _ => { + dict.insert(headers[column_num].clone(), Value::nothing()); + } + } + column_num += 1; + } + + yield ReturnSuccess::value(dict.into_tagged_value()); + } + + + }; + + Ok(OutputStream::new(stream)) +} diff --git a/src/commands/vtable.rs b/src/commands/vtable.rs deleted file mode 100644 index 5abd4c6d1f..0000000000 --- a/src/commands/vtable.rs +++ /dev/null @@ -1,47 +0,0 @@ -use crate::commands::WholeStreamCommand; -use crate::errors::ShellError; -use crate::format::VTableView; -use crate::prelude::*; - -pub struct VTable; - -#[derive(Deserialize)] -pub struct VTableArgs {} - -impl WholeStreamCommand for VTable { - fn name(&self) -> &str { - "vtable" - } - - fn signature(&self) -> Signature { - Signature::build("vtable") - } - - fn usage(&self) -> &str { - "View the contents of the pipeline as a vertical (rotated) table." - } - - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - args.process(registry, vtable)?.run() - } -} - -pub fn vtable(_args: VTableArgs, context: RunnableContext) -> Result { - let stream = async_stream_block! { - let input = context.input.into_vec().await; - - if input.len() > 0 { - let mut host = context.host.lock().unwrap(); - let view = VTableView::from_list(&input); - if let Some(view) = view { - handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); - } - } - }; - - Ok(OutputStream::new(stream)) -} diff --git a/src/format.rs b/src/format.rs index 10b92000b9..6cdd5b256e 100644 --- a/src/format.rs +++ b/src/format.rs @@ -2,14 +2,12 @@ pub(crate) mod entries; pub(crate) mod generic; pub(crate) mod list; pub(crate) mod table; -pub(crate) mod vtable; use crate::prelude::*; pub(crate) use entries::EntriesView; pub(crate) use table::TableView; -pub(crate) use vtable::VTableView; pub(crate) trait RenderView { fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError>; diff --git a/src/format/vtable.rs b/src/format/vtable.rs deleted file mode 100644 index fe151224f4..0000000000 --- a/src/format/vtable.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::data::Value; -use crate::format::RenderView; -use crate::prelude::*; -use derive_new::new; - -use prettytable::format::{FormatBuilder, LinePosition, LineSeparator}; -use prettytable::{color, Attr, Cell, Row, Table}; - -#[derive(new)] -pub struct VTableView { - entries: Vec>, -} - -impl VTableView { - pub fn from_list(values: &[Tagged]) -> Option { - if values.len() == 0 { - return None; - } - - let item = &values[0]; - let headers = item.data_descriptors(); - - if headers.len() == 0 { - return None; - } - - let mut entries = vec![]; - - for header in headers { - let mut row = vec![]; - - row.push(header.clone()); - for value in values { - row.push(value.get_data(&header).borrow().format_leaf(Some(&header))); - } - entries.push(row); - } - - Some(VTableView { entries }) - } -} - -impl RenderView for VTableView { - fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError> { - if self.entries.len() == 0 { - return Ok(()); - } - - let mut table = Table::new(); - table.set_format( - FormatBuilder::new() - .column_separator('│') - .separator(LinePosition::Top, LineSeparator::new('━', '┯', ' ', ' ')) - .separator(LinePosition::Title, LineSeparator::new('─', '┼', ' ', ' ')) - .separator(LinePosition::Bottom, LineSeparator::new('━', '┷', ' ', ' ')) - .padding(1, 1) - .build(), - ); - - for row in &self.entries { - table.add_row(Row::new( - row.iter() - .enumerate() - .map(|(idx, h)| { - if idx == 0 { - Cell::new(h) - .with_style(Attr::ForegroundColor(color::GREEN)) - .with_style(Attr::Bold) - } else { - Cell::new(h) - } - }) - .collect(), - )); - } - - table.print_term(&mut *host.out_terminal()).unwrap(); - - Ok(()) - } -} From 0beb0672112e713d8e33f8769866a48d57078656 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 17 Sep 2019 19:33:52 +1200 Subject: [PATCH 104/342] Update README.md Add note about pivot --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index ca3602fd99..2fecc2a923 100644 --- a/README.md +++ b/README.md @@ -258,6 +258,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | first amount | Show only the first number of rows | | last amount | Show only the last number of rows | | nth row-number | Return only the selected row | +| pivot --header-row | Pivot the tables, making columns into rows and vice versa | | str (column) | Apply string function. Optionally use the column of a table | | tags | Read the tags (metadata) for values | | to-json | Convert table into .json text | @@ -298,8 +299,6 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | table | View the contents of the pipeline as a table | | textview | Autoview of text data | | tree | View the contents of the pipeline as a tree | -| vtable | View the contents of the pipeline as a vertical (rotated) table | - # License The project is made available under the MIT license. See "LICENSE" for more information. From 2cf7249794d8ff8350a34f7a496ae86e43e664da Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 18 Sep 2019 18:37:04 +1200 Subject: [PATCH 105/342] Fix autoview breakage --- src/commands/autoview.rs | 1 + src/commands/enter.rs | 3 +- src/commands/fetch.rs | 71 ++++++++++++++++++++++++++++------------ src/commands/open.rs | 50 +++++++++++++++++++++------- src/data/meta.rs | 4 +++ 5 files changed, 95 insertions(+), 34 deletions(-) diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index c135fecd67..7bf9b9e310 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -110,6 +110,7 @@ fn is_single_origined_text_value(input: &Vec>) -> bool { if input.len() != 1 { return false; } + if let Tagged { item: Value::Primitive(Primitive::String(_)), tag: Tag { diff --git a/src/commands/enter.rs b/src/commands/enter.rs index ee19b096ed..cca73ca0ff 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -1,6 +1,7 @@ use crate::commands::command::CommandAction; use crate::commands::PerItemCommand; use crate::commands::UnevaluatedCallInfo; +use crate::data::meta::Span; use crate::errors::ShellError; use crate::parser::registry; use crate::prelude::*; @@ -70,7 +71,7 @@ impl PerItemCommand for Enter { crate::commands::open::fetch( &full_path, &location_clone, - Tag::unknown(), + Span::unknown(), ) .await.unwrap(); diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index c9e16cb45a..e8520f3eca 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -1,5 +1,6 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; +use crate::data::meta::Span; use crate::data::Value; use crate::errors::ShellError; use crate::parser::hir::SyntaxShape; @@ -9,6 +10,7 @@ use mime::Mime; use std::path::PathBuf; use std::str::FromStr; use surf::mime; +use uuid::Uuid; pub struct Fetch; impl PerItemCommand for Fetch { @@ -51,14 +53,14 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_tag = path.tag(); + let path_span = path.span(); let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); let stream = async_stream_block! { - let result = fetch(&path_str, path_tag).await; + let result = fetch(&path_str, path_span).await; if let Err(e) = result { yield Err(e); @@ -129,13 +131,13 @@ fn run( pub async fn fetch( location: &str, - tag: Tag, + span: Span, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { if let Err(_) = url::Url::parse(location) { return Err(ShellError::labeled_error( "Incomplete or incorrect url", "expected a full url", - tag, + span, )); } @@ -151,10 +153,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( @@ -163,10 +168,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { @@ -174,13 +182,16 @@ pub async fn fetch( ShellError::labeled_error( "Could not load binary file", "could not load", - tag, + span, ) })?; Ok(( None, Value::binary(buf), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )) } @@ -190,10 +201,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load svg from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::IMAGE, image_ty) => { @@ -201,13 +215,16 @@ pub async fn fetch( ShellError::labeled_error( "Could not load image file", "could not load", - tag, + span, ) })?; Ok(( Some(image_ty.to_string()), Value::binary(buf), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )) } @@ -217,10 +234,13 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { @@ -241,17 +261,23 @@ pub async fn fetch( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + span, ) })?), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )) } (ty, sub_ty) => Ok(( None, Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), } @@ -259,7 +285,10 @@ pub async fn fetch( None => Ok(( None, Value::string(format!("No content type found")), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::Url(location.to_string()), )), }, @@ -267,7 +296,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "URL could not be opened", "url not found", - tag, + span, )); } } diff --git a/src/commands/open.rs b/src/commands/open.rs index fa068d63e6..6f33412d56 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,11 +1,13 @@ use crate::commands::UnevaluatedCallInfo; use crate::context::SpanSource; +use crate::data::meta::Span; use crate::data::Value; use crate::errors::ShellError; use crate::parser::hir::SyntaxShape; use crate::parser::registry::Signature; use crate::prelude::*; use std::path::{Path, PathBuf}; +use uuid::Uuid; pub struct Open; impl PerItemCommand for Open { @@ -52,7 +54,7 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_span = path.tag(); + let path_span = path.span(); let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); @@ -131,7 +133,7 @@ fn run( pub async fn fetch( cwd: &PathBuf, location: &str, - tag: Tag, + span: Span, ) -> Result<(Option, Value, Tag, SpanSource), ShellError> { let mut cwd = cwd.clone(); @@ -143,7 +145,10 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => { @@ -159,13 +164,19 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, Value::binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -173,7 +184,10 @@ pub async fn fetch( Ok(( None, Value::binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )) } @@ -188,13 +202,19 @@ pub async fn fetch( cwd.extension() .map(|name| name.to_string_lossy().to_string()), Value::string(s), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, Value::binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -202,7 +222,10 @@ pub async fn fetch( Ok(( None, Value::binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )) } @@ -210,7 +233,10 @@ pub async fn fetch( _ => Ok(( None, Value::binary(bytes), - tag, + Tag { + span, + origin: Some(Uuid::new_v4()), + }, SpanSource::File(cwd.to_string_lossy().to_string()), )), } @@ -220,7 +246,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "File could not be opened", "file not found", - tag, + span, )); } } @@ -228,7 +254,7 @@ pub async fn fetch( return Err(ShellError::labeled_error( "File could not be opened", "file not found", - tag, + span, )); } } diff --git a/src/data/meta.rs b/src/data/meta.rs index fd1c597c10..78711cc882 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -86,6 +86,10 @@ impl Tagged { self.tag } + pub fn span(&self) -> Span { + self.tag.span + } + // TODO: This should not be optional pub fn origin(&self) -> Option { self.tag.origin From 72e6222992bc1a5dea2c236f8b7499c6bf784e03 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 18 Sep 2019 19:05:33 +1200 Subject: [PATCH 106/342] Switch to using Uuid::nil() and fix test --- src/commands/autoview.rs | 5 +---- src/commands/enter.rs | 4 ++-- src/commands/fetch.rs | 22 +++++++++++----------- src/commands/open.rs | 20 ++++++++++---------- src/commands/post.rs | 4 ++-- src/commands/save.rs | 2 +- src/commands/tags.rs | 2 +- src/data/meta.rs | 39 ++++++++++++++++++++++++--------------- src/parser/parse/files.rs | 2 +- src/plugins/textview.rs | 2 +- 10 files changed, 54 insertions(+), 48 deletions(-) diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 7bf9b9e310..b9b9d8941c 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -113,10 +113,7 @@ fn is_single_origined_text_value(input: &Vec>) -> bool { if let Tagged { item: Value::Primitive(Primitive::String(_)), - tag: Tag { - origin: Some(origin), - .. - }, + tag: Tag { origin, .. }, } = input[0] { origin != uuid::Uuid::nil() diff --git a/src/commands/enter.rs b/src/commands/enter.rs index cca73ca0ff..9388abb941 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -75,10 +75,10 @@ impl PerItemCommand for Enter { ) .await.unwrap(); - if let Some(uuid) = contents_tag.origin { + if contents_tag.origin != uuid::Uuid::nil() { // If we have loaded something, track its source yield ReturnSuccess::action(CommandAction::AddSpanSource( - uuid, + contents_tag.origin, span_source, )); } diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index e8520f3eca..79806e76b2 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -76,10 +76,10 @@ fn run( file_extension.or(path_str.split('.').last().map(String::from)) }; - if let Some(uuid) = contents_tag.origin { + if contents_tag.origin != uuid::Uuid::nil() { // If we have loaded something, track its source yield ReturnSuccess::action(CommandAction::AddSpanSource( - uuid, + contents_tag.origin, span_source, )); } @@ -158,7 +158,7 @@ pub async fn fetch( })?), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -173,7 +173,7 @@ pub async fn fetch( })?), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -190,7 +190,7 @@ pub async fn fetch( Value::binary(buf), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )) @@ -206,7 +206,7 @@ pub async fn fetch( })?), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -223,7 +223,7 @@ pub async fn fetch( Value::binary(buf), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )) @@ -239,7 +239,7 @@ pub async fn fetch( })?), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -266,7 +266,7 @@ pub async fn fetch( })?), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )) @@ -276,7 +276,7 @@ pub async fn fetch( Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -287,7 +287,7 @@ pub async fn fetch( Value::string(format!("No content type found")), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), diff --git a/src/commands/open.rs b/src/commands/open.rs index 6f33412d56..603bb4da0b 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -77,10 +77,10 @@ fn run( file_extension.or(path_str.split('.').last().map(String::from)) }; - if let Some(uuid) = contents_tag.origin { + if contents_tag.origin != uuid::Uuid::nil() { // If we have loaded something, track its source yield ReturnSuccess::action(CommandAction::AddSpanSource( - uuid, + contents_tag.origin, span_source, )); } @@ -147,7 +147,7 @@ pub async fn fetch( Value::string(s), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -166,7 +166,7 @@ pub async fn fetch( Value::string(s), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -175,7 +175,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -186,7 +186,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )) @@ -204,7 +204,7 @@ pub async fn fetch( Value::string(s), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -213,7 +213,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -224,7 +224,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )) @@ -235,7 +235,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Some(Uuid::new_v4()), + origin: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), diff --git a/src/commands/post.rs b/src/commands/post.rs index f653e6492a..6d5627a65f 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -85,10 +85,10 @@ fn run( file_extension.or(path_str.split('.').last().map(String::from)) }; - if let Some(uuid) = contents_tag.origin { + if contents_tag.origin != uuid::Uuid::nil() { // If we have loaded something, track its source yield ReturnSuccess::action(CommandAction::AddSpanSource( - uuid, + contents_tag.origin, span_source, )); } diff --git a/src/commands/save.rs b/src/commands/save.rs index 253045b3f9..9c12fd2414 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -136,7 +136,7 @@ fn save( // If there is no filename, check the metadata for the origin filename if input.len() > 0 { let origin = input[0].origin(); - match origin.and_then(|x| source_map.get(&x)) { + match source_map.get(&origin) { Some(path) => match path { SpanSource::File(file) => { full_path.push(Path::new(file)); diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 2b45105c2d..9180ba2c61 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -42,7 +42,7 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result { tags.insert("origin", Value::string(source)); } diff --git a/src/data/meta.rs b/src/data/meta.rs index 78711cc882..010c98037f 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -39,7 +39,7 @@ pub trait TaggedItem: Sized { self, Tag { span: Span::unknown(), - origin: None, + origin: uuid::Uuid::nil(), }, ) } @@ -90,15 +90,14 @@ impl Tagged { self.tag.span } - // TODO: This should not be optional - pub fn origin(&self) -> Option { + pub fn origin(&self) -> uuid::Uuid { self.tag.origin } pub fn origin_name(&self, source_map: &SourceMap) -> Option { - match self.tag.origin.map(|x| source_map.get(&x)) { - Some(Some(SpanSource::File(file))) => Some(file.clone()), - Some(Some(SpanSource::Url(url))) => Some(url.clone()), + match source_map.get(&self.tag.origin) { + Some(SpanSource::File(file)) => Some(file.clone()), + Some(SpanSource::Url(url)) => Some(url.clone()), _ => None, } } @@ -168,20 +167,23 @@ impl From<&std::ops::Range> for Span { Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters, )] pub struct Tag { - pub origin: Option, + pub origin: Uuid, pub span: Span, } impl From for Tag { fn from(span: Span) -> Self { - Tag { origin: None, span } + Tag { + origin: uuid::Uuid::nil(), + span, + } } } impl From<&Span> for Tag { fn from(span: &Span) -> Self { Tag { - origin: None, + origin: uuid::Uuid::nil(), span: *span, } } @@ -190,7 +192,7 @@ impl From<&Span> for Tag { impl From<(usize, usize, Uuid)> for Tag { fn from((start, end, origin): (usize, usize, Uuid)) -> Self { Tag { - origin: Some(origin), + origin, span: Span { start, end }, } } @@ -199,7 +201,11 @@ impl From<(usize, usize, Uuid)> for Tag { impl From<(usize, usize, Option)> for Tag { fn from((start, end, origin): (usize, usize, Option)) -> Self { Tag { - origin, + origin: if let Some(uuid) = origin { + uuid + } else { + uuid::Uuid::nil() + }, span: Span { start, end }, } } @@ -208,7 +214,7 @@ impl From<(usize, usize, Option)> for Tag { impl From> for Tag { fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Tag { Tag { - origin: Some(input.extra), + origin: input.extra, span: Span { start: input.offset, end: input.offset + input.fragment.len(), @@ -231,19 +237,22 @@ impl From<&Tag> for Span { impl Tag { pub fn unknown_origin(span: Span) -> Tag { - Tag { origin: None, span } + Tag { + origin: uuid::Uuid::nil(), + span, + } } pub fn unknown_span(origin: Uuid) -> Tag { Tag { - origin: Some(origin), + origin, span: Span::unknown(), } } pub fn unknown() -> Tag { Tag { - origin: None, + origin: uuid::Uuid::nil(), span: Span::unknown(), } } diff --git a/src/parser/parse/files.rs b/src/parser/parse/files.rs index 6cedb1e99c..65a2620936 100644 --- a/src/parser/parse/files.rs +++ b/src/parser/parse/files.rs @@ -22,7 +22,7 @@ impl language_reporting::ReportingFiles for Files { } fn file_id(&self, tag: Self::Span) -> Self::FileId { - tag.origin.unwrap() + tag.origin } fn file_name(&self, _file: Self::FileId) -> FileName { diff --git a/src/plugins/textview.rs b/src/plugins/textview.rs index 423cae8765..cad2e16e62 100644 --- a/src/plugins/textview.rs +++ b/src/plugins/textview.rs @@ -219,7 +219,7 @@ fn view_text_value(value: &Tagged, source_map: &SourceMap) { let value_origin = value.origin(); match value.item { Value::Primitive(Primitive::String(ref s)) => { - let source = value_origin.and_then(|x| source_map.get(&x)); + let source = source_map.get(&value_origin); if let Some(source) = source { let extension: Option = match source { From 3659e511633993ff0807ad34dde5057d9009c912 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 18 Sep 2019 19:18:58 +1200 Subject: [PATCH 107/342] Fix origin in binaryview --- src/plugins/binaryview.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index 3b92177374..895ec97fe2 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -24,7 +24,7 @@ impl Plugin for BinaryView { let value_origin = v.origin(); match v.item { Value::Primitive(Primitive::Binary(b)) => { - let source = value_origin.and_then(|x| call_info.source_map.get(&x)); + let source = call_info.source_map.get(&value_origin); let _ = view_binary(&b, source, call_info.args.has("lores")); } _ => {} From c9310265feaa6a71d3e65cb4f5df129056bf967e Mon Sep 17 00:00:00 2001 From: Jan Koprowski Date: Wed, 18 Sep 2019 17:04:31 +0200 Subject: [PATCH 108/342] Remove Dockerfile.bionic from docker directory --- docker/Dockerfile.bionic | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 docker/Dockerfile.bionic diff --git a/docker/Dockerfile.bionic b/docker/Dockerfile.bionic deleted file mode 100644 index 5d5bafc48c..0000000000 --- a/docker/Dockerfile.bionic +++ /dev/null @@ -1,23 +0,0 @@ -FROM ubuntu:18.04 - -# docker build -f docker/Dockerfile.bionic . - -ENV DEBIAN_FRONTEND noninteractive -RUN apt-get update && apt-get install -y libssl-dev \ - libxcb-composite0-dev \ - libx11-dev \ - pkg-config \ - curl \ - devscripts \ - debhelper - -WORKDIR /code -COPY ./rust-toolchain ./rust-toolchain -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` -ENV PATH=/root/.cargo/bin:$PATH -COPY . /code -RUN rustc -Vv && cargo build --release -RUN debuild -b -us -uc -i -RUN dpkg -i ../nu_0.2.0-1_amd64.deb -RUN chsh -s /usr/bin/nu -RUN echo 'ls | get name | echo $it' | /usr/bin/nu From a8e2801e0b8c30ddbff3408ae848bdb2001cdc0f Mon Sep 17 00:00:00 2001 From: Jan Koprowski Date: Wed, 18 Sep 2019 17:43:06 +0200 Subject: [PATCH 109/342] Enhance docker/packaging/README.md about issue links --- docker/packaging/README.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docker/packaging/README.md b/docker/packaging/README.md index 8ca442bda3..f3703fe2bf 100644 --- a/docker/packaging/README.md +++ b/docker/packaging/README.md @@ -8,14 +8,23 @@ Start with: `docker build -f docker/packaging/Dockerfile.ubuntu-bionic .` -after building the image please copy dpkg package from inside: +after building the image please run container -`docker cp $(docker ps -q -a | head -n1):/nu_0.2.0-1_amd64.deb .` +`docker run -d --name nushell $(docker images -q -a | head -n+1)` + +and copy deb package from inside: + +`docker cp nushell:/nu_0.2.0-1_amd64.deb .` ## What should be done * We should run sbuild command to create chroot and then install dpkg. For two reasons. First: we want to use the same tools as Ubuntu package builders to handle the cornercases. Second: we want to test dpkg requirements. +https://github.com/nushell/nushell/issues/681 + * File debian/changelog file should be generated based on git history. -* Building package and nu version should be parametrized. \ No newline at end of file +https://github.com/nushell/nushell/issues/682 + +* Building package and nu version should be parametrized. +https://github.com/nushell/nushell/issues/683 \ No newline at end of file From 5ff94004c68ce932030ec16fcb7ead9445e5829a Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 19 Sep 2019 16:25:29 +1200 Subject: [PATCH 110/342] Add urlencode/urldecode --- Cargo.lock | 1 + Cargo.toml | 1 + src/cli.rs | 2 + src/commands.rs | 4 ++ src/commands/from_url.rs | 85 +++++++++++++++++++++++++++++++ src/commands/to_url.rs | 85 +++++++++++++++++++++++++++++++ src/utils.rs | 4 ++ tests/filters_test.rs | 16 ++++++ tests/fixtures/formats/sample.url | 1 + 9 files changed, 199 insertions(+) create mode 100644 src/commands/from_url.rs create mode 100644 src/commands/to_url.rs create mode 100644 tests/fixtures/formats/sample.url diff --git a/Cargo.lock b/Cargo.lock index cd99727678..075a712032 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1563,6 +1563,7 @@ dependencies = [ "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "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_urlencoded 0.6.1 (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)", diff --git a/Cargo.toml b/Cargo.toml index 46fd25c2c6..cfe107e9be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ pin-utils = "0.1.0-alpha.4" num-bigint = { version = "0.2.3", features = ["serde"] } bigdecimal = { version = "0.1.0", features = ["serde"] } natural = "0.3.0" +serde_urlencoded = "0.6.1" neso = { version = "0.5.0", optional = true } crossterm = { version = "0.10.2", optional = true } diff --git a/src/cli.rs b/src/cli.rs index c02b919066..8ed2b9bd55 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -239,6 +239,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(ToDB), whole_stream_command(ToTOML), whole_stream_command(ToTSV), + whole_stream_command(ToURL), whole_stream_command(ToYAML), whole_stream_command(SortBy), whole_stream_command(Tags), @@ -253,6 +254,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(FromDB), whole_stream_command(FromSQLite), whole_stream_command(FromTOML), + whole_stream_command(FromURL), whole_stream_command(FromXML), whole_stream_command(FromYAML), whole_stream_command(FromYML), diff --git a/src/commands.rs b/src/commands.rs index 5f9b0e5d5e..72c07e38e6 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -24,6 +24,7 @@ pub(crate) mod from_json; pub(crate) mod from_sqlite; pub(crate) mod from_toml; pub(crate) mod from_tsv; +pub(crate) mod from_url; pub(crate) mod from_xml; pub(crate) mod from_yaml; pub(crate) mod get; @@ -60,6 +61,7 @@ pub(crate) mod to_json; pub(crate) mod to_sqlite; pub(crate) mod to_toml; pub(crate) mod to_tsv; +pub(crate) mod to_url; pub(crate) mod to_yaml; pub(crate) mod trim; pub(crate) mod version; @@ -91,6 +93,7 @@ pub(crate) use from_sqlite::FromDB; pub(crate) use from_sqlite::FromSQLite; pub(crate) use from_toml::FromTOML; pub(crate) use from_tsv::FromTSV; +pub(crate) use from_url::FromURL; pub(crate) use from_xml::FromXML; pub(crate) use from_yaml::FromYAML; pub(crate) use from_yaml::FromYML; @@ -128,6 +131,7 @@ pub(crate) use to_sqlite::ToDB; pub(crate) use to_sqlite::ToSQLite; pub(crate) use to_toml::ToTOML; pub(crate) use to_tsv::ToTSV; +pub(crate) use to_url::ToURL; pub(crate) use to_yaml::ToYAML; pub(crate) use trim::Trim; pub(crate) use version::Version; diff --git a/src/commands/from_url.rs b/src/commands/from_url.rs new file mode 100644 index 0000000000..81113a83d4 --- /dev/null +++ b/src/commands/from_url.rs @@ -0,0 +1,85 @@ +use crate::commands::WholeStreamCommand; +use crate::data::{Primitive, TaggedDictBuilder, Value}; +use crate::prelude::*; + +pub struct FromURL; + +impl WholeStreamCommand for FromURL { + fn name(&self) -> &str { + "from-url" + } + + fn signature(&self) -> Signature { + Signature::build("from-url") + } + + fn usage(&self) -> &str { + "Parse url-encoded string as a table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + from_url(args, registry) + } +} + +fn from_url(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let tag = args.name_tag(); + let input = args.input; + + let stream = async_stream_block! { + let values: Vec> = input.values.collect().await; + + let mut concat_string = String::new(); + let mut latest_tag: Option = None; + + for value in values { + let value_tag = value.tag(); + latest_tag = Some(value_tag); + match value.item { + Value::Primitive(Primitive::String(s)) => { + concat_string.push_str(&s); + } + _ => yield Err(ShellError::labeled_error_with_secondary( + "Expected a string from pipeline", + "requires string input", + tag, + "value originates from here", + value_tag, + )), + + } + } + + let result = serde_urlencoded::from_str::>(&concat_string); + + match result { + Ok(result) => { + let mut row = TaggedDictBuilder::new(tag); + + for (k,v) in result { + row.insert(k, Value::string(v)); + } + + yield ReturnSuccess::value(row.into_tagged_value()); + } + _ => { + if let Some(last_tag) = latest_tag { + yield Err(ShellError::labeled_error_with_secondary( + "String not compatible with url-encoding", + "input not url-encoded", + tag, + "value originates from here", + last_tag, + )); + } + } + } + }; + + Ok(stream.to_output_stream()) +} diff --git a/src/commands/to_url.rs b/src/commands/to_url.rs new file mode 100644 index 0000000000..d98a765a29 --- /dev/null +++ b/src/commands/to_url.rs @@ -0,0 +1,85 @@ +use crate::commands::WholeStreamCommand; +use crate::data::Value; +use crate::prelude::*; + +pub struct ToURL; + +impl WholeStreamCommand for ToURL { + fn name(&self) -> &str { + "to-url" + } + + fn signature(&self) -> Signature { + Signature::build("to-url") + } + + fn usage(&self) -> &str { + "Convert table into url-encoded text" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + to_url(args, registry) + } +} + +fn to_url(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let tag = args.name_tag(); + let input = args.input; + + let stream = async_stream_block! { + let input: Vec> = input.values.collect().await; + + for value in input { + match value { + Tagged { item: Value::Row(row), .. } => { + let mut row_vec = vec![]; + for (k,v) in row.entries { + match v.as_string() { + Ok(s) => { + row_vec.push((k.clone(), s)); + } + _ => { + yield Err(ShellError::labeled_error_with_secondary( + "Expected table with string values", + "requires table with strings", + tag, + "value originates from here", + v.tag, + )) + } + } + } + + match serde_urlencoded::to_string(row_vec) { + Ok(s) => { + yield ReturnSuccess::value(Value::string(s).tagged(tag)); + } + _ => { + yield Err(ShellError::labeled_error( + "Failed to convert to url-encoded", + "cannot url-encode", + tag, + )) + } + } + } + Tagged { tag: value_tag, .. } => { + yield Err(ShellError::labeled_error_with_secondary( + "Expected a table from pipeline", + "requires table input", + tag, + "value originates from here", + value_tag, + )) + } + } + } + }; + + Ok(stream.to_output_stream()) +} diff --git a/src/utils.rs b/src/utils.rs index 703f277254..6b1318f9e8 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -464,6 +464,10 @@ mod tests { loc: fixtures().join("sample.ini"), at: 0 }, + Res { + loc: fixtures().join("sample.url"), + at: 0 + }, Res { loc: fixtures().join("sgml_description.json"), at: 0 diff --git a/tests/filters_test.rs b/tests/filters_test.rs index b08115a9c0..f994fa4494 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -423,6 +423,22 @@ fn can_convert_table_to_yaml_text_and_from_yaml_text_back_into_table() { assert_eq!(actual, "nushell"); } +#[test] +fn can_encode_and_decode_urlencoding() { + let actual = nu!( + cwd: "tests/fixtures/formats", h::pipeline( + r#" + open sample.url + | to-url + | from-url + | get cheese + | echo $it + "# + )); + + assert_eq!(actual, "comté"); +} + #[test] fn can_sort_by_column() { let actual = nu!( diff --git a/tests/fixtures/formats/sample.url b/tests/fixtures/formats/sample.url new file mode 100644 index 0000000000..361d70dbb6 --- /dev/null +++ b/tests/fixtures/formats/sample.url @@ -0,0 +1 @@ +bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter \ No newline at end of file From 85a5ed70b1fef32d1ed7b39df45cba55c6a66fa5 Mon Sep 17 00:00:00 2001 From: Jan Koprowski Date: Thu, 19 Sep 2019 08:16:39 +0200 Subject: [PATCH 111/342] Replace command with --- docker/packaging/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/packaging/README.md b/docker/packaging/README.md index f3703fe2bf..ecefa0150d 100644 --- a/docker/packaging/README.md +++ b/docker/packaging/README.md @@ -10,7 +10,7 @@ Start with: after building the image please run container -`docker run -d --name nushell $(docker images -q -a | head -n+1)` +`docker run -d --name nushell ` and copy deb package from inside: From a96836facb8282e562aad8a380478776cfe22c8f Mon Sep 17 00:00:00 2001 From: Jan Koprowski Date: Thu, 19 Sep 2019 17:57:36 +0200 Subject: [PATCH 112/342] Use tags instead container id and add all binaries to debian/install --- debian/install | 9 +++++++++ docker/packaging/README.md | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 39 insertions(+), 5 deletions(-) diff --git a/debian/install b/debian/install index eca0e05134..75cf2844d9 100644 --- a/debian/install +++ b/debian/install @@ -1 +1,10 @@ target/release/nu usr/bin +target/release/nu_plugin_binaryview usr/bin +target/release/nu_plugin_edit usr/bin +target/release/nu_plugin_inc usr/bin +target/release/nu_plugin_skip usr/bin +target/release/nu_plugin_str usr/bin +target/release/nu_plugin_sum usr/bin +target/release/nu_plugin_sys usr/bin +target/release/nu_plugin_textview usr/bin +target/release/nu_plugin_tree usr/bin diff --git a/docker/packaging/README.md b/docker/packaging/README.md index ecefa0150d..e825c2780f 100644 --- a/docker/packaging/README.md +++ b/docker/packaging/README.md @@ -6,15 +6,40 @@ This directory contains docker images used for creating packages for different d Start with: -`docker build -f docker/packaging/Dockerfile.ubuntu-bionic .` +```bash +$ docker build -f docker/packaging/Dockerfile.ubuntu-bionic -t nushell/package:ubuntu-bionic . +``` -after building the image please run container +after building the image please run container: + +```bash +$ docker run -td --rm --name nushell_package_ubuntu_bionic nushell/package:ubuntu-bionic +``` -`docker run -d --name nushell ` - and copy deb package from inside: -`docker cp nushell:/nu_0.2.0-1_amd64.deb .` +```bash +$ docker cp nushell_package_ubuntu_bionic:/nu_0.2.0-1_amd64.deb . +``` + +or shell inside, and test install: + +```bash +$ docker exec -it nushell_package_ubuntu_bionic bash +$ dpkg -i /nu_0.2.0-1_amd64.deb + +(Reading database ... 25656 files and directories currently installed.) +Preparing to unpack /nu_0.2.0-1_amd64.deb ... +Unpacking nu (0.2.0-1) over (0.2.0-1) ... +Setting up nu (0.2.0-1) ... +``` + +When you are finished, exit and stop the container. It will be removed since we +used `--rm`. + +```bash +$ docker stop nushell_package_ubuntu_bionic +``` ## What should be done From 44b7e07569edf6259a656166b203052400071bbe Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Sun, 15 Sep 2019 23:05:13 +0200 Subject: [PATCH 113/342] Add Sublime style history search demo --- src/histsearch.rs | 171 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 src/histsearch.rs diff --git a/src/histsearch.rs b/src/histsearch.rs new file mode 100644 index 0000000000..d5db186809 --- /dev/null +++ b/src/histsearch.rs @@ -0,0 +1,171 @@ +use ansi_term::Colour; +use crossterm::{cursor, terminal, ClearType, InputEvent, KeyEvent, RawScreen}; +use std::io::Write; +use sublime_fuzzy::best_match; + +fn select_from_list(lines: &Vec<&str>) { + const MAX_RESULTS: usize = 5; + #[derive(PartialEq)] + enum State { + Selecting, + Quit, + Selected(usize), + Edit(usize), + } + let mut state = State::Selecting; + if let Ok(_raw) = RawScreen::into_raw_mode() { + // User input for search + let mut searchinput = String::new(); + let mut selected = 0; + + let mut cursor = cursor(); + let _ = cursor.hide(); + let input = crossterm::input(); + let mut sync_stdin = input.read_sync(); + + while state == State::Selecting { + let search_result = search(&searchinput, &lines, MAX_RESULTS); + let selected_lines: Vec<&str> = search_result + .iter() + .map(|item| &item.highlighted_text as &str) + .collect(); + paint_selection_list(&selected_lines, selected); + if let Some(ev) = sync_stdin.next() { + match ev { + InputEvent::Keyboard(k) => match k { + KeyEvent::Esc | KeyEvent::Ctrl('c') => { + state = State::Quit; + } + KeyEvent::Up => { + if selected > 0 { + selected -= 1; + } + } + KeyEvent::Down => { + if selected + 1 < selected_lines.len() { + selected += 1; + } + } + KeyEvent::Char('\n') => { + state = State::Selected(search_result[selected].text_idx); + } + KeyEvent::Char('\t') | KeyEvent::Right => { + state = State::Edit(search_result[selected].text_idx); + } + KeyEvent::Char(ch) => { + searchinput.push(ch); + selected = 0; + } + KeyEvent::Backspace => { + searchinput.pop(); + selected = 0; + } + _ => { + // println!("{}", format!("OTHER InputEvent: {:?}\n\n", k)); + } + }, + _ => {} + } + } + cursor.move_up(selected_lines.len() as u16); + } + let (_x, y) = cursor.pos(); + let _ = cursor.goto(0, y); + let _ = cursor.show(); + + let _ = RawScreen::disable_raw_mode(); + } + let terminal = terminal(); + terminal.clear(ClearType::FromCursorDown).unwrap(); + + match state { + State::Selected(idx) => { + print!("{}", lines[idx]); + } + State::Edit(idx) => { + print!("{}", lines[idx]); + } + _ => {} + } +} + +struct Match { + highlighted_text: String, + text_idx: usize, +} + +fn search(input: &String, lines: &Vec<&str>, max_results: usize) -> Vec { + if input.is_empty() { + return lines + .iter() + .take(max_results) + .enumerate() + .map(|(i, line)| Match { + highlighted_text: line.to_string(), + text_idx: i, + }) + .collect(); + } + + let mut matches = lines + .iter() + .enumerate() + .map(|(idx, line)| (idx, best_match(&input, line))) + .filter(|(_i, m)| m.is_some()) + .map(|(i, m)| (i, m.unwrap())) + .collect::>(); + matches.sort_by(|a, b| b.1.score().cmp(&a.1.score())); + + let highlight = Colour::Cyan; + let results: Vec = matches + .iter() + .take(max_results) + .map(|(i, m)| { + let r = &lines[*i]; + let mut outstr = String::with_capacity(r.len()); + let mut idx = 0; + for (match_idx, len) in m.continuous_matches() { + outstr.push_str(&r[idx..match_idx]); + idx = match_idx + len; + outstr.push_str(&format!("{}", highlight.paint(&r[match_idx..idx]))); + } + if idx < r.len() { + outstr.push_str(&r[idx..r.len()]); + } + Match { + highlighted_text: outstr, + text_idx: *i, + } + }) + .collect(); + results +} + +fn paint_selection_list(lines: &Vec<&str>, selected: usize) { + let dimmed = Colour::White.dimmed(); + let cursor = cursor(); + let (_x, y) = cursor.pos(); + for (i, line) in lines.iter().enumerate() { + let _ = cursor.goto(0, y + (i as u16)); + if selected == i { + println!("{}", *line); + } else { + println!("{}", dimmed.paint(*line)); + } + } + let _ = cursor.goto(0, y + (lines.len() as u16)); + print!( + "{}", + Colour::Blue.paint("[ESC to quit, Enter to execute, Tab to edit]") + ); + + let _ = std::io::stdout().flush(); + // Clear additional lines from previous selection + terminal().clear(ClearType::FromCursorDown).unwrap(); +} + +fn main() { + let hist = std::fs::read_to_string("history.txt").expect("Cannot open history.txt"); + let lines = hist.lines().rev().collect(); + select_from_list(&lines); +} From 1e3549571c41eb9ad4bcb7e1a8a1eca323f6b6b3 Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Mon, 16 Sep 2019 22:48:22 +0200 Subject: [PATCH 114/342] Bind fuzzy history search to Ctrl-R --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/cli.rs | 16 +++++++++++++++- src/histsearch.rs | 8 +------- src/lib.rs | 1 + 5 files changed, 25 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 075a712032..45ab8ebe26 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1566,6 +1566,7 @@ dependencies = [ "serde_urlencoded 0.6.1 (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)", + "sublime_fuzzy 0.5.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.2 (registry+https://github.com/rust-lang/crates.io-index)", "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2443,6 +2444,11 @@ name = "strsim" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "sublime_fuzzy" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "subprocess" version = "0.1.18" @@ -3247,6 +3253,7 @@ dependencies = [ "checksum stackvector 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1c4725650978235083241fab0fdc8e694c3de37821524e7534a1a9061d1068af" "checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" +"checksum sublime_fuzzy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97bd7ad698ea493a3a7f60c2ffa117c234f341e09f8cc2d39cef10cdde077acf" "checksum subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "28fc0f40f0c0da73339d347aa7d6d2b90341a95683a47722bc4eebed71ff3c00" "checksum surf 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "018eed64aede455beb88505d50c5c64882bebbe0996d4b660c272e3d8bb6f883" "checksum syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ee06ea4b620ab59a2267c6b48be16244a3389f8bfa0986bdd15c35b890b00af3" diff --git a/Cargo.toml b/Cargo.toml index cfe107e9be..e81bc0ee69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ num-bigint = { version = "0.2.3", features = ["serde"] } bigdecimal = { version = "0.1.0", features = ["serde"] } natural = "0.3.0" serde_urlencoded = "0.6.1" +sublime_fuzzy = "0.5" neso = { version = "0.5.0", optional = true } crossterm = { version = "0.10.2", optional = true } diff --git a/src/cli.rs b/src/cli.rs index 8ed2b9bd55..af5a02a313 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -10,6 +10,7 @@ use crate::context::Context; use crate::data::Value; pub(crate) use crate::errors::ShellError; use crate::git::current_branch; +use crate::histsearch; use crate::parser::registry::Signature; use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode}; use crate::prelude::*; @@ -333,6 +334,12 @@ pub async fn cli() -> Result<(), Box> { rl.set_edit_mode(edit_mode); + // Register Ctrl-r for history fuzzy search + // rustyline doesn't support custom commands, so we override Ctrl-D (EOF) + rl.bind_sequence(rustyline::KeyPress::Ctrl('R'), rustyline::Cmd::EndOfFile); + // Redefine Ctrl-D to same command as Ctrl-C + rl.bind_sequence(rustyline::KeyPress::Ctrl('D'), rustyline::Cmd::Interrupt); + let readline = rl.readline(&format!( "{}{}> ", cwd, @@ -347,6 +354,12 @@ pub async fn cli() -> Result<(), Box> { rl.add_history_entry(line.clone()); } + LineResult::SearchHist => { + let hist = std::fs::read_to_string("history.txt").expect("Cannot open history.txt"); + let lines = hist.lines().rev().collect(); + histsearch::select_from_list(&lines); + } + LineResult::CtrlC => { if ctrlcbreak { std::process::exit(0); @@ -390,6 +403,7 @@ pub async fn cli() -> Result<(), Box> { enum LineResult { Success(String), + SearchHist, Error(String, ShellError), CtrlC, Break, @@ -517,7 +531,7 @@ async fn process_line(readline: Result, ctx: &mut Context LineResult::Success(line.clone()) } Err(ReadlineError::Interrupted) => LineResult::CtrlC, - Err(ReadlineError::Eof) => LineResult::Break, + Err(ReadlineError::Eof) => LineResult::SearchHist, // Override LineResult::Break Err(err) => { println!("Error: {:?}", err); LineResult::Break diff --git a/src/histsearch.rs b/src/histsearch.rs index d5db186809..495b0d5a4e 100644 --- a/src/histsearch.rs +++ b/src/histsearch.rs @@ -3,7 +3,7 @@ use crossterm::{cursor, terminal, ClearType, InputEvent, KeyEvent, RawScreen}; use std::io::Write; use sublime_fuzzy::best_match; -fn select_from_list(lines: &Vec<&str>) { +pub fn select_from_list(lines: &Vec<&str>) { const MAX_RESULTS: usize = 5; #[derive(PartialEq)] enum State { @@ -163,9 +163,3 @@ fn paint_selection_list(lines: &Vec<&str>, selected: usize) { // Clear additional lines from previous selection terminal().clear(ClearType::FromCursorDown).unwrap(); } - -fn main() { - let hist = std::fs::read_to_string("history.txt").expect("Cannot open history.txt"); - let lines = hist.lines().rev().collect(); - select_from_list(&lines); -} diff --git a/src/lib.rs b/src/lib.rs index 1aedc2e11f..9802e2f686 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ mod errors; mod evaluate; mod format; mod git; +mod histsearch; mod parser; mod plugin; mod shell; From 1c95bf05dc134aac493d1c7ce8a99de3d458561b Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Wed, 18 Sep 2019 00:21:39 +0200 Subject: [PATCH 115/342] Process selected command --- src/cli.rs | 40 ++++++++++++++++++++++++++++++---------- src/histsearch.rs | 20 +++++++++++--------- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index af5a02a313..5cfcb1475a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -340,26 +340,47 @@ pub async fn cli() -> Result<(), Box> { // Redefine Ctrl-D to same command as Ctrl-C rl.bind_sequence(rustyline::KeyPress::Ctrl('D'), rustyline::Cmd::Interrupt); - let readline = rl.readline(&format!( + let prompt = &format!( "{}{}> ", cwd, match current_branch() { Some(s) => format!("({})", s), None => "".to_string(), } - )); + ); + let mut initial_command = Some(String::new()); + let mut readline = Err(ReadlineError::Eof); + while let Some(ref cmd) = initial_command { + readline = rl.readline_with_initial(prompt, (&cmd, "")); + if let Err(ReadlineError::Eof) = &readline { + // Fuzzy search in history + let hist = std::fs::read_to_string("history.txt").expect("Cannot open history.txt"); + let lines = hist.lines().rev().collect(); + let selection = histsearch::select_from_list(&lines); // Clears last line with prompt + match selection { + histsearch::SelectionResult::Selected(line) => { + println!("{}{}", &prompt, &line); // TODO: colorize prompt + readline = Ok(line.clone()); + initial_command = None; + } + histsearch::SelectionResult::Edit(line) => { + initial_command = Some(line); + } + histsearch::SelectionResult::NoSelection => { + readline = Ok("".to_string()); + initial_command = None; + } + } + } else { + initial_command = None; + } + } match process_line(readline, &mut context).await { LineResult::Success(line) => { rl.add_history_entry(line.clone()); } - LineResult::SearchHist => { - let hist = std::fs::read_to_string("history.txt").expect("Cannot open history.txt"); - let lines = hist.lines().rev().collect(); - histsearch::select_from_list(&lines); - } - LineResult::CtrlC => { if ctrlcbreak { std::process::exit(0); @@ -403,7 +424,6 @@ pub async fn cli() -> Result<(), Box> { enum LineResult { Success(String), - SearchHist, Error(String, ShellError), CtrlC, Break, @@ -531,7 +551,7 @@ async fn process_line(readline: Result, ctx: &mut Context LineResult::Success(line.clone()) } Err(ReadlineError::Interrupted) => LineResult::CtrlC, - Err(ReadlineError::Eof) => LineResult::SearchHist, // Override LineResult::Break + Err(ReadlineError::Eof) => LineResult::Break, Err(err) => { println!("Error: {:?}", err); LineResult::Break diff --git a/src/histsearch.rs b/src/histsearch.rs index 495b0d5a4e..25e1fb7196 100644 --- a/src/histsearch.rs +++ b/src/histsearch.rs @@ -3,7 +3,13 @@ use crossterm::{cursor, terminal, ClearType, InputEvent, KeyEvent, RawScreen}; use std::io::Write; use sublime_fuzzy::best_match; -pub fn select_from_list(lines: &Vec<&str>) { +pub enum SelectionResult { + Selected(String), + Edit(String), + NoSelection, +} + +pub fn select_from_list(lines: &Vec<&str>) -> SelectionResult { const MAX_RESULTS: usize = 5; #[derive(PartialEq)] enum State { @@ -70,7 +76,7 @@ pub fn select_from_list(lines: &Vec<&str>) { cursor.move_up(selected_lines.len() as u16); } let (_x, y) = cursor.pos(); - let _ = cursor.goto(0, y); + let _ = cursor.goto(0, y - 1); let _ = cursor.show(); let _ = RawScreen::disable_raw_mode(); @@ -79,13 +85,9 @@ pub fn select_from_list(lines: &Vec<&str>) { terminal.clear(ClearType::FromCursorDown).unwrap(); match state { - State::Selected(idx) => { - print!("{}", lines[idx]); - } - State::Edit(idx) => { - print!("{}", lines[idx]); - } - _ => {} + State::Selected(idx) => SelectionResult::Selected(lines[idx].to_string()), + State::Edit(idx) => SelectionResult::Edit(lines[idx].to_string()), + _ => SelectionResult::NoSelection, } } From 0a0be19bed94f960b8b427e0bda75ef739cbb4cc Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Wed, 18 Sep 2019 18:27:53 +0200 Subject: [PATCH 116/342] Rename histsearch to fuzzysearch --- src/cli.rs | 13 ++++++------- src/{histsearch.rs => fuzzysearch.rs} | 11 +++++------ src/lib.rs | 2 +- 3 files changed, 12 insertions(+), 14 deletions(-) rename src/{histsearch.rs => fuzzysearch.rs} (93%) diff --git a/src/cli.rs b/src/cli.rs index 5cfcb1475a..36ef87dcb7 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -9,8 +9,8 @@ use crate::commands::whole_stream_command; use crate::context::Context; use crate::data::Value; pub(crate) use crate::errors::ShellError; +use crate::fuzzysearch::{interactive_fuzzy_search, SelectionResult}; use crate::git::current_branch; -use crate::histsearch; use crate::parser::registry::Signature; use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode}; use crate::prelude::*; @@ -354,19 +354,18 @@ pub async fn cli() -> Result<(), Box> { readline = rl.readline_with_initial(prompt, (&cmd, "")); if let Err(ReadlineError::Eof) = &readline { // Fuzzy search in history - let hist = std::fs::read_to_string("history.txt").expect("Cannot open history.txt"); - let lines = hist.lines().rev().collect(); - let selection = histsearch::select_from_list(&lines); // Clears last line with prompt + let lines = rl.history().iter().rev().map(|s| s.as_str()).collect(); + let selection = interactive_fuzzy_search(&lines, 5); // Clears last line with prompt match selection { - histsearch::SelectionResult::Selected(line) => { + SelectionResult::Selected(line) => { println!("{}{}", &prompt, &line); // TODO: colorize prompt readline = Ok(line.clone()); initial_command = None; } - histsearch::SelectionResult::Edit(line) => { + SelectionResult::Edit(line) => { initial_command = Some(line); } - histsearch::SelectionResult::NoSelection => { + SelectionResult::NoSelection => { readline = Ok("".to_string()); initial_command = None; } diff --git a/src/histsearch.rs b/src/fuzzysearch.rs similarity index 93% rename from src/histsearch.rs rename to src/fuzzysearch.rs index 25e1fb7196..5a253328e4 100644 --- a/src/histsearch.rs +++ b/src/fuzzysearch.rs @@ -9,8 +9,7 @@ pub enum SelectionResult { NoSelection, } -pub fn select_from_list(lines: &Vec<&str>) -> SelectionResult { - const MAX_RESULTS: usize = 5; +pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> SelectionResult { #[derive(PartialEq)] enum State { Selecting, @@ -30,7 +29,7 @@ pub fn select_from_list(lines: &Vec<&str>) -> SelectionResult { let mut sync_stdin = input.read_sync(); while state == State::Selecting { - let search_result = search(&searchinput, &lines, MAX_RESULTS); + let search_result = fuzzy_search(&searchinput, &lines, max_results); let selected_lines: Vec<&str> = search_result .iter() .map(|item| &item.highlighted_text as &str) @@ -96,8 +95,8 @@ struct Match { text_idx: usize, } -fn search(input: &String, lines: &Vec<&str>, max_results: usize) -> Vec { - if input.is_empty() { +fn fuzzy_search(searchstr: &str, lines: &Vec<&str>, max_results: usize) -> Vec { + if searchstr.is_empty() { return lines .iter() .take(max_results) @@ -112,7 +111,7 @@ fn search(input: &String, lines: &Vec<&str>, max_results: usize) -> Vec { let mut matches = lines .iter() .enumerate() - .map(|(idx, line)| (idx, best_match(&input, line))) + .map(|(idx, line)| (idx, best_match(&searchstr, line))) .filter(|(_i, m)| m.is_some()) .map(|(i, m)| (i, m.unwrap())) .collect::>(); diff --git a/src/lib.rs b/src/lib.rs index 9802e2f686..f4ccb4e4e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,8 +12,8 @@ mod env; mod errors; mod evaluate; mod format; +mod fuzzysearch; mod git; -mod histsearch; mod parser; mod plugin; mod shell; From 0c9a62aeecedee3f62dd2e1f33d22f11040d2d6b Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Wed, 18 Sep 2019 22:18:16 +0200 Subject: [PATCH 117/342] Separate highlighting from fuzzy search --- src/fuzzysearch.rs | 85 ++++++++++++++++++++++++---------------------- 1 file changed, 45 insertions(+), 40 deletions(-) diff --git a/src/fuzzysearch.rs b/src/fuzzysearch.rs index 5a253328e4..d01e725d02 100644 --- a/src/fuzzysearch.rs +++ b/src/fuzzysearch.rs @@ -14,8 +14,8 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select enum State { Selecting, Quit, - Selected(usize), - Edit(usize), + Selected(String), + Edit(String), } let mut state = State::Selecting; if let Ok(_raw) = RawScreen::into_raw_mode() { @@ -29,11 +29,9 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select let mut sync_stdin = input.read_sync(); while state == State::Selecting { - let search_result = fuzzy_search(&searchinput, &lines, max_results); - let selected_lines: Vec<&str> = search_result - .iter() - .map(|item| &item.highlighted_text as &str) - .collect(); + let mut search_result = fuzzy_search(&searchinput, &lines, max_results); + let selected_lines: Vec = + search_result.iter().map(|item| highlight(&item)).collect(); paint_selection_list(&selected_lines, selected); if let Some(ev) = sync_stdin.next() { match ev { @@ -52,10 +50,10 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select } } KeyEvent::Char('\n') => { - state = State::Selected(search_result[selected].text_idx); + state = State::Selected(search_result.remove(selected).text); } KeyEvent::Char('\t') | KeyEvent::Right => { - state = State::Edit(search_result[selected].text_idx); + state = State::Edit(search_result.remove(selected).text); } KeyEvent::Char(ch) => { searchinput.push(ch); @@ -66,7 +64,7 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select selected = 0; } _ => { - // println!("{}", format!("OTHER InputEvent: {:?}\n\n", k)); + // println!("OTHER InputEvent: {:?}", k); } }, _ => {} @@ -84,26 +82,25 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select terminal.clear(ClearType::FromCursorDown).unwrap(); match state { - State::Selected(idx) => SelectionResult::Selected(lines[idx].to_string()), - State::Edit(idx) => SelectionResult::Edit(lines[idx].to_string()), + State::Selected(line) => SelectionResult::Selected(line), + State::Edit(line) => SelectionResult::Edit(line), _ => SelectionResult::NoSelection, } } -struct Match { - highlighted_text: String, - text_idx: usize, +pub struct Match { + text: String, + char_matches: Vec<(usize, usize)>, } -fn fuzzy_search(searchstr: &str, lines: &Vec<&str>, max_results: usize) -> Vec { +pub fn fuzzy_search(searchstr: &str, lines: &Vec<&str>, max_results: usize) -> Vec { if searchstr.is_empty() { return lines .iter() .take(max_results) - .enumerate() - .map(|(i, line)| Match { - highlighted_text: line.to_string(), - text_idx: i, + .map(|line| Match { + text: line.to_string(), + char_matches: Vec::new(), }) .collect(); } @@ -117,41 +114,43 @@ fn fuzzy_search(searchstr: &str, lines: &Vec<&str>, max_results: usize) -> Vec>(); matches.sort_by(|a, b| b.1.score().cmp(&a.1.score())); - let highlight = Colour::Cyan; let results: Vec = matches .iter() .take(max_results) - .map(|(i, m)| { - let r = &lines[*i]; - let mut outstr = String::with_capacity(r.len()); - let mut idx = 0; - for (match_idx, len) in m.continuous_matches() { - outstr.push_str(&r[idx..match_idx]); - idx = match_idx + len; - outstr.push_str(&format!("{}", highlight.paint(&r[match_idx..idx]))); - } - if idx < r.len() { - outstr.push_str(&r[idx..r.len()]); - } - Match { - highlighted_text: outstr, - text_idx: *i, - } + .map(|(i, m)| Match { + text: lines[*i].to_string(), + char_matches: m.continuous_matches(), }) .collect(); results } -fn paint_selection_list(lines: &Vec<&str>, selected: usize) { +fn highlight(textmatch: &Match) -> String { + let hlcol = Colour::Cyan; + let text = &textmatch.text; + let mut outstr = String::with_capacity(text.len()); + let mut idx = 0; + for (match_idx, len) in &textmatch.char_matches { + outstr.push_str(&text[idx..*match_idx]); + idx = match_idx + len; + outstr.push_str(&format!("{}", hlcol.paint(&text[*match_idx..idx]))); + } + if idx < text.len() { + outstr.push_str(&text[idx..text.len()]); + } + outstr +} + +fn paint_selection_list(lines: &Vec, selected: usize) { let dimmed = Colour::White.dimmed(); let cursor = cursor(); let (_x, y) = cursor.pos(); for (i, line) in lines.iter().enumerate() { let _ = cursor.goto(0, y + (i as u16)); if selected == i { - println!("{}", *line); + println!("{}", line); } else { - println!("{}", dimmed.paint(*line)); + println!("{}", dimmed.paint(line)); } } let _ = cursor.goto(0, y + (lines.len() as u16)); @@ -164,3 +163,9 @@ fn paint_selection_list(lines: &Vec<&str>, selected: usize) { // Clear additional lines from previous selection terminal().clear(ClearType::FromCursorDown).unwrap(); } + +#[test] +fn fuzzy_match() { + let matches = fuzzy_search("cb", &vec!["abc", "cargo build"], 1); + assert_eq!(matches[0].text, "cargo build"); +} From 639a31667705078d5d0fe4050a1f440b70a8adfc Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Wed, 18 Sep 2019 23:08:00 +0200 Subject: [PATCH 118/342] Fix selection list display glitches --- src/fuzzysearch.rs | 59 ++++++++++++++++++++++++++++------------------ 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/src/fuzzysearch.rs b/src/fuzzysearch.rs index d01e725d02..b27745590e 100644 --- a/src/fuzzysearch.rs +++ b/src/fuzzysearch.rs @@ -1,4 +1,4 @@ -use ansi_term::Colour; +use ansi_term::{ANSIString, ANSIStrings, Colour, Style}; use crossterm::{cursor, terminal, ClearType, InputEvent, KeyEvent, RawScreen}; use std::io::Write; use sublime_fuzzy::best_match; @@ -29,9 +29,8 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select let mut sync_stdin = input.read_sync(); while state == State::Selecting { - let mut search_result = fuzzy_search(&searchinput, &lines, max_results); - let selected_lines: Vec = - search_result.iter().map(|item| highlight(&item)).collect(); + let mut selected_lines = fuzzy_search(&searchinput, &lines, max_results); + let num_lines = selected_lines.len(); paint_selection_list(&selected_lines, selected); if let Some(ev) = sync_stdin.next() { match ev { @@ -50,10 +49,18 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select } } KeyEvent::Char('\n') => { - state = State::Selected(search_result.remove(selected).text); + state = if selected_lines.len() > 0 { + State::Selected(selected_lines.remove(selected).text) + } else { + State::Edit("".to_string()) + }; } KeyEvent::Char('\t') | KeyEvent::Right => { - state = State::Edit(search_result.remove(selected).text); + state = if selected_lines.len() > 0 { + State::Edit(selected_lines.remove(selected).text) + } else { + State::Edit("".to_string()) + }; } KeyEvent::Char(ch) => { searchinput.push(ch); @@ -70,16 +77,16 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select _ => {} } } - cursor.move_up(selected_lines.len() as u16); + if num_lines > 0 { + cursor.move_up(num_lines as u16); + } } let (_x, y) = cursor.pos(); let _ = cursor.goto(0, y - 1); let _ = cursor.show(); - let _ = RawScreen::disable_raw_mode(); } - let terminal = terminal(); - terminal.clear(ClearType::FromCursorDown).unwrap(); + terminal().clear(ClearType::FromCursorDown).unwrap(); match state { State::Selected(line) => SelectionResult::Selected(line), @@ -125,33 +132,39 @@ pub fn fuzzy_search(searchstr: &str, lines: &Vec<&str>, max_results: usize) -> V results } -fn highlight(textmatch: &Match) -> String { - let hlcol = Colour::Cyan; +fn highlight(textmatch: &Match, normal: Style, highlighted: Style) -> Vec { let text = &textmatch.text; - let mut outstr = String::with_capacity(text.len()); + let mut ansi_strings = vec![]; let mut idx = 0; for (match_idx, len) in &textmatch.char_matches { - outstr.push_str(&text[idx..*match_idx]); + ansi_strings.push(normal.paint(&text[idx..*match_idx])); idx = match_idx + len; - outstr.push_str(&format!("{}", hlcol.paint(&text[*match_idx..idx]))); + ansi_strings.push(highlighted.paint(&text[*match_idx..idx])); } if idx < text.len() { - outstr.push_str(&text[idx..text.len()]); + ansi_strings.push(normal.paint(&text[idx..text.len()])); } - outstr + ansi_strings } -fn paint_selection_list(lines: &Vec, selected: usize) { - let dimmed = Colour::White.dimmed(); +fn paint_selection_list(lines: &Vec, selected: usize) { + let terminal = terminal(); + let size = terminal.terminal_size(); + let width = size.0 as usize; let cursor = cursor(); let (_x, y) = cursor.pos(); for (i, line) in lines.iter().enumerate() { let _ = cursor.goto(0, y + (i as u16)); - if selected == i { - println!("{}", line); + let (style, highlighted) = if selected == i { + (Colour::White.normal(), Colour::Cyan.normal()) } else { - println!("{}", dimmed.paint(line)); + (Colour::White.dimmed(), Colour::Cyan.normal()) + }; + let mut ansi_strings = highlight(line, style, highlighted); + for _ in line.text.len()..width { + ansi_strings.push(style.paint(' '.to_string())); } + println!("{}", ANSIStrings(&ansi_strings)); } let _ = cursor.goto(0, y + (lines.len() as u16)); print!( @@ -161,7 +174,7 @@ fn paint_selection_list(lines: &Vec, selected: usize) { let _ = std::io::stdout().flush(); // Clear additional lines from previous selection - terminal().clear(ClearType::FromCursorDown).unwrap(); + terminal.clear(ClearType::FromCursorDown).unwrap(); } #[test] From d7e7f48aaa5edf27c94f2ab8485c0d331a440130 Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Thu, 19 Sep 2019 20:45:58 +0200 Subject: [PATCH 119/342] Deactivate fuzzy search on Windows for now --- src/cli.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cli.rs b/src/cli.rs index 36ef87dcb7..fb158b1af6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -336,6 +336,7 @@ pub async fn cli() -> Result<(), Box> { // Register Ctrl-r for history fuzzy search // rustyline doesn't support custom commands, so we override Ctrl-D (EOF) + #[cfg(not(windows))] // https://github.com/nushell/nushell/issues/689 rl.bind_sequence(rustyline::KeyPress::Ctrl('R'), rustyline::Cmd::EndOfFile); // Redefine Ctrl-D to same command as Ctrl-C rl.bind_sequence(rustyline::KeyPress::Ctrl('D'), rustyline::Cmd::Interrupt); From df7a3a48636fb02e01f2933e9f27020ea898df72 Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Thu, 19 Sep 2019 22:28:48 +0200 Subject: [PATCH 120/342] Store history.txt in user data path --- src/cli.rs | 22 +++++++++++++++++++--- src/data/config.rs | 16 ++++++++++++---- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index fb158b1af6..b410fadf00 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -7,6 +7,7 @@ use crate::commands::plugin::JsonRpc; use crate::commands::plugin::{PluginCommand, PluginSink}; use crate::commands::whole_stream_command; use crate::context::Context; +use crate::data::config; use crate::data::Value; pub(crate) use crate::errors::ShellError; use crate::fuzzysearch::{interactive_fuzzy_search, SelectionResult}; @@ -22,6 +23,7 @@ use std::env; use std::error::Error; use std::io::{BufRead, BufReader, Write}; use std::iter::Iterator; +use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; #[derive(Debug)] @@ -210,6 +212,20 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { Ok(()) } +struct History; + +impl History { + pub fn path() -> PathBuf { + const FNAME: &str = "history.txt"; + config::user_data() + .map(|mut p| { + p.push(FNAME); + p + }) + .unwrap_or(PathBuf::from(FNAME)) + } +} + pub async fn cli() -> Result<(), Box> { let mut context = Context::basic()?; @@ -302,7 +318,7 @@ pub async fn cli() -> Result<(), Box> { } // we are ok if history does not exist - let _ = rl.load_history("history.txt"); + let _ = rl.load_history(&History::path()); let ctrl_c = Arc::new(AtomicBool::new(false)); let cc = ctrl_c.clone(); @@ -323,7 +339,7 @@ pub async fn cli() -> Result<(), Box> { context.shell_manager.clone(), ))); - let edit_mode = crate::data::config::config(Tag::unknown())? + let edit_mode = config::config(Tag::unknown())? .get("edit_mode") .map(|s| match s.as_string().unwrap().as_ref() { "vi" => EditMode::Vi, @@ -417,7 +433,7 @@ pub async fn cli() -> Result<(), Box> { } // we are ok if we can not save history - let _ = rl.save_history("history.txt"); + let _ = rl.save_history(&History::path()); Ok(()) } diff --git a/src/data/config.rs b/src/data/config.rs index 6b4d1383f5..1cb4533d8e 100644 --- a/src/data/config.rs +++ b/src/data/config.rs @@ -23,10 +23,7 @@ pub const APP_INFO: AppInfo = AppInfo { }; pub fn config_path() -> Result { - let path = app_root(AppDataType::UserConfig, &APP_INFO) - .map_err(|err| ShellError::string(&format!("Couldn't open config path:\n{}", err)))?; - - Ok(path) + app_path(AppDataType::UserConfig, "config") } pub fn default_path() -> Result { @@ -49,6 +46,17 @@ pub fn default_path_for(file: &Option) -> Result { Ok(filename.clone()) } +pub fn user_data() -> Result { + app_path(AppDataType::UserData, "user data") +} + +pub fn app_path(app_data_type: AppDataType, display: &str) -> Result { + let path = app_root(app_data_type, &APP_INFO) + .map_err(|err| ShellError::string(&format!("Couldn't open {} path:\n{}", display, err)))?; + + Ok(path) +} + pub fn read( tag: impl Into, at: &Option, From 484d8c26ac2715fd522bd6ed03cf1d877901e664 Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Thu, 19 Sep 2019 22:55:53 +0200 Subject: [PATCH 121/342] Save history when leaving with Ctrl-C --- src/cli.rs | 1 + src/commands/classified.rs | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index b410fadf00..ef809ecc10 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -399,6 +399,7 @@ pub async fn cli() -> Result<(), Box> { LineResult::CtrlC => { if ctrlcbreak { + let _ = rl.save_history(&History::path()); std::process::exit(0); } else { context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)")); diff --git a/src/commands/classified.rs b/src/commands/classified.rs index b042441403..eb51f9c4c6 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -130,7 +130,7 @@ impl InternalCommand { CommandAction::AddSpanSource(uuid, span_source) => { context.add_span_source(uuid, span_source); } - CommandAction::Exit => std::process::exit(0), + CommandAction::Exit => std::process::exit(0), // TODO: save history.txt CommandAction::EnterHelpShell(value) => { match value { Tagged { @@ -170,7 +170,7 @@ impl InternalCommand { CommandAction::LeaveShell => { context.shell_manager.remove_at_current(); if context.shell_manager.is_empty() { - std::process::exit(0); + std::process::exit(0); // TODO: save history.txt } } }, From 112e5d096fc2f7469f9621eb6e38f82f2b7b927f Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Thu, 19 Sep 2019 21:39:47 +0200 Subject: [PATCH 122/342] Include config path in env command --- src/commands/env.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/commands/env.rs b/src/commands/env.rs index 6fc26507cc..9a98164ef2 100644 --- a/src/commands/env.rs +++ b/src/commands/env.rs @@ -1,3 +1,4 @@ +use crate::data::config; use crate::data::{Dictionary, Value}; use crate::errors::ShellError; use crate::prelude::*; @@ -41,6 +42,9 @@ pub fn get_environment(tag: Tag) -> Result, Box Date: Thu, 19 Sep 2019 23:10:29 +0200 Subject: [PATCH 123/342] Include history path in env command --- src/cli.rs | 2 +- src/commands/env.rs | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cli.rs b/src/cli.rs index ef809ecc10..d0c70876f6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -212,7 +212,7 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { Ok(()) } -struct History; +pub struct History; impl History { pub fn path() -> PathBuf { diff --git a/src/commands/env.rs b/src/commands/env.rs index 9a98164ef2..c0af785557 100644 --- a/src/commands/env.rs +++ b/src/commands/env.rs @@ -1,3 +1,4 @@ +use crate::cli::History; use crate::data::config; use crate::data::{Dictionary, Value}; use crate::errors::ShellError; @@ -45,6 +46,9 @@ pub fn get_environment(tag: Tag) -> Result, Box Date: Sun, 22 Sep 2019 18:49:11 -0700 Subject: [PATCH 124/342] Initial Docker command implementation. --- Cargo.lock | 6 +- src/cli.rs | 1 + src/commands.rs | 2 + src/commands/docker.rs | 256 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+), 3 deletions(-) create mode 100644 src/commands/docker.rs diff --git a/Cargo.lock b/Cargo.lock index 3fea3453ee..eee0ffe248 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1556,7 +1556,7 @@ dependencies = [ "rawkey 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustyline 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rustyline 5.0.2 (git+https://github.com/kkawakam/rustyline)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2196,7 +2196,7 @@ dependencies = [ [[package]] name = "rustyline" version = "5.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/kkawakam/rustyline#5e68e972810133a7343b75db30addc98aea63ba0" dependencies = [ "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3216,7 +3216,7 @@ dependencies = [ "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustyline 5.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f8ee0838a6594169a1c5f4bb9af0fe692cc99691941710a8cc6576395ede804e" +"checksum rustyline 5.0.2 (git+https://github.com/kkawakam/rustyline)" = "" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" "checksum safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e133ccc4f4d1cd4f89cc8a7ff618287d56dc7f638b8e38fc32c5fdcadc339dd5" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" diff --git a/src/cli.rs b/src/cli.rs index 7b6f6e863e..d834468db9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -223,6 +223,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Next), whole_stream_command(Previous), whole_stream_command(Debug), + whole_stream_command(Docker), whole_stream_command(Lines), whole_stream_command(Shells), whole_stream_command(SplitColumn), diff --git a/src/commands.rs b/src/commands.rs index c6cc7b7285..c15b32e590 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -11,6 +11,7 @@ pub(crate) mod config; pub(crate) mod cp; pub(crate) mod date; pub(crate) mod debug; +pub(crate) mod docker; pub(crate) mod echo; pub(crate) mod enter; pub(crate) mod exit; @@ -76,6 +77,7 @@ pub(crate) use config::Config; pub(crate) use cp::Cpy; pub(crate) use date::Date; pub(crate) use debug::Debug; +pub(crate) use docker::Docker; pub(crate) use echo::Echo; pub(crate) use enter::Enter; pub(crate) use exit::Exit; diff --git a/src/commands/docker.rs b/src/commands/docker.rs new file mode 100644 index 0000000000..ae5d37b9d1 --- /dev/null +++ b/src/commands/docker.rs @@ -0,0 +1,256 @@ +use crate::commands::WholeStreamCommand; +use crate::data::{Dictionary, Value}; +use crate::errors::ShellError; +use crate::parser::registry::Signature; +use crate::prelude::*; +use indexmap::IndexMap; +use std::process::Command; +use std::str; + +pub struct Docker; + +#[derive(Deserialize)] +pub struct DockerArgs { + sub_command: Tagged, + rest: Vec>, +} + +impl WholeStreamCommand for Docker { + fn name(&self) -> &str { + "docker" + } + + fn signature(&self) -> Signature { + Signature::build(self.name()) + .required("sub_command", SyntaxType::Member) + .rest(SyntaxType::Member) + } + + fn usage(&self) -> &str { + "e.g. docker ps, docker images" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, docker_arg)?.run() + // docker(args, registry) + } +} +pub fn docker_arg( + DockerArgs { + sub_command, + rest: _fields, + }: DockerArgs, + RunnableContext { input: _, .. }: RunnableContext, +) -> Result { + // let mut docker_out = VecDeque::new(); + // docker_out.push_back(Value::Primitive(Primitive::String("docker command"))); + // + // println!("Sub Command: {:?}", sub_command); + // match sub_command.item() { + // Tagged { item: val, .. } => println!("Val: {}", val), + // _ => {} + // } + + match sub_command.item().as_str() { + "ps" => { + // println!("ps command") + return docker_ps(); + } + "images" => { + // println!("images command"); + return docker_images(); + } + _ => { + return Err(ShellError::labeled_error( + "Unsupported Docker command", + format!("'{}'?", sub_command.item()), + Span::unknown(), + )) + } + } + + // let stream = input + // .values + // .map(move |item| { + // let mut result = VecDeque::new(); + + // let member = vec![member.clone()]; + + // let fields = vec![&member, &fields] + // .into_iter() + // .flatten() + // .collect::>>(); + + // for field in &fields { + // match get_member(field, &item) { + // Ok(Tagged { + // item: Value::Table(l), + // .. + // }) => { + // for item in l { + // result.push_back(ReturnSuccess::value(item.clone())); + // } + // } + // Ok(x) => result.push_back(ReturnSuccess::value(x.clone())), + // Err(x) => result.push_back(Err(x)), + // } + // } + + // result + // }) + // .flatten(); + + // Ok(docker_out.to_output_stream()) + // Ok(docker_out.to_output_stream()) +} + +fn process_docker_output(cmd_output: &str) -> Result { + let mut docker_out = VecDeque::new(); + let mut columns: Vec<&str> = cmd_output.lines().collect(); + // println!("{:#?}", columns); + + let header: Vec<&str> = columns + .iter() + .take(1) + .next() + .unwrap() + .split_whitespace() + .collect(); + + // println!("{:#?}", header); + + columns.remove(0); + + // let span = args.call_info.name_span; + for line in columns { + let values: Vec<&str> = line + .trim_end() + .split(" ") // Some columns values contains spaces to split by two spaces + .filter(|s| s.trim() != "") + .collect(); + + // println!("len: {}", values.len()); + // println!("Values: {:#?}", values); + let mut indexmap = IndexMap::new(); + for (i, v) in values.iter().enumerate() { + // println!("{}", i); + // println!("{}", header[i]); + indexmap.insert( + header[i].to_string(), + Tagged::from_simple_spanned_item( + Value::Primitive(Primitive::String(v.trim().to_string())), + Span::unknown(), + ), + ); + } + + docker_out.push_back(Tagged::from_simple_spanned_item( + Value::Row(Dictionary::from(indexmap)), + Span::unknown(), + )) + } + + // let t = dict.into_tagged_value(); + + // docker_out.push_back(ReturnSuccess::value(t)); + + Ok(docker_out.to_output_stream()) +} + +pub fn docker_images() -> Result { + // let mut docker_out = VecDeque::new(); + // docker_out.push_back(Value::Primitive(Primitive::String("docker command"))); + // Ok(docker_out.to_output_stream()) + // + // let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(cmd_args.call_info.name_span)); + // dict.insert("name", Value::string("test name")); + // println!("{:#?}", cmd_args.call_info); + + // let args = cmd_args.evaluate_once(registry)?; + // println!("{:#?}", args.call_info); + + // let arg = args.nth(0); + // println!("{:?}", arg); + + // match &args.nth(0) { + // Some(val) => println!("Val: {:?}", val), + // _ => {} + // } + + let output = Command::new("docker") + .arg("images") + // .arg("--format") + // .arg("table {{.ID}}\t{{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}") + .output() + .expect("failed to execute process."); + + let ps_output = str::from_utf8(&output.stdout).unwrap(); + let out = process_docker_output(ps_output); + + // let mut columns: Vec<&str> = ps_output.lines().collect(); + // // println!("{:#?}", columns); + + // let header: Vec<&str> = columns + // .iter() + // .take(1) + // .next() + // .unwrap() + // .split_whitespace() + // .collect(); + + // println!("{:#?}", header); + + // columns.remove(0); + + // let span = args.call_info.name_span; + // for line in columns { + // let values: Vec<&str> = line + // .trim_end() + // .split(" ") // Some columns values contains spaces to split by two spaces + // .filter(|s| s.trim() != "") + // .collect(); + + // // println!("len: {}", values.len()); + // // println!("Values: {:#?}", values); + // let mut indexmap = IndexMap::new(); + // for (i, v) in values.iter().enumerate() { + // // println!("{}", i); + // // println!("{}", header[i]); + // indexmap.insert( + // header[i].to_string(), + // Tagged::from_simple_spanned_item( + // Value::Primitive(Primitive::String(v.trim().to_string())), + // span, + // ), + // ); + // } + + // docker_out.push_back(Tagged::from_simple_spanned_item( + // Value::Row(Dictionary::from(indexmap)), + // span, + // )) + // } + + // let t = dict.into_tagged_value(); + + // docker_out.push_back(ReturnSuccess::value(t)); + + // Ok(docker_out.to_output_stream()) + out +} + +pub fn docker_ps() -> Result { + let output = Command::new("docker") + .arg("ps") + .output() + .expect("failed to execute process."); + + let ps_output = str::from_utf8(&output.stdout).unwrap(); + let out = process_docker_output(ps_output); + + out +} From 3c2666a2dff31ddc1fb0dd853ccea7a96c002884 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 23 Sep 2019 15:08:24 +1200 Subject: [PATCH 125/342] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2fecc2a923..2b90a786e1 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Nu comes with a set of built-in commands (listed below). If a command is unknown There are a few good resources to learn about Nu. First, there is a [book](https://book.nushell.sh) about Nu, currently in progress. The book focuses on using Nu and its core concepts. -If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-handbook/tree/master/en) to help get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. +If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-book/tree/master/en) to help get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. We also have an active [discord](https://discord.gg/NtAbbGn) and [twitter](https://twitter.com/nu_shell) if you'd like to come chat with us. From e6bdef696d923c98e653be2fa92e875c5972cfba Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Sun, 22 Sep 2019 20:19:43 -0700 Subject: [PATCH 126/342] Some cleanup. --- src/commands/docker.rs | 182 +++++------------------------------------ 1 file changed, 21 insertions(+), 161 deletions(-) diff --git a/src/commands/docker.rs b/src/commands/docker.rs index ae5d37b9d1..0149b4e04d 100644 --- a/src/commands/docker.rs +++ b/src/commands/docker.rs @@ -1,5 +1,6 @@ use crate::commands::WholeStreamCommand; -use crate::data::{Dictionary, Value}; +use crate::data::meta::Span; +use crate::data::Value; use crate::errors::ShellError; use crate::parser::registry::Signature; use crate::prelude::*; @@ -22,8 +23,8 @@ impl WholeStreamCommand for Docker { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("sub_command", SyntaxType::Member) - .rest(SyntaxType::Member) + .required("sub_command", SyntaxShape::Member) + .rest(SyntaxShape::Member) } fn usage(&self) -> &str { @@ -44,74 +45,22 @@ pub fn docker_arg( sub_command, rest: _fields, }: DockerArgs, - RunnableContext { input: _, .. }: RunnableContext, + RunnableContext { input: _, name, .. }: RunnableContext, ) -> Result { - // let mut docker_out = VecDeque::new(); - // docker_out.push_back(Value::Primitive(Primitive::String("docker command"))); - // - // println!("Sub Command: {:?}", sub_command); - // match sub_command.item() { - // Tagged { item: val, .. } => println!("Val: {}", val), - // _ => {} - // } - match sub_command.item().as_str() { - "ps" => { - // println!("ps command") - return docker_ps(); - } - "images" => { - // println!("images command"); - return docker_images(); - } - _ => { - return Err(ShellError::labeled_error( - "Unsupported Docker command", - format!("'{}'?", sub_command.item()), - Span::unknown(), - )) - } + "ps" => docker_ps(name), + "images" => docker_images(name), + _ => Err(ShellError::labeled_error( + "Unsupported Docker command", + format!("'{}'?", sub_command.item()), + Span::unknown(), + )), } - - // let stream = input - // .values - // .map(move |item| { - // let mut result = VecDeque::new(); - - // let member = vec![member.clone()]; - - // let fields = vec![&member, &fields] - // .into_iter() - // .flatten() - // .collect::>>(); - - // for field in &fields { - // match get_member(field, &item) { - // Ok(Tagged { - // item: Value::Table(l), - // .. - // }) => { - // for item in l { - // result.push_back(ReturnSuccess::value(item.clone())); - // } - // } - // Ok(x) => result.push_back(ReturnSuccess::value(x.clone())), - // Err(x) => result.push_back(Err(x)), - // } - // } - - // result - // }) - // .flatten(); - - // Ok(docker_out.to_output_stream()) - // Ok(docker_out.to_output_stream()) } -fn process_docker_output(cmd_output: &str) -> Result { +fn process_docker_output(cmd_output: &str, tag: Tag) -> Result { let mut docker_out = VecDeque::new(); - let mut columns: Vec<&str> = cmd_output.lines().collect(); - // println!("{:#?}", columns); + let columns: Vec<&str> = cmd_output.lines().collect(); let header: Vec<&str> = columns .iter() @@ -121,136 +70,47 @@ fn process_docker_output(cmd_output: &str) -> Result { .split_whitespace() .collect(); - // println!("{:#?}", header); - - columns.remove(0); - - // let span = args.call_info.name_span; - for line in columns { + for line in columns.iter().skip(1) { let values: Vec<&str> = line .trim_end() .split(" ") // Some columns values contains spaces to split by two spaces .filter(|s| s.trim() != "") .collect(); - // println!("len: {}", values.len()); - // println!("Values: {:#?}", values); let mut indexmap = IndexMap::new(); for (i, v) in values.iter().enumerate() { - // println!("{}", i); - // println!("{}", header[i]); indexmap.insert( header[i].to_string(), - Tagged::from_simple_spanned_item( - Value::Primitive(Primitive::String(v.trim().to_string())), - Span::unknown(), - ), + Value::string(v.trim().to_string()).tagged(tag), ); } - docker_out.push_back(Tagged::from_simple_spanned_item( - Value::Row(Dictionary::from(indexmap)), - Span::unknown(), - )) + docker_out.push_back(Value::Row(indexmap.into()).tagged(tag)) } - // let t = dict.into_tagged_value(); - - // docker_out.push_back(ReturnSuccess::value(t)); - Ok(docker_out.to_output_stream()) } -pub fn docker_images() -> Result { - // let mut docker_out = VecDeque::new(); - // docker_out.push_back(Value::Primitive(Primitive::String("docker command"))); - // Ok(docker_out.to_output_stream()) - // - // let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(cmd_args.call_info.name_span)); - // dict.insert("name", Value::string("test name")); - // println!("{:#?}", cmd_args.call_info); - - // let args = cmd_args.evaluate_once(registry)?; - // println!("{:#?}", args.call_info); - - // let arg = args.nth(0); - // println!("{:?}", arg); - - // match &args.nth(0) { - // Some(val) => println!("Val: {:?}", val), - // _ => {} - // } - +pub fn docker_images(tag: Tag) -> Result { let output = Command::new("docker") .arg("images") - // .arg("--format") - // .arg("table {{.ID}}\t{{.Repository}}\t{{.Tag}}\t{{.CreatedSince}}") .output() .expect("failed to execute process."); let ps_output = str::from_utf8(&output.stdout).unwrap(); - let out = process_docker_output(ps_output); + let out = process_docker_output(ps_output, tag); - // let mut columns: Vec<&str> = ps_output.lines().collect(); - // // println!("{:#?}", columns); - - // let header: Vec<&str> = columns - // .iter() - // .take(1) - // .next() - // .unwrap() - // .split_whitespace() - // .collect(); - - // println!("{:#?}", header); - - // columns.remove(0); - - // let span = args.call_info.name_span; - // for line in columns { - // let values: Vec<&str> = line - // .trim_end() - // .split(" ") // Some columns values contains spaces to split by two spaces - // .filter(|s| s.trim() != "") - // .collect(); - - // // println!("len: {}", values.len()); - // // println!("Values: {:#?}", values); - // let mut indexmap = IndexMap::new(); - // for (i, v) in values.iter().enumerate() { - // // println!("{}", i); - // // println!("{}", header[i]); - // indexmap.insert( - // header[i].to_string(), - // Tagged::from_simple_spanned_item( - // Value::Primitive(Primitive::String(v.trim().to_string())), - // span, - // ), - // ); - // } - - // docker_out.push_back(Tagged::from_simple_spanned_item( - // Value::Row(Dictionary::from(indexmap)), - // span, - // )) - // } - - // let t = dict.into_tagged_value(); - - // docker_out.push_back(ReturnSuccess::value(t)); - - // Ok(docker_out.to_output_stream()) out } -pub fn docker_ps() -> Result { +pub fn docker_ps(tag: Tag) -> Result { let output = Command::new("docker") .arg("ps") .output() .expect("failed to execute process."); let ps_output = str::from_utf8(&output.stdout).unwrap(); - let out = process_docker_output(ps_output); + let out = process_docker_output(ps_output, tag); out } From ccb6dc264e9dcb14701794eea37783721eb5d360 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 23 Sep 2019 15:52:01 +1200 Subject: [PATCH 127/342] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2b90a786e1..98b1fc10bb 100644 --- a/README.md +++ b/README.md @@ -269,6 +269,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | to-bson | Convert table into .bson binary data | | to-tsv | Convert table into .tsv text | | to-sqlite | Convert table to sqlite .db binary data | +| to-url | Convert table to a urlencoded string | ## Filters on text (unstructured data) | command | description | @@ -280,6 +281,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | from-sqlite | Parse binary data as sqlite .db and create table | | from-toml | Parse text as .toml and create table | | from-tsv | Parse text as .tsv and create table | +| from-url | Parse urlencoded string and create a table | | from-xml | Parse text as .xml and create a table | | from-yaml | Parse text as a .yaml/.yml and create a table | | lines | Split single string into rows, one per line | From 51b8b0538a854da1e0cb3fcc23b55786335fde3d Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 23 Sep 2019 15:55:52 +1200 Subject: [PATCH 128/342] Update README.md --- README.md | 56 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 98b1fc10bb..22d5d63226 100644 --- a/README.md +++ b/README.md @@ -220,56 +220,57 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | ------------- | ------------- | | cd path | Change to a new path | | cp source path | Copy files | +| date (--utc) | Get the current datetime | +| fetch url | Fetch contents from a url and retrieve data as a table if possible | +| help | Display help information about commands | | ls (path) | View the contents of the current or given path | | mkdir path | Make directories, creates intermediary directories as required. | | mv source target | Move files or directories. | -| date (--utc) | Get the current datetime | +| open filename | Load a file into a cell, convert to table if possible (avoid by appending '--raw') | +| post url body (--user ) (--password ) | Post content to a url and retrieve data as a table if possible | | ps | View current processes | | sys | View information about the current system | | which filename | Finds a program file. | -| open filename | Load a file into a cell, convert to table if possible (avoid by appending '--raw') | -| fetch url | Fetch contents from a url and retrieve data as a table if possible | -| post url body (--user ) (--password ) | Post content to a url and retrieve data as a table if possible | | rm {file or directory} | Remove a file, (for removing directory append '--recursive') | +| version | Display Nu version | + +## Shell commands | exit (--now) | Exit the current shell (or all shells) | | enter (path) | Create a new shell and begin at this path | | p | Go to previous shell | | n | Go to next shell | | shells | Display the list of current shells | -| help | Display help information about commands | -| version | Display Nu version | ## Filters on tables (structured data) | command | description | | ------------- | ------------- | -| pick ...columns | Down-select table to only these columns | -| reject ...columns | Remove the given columns from the table | -| get column-or-column-path | Open column and get data from the corresponding cells | -| sort-by ...columns | Sort by the given columns | -| where condition | Filter table to match the condition | -| inc (column-or-column-path) | Increment a value or version. Optionally use the column of a table | | add column-or-column-path value | Add a new column to the table | -| embed column | Creates a new table of one column with the given name, and places the current table inside of it | -| sum | Sum a column of values | | edit column-or-column-path value | Edit an existing column to have a new value | +| embed column | Creates a new table of one column with the given name, and places the current table inside of it | +| first amount | Show only the first number of rows | +| get column-or-column-path | Open column and get data from the corresponding cells | +| inc (column-or-column-path) | Increment a value or version. Optionally use the column of a table | +| last amount | Show only the last number of rows | +| nth row-number | Return only the selected row | +| pick ...columns | Down-select table to only these columns | +| pivot --header-row | Pivot the tables, making columns into rows and vice versa | +| reject ...columns | Remove the given columns from the table | | reverse | Reverses the table. | | skip amount | Skip a number of rows | | skip-while condition | Skips rows while the condition matches. | -| first amount | Show only the first number of rows | -| last amount | Show only the last number of rows | -| nth row-number | Return only the selected row | -| pivot --header-row | Pivot the tables, making columns into rows and vice versa | +| sort-by ...columns | Sort by the given columns | | str (column) | Apply string function. Optionally use the column of a table | +| sum | Sum a column of values | | tags | Read the tags (metadata) for values | -| to-json | Convert table into .json text | -| to-toml | Convert table into .toml text | -| to-yaml | Convert table into .yaml text | -| to-bson | Convert table into .bson text | -| to-csv | Convert table into .csv text | | to-bson | Convert table into .bson binary data | -| to-tsv | Convert table into .tsv text | +| to-csv | Convert table into .csv text | +| to-json | Convert table into .json text | | to-sqlite | Convert table to sqlite .db binary data | +| to-toml | Convert table into .toml text | +| to-tsv | Convert table into .tsv text | | to-url | Convert table to a urlencoded string | +| to-yaml | Convert table into .yaml text | +| where condition | Filter table to match the condition | ## Filters on text (unstructured data) | command | description | @@ -295,12 +296,13 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | command | description | | ------------- | ------------- | | autoview | View the contents of the pipeline as a table or list | -| binaryview | Autoview of binary data | -| clip | Copy the contents of the pipeline to the copy/paste buffer | +| binaryview | Autoview of binary data (optional feature) | +| clip | Copy the contents of the pipeline to the copy/paste buffer (optional feature) | | save filename | Save the contents of the pipeline to a file | | table | View the contents of the pipeline as a table | | textview | Autoview of text data | -| tree | View the contents of the pipeline as a tree | +| tree | View the contents of the pipeline as a tree (optional feature) | + # License The project is made available under the MIT license. See "LICENSE" for more information. From 630ff2495f6f36638b79a7958f93e8b19b131c1d Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 23 Sep 2019 19:32:17 +1200 Subject: [PATCH 129/342] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 22d5d63226..c9f4e66365 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ cargo +nightly install nu You can also install Nu with all the bells and whistles: ``` -cargo +nightly install nu --features raw-key,clipboard +cargo +nightly install nu --all-features ``` The following optional features are currently supported: From b8964bd3207d851ef05385f3b993c82c5bc98bfb Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 23 Sep 2019 19:32:42 +1200 Subject: [PATCH 130/342] Update README.md --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index c9f4e66365..2dfe463fd6 100644 --- a/README.md +++ b/README.md @@ -53,11 +53,6 @@ You can also install Nu with all the bells and whistles: cargo +nightly install nu --all-features ``` -The following optional features are currently supported: - -* **raw-key** - direct keyboard input, which creates a smoother experience in viewing text and binaries -* **clipboard** - integration with the native clipboard via the `clip` command - ## Docker If you want to pull a pre-built container, you can browse tags for the [nushell organization](https://quay.io/organization/nushell) From cbba37a6b193ff51b8e2ccf37c92b6b5399dd949 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 23 Sep 2019 19:56:05 +1200 Subject: [PATCH 131/342] Add files via upload --- images/nushell-autocomplete.gif | Bin 0 -> 1860315 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/nushell-autocomplete.gif diff --git a/images/nushell-autocomplete.gif b/images/nushell-autocomplete.gif new file mode 100644 index 0000000000000000000000000000000000000000..87540af37f1f165037841a963f2833b0666243cd GIT binary patch literal 1860315 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
u1yy89fo8S+ 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 z˯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 uUe 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 z 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

WuP5pgPHLa;4!IFkyBDWCgu+|i1Y9}U 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�!h zaN&BV2SN!w^w4|n)r1bxL8Kcx0wMw;AZj21LXj?_6ai5Xk=~SI=tVlHh=`$z2#A7; z?eOKC@7_D!-}#l1J@&|Y)-&fU__ctdwN~>2Ar2${0ud2-Fy9=!ENqS6g9c;?8WjNI zG=a~@MofjdR^WdtJ@$#9e=9xCov~-hX6WVz9FE0znP5i;61UdEf=?GOy;5xYS*D*0 zUcunPwX*+rK`353^cBtM7LVFYQl@b;c z!u^`EOLqg#{#oPwKpoatyOhIuW}`*&=@?g8Wb{Br{%tb|k1yH_&Knvbb~}|5prFs* z;5V-Q?*2N>_p%#F_Y*VEh-hyAC$Kbq&`*i^chK)EMIZFzcvyV_4%Y(lrCbKnpY;^h z>lF-3{@)*3^lgNt8H9ai2-7MRzsm5nUMdF5VXeBdH*HYGOn)m3AkX zl5$fGV2Vw3Rpxmw1{+^20#j-f#+ef^u&k?Pa%=yx8a!1(LwmVswnwY@x!bIOYu; z>qbv$a~Im06QGZdI^lDEBy3UB37krbaz#W2BW~NL%eWd~usBFJ{ThwPyo-zC<(DEG z$y6E1l~muEF)CR!R{ArbEPqN>9jRtwtl>Wp8fmOuV5}<+=;>GKFC?1o4k$rQs3Jtf zwzyWO5&k2}zAH60_R$evA;H%R`k*H zQUDlrN(`&$eJ3I-W6y}q?@KypB#fU5j2;$@z9%G(lrxs`2APX?ONwQ-Lve^;d%W7} zouJM>4uKSBeg4!I`kwi)k9wl-GFPhDDSvt6GHIgHOp0E<0l0`9LeHw9iEi4NuwcN9 zlImsMAjv*#IAMO`H&`5#F!GKvVyvGY-))|;U~YaiVga>C6t`$qwIDlNr2HvN%^W>T z2Bi<0UmN6qK>3drSxi*Gk^Vv50pS?bk>G58ZpTAXP;DlgC)_6%uv`_Xavo2@j8`5> z6_t-v(ZPQzTmhL>aFwY`ZMa1gP@%%LkYN8eLZ6>1d*@oI(ry{nz$GF;sWLBGtY3hM z+~Ll`j;B>fR7ST&I679uozuEMp}e(Lg-2R5x*T46|E^;*odpC*VGwv}(QIpzKAIIB zjRI32n=%6W!D0~fX*g1<3rjRo0ZuihmNX*En&5sGi3nw2^EY<^8j5M;?%JMcm{2)+ zp7>y_ZsNs+_13#Gj9c_T})&eL*tVO#0Ex$ITma}Gwlmau8e#EOinv2a}41~N{gKg0V#|kH;p7w1P;n!tQ$*zvYp=0DA_y;)5M{=d{7-k)YDGOlb3_29t=h7%v7HR z1N_3MWB^l`^rZAnd)YQe`In9wLsN>Ij1A~E7}`lSdA2PWr5T&i36vlj6D78jB+y{L z**l2fo3AN1-{2^3Y3LPY%I{;g(gWvrw3`U(g5n$JB|<*`7@Gkb2L^-&>0}$qkX(wRLElOak7Yz@m_4{?^rhF{%OFpOwb>jyn-T~H5^4`d2iy#u zd}YPhv+RlpC?c(Q@iI1{lQ`J5WbzP{GpbG5x|XSWV;o;;`A5nW4rv%2jprXn%2cq+ z{fEZe{LOS#f7^qppj3EEX;gbFiR_7E#hTjyXOqyzT(hO^n`Or6iH^oWQhRR ze@=zIar2Qu5}`!1^TS`+xI$FW4v*XtD@<=cy3IFns=Dn>u&Hp&>YJj9+ZlP3t0Her z7;izSC1$I{`oDZpI=na0?k1w{_rKjPjN=XNE40m(a;U(hy5*m{gDG+-hfs^|y(`N4 ziMb8q?|iBLv?rHb;c-i6ssA!B9J}0e#y#T_cl+fx6kgBZr$v_d<={JoAwJ9B5{vJx z63cn-l41qA#wagu@jtWVAI+>_{krtdK=@rU&q+s9QJIg|>nHppe!}ksd1z;_t54p& z-sJzwP};9kGMJXLEm-=|)O&@_0B^3mX!M?|SlMT$|11I@6$FB87%5 zq%l0CF}BlC!!)L>SHxLvraqRrW7=<@;+|O=`xFa1yZ}`z%Bji0rMZgk%Z**kbprWt zKNNQL$=SWO`jdGT?!Yg&_F8CXRUqaxALH7wN@1BNQ30K_l;CzI?cLKxmW{^>0+KxZ z?LrT>v*KM5q4wmW9e%l2nUc*zf`M7+>$groMO7?i@7T`6OQ^oj$4;?HCyy_s;Hg|g zcYNU@>n2U&N7wG6r^K_libcvO22)vvW#%T(jSnDzUwc}?6(|j}QwbL^8Ky|JY*eMK zZ#ks*eo9haBwsFq^GfrcvD|PE+h8*Dmv%r!s#g2-A$?UPwX@cqucpy`6QYmlz6of@ z6Qr?_-x+WJyY>YJy;4_x@msTO*gixx%5BBR-@~{jDZ9FKbp}jobG_k&TdGV(VBwV! z`NQ#bqm`xb8t4Tg;-Y;rm>*cIL<+r4i4BHZjy|mc%T`$VNv~|I^__Cf(SS52T}37% z`W4=R-7~a-XqTXvSD-(iU}=>#C-IHaz7Y90d37Y)I4a6+MF5G(WBDs2{ zQ102%v#{RUx~1AFdN9_9{x7{;tP`2_|Ki7^$XrpGd?Oi3-gdfd%S*=;SZ7M_T-v4Y z3Q^ZFOciyo&bkSA*7aY~(_n?!{$PY8^Xjvmwkl-FtMg^w&zBD)D-<8)HyiPnNLOj{ zJO~V_4i{c0h_`@h&@ufs+6aDCfUhxiTC7#xgJ>KzYH5Kuy$v=c!WGaN8CX;>j-=iR zFTtjxiyIZ+28~<_v=$|9K|V&%;2Sqparad-gMp#Iuo1Iva4^6)nnG9v9kJKyhpIt- zx5#89pLqZNMEFFJP$%S<-MzBkB+QW!meK~sHQnA9fI^=ZUr&rB!j%r-my0022ckod z8h(#A{9Q}?BHM6i)zDk6X6i~5gGhm;!;GVXc8;5s`CC-4wyJ-UT7KBNar2^?-rMG~ z{hi+Z-Ie`}?eITyO47)SUsaXfYJL_mzW9yB@v}~$0+OUq15R2@>^2PKmPDB!kN;kH z25Z#LobTL_1Vv(C&t;c9w$j|C5j<8zPX3OdjP~=7H6P$NAAK*gD{S*)OUVNQV=}bM z?rUdif8o40oamrY@s{DPhi(d~v$R+{X`9}D0MLj}f(xNL1RY}jM%dOfS(ftAIBB)I zloJPHB6bs*4KUnB%|kjH75gT!zmgv$bHX+1C(rC@TL;We#{T7<`jw%w7#3oPH{RR3TosV%qdhe_lknaBfABX~^v8m&wzwb%%+e78Xd`Z{KPd@vT>k>Wly&%I2T2MhDcn zKGNjdXW2iHsNiHTLXzRZ4?25HnnaRP8OS$8-M*Mkq_nBY|I%>gOHCNiE-{v@8PGd2 zu|G0aw)O^hnoLz2_uYW>Yb6XSfrf$-G9wa732{)lnJxWfFr%2-QBgotsRFLxAi+0Ea158 z4=T&I!W2!n&xw1BT)oR_nJI56%o&E8ftP4v7T6!&S{7tfLc7^?I=St#-8(Toh}hbw0ybMiB`_w&rfH@3}u|1DrCYK+%}UGx!q54T;JHEAdPKEeBu@&bLw>++7ePvsr0Z1T47Ses=~+Bn+h3W{QoXm%RMh zKn#FIt|e#f;tVLDdmRaZD&u(Kl@38XX)^xQFxLqc^F-2o%?uWR&IJ zeGULR6G*(!=~GnxKMk7$4JyM4Jz_JE$;;k&?#2nMbIr^(WbDCh>#%~ko7@|ne%Yr& zI5%?Ac;kexTk*ZDazvGkNLHf3=mGX%`5Bn8u`;%j9Gg3kjb$xzCIS@7Aq)TAeX@m8 zg1kzy+O`E~R`y4yk%{my@(VQ)!)8xR9SloEXueP(fYesCji92zKC$d6^8vS|TLgVJ zGc->h(-4{j1Bwvrlj)r`Glte*A$2X>ZaCS^#nog{3Bu)mjhRNDWVMC5XGGAOT6T#f zaMjvP-8|33|VDP{K= zs-c@l?t9dXF^|`}mDp|h$uXHzg`Bwx#TnrYDmMh*R5(7q#s-o&phs);<tMe8WT!J~;q?z^kxwyq^1Zg(&l}Du0qo{`X9V z_Rhi@w{hpEsbOR29~_IGe`~M755Xtl6*~~D2n&y8I8;&_&P$$_aJ~HA0mO`~GBo(G zjngqZ`7d3={6`dp`h{iQ4j@af(eR8cgygtbe_&8e=xbwM3Pe(w;jpuR!-T-t&zpr% zcYg(#G^WQDK)9nChXt5k5*@2ZLVFPi4f+chEZ-0}-STe8#KMT9GD7Uv`n?lC8V6nq zNkTefAxcNob%|-Bc`FwFzM}_GVQqJEH^&r81#`P5)7@GCihwToh$MjMBDo6~Re|?4 zM40S01%Tp;IROAZ$9on8G@5b&QVB1=h;=AJ4P_jWaxk_?U_yV24+{ z{BOb{?(_2-5w**{WR-x^1cTH$&5a!M&RSEIQbeSTnj^11I?WwZlm|XBxGWCK$rNXj ze`Tc)rWJPji)T=gB&C!~W|a#Trhz|AE!~6iBR8nrN>vk(qPaVz)_%?1(g0UYc|Oxe zhU->T#8oCgLoo0_^b$t-LgaigpU9SS@_FmahT?Y^swA22j#&n;%$B|2Tvvp|H{nDN zU9UVkga7pCbfW5lVu8TT-;3~x%p-r6Ps9O)JQE}k;%Tq`9HJKN8!fWSC9W9{wuBi%!2WDmLZ8c^Fve?IS96qjM!T-A4 zmd-j=Ikwe4u`WE*lI|?3KEPf*FY7uW`zlc4G_>Uyu5S^f`{(9K+mrBsLi~H#*F@b- z$dwW~??c_!es1~qa{M2AL*y3w*xMUZ4CH+|9xo25wl`&6llK>sdo$+9T|N5CFF@__ zo5@;5BUj5r#)so>N#P$_DmLqUgxxC^D%#uX4eHK_7bq*vd~IWt*oz7D@EGrDV*Vwh+K9C%SABfIH! z&jFW72IE@)*R^#)3dfTRYP)V=bprcJQK}~QDwvzkMm58#t5|)4G7TdG94v!?l)^rwo!yt z`rwD)ZGzRUhLTMMDQUh@0%Ot}5k^3{eCo=SnhIYIpcK7|+Qg+`7pWb?+|i8 z?_I0f))D0_d`)@P{z?q+?C%9xt#v_?^ic&0y^qMOUZ&f`G?TtGStu0&zqYh`MXi8O zEbGV}4UN$^Ccr}`8fd-E1rM14g0c63U6(KEmf>Gr%IQWi0#vt%W?`9L&L`~+z``oB zV8=!b<7*8wFC2~%M8gDvBk+(vjew1bdXpII_(W({Zt;hN56as=r%8rKmBL=TR;RKm z*uNju*!Ao*YA{%6w!nB?`3c}gs*V}x0Im{Bw=*9m4gO%N*TcnAtA35h_}a*xDTjYRi^fy=4t5ixKX$~7K_N_QHSDX z9xPtQE^6w#pYbPvBC+6yL?uoV7r~fkmsr6(1hy@M6jDKCV`XLNkRTBh22n{6Ft!KO zqL=q1Dn{c@W*IWUdh&he1IVsLM%osA3>E{+Z#Z29a2$p{oW5kxAL@l}YBjD8lR!dO z0m5_`tecH_(b#tz#6OpT0r!u@Qdp(?4QNS;NL~(I5zY{2OqLICnGavR55FRne|S}Z zrBYB45ZVzD7IpUha*w&Zk9*ntoyKX*zOl#*%w;i2y{h7)O1VO;G1#3ahRsBQ3XFM~ zOI=^HM-!#@sq`;tctFC}khldWkR|7mGulYVTHUX@2C@}76!eeFE4uP@gPw$=^5 z-v>w9;ONJ6%*M9F23X$Tcz;DwmK!uky(QDv?#Wb2?yu5@wRR>e=^HadP#xQbAof)7 z_U(s&nEVpbI_rgXBmkT7{ z5*2`CaX@y0@#lnY?)$$+SD;R@|h`QP$YK4_fk|e<5J{{(PH5$t&uz8?o zjl=G{4+jNQVL{>}Ci2cn$U%Q06P?SP*OsJAgH7Vh2BdQW6)?uK+Aw)_l2;aOLtz^& zPeq1%^dA!ymPyzHA)mk1kx*$<7?8*(og^zw`pTwgAWgE8R&?0bc9l*E3HJFW9N|?I z(li!vWTdtYprAO<^8PD-wqolDm!{5M_BQw5+RplZQWMK1%;8DTEyZ+dM0i(J>U&v4(9H4`=7DE_MNKLTS=|&P3jdE8u z8Ltm(8pTz|q`=e%G=o8zJ1>VBjR$S(O1SMuv+8*7hSe}pUo_WEC{kyKcPCizlPh6U zaBhG|1tURcaN^1OL;%~Tsy@9s0|V$A`wOJq85S?h6SbKEa9y@M=cDWk7hcy6yjCTj zh?7LmAkMMjc-4({3yjqlKtQm<>-fDs$2Y1(GDsQnd*)NrSe9tmm~GgeT9q;4!dv zu1|bftD+Bm{FqY{XxuGSqEW$YL^&IxkxBHztfXrCuwEz$q}ZD4$N{dveAuyG(USjiD0l(V!EBn^ns1# z#R)=5uu1)k$rB+bVCTDgz zs+;6|6dcc&gk>W6qwe$P-)pW>5ovpiYR&mo!lOr|V$KeOHdOu|Gzs6R7Zg?%m1-8v zJ`-a-^-N<>EIUPP-A-JoS>l|kWKg8b&@IU;%~Cg1rBj=wvsGnoH_O~pm91`;ZTLF$ zCtu|8r7%;Wz$R4%)=XZhX3lfX;2~qq+(`Kr7X`#&h|MK|#{r+Ei#>YSKP{@B@5{3^ z8H~hyP$JE!264OCAXKKCm7}sVzZGfn6sYq=@kBLKr9HI7#&sILXxu)OuRcugRP$6g zQB!t8s_I3V;?&Z~hd*e=+B~mxgQ65K9^ypQv=OZX+fk>kMrp9BpwHl#Jtv0-LTnDhO0|weg_L$e2gT`tW{gGrl!Jwl6pH#Dp+ZH|Q;op|@ zQR-^^(U3Fh4I)Q+1L}5%YW7{x+J{liV=Z=S>R2xIPv|zg6Ey*no!MiG(@8Y;fr&o- zBL3iX*#jPs0JC~Ip3if@vGK_E^C4%~D=noXEMCKQ{?OFC%@*b8AjPP!_sz*p!)g7S z!rRN9hnF3?j?5tK=RT=<`n@_wVHy*_TOV5}6p+vfw6vKv_4Nh^4>gU~YAaSJM;;!R z1rQoI)Me|>mlYwJeZZ0Z$Txg(`%Y;wo0e%h*vuVDcC_=%IA^8E- zDk{RtgunZ{;oC!_)7;7xIX&vUh?#!GHN>+=#DWww~rJ2oN zZNJ_?5PquKaD4OrkCL~?G4G$2{Ll{h{9~mi?(Vt{*(2ovA^o8^U10tlctGrZ1LmjT zHLM3anq!E1uWtLfcxCHkO}E;-{(1L9L7s7IY`&H^>-RjKpH~pEH6F(``7ve1@wTZ? zy*Q}!i$~Kx_9uVr0Op*{f<(<=mJhL6#Xp`lo*Y+2Nfw=LNj}zj7^)o?s#PN#OMV?! zE2ifoe7X7Yv!W>NSi7qpXZ0RE)-DgyyOY{=Ze*7G1s94aj!>LS!h?QR9wgHr`0XGHYnxl=g-I&kEthG#W#NUh3e+>a0_0H zO8KNGdM*Lt<}#Sw;d(ydHY9VfOMCcs_ZTU>PdIY?p8kuDgoJ0B^7CCUru8Q~3Y$-T znS9+n^;v)VNB8uJ{tV);xlMOVc0y&*E4fttS=6=3{JWzheLo$8{mrXd@c8FSe*?B( z5nt%fcXSVVLDr}mDjlz0ry7K3|MgZlgcJ~i$!1ImBB@%7SBH2ezNot}r9#fBy^Mn9 z+|-=7V&L!%diIIBF#*urFg8S+XcoMo|Fc$~FxKUSMD?^AV+_~i4A+00w5IBN0R&+6 z7C?8u;%KBfo9nGw)U*e#LyBp7;XQYZ4J=lBP8|OcEQapfUu=kH&KK{w&hwYNYyfC0 z@7;TreG7M=2XZ{aX1 zIF(hztU&uxACLhpA^Jf`R3M#|;oni{ka<3b!b5V=+?wTWHU>1XO0tsxv%1wLJk75j zEWl_)9tEpzjOH1f+VUJ@|2S5p7P=B$)DqzInCsC$;`RY2ARaEDu>4U!ZO0q{hBNt( z+V0z?yNpiy-5+_I0B_>2@96vte6+x%}LCg2=Dg|ktIu{jcdFVxCe{J7~e zvs>+R65@PmG%^2@x8p*52N^;{T+5VHZe~TlS%QGoi79o&4>qt z&FQEwJqk&u6`vhH4W;Y8M+3`Xg}l&|A!iR9)T#B3d8vaxp|7I5&aJ%RIoN}!Jb=Dx z-&PTBEYtICDe8sP&+Qi0Q-7`3rk{UVIzcMeKkb%5H$Zv94)CuE?l$(L)Ryg)SpS0S zD%e=(`v#Hc^=3tco-G*`H7ynP}1t5Z$ zO48t2Iy*a=qd%nA=BHsh@imE-b4^t!)AHG*L!ZTSun-Egz|hNqIDGDcolYN1HhF*{O4v|sqAjLdbt2fa`nHi4LNye zqiWt(|C{=e-NlC|X}7JMimz!doj+UqeJQNNv}5?7Uy!gI`Hrmi?(*AfuW1WgPkyW< z{O~G&ulso-YT6K?xY)CR`&)2Yy?qhPEaFI@O)P>f2>kxuQ&w*Dg%g*{l%$YWH>uKqRwCY=(Z#{M(5gLgAo;<=-+BjqC@t0pJEN8XrKh8FG#@6h z+%@L+O=*1sa_Y}rgPw1XsPG7x$BbNpL>&DRzt61aQjbAx}qNrTagdaBiT$ZilD$>$3;NpRK29! zB+dgY90d@Sj}aPi7JZ@Pv1Sa;1R~4g09QsI*MO*FKZmY=vO)qNG`tGOSU}Y&5$1|n zx|i_ttnfU7s=adtD_-e_DvXchIj!AiIY!DGoET)}?)|1@tQ+ z)bnknKIHBrzy~?N;+zBKZ|q}+9X||%MgV?c1LwE8Ej75YFH&2bC|u`-=va34z$gLq z$OazD90I;t(;o@GNx7J)SHC)3&APsw4Z*KNJeG~o-->G-weL9Wh2QSNysI&}%Sd5Y zr}FjFs0e}nET5VJ4fZyR*b_$Kb8&r$U0TX~cm6!5Bp^7`C=kMbaKfbZiX?PI9Z@b| z>^eC528=~mDhXP8JWd%4JvU=E=**G-zqB^){C~9ev$kX=0@1Tt=5}Y)gHR&ElRhac zNL))af86=#pZNX4?~Ht*{aQxA5)n9Zu}~mu~-FhFJV~wm!eFi&9=`ajQem zpy?@$3RJLtKRAJ@_{ZHfp)Sk-Caj5xpMDSWp$G|Liol<7z9pfANtIu2B(oF0L0=Q$ z{B<;@5R(NFXc1KBSOobaL0r<{ByJ{kA8kiBpqp%yRLG4*R?L0i$;Ff^$-e@>1Mcny zXmy49CxKLQ5YWWFN9wzcP!(sflhO`)4sC8O|bHh=$4@eyc0vbu}&AG!DL zVsfm%fn;AQ1feMI!)36iy7bxtFgq)v@22f1duShl`!z|zjra?9=JgFxI1{Dg zMzV}HL@>d{Cgj?`@m1m(T*q!}M}STY8{70P05YRcs}F}S!Er7LrHun9sLzk>sPnrhPb#s#JdgK*bo!5HLa#pr`F63hnu56%6;3AI?>x909*pk-@aCrLyDa>V8APEp z$rx#Ofi?NC4}95`qK-7kPDotU=CcUcYRUnYt$Ek+lkkwWfS_nwJ2U^C>JDi6H& ze!u8iF02+T%8L^0Zf^0IW#cITGsI01Hmw{YNp!$hX=6 zzZO*B6jd^{e?KKa0>K`Lr1762@?UW@rkj;?Xae(>1V6C>%WT5W(^;IvrMy*PzN!*| z&63Ie;*oiv!e%LcRe706X{BcAy${kbtjx=1(GqsqmtSr?Y?gg#_78irjNy}CCs~g{ zVSOwq$&%HHd^4%{cSe=$lFn{jQfZ1*5>Zo?h*FiSRk>QJB+RZ1%ZDK6mAyiF74lV; zf;5#|I@F_-S<97C^uw#Ty6L>EBZ?f8jr>Cs%ClEFr>1o|N-M5KOF>riiH~OJp{CrF z)*)Owd0x}Ocsw&o(}c>jDk4~y$m&exDn8V0QqyaT(mS=L^ISxie&%ZUs`6CLxtl}( zQB>$rv;LDLfE+0^G@<(@%J5^0;U35Qa*M!f3uDHi;RUf%FI5eH&!1vw#W5e@*wpb{ z(RkigyxUCu5v!G?S0kSG*thaU!&SRYl7A(i3akg>NHgY9w>{ky&_~kL0>hz~Z9tS|>SNX{} zSf0AsqXkN*qy!&zmILogtdGDW_8A??J}R2 zzv<$7Kv6j82rF2K)iIR)(yrkpmG6YYuE)^CFAzXhC@(KD>)|Tvi8SwB*tw$sl{u*2 zQ~+SRb-41aG3C+f_wpKvPXWfloh1>j)bs4zzH8}g1h02~H zUtS-yR)l}Uh0FFw%!}~dFao`?RXn)%bzHP$_)$r#QQhcHlWb+F>I<=rw6fi_X89N8 zG{%a?Pa=x1{!)M#%ppIV+RH?Wvd`Z z>@9oHwJ#lAh7#_XK?vP&n2r<(f~XYXDQr%=YW$9P3HwKKFZhVEJ+f69;M@++yQCyI3_-_}Dzu}4>(i=V|ux!&<>j1ltDgB|26#~0d z6lVog`R7MkHmQvp)2T^zThKKFG4}!wkB?(`LT_raIyv}yU-#jMiaeoq6ehoM4vc0f zBqG41t#C2Pf0O_O+Uq1fmtEl!i+m+I^b1-+6s(ntQA5*<7`6>LP@5Tq-3Qi|RYu+Z zV$&}Gg&8?Z3jI}W+A?kBByX3m<}|OYW0h^*y8WkF=;UuiYRXMsyZgbxy3zx1)yj&m-7c(SmUl<-)-a;ERT5tI*-CdI8b zZ`s(7dfY`?Y_9Tclk=P8109L2m)vAt5TS zx_aJwJo%8{G0?}{NkpyS?#YN=h)D133FBS8>phI;D=b#s+~wIKqK#|LLY5_u$5;zOjXki=v&m9ZE9Smdv{SthZiuxbyw7vu z4lrb_PG5T#$57#ezOQ~Q#`)5H7vSLlPwP>#*d5WaJ79oN!IR^|M_djiR|pi+AO)gh zkgI_6pD|0eGMtMQKgWxUx^@HmDGsJ*;twU=eLzXu(K_$uA+8>JZC*xliT`MdpSvHI z)Gp0CFx9gVM3j^x8gsW25%s`O?F3ID-u78JC7Zh4Y6AzCO7u zh>zpvOQOjO{)!xIU%J(*G8`T*`xo)^&PC|&UEejpmcY4K;W^uFI?}T|{ntK7)4Dzk z(Mg5dziXW>#M)Z z@ooR``scCs1$-#T?9;`>v-Uv&LmtZr1n{cZv5lA^d+aG!+Y3?j=QjeKMQee$f#95F zUSjYpzZc`2WWXb_Ijw>Sk!lS`A@$eug&w`WJBC-prhr%V7i~MgUp%+Z0GxK0f`(m`B|S*eD}vyF_#%eot1Cf zL-R%i{G96K+^q~31YJ(@GX8g=1`&{d2w_qlxbmo+)d~KR_wZ|9IV|Yam$7k_Uhi7& zv-~E)0r%P{B(Jyc#Y6ISvLJMksP*L*I_ayzXnCj^uU5Z3$V8Dlsyg>GY%CaQy;cDP|8Si#^x?=5FpC=l_!Btf@v6w0bU0G~VUikR= z>ay#MAvKj*Rw0kFLqbAoYwO2D9@qCWhSn8V%7#8^Ur7(Gf3`pVxbo^Z#<0eImeXNP z!$KKh&EtwM!dj*bQ5Rb0Y)|i%w@Rv4FrFWdJITpsQYMH2aSn~+%J{{ND!jg8nk408 z{1~{aqPI&CvFDO;v0*t+QmO5C)~k!XB3`Gl?CTM;=vr`r2($jqf}@c)1~8s{dO00y zt9M)@M(#;<2q^?Gp%YCb<>@6~h6Ug26+Qqz@0RZKPYgqY{5%H+3g*W3xhKcpm8dl` z0m@LG;7$izWeO|@H;@Ev0>u8<4fuX9!{Y@fR>S#%o}70fEqmYH^alh!g5b`+lln_c{YiK~ z_Sp%;3rw0g$YamQEn~~oSzFrXapeh&iTpIv)sfkH?dC>CCpBp$tc>f&vfRU28!p9d zuh~7rvL65R`T<3~y>=x2k8cO+7`Txr+w1t16!Mp{Wcsg7NmgIiOFsGk7$TG~1hik9FZHmbrufy99SD zl=IX0+9}|beEgDC=G_D+SoolV{g0dt^Tnb8!SBjG9O~BSd**{bmlb6!J$*SWqcnx0 zD^O;}cYkyH0gNXJ>%7a_NyvJ(tn;yU(1-Lq%Yq6c@6in5zXdcZtBtq>z}c9rYgwAyk}`%hW)cLVFd4nd zd#cC?srBZ+-8pg;4Y*pJM4zau9;ZF@MII0f^>GTz!KaP=El+!_Io?M56!8@1W`%06 zApzv3^l7(jr{NYRS`kT=JA&B;Dx!n1Fw)%0%?e-w!!=dVf^AKJ{!Y`0iVH=+U{2+x zR{`)J`0oDGDr*=~_O+_ofH!H{eMR+l)^@e}KfE)E+|C76>JqXHRQpsT=St2tX)_I3 zr|efMRdN0xOPh4ZTg?j(l#>H=N|8j&0Ty-!XUx?d#0x2+@joIUcynf${QU>63T_zl zM2`*>*V!TN;fL7roboPA_|WUmDJ)D$lo+(R@Vxd6NS*+(bZ#!yAYn7W^ybbE?Qx(S z4q}qS>^F#ot5VtFC2Y*v6Mg(Cw*vIbS78@x{8$Dto_KiYDM+5bf;C#mFQT|Z*n8Xt z9xthWP0ohz;Q^m}1YTc{AGjDEp8u~zcUS6J)UP>UK2VG7VfVvEx+J;6RyVf1nJ4%U#IDPLV?2)&mqD-S}JYOkGuAER+@4vS-E6bp( zoknxt3CAHvGnS|BtD=jB7G(`v!h48QU168%DR% z(vEH%AtE4-lok{T0o~~Cl5&Iy2s#iH6m@iplz@mjIxJ9BRFr34*Zn-t{bH~8`Rv8H z|9Sj>$M;8KihyE1;=(T}V+QD$G8|0Y?tBeYvzyFXOA@j0VCHeY5*v2K8}J+L@kfQm z-*OOq+7r(z8c{q0Okr7~JOJKZOe@a6&==J5A7pEhewo%0E!ulxiNI-Um*63mG-D*6 z5b0KCcs1oVFH97D?z^AVE~X*$?2fM%497gJ1eK%1wnO1iAKN&REvFLmk5GgPXL9oL zHP|j>0*|()UqU@hv^hwW!~(CB;;_{*qdhTCH)SSgboS1~1`{~|95_Feg)#&;patL9IJI(^DGy*@SFqBWhenogC;Ff>mz*Jn;JWO74jgIHJ&5_1Aucp5IJ1Hz*_wLq(;^rw`yrpaZfxIdB?FjdR?{MziJONpj ze|i|e_Fg(|So6quOX7Oj0_V5Wz*OCI1$ zm~zn^XEP!jkE;NrsJvvWk|QXzQ7AJjaW74 z69P*JE**-4FW@S^nMhd3Ua_Q<#r0gcMz9EB^ZU9AK3yv~5M2SoORV^U*TrBNPC)q{ zVC8uniep}&zzk@Y+GZotqM#Wvk*G~0JsfcqQQ#dlxKAj<{to+`VM z#Ls+@m>wNeuI*rI!m=!SwM!zDO|P_*b=L?u?FE3R;6jgh+EU9Ia_{5qVrc?PJjq7U z*=Vb=>J66&E}aLhR0B2e{CJ~gcckVGy{7+R%s0uZHUHWYnd{o4wfe|9&uPHOrS8QE zcJrpXclkm#WmO9E-jW1?b0j#52D2jaG2`Gx-V(6tbp<*EPvyT#<2zyI1tJ_SHgJS#W$9XHkQdAg%sfbd1;9b z=Nk!_2s$jC#0d=r&jTz$MA!r_AC(fkxS`w_z=LBF`|&d-th)IQKMxE{Ig+IaTT*(acg3d?Vue za}Arn)-?ET(jX+NNTtbl4ojmsEo@^?|^6=sE?><^cRZ z*VjZXR|q28`bszfuX(sE0@MfwzJ72~;GvHZU=hkPHHk+|1#ahJUWLL&>;U~q)XJ_B z+hxXv6y^*?pp6F3DCN2OTO{RmbLCgmcaK8wN>{8-%R{4>J?_lqfMn!%lV5oTK%Xkfz|78fz{OHWn>8g1Z zB++)%ExpoRoYD1+(iNxlWaHZtS<;im$5KmUofd4dbor-ifyj-Fr#m|Leoj68Eb#2B z>$4x_&wjsthTeUak5UE)@%2M5?$(2se<+)JipSUy0a|FPXoVs5WO+BT2GbI<) z2Tr?1(qBrRw>KH@m-PN3-RNr4^x#ng`_R2|-fvele0u?5FX^bZr+aICGHOzA0|~)h z(736Q`=Rvn$5_A2!FTcJ{8#!kG9{B%r1jL;Z^Qx?U-~P9DpkEh!AgCVL0U{y2}#-! z(|08_oNdr6R^-^BZEmJaxX}m`FkIZsLqSW%z(=Zd0a~n7?cu;JUEJ-8(evG-Er+A6 znF*d~z&mJ+{*C{RR+;L|V8!P1rDi$(+LpTd=LZjmVlyXZW}jc`e)>w3Xfq8wATn8b zO3dI{I`<0t#3okVcw?tHLqs#aSifK*zBo__+0LA-F}%n8eDZSdeI^`wj@s}K^~H(4 zcT;2)n&LP&#q(xT+xu52ycb8jHM% zc71~OO>e#`i1tivro+}t7tWzM2r$4QI~r&B<8tlkCr>Az9G|v%6K@E>2m&Y)eEkWu zDVndh$IzXC{^`eIXn+o(Lft4Z>p4z;f7ZAy@pJPq4=VF}B^DLzk^XAIs}?Hj7UbO* zn|Ibyi?z=KG@}SslQ;$f4h=M;oH0kx-uI9nn0v;`7C#*Fp= zgL8(*3%zQT8CPv@6fX%qQ;0QA$x#Ix!BV`wRx<;4o+)aTTQv%v&z=TD85oQqZf)R z^SI}NJ9aTvG!Q_~bMLZs3*!S}ZC4AEh7uiFZlOT~M~?oL>? zUUS|HukE<@zU%4xC;9-_6+4>n=|26_Vm_ybvQLjaJv|}pp!boVtP6O+))${(y>x<3 z3!~48ZWjKP2jxO{cH!j|79G02rE&p07wBzq3R-sxl{^#n6ma>Qv~|VvJ=ZA#6pLRj ze4e5ud$uL<*bI#8+~WsgPS}d3RYp-g8a3(a7`|PMWeuerTTBXhbLF||)6F@_+Z*Oy z8XGW8DItD16N>u&9A9A`6HmYuOn)d#F$vR3IKt{g5Sbqk@fyq#lHOP;4^CeX-a)|p zg$LR;>E!iux9%jV&z(29SZsc8KaF{Ze9c`7`_19}w=?I7XFq$P=e}VEjfm&Hy>h$- z9(eCxle4jdc}qYDsCl?0<1!11EN7kuFkR9RBe2v0 z5JKSgS}yW^DDy|v+sCqlM(YnarUIXF2lYvb@%{+rpND6DXUTp5yfxN9Vf9e-eZDea za~E?S3mGFYd(VV?=B}yx$ny=3r8OKWp9N?o9qed!B||efo^12(L8y|Fj|hbc#n?Aq zm|8W<71h&F;<6oO_IGtl6~yD7Yib@t^S+v|2aMAMxt-l|m2Do%q1Y#bREdnz?#;Jx zX|2unr{ou)FpHjLP(l`@J!E~91R}%0%>2d;xz$D$9P;ktGSeSjI|Vz~f!14N*+pUB z0y?Jey6Z>I)gR-<_ng&_{kHQmevgC)6Cag21EvZH5SWQWzkNNcHzO*(CX3zIzu+OS zAX?msQ4DqFF5G<~1`<8brniVMbbiphGg-#(agl4{^yjd(k)q=l7{k@eTT{1Aoy2r! z4FMK!nfdj*5Blt68rR37bEwqMX0^X% z+`?@hHo)By9ahbIL800dQ7|5+1@_zmXUQpz*=x7Be~HhD;%>B*M^by%Q$fO|fn**k z83G4Mq)jP1kFwJNj|bKClro8*l7e__o+*Os%hJrLldXdI2a#{7l%jK`TSfwqhy^&` zP?^N|-maGt`eQ^{Y$I!eU+Zt(KtWYlY*hJq`dJk!^ zmhRfba^L2l02ciYJsIH>nqscF5;bg(2GkE%=66>GNQm?d3$oy9o2Eh0eh-sebk%6GZRs(xm!Cm8DQopjtP*v zi&&BiBSZt!tsF@@byiYT`ey8+e~Jj+e%N{n{=i}bAF~(=7L1@wt_oPQyk4AHa7Pri zxs?)(`H!$*#O*gAw*fwlI*v23VS@sZZsP9e)jh;L!UkD^6rts^Tc8TwA(Y6%vhRJS z`j(nO=p-2~A1n|xc<<^~HPcOx;V1cPGbz?TNZt=D7}47gcI$T}<(s$PN?ZJF>W&YQ zqoaaX;DFV*RHHA32U==k(!x)asEQhGHGvyO|2Vf!Pr2^)W}d5{GUByPK$`m-o{xWi z?5Ew{^@%OnoR8kJ$hiuo3V&}OdIE}Kja?DH`DcPjce(Xb7zzR5FQis;HsHOcrbc-O ztJyg;y>N-G0`uMRg`qevW-A<+ZNh*tVaC}pGn2T!gVLeaJ;L4;K(nyAF5iv_>WnHn zI+GdC$IvDKns6$e3HEifZ1Af0-tAV4{?4mr3lm#yo5&G8yp{ zz|l!FsU{?eV`0ajns~mq%c=Kx?ao#TF!6KmLN1}bOrWL3ST_=9IKeAnvImU_qMN^9 z_C&-$YjP)H+!hLW6s8S@Wv3pJoBweIZ>s4A3B8a{c4Iw?ah@Kmu}>B;GZHWRq+i@@c%3vL*sPpVrAFc0rkLq08L-H7HvsnBWFZ$JkwYG{;TJjN%t$ZgD&Wv#J`CBu1bN~ z_!(L4hVi-qOilUms(Lu_IYmbADh-4Yqyr+j?@UAh;?YNfxryRIj+FRdy>gfW-M7ty zvI54^A=$zatvTlelWfZwm<6bfl?t4{ngl`y-@4*4$q~Q2-}kjX^2vN8i8D$7zFMV` ze4gvm87)>|K;!%49}7WQOUCoB-DjB`m4Y#`I5TS4c=A#kSH{h9aWD>mWPjw!QtK`f zEPY^#!2xL^t^-FOq_j{jHyrw>?0^^Wh!GyD46%g7`kmLbR|^C9B*eyZN|Oe(PLMJq z6IX>#MGmLvT`Iq+yFULGIVPITRWVYrp%{5}BJ%B57$<$xprqqTb1O9xxe*N&@XY>5g@6GE+e~zvLXgo*| z4_3hcHmX?1;~{Q%co1#Q0iO+MyhYJQ%ek1tX&WT`8cnyS9FIQ4W6=g~H^h4c5(52H z;j{S7Qr%Hy15VwO!bmUSmcl@?nHem1dgYDV2K+US zIdiD()=J`Zg)0mZZiRkPy`oD-`o_=7w~~Ab(BlpNw7Il`le&in8$Ci&;|Ak_5+X7g)EYfEr5L=v9c+_f=ijM z@WS|rpaQG-pwbj(0ciH5iMv~}fl?{CVjwT)6c`mIw8adTf%xyn`ey>9Ic4pd} zk~yg5dSs4(@0v1R4)U;Om(QBr!p27v71sa9KtU^HzsKSq8kVhZ4t``>mg^jd%VW zpKQ14t}vT1wt1Ndj+?S--yfSdu2>i!Z8<75<$nBpY3cb}Z_+#T#PT?UlqCNn>C9QS zr_=qIGss&74W;Q#oyfn1>9B)SKj@G!L<0X$Pz)_qc-$!c{i!A@vWe3g*)n;28Y$07 z4&a6HD#>RPWM6B>KT88KP^h$pw74y1Nis8Jsz!sBX{?4RAx_;uWHXTn;G)MIBd1zr z5YLq|lA{nDw^D7zCeHRuUVAl(v6&z+PqBR?WD}=glhXO=b}OY*r43W>JM9`%C+|#7 zeWFjXiDe`@r{Zi-Vm_z?M@eDl0>uT;Eisk;SM0YzY;2-zcd0XS(c;dz$-E70G$c$- zo22WgoHgm7`_ce)&e8M@FZxK4w9A{*SCLipC<`{3%q~NQHL6SOF@u$F@B|Y~#`AHY z(^R~pMkZIqB0F~&8*g5oSaqIl(KE9w$6j;WMa0z7uwxeWR1U+Jb50Y)aoe44{rS_; z&joM{mOAeF^4L#y#yQ?usNn3mnLc^I$&DmRaNMAwD=ir17U0{}e5C4}a?vy8U1`4I zH(&9=ZhbLc#h4HBsjPUU(#lfT@U-!0v{d*A zBASag{-;wF+nJlurKvY~3%huWtNZUeNO~D?huFwoQ!H{2Z3v3Wd&ui_C^Ki`y-*hF zd|mcTYKLGN%Y1UHbM?`Pb5RfP5N}UQs=;Ox*w!(7r>pA2E8#vtQc7nUuwr`)M%mgM z2a}R~4|qLw9wb^idj$*EXSv99oT0M34jB*(nykp>I%@|?e)_Xq2n+Z{Q4VE|{0tWi z<2X!bg@&9LH}@7na+F#V91MALPxj_-gPVvpr#AK1xv--4bFTh=u6|J{4@sw}bIv9H z3xOY9K-zgV>bSa(RiD|G;3^`AT5fKwhKvL;=K@F#0g)`NnyZ#{oo1i!%`Azzr7_9# z8g`pU643oe3dfhrbb&h4e6Y#q3uvbUBq6kO*<7w$S#M7?)#{AaYO1qIvvb*N#pm0Q ztrsOCo9eC}b=6VjNvk&Qn9%xsK6cM3o{cN+VFAD^MQNzv?QjQ4Fj3lbN20FUU3aIG z`AzDFB(YBej&7*5o6e*?NvBm4ccRrhOO+en|I}}=NT1C=f2sG@uk~BMVNxE8=L4p> zun5Rj4X!Ba?ZaKg;rwP8MFGGkUf@lT#6o=WO!=dV{q_n{E60dLCIcMUnD-`+Jjqdp zNoN;qA_H1dX%=)>*pBm*;ZEx-kZWp_6E|^{{^o!wz4Pdie0-!v98=lFn^EXzAcE!fn?bUDYEa zI--(0uD<1M)}-(2xvFVBZ0R#O#FAXZyIfu)id}&cFClWdhUf2B-^J1Sce{!-e9MoL zJ;~yo7g5X}@(-(QPvp8j%8A)xY1J6D*shSz$-8a8=!=1Yah4qFpK@*>X zUVMM@$(hrGswYg-ds7fZt9vqqc{=m`{fleC3)#W_-?Kmc)<0K&=2Q7oH_XXb|4fVE ztIWE_4ufRjr-9t7)LN>33Y-NXRhtQfPb&-S`T<6O6+Ynke-j#0=INJZ@ z0=h^IR?DRU1zb2aLkyg2JM zbIcjcMW<;%O(569(1))cHnN_`_vCpfjlHnPRhMBu%V?bti@lIiN{pV-SY#4ss!CaxbWOS^|+(cHWj zzrIbl0Vlz|Wq_~ze3kt}@v#{f#E?&MOe!;wi}Vx^oD$Iu@MvfKRA8cd^vtB7d>BSG z&zv2G(am!bhX!#cdr_o+qA5?Vj`T2Y^6R(tuOKS;>2B@-xzF{-BUIpV$%9eY)TfxwWETJ*zR`mzIdf} zYHMkHA~y8IyND_zz#o;OO|ldWN>drOJ-%SOU_vYry+V(!4x~cEQg6jj7ZTFKhU{LB-IjimoBFHqdQsE)^!g~|v2UlC~7Jp_8ox=V9DE~}dxX{~m0*UU5lbwo_vzSGzhP^wwo=RusSh)^|eR5p8T?D2W z2o#f0p4^*D0G%Px2NMCZ+MA{Z^IWg<0$`M|VWv!& z*g!h>anJFFF!7;m;gj)b*-H{5`BJB9tQ*_vNZ|ZB7Q1E1H#fuq1&mD8WRhVrnBUlS z7gw8?0^x?5?`CrXDW;&*-0-X_bq#}T%y5&sd=EToqD6e(mfk13aID99rKLFg=2VRy zkqGoXc4kEcJ&y^O94J2SHPIY>-_thXbkwn+RVNv{TJs{6E02hPW20k>=gaZ}(iy7f z$T6i1Cq>tXq3hEf8D}m#J>>*(B4%LE!D{y(>K^fa(Dr1gigg2oy=4fR*N8=54Zr;6 zZuMD?(mb<`o{j%uRhuXJ^aMU(j$hOL`DNop?Wudhe_s4w)K%#X2TH`c)R{*1<~yxG znDx$493XiYa^=!_78xz6K=bLya}lqV^G-8_2#!-)4~7BA^^qWlcsHqqS|kMf$;3V8 z^T3Joahg|d-#qo_4-du5f-4FagjCtWB|^`pR7QZ$rdB5lUR^k!Dqb8Vmbd{T^s-xp zxAt@UC6deImW3n-!66E{IFy0{9?wVj10{e?eyC->!YWBm36J{Youw?ROQgvy@kxnC zy$A6(P!iZ(8md#OjIjufBZZxQ&3JU)O57XKL978!WfutrqA!<%EN>hTZGG5jWVdSz zUYvnf-rm@(;=+U)s>jo4v@|Fij#OtfD4aBcRr>g`c)aF{sv0ZyB8PRBRBV=)# zbn_{P+?l$v0HCQUU{J2=Dm&l%=8m_Pxp%vNA)Be2pj<ze5;o3=+EI~WdtTnT- zWJHQj@zjJ`{2>jk?Y)Eq!=s01;0V^;%`vU>y9^waNnl|!GGBDDbx0&Zux(h}EK%No z6{l>=rR~<sNYjXA>QvbU6_CuGaTSKeJJ#B!Iv@MBUF)-Mz>AYidf`({=GLtPyvN_{ z&)8iw?Re=N_~cWx3mYwg$EuE~5zSrLhS#3kt?oT{)%-q9{k?=2gsH_t{ATO{TBQ(i zXWRj!aM@XP$jCpn&j`unto(uto$L1a&)U?0SN5iKk}*Aig$DM{O`{_ z98QzFOq>@ZhBIz_Dc#iF(lfp~d%wzwdRy;P^u=?_szN)CKY*{*|2pnP4%f_1{ti0| z4s}s}`Iz%W6;zRA-5ZSR;`?+g)lg83EyKL3 zduia@NcynT)dM45`nuTRPf}yJc|B~FkW5(00HC>j$~P>Xp$*F{&&=yVyV5C=^aQ=+ zJw|&b31IjTsGxzC?zM~Z7S=uNqSO8JddlhCH$1r_Q%yjAcDd)&ym)V_nF_2-rWaU< zGtDgwo)`?vzEbz*&pueD6x5_HlUK`}GZzpi4!b4{E1iXszr!0`a%Iy8J=pj{BLB;batk47`&gDkgNr&h)aZf6aN@( z!6RZgBv{;5r&1|v6ByudVFMPiS6V4@rYX+{iHENEG3hhbnlb(R^_J)49Ljeb(?1Y3 z+I#HlobpScykt@9eT1+GnDH|L(Bgw+tYfZY8g}iDR;--s{3`*rLPLSC3xhbv&_RIs zFMw+%7Y-zp|1a_r@x@C9yzy}wN$U7MdmHC^6vQTkdZlwr)mw)&%&mO~5>vF+bT+up z{E?*` zlfX2}Ae(SaO1J_D|LiK{<0r8~XuBbUYshScdM(`JbA|s(vj}?gxjqd zh~qTq$if{(tl%4b$HBnY)V<83oAQQ7Z=*)Et_^3$9IlolZVa0a-o4t^mLQpEKpSnB zo1u`C)!Y(9jVu7A2g*#dHVMxlh#+l+DVPb~2xa@_vo|7lu?@ounMj2TZqTzd-$^nr z@D5G9PM6@_#~f@Lw@7KI8Vrk1DTV)4Zl|1#EpW13T)5V}C&27M5wv@UaJ&Su1_PHD ze6IKebH*?7&>~_wd)Mc#bU4YqfR5w{i`75tp5Rf6lnr|N^(O0|NgI2xiDM0Wx=hd6 z%hLkzBgUroDd`aqJC((|)EUoQ2FuVtfrN;Pos<{SIkROWO{wrbcBLzWi>ize$~6s& z61g5Rl$sKW@RXq{530)f1MJmeCV&5uRa1MQk0YDq&ohrXk2-oi-@17rb6`7Z@NC_H zOFQiQK+^X{(~m5u3m@KHI596(@sh2EWS|46{{=&Bkk2Bq+#$Np$05 zdNlUQdOls z9?W~kA)GFFBS}1b0ZcQM^XtP*PN0_hL<`X%UJ|cg(J+vfh-L=a6IEa%iHT!L-or=9 z;ln8kBWz`<5Pdp-mFUj42_KRcrK;4 z7=?3O73P*p#@4mF5TzG2AGOCCa`zXQ0t?2_bn$` zlA0Oo)C+kWh91Y^mIFmSI-2q11xqIhkQT!5o-UM60>RV|gQx*b?c_wh^oO0uJUmaL zvv?;hy?N47d1bU~w&oG9Sd1E}GIs3F_)-+-bFBx+6TV3&^2dC~#U4Q`g|ZWoj=f?k zH7KGnDAa%l;mso>H0C{LPD`%E_Hole8oaQlTc~FT8ED-hxupbotih28Zvu9SBVBV; zhSI~cG%)MPA^>D_>h_~D6BIgo*mtvz_eu;8Pf0)O2!GIXSVMf!k^^NY$o3y#^!6gH>@1o_PT;+iSDgOY%Z~SehG%5MNxbs`$C*S(GyF)+5(u`sO^|nMF z&^w!4&-nep(q^pBht$nhlK7XiPFQwS(A36=9rD$bXd|0IS6+bj7KJVi^b;^ECh-Kg zkd3r)92+MeVnmR%X);~vCtY$dFJ6=G_5gcLs%g#mLHNHBxe)VceY(PKAnO2DQ=5Zr)>jL(T$VN($Hd_%O zBvN5SqNdM`#+8|p2>%+n9ZRg77T=B*8Rx1Vpd}=yzbJcdQPTbH^qwvaxFToqfkV$A z`=nw(W0~C5#DJ4m*4CaoHD&KK)mKF=HRc5rCR=j;tqi!k6Hp^DTdGiaORlKp*N!nL zknz3sZJT%H-nXd)-jDBZ4rsb>`M%x$&V6C|mKtG8gFJIAbX7EFD zwNPHy4yIk|PU|-3sU+jkP=np1z$dnMx~JuR<2XT?J?c@XEWMqY;Zq%^?@eO4B;_ix zj5<$2m{XRAxh7Je6Ce31aG)&UsNYuMxShho(J8Mm(jc<2T8$=0F$uC8eOMeg+}4C` zl{Y0Mvp~HiH|_*#D)=WsA$8;2TO@K~^T4m5l9dmdTR|y1?`6)G1_qzW&Gw7Jk~l~Z zfgpL){$UH}651zB&x7~ISUj3^gIz7@M2m5B(Kn2 zc(pGHQlx2PXONa-j&Ky@B)V_#TewozBv{19#1YmG6W2B_hTKuV5drOcY+ zWRK{pg+~#pn=2VpiLQn1y~_w7q%^Xyb35mOaPi zHiDUTwG4m#Fbvz6It<-9_cXHbb(bQ*QkQPDMeeG0S)S4+3n z1xLTRftm#;K^a@YK~ZWX*~A3S3Xe^|O&S@+OY^IqG+9f@(hUKVT6qgv3Fu2$ zPL)O$Hn5)YoGCOY)PQ$;&b=gSA`a~O-32o^ja?)@9^2> zRHaT7Yd^qxb5GsM0US=#0Sx&L!PGREQo9YPU%schpsFvj|7oeul#^CuE(Y1ZCBO+Nlo;;`y*oGC?5{u!STflcO?20^ zRPIcW3VL4b0?jr~e&l)C?qorJRUfV{UQr=ItS(+z;r!RK1!dKhZ6(7*7ZKT;uC{bVA^Z{I$A&)aeEj92ov z9ZZlLGiw6?>`R7VTP$c4wEd7Zk7xmB)y+oh8x>YL)5ps+i zhcP1-d_`_HUb)UNo@S`ppUB-a7&sJx8BFr=dfllOMjz%*2i6u*B=maEB&c5I8hq8J0|4V>2v<*j=X08~idCO$4=;T27 z$0Oq$lpj=BHVLZmcX??iXXwV)c1?;slZ~{%TGi^NKN#sSu02stN~jM_zy0B1gC^9# zd+b5O#ZSX+rb@v4L0PP!8K*T9-!)T=$pnV{4`1Hm`)Dk2 z+`n=Hy>LC|r`!men43|;{(BuN=!!FNq{IN5_s5>0@n!d-%N-<^kRJ=rbX6(RUIST^ zQMVlDSf=SLuD(9+y4Nj=H7%l87MjsY29dAm(8YyJqq{a8?O8@!(Mv%rV}`~S7mW*d zfudbfDy7*3{a;nQ{)x$LKVKMNUav-|_nDsH&4lbC0Jg#em4zG=8LXb;DZlH{)XfP% z3D4e{pgv4vCK>6?ee93OoWPbRFxD{i#B3F z=8|vHXMk1u=oBEtYRgIZs(WhRI_I7$LD%=i12cm5h#jWOYUu1squks$-=v&?{HEBV zQUHPUH9^MWtuig*O`%7~>?yDWI}sH#IL`|aJcbUB<&;DK==TCihr7;3Asmv{^Op*|q9_X_-CQnIn+nu}X zZ?Y8Zy&ii#F!tr|{QITW*FJYopw9CYY%4Fbmrcvg7eHUm2)=q}%Xe?uv--))$4_i* zc50pOy?m-CUm5de^+``xlU{Jt+s_l#zvJlhh1A{?t#>=<;@4K>|E{QBTh;lynlk5J z;fQNYl$p}Ib@s1>(KUwwxkqo$ZKC8hHw(Ew6y}!vc~c9e2L90&`fFnRXG5oOmU8;p zU-`Eq*G4A)4y|yuLTE?WP!K#uCHEyXY=94#4#o!>W8%DmcWTa}difm``%I=g{tVesp(CT9@b9(>%Mt!6(ikB?kBy`NL= zvi+ArJtA_=d4Ql~7=)QyEeh*lm;lB|prmqf{|2a?q=sEOkR_nZErACB03)bftag^L z3K2N>NMRu#OdFDOdq92FMzZ08^PGxU>QpA@#RXseei%KwH3ibYF17B==o)B*ob1h? zlN_(VGUH8j{1v_fPM#7@R*&HhcIgPLmNT)SF>7ilArwwGO!d}fAnJunFtD$%%6h^I~j-~3vY3+Iu^Za1< zM?MKeYmwCb!}(h~f5g0xg#UWIs$}LQ`LsYG5T{Q`E3uQvOB)_WtQQaX>g#syj)gq_ zb=98{VBBfEz{y>YB6VFECEx9afd#2F2uhR2?_sKf14Tqc)1Uoxi=7&nQ$tcfez~%A z5Oqm><%UT8)*U_G+hU{vkgz$0E2Pvf3$!Z?nd$746^cEL9mO zsC5J;fqlsYL!_b`1s%FUkg~Rk#|}RZ;>PB5Z@=Z};SAoeyTHlk?7RcWH{VF93a@{4 z3kqPAMEO{>)L2c$-fOFjNOE0bx0G#cXCK>WFU%$#dxpL|uJR%?v=@0h@auEU&ALyI zb%x%DzR*8HhZAUiPz{R`+CMSB;L>#{FTjH}_`Vg`LC@&?)6P zN-DI(%c)(1?BSjuO6}#Da}VFZkbb22AV#eUoR7%6^*VHO*S9Q&FM`)I4JC6#EVvw{ zbdVH@sn(=dTyeJ=1f|B@#=v<2&1x0MK`V<;q=u}EM5C7H!^DbACjR5yb{8<7^&YL> zVI#MiSTD8n^{4nOkXP2>#>0c9yoSjhB>Ry)Q?}z*waPuC)^E+fg}oKW9oK(^wNVD= z(|ij>yC*mDaR8C>zQCb=f1utqT00=I{_d}x=7&<0_hI75m=A3)Y-2t?c$fV+v+Mm+ zf%NXx%7<^a{$gT7`;jMg_XY&6>s~5k>UCAmHE1e|IhO=Cri1UQ*F)C~gzYIyf6Sc?>)Xx5 z|JNq4dowy7bs>*Ym%>OR9mfdHmK0=AJJFfGH&b?c*W_`{B`3@h=~EC~BH$ zZY(aQ47aQxa{LgjgyHSd9ouUltoULDvHT48LR;RKLr=f-3}yN`iUSM7z=>YFYz{R% z%xt)LrL~6z+_?s%rz9&fJb^2%9?qtmM_E3ZiOIoHH&m+Z;>qDO;k!{Z$bW`2Ewbw4B9T2auSfQk3j-!NUE-VyI}nve|=N(_+bZ z7~O|qyaGhzWe`a4g4PQyArz3nYeoT-lEa`u!kpEgjYL7(q%12z5cA7U1_+Qp7ARGC z;+mhCu4!KUOY@Jg@4TLDEKOPLg`^Uc(t_Sine2249Fr60n2t{uXIf`?x^i#=zHr1E zV|*E4c23G4owYcT99r_ES=#EeIgoW}wibojHqYt=3TpQGy zS)B0&pKdT#B-!zi9ixxruG~3*W}5O66Ub{r7e#*B@D^e@YAV+O<^{NlbDk1uP8K;Y85Ytap`Dc#FBaw8u&S?*DJG4tuj_T;r~ulB2O! z7@&>f_k}J}@$wNpiTb5#z^l~cN#6I)+_y|&N*yaeZ#sYaTDxm=^9LVQfy8HQ6i}4r zkoNEtMv~bZiAm~V3}`^o$s(=Ux^xvdCOiG`Gh^`1gLU#vA{2l6Loz;FZQxEgw$bTj;-M8`iuWxZ?8c!<_q3cIAx| zZSUP5)8~8KY1E9kAYKWblh@q8o!=TIaekl`9Wy7J77lga82NHsS@u7<@C%>YyHJz8 zVAHc^&!zxIn7Mb%d6(Q7mk9Z_m!OB$U;I{hR!`V0&Ckg8leOxv_MiXBo+FVLEi3L^ zdt1Q+grzwgUgwvmsU(8N@?zlvE(pUu&{SQiDsbUy2uZ^}5ADg=ShhQ96?n`Gf?c37 zuVVYL=2mN}VjpXm;%-RtO_H)6if+FM&o|eKg|gnpvm-A=!I-8$-rK3z)R$&{@C?Zg z_KpQz;7N7edJP2fio<63rI4#!#lgdgJ?WxOil*(~YeZ5Mw( z`uw}B4q(ldhT;MHZ0p^RHbCN`;CHBRze4^je*VLaN9u=kVQ4%P z%cv+EuocjWx;KJ}LfWxA5f6t{~YQFF|ZxtUb{+)2Jd!kqmJW>Esz z^$u%e?w_R`J}Lnvl!bbNMYkHDwGaZMyXeMvKp~Vxm2i;y0@n##z!RAhi5q^la`tzu z`V&~^s*#txnPNOZ459T)!aFFfi*6vGAquZcIQ2guDlM*=xy*8694~(~Y1D3+D+kax z#e#*KJ=nr)B`PRFQo;BtR>0ei-*1Xr)huaRRH$tk39SKQDLnl>Tzdhax;*|@G#rA? zG)xv62KY9W6%Jv{{YPX}#r83V?kW{Ly}Qhg^O{1C5Zn|&dJn%`bsTByf2nsA);iF= z;V{98Fh7iiVDT-&#xgQOJjuF8pl45fnj-E%)4}J1>iamu^LXN>*0imlP9DOeAnpws z{{~*0pTap!7GCTTY5*a*Nua@2LG>@$$S*jKOQKvgqW`T)Io`&j@`dXJyQJPU()ml= zqdkGXy?R|0X6I>1k2W3?m7{LWvm63!$T@ZzuU^eT{CrcvcZ_Zw=}1RONK1)`qZ{cGaFm3C zQ;`t#GdctXq(vNpl%Uw4e%w93-|M>X`yb#J;5fGPb)JtW6+$2?oPDmuj8PU>5knM1 zPezbQb@RIH$o!QR8zEpKR6fc&IM(vT}7umz|eK69hn(Xt+N)Pqf zsrp|WUa%1j{^i8NO0b15Rix7cxXjYuZdSHQTId%CemG|qVxj1yDl5%J@BSEUN}B;Gh5Z{Fxd(Dh=GZjUf088@@%WGdP zU5u^X3}M>7lF5$N>_qD)C8!&ngM?1$UB|N@qx4M(sKo)aKDFV93kPw+joI+Nw&cCL z!JI{5|FD6IkHW={o@M2>yMndjTd)&}R%($kzi`B>wBXQ`jHntwIbw$N7aY%V^L;xf zYC?B|LBewb{KrH`JEvYIZc+S#lb4gTb*ZzOtx8U{ee9^+^-_)R zYJ^i|-dCexy+F0uS?|+b;tJ|M0|$0{Oh7tSJyJYKm;`MjVA?PYf4E&-+fCOOY6RZfBhG=9>i*BwL)2n?j8!C^>m@zG z{+TaI(Ew`?6%8_8oChyHKe!kX!#wd;%u(${@?u0&Xh^iJB4S7}${)i|g~kz>gR!Ws zBo-#xyf^}2r=MnYQP?`Y1V&lCXWyzwmxo8DgPg;eqW~TAXLvUaRXGiUcdX#W?wFlu zTvHRXm9vJokzX$s1`rslVNnZ%wk2Met>G{^5`X>T8wh9u)>cjZR zEn~zmHr>m_YRkml*Yu*j(@dk29296fcb0{)xlXcqtYPau@=)ws1g%@RNrodUcTn-& zXs$%49FZ?(AmVl@@Duo!njFMlZk{ttRFf3d&ti0Zsb)&zSMc(crl9A&KnaG7>!$z{ zyijKsHP;t5OMy-0i>M}@ed;5OraJq6KPx|CQQ!-37^P_OGU)QA@?g`fk5m0=CTT9b zp^o8#1blj6g);?`@db)2_H(Iv=i&_pf_#`AiDAF2!g(ms2kBODDj-L^tQRZJhz$!E z^JF0SXm9(hr~6W1Y!c>v{M%k)Qs@Wf7XwBw8rjKAnuIKmhL~)N7f1+_0MF3Sd_4wV zlXU-I8kQj5;$}79gX&i%7Ljon#@U40O?Da5)fPy{JLQZq4|(ioxY* z6~4w|&iG^4jkxg)1u=kyz>j#=X;L25OhY*SvUUd|{dGlQDoD@HCPG83byucC zA>h`O482uAf@YJpaWwu|6M0dgw;QbMEQ{K%uBB>?I<%`(nMzT&+8W%|fg-99bU@8S zWA3x)Xu!_~m!($qpiU(*kTz8asAEY43E_V6J)MxXPyz8jGeA>h=^bDuMXoY$7{`E6 zBq)f1o&4VUtbtKy15;UuKzH=b?rU!RM7JJTeb0Nw-qNdr6|Z{lUk2*0IygiF`}-XQ z`9K?>=}mx_oj>(GUOxX4=Bq@@?<%Vwr$>KVlZA}M<7|fG-*>0_fCQ_0a;;q@-F7mG zf?FUaw&yU?^39%`(eHgolSzd2YzjC2+iEb46%#j8mZP#-of2CYJ~g8Lr7??Z~^ zz3@WX51`8oIqU~`J-2Nn^5)Dot|bYInAJ%>q;>GF8FH@T{vq7`XVJ}6-e%PL(P)_N zSZx2;w8Y&0hDe(Gv#hmo5n!UUdg4OvKfVV=mIZH;r&x{Z^#?I5F<6*ACC;WRxbE$_ z6P+@>^V|ZQYvJwE@c!FW+sQKdndF=q7S>q?n5}4nPhuUW%b?z!1}(vtgi9@GY90@5 zqH30s9$s^vfd_90LzDDFSZ8kSINqFI;07=H)hvbxEgkwSIgysUs7v?sUS(Vp%>H+| z_}>>Jc`F|~*FVU=A=Cq*kkaXi(zzR|Uumxj&@1e^-3kdGKdR<$4oB=bhe=?JQL;C7 zRl%lcEI0o{J+_>^>JMW2gV1F)=6~P%ea`<@1!Z5~U}-yZ?Jd2a`g-n3S*eOpvG|g_ z^0|Pi{7va}QC}|sET-)2?-CCTaVCjBNPX+Y;FiXjcM%rUi(Uc%h`q{-UqLi9eug!Q zO@oK@a}9%#?l|umhluwo@rP}G6;hrP)VeJEZZ?01k;IiCyqi|LYZ?p6U*Cg!@7?rN zzx5}pA#vYM@y+4VUGCo&YRSjBAH#}2f>Ht2K>~9RiFt;a`su0?J{9mrrmh0exf?*+ z407{D*dHX53>me6goyv?VMT(z(GDQS1%nEqsPQBblNkjG17veUorHpzK&7{h0n@Cb zR*v0*>7NK6gA408-ls(P{kpc{x6t_{u2iX)W={mDMCKv_H@>yqg6%lBCoOB+~(IWqbIk++e4bV3?iR#+8EnvIk z*X*dHJK^wdzYur#5Tiwp({<6V|_0RY#=nNtHqc<0xQ5 z;rVKtgimpFD-}910FPq8Zx$+Yxe7G|G8HtGIC(l^K>+lMPfmYfX;=1O^1TgrUD|7|y(lZ&dOMLQd;bpqtNlCo6#(&eqt)#9o%7k6{+D^2GF*JC zM>>lD_|$ziK@n(%ymig}zz?_8P0Lp63C^cK_J3Wx^zq5HRhQyE9kWpdQo}$_*pr`g z!}nHbQG8HGZyRbj`MwYY>K8;~A^*c}$mVLNY#ZP!Xb^WpbbY_tl+wS-CA?x4St>Pqa#>L2SN zDdS;pQgig;f+63|_fTo50!_FrP4%ypy5V5WUV?+Yb80LAXP%asboG_{gYX~7dB#;K zb2$GnUoCsLfSy;+K!hp}Ox65+&U!a;;y@!wfZj@wcsHyW(1QdRHo_eoBfe1`@@Z#W zcaJal()m-mlPdQX%>4LOBJkk=RbZy!)PsC_xv?9ewbZ@yr|4w&j)8XZhc~~7tR7KG zqi7j`l5a&#Qw#>N22hPyusqTWCJa!0sUQgO|NkF&_6OT z|I7u>0&oGMKPR(|x8NM6_ESM~3bYd}gp=l?p9g2iFB=NHA|?YX=78mIZ#H+{5nZkb zRsB42>V2&X+>WGXxLJ7ekq*H4?3AhOI(st8{Y`DfaLOOM=aB|76!hSXRt>3+>51#K zl^iZFrqfv17oP?449~YK8RAbNkLn59m9+)<2=DWzvE;`9r|~|*UuhO&sxfvY0FHv* zuc$>4$VH>RKEiwWQ49gs_mqPKkjZ9lEQGYHsbad({SBV(-mu7VQ}%W0n4+toG#RT33pd}8n7JS`06*Y88wc$?UF9Sp&_WOi(7cobVak+pXq z_2}^ajq5C1m}kn6db4D^vbvDh+?ZQ{A>K%z+f+EtYrE z8xvd(g35(C!It78d=r-FcYu^dy4roqNqs(4VRmpm%|>|N9LrQxz}&KnQ);uzkLPL> zlWbL~W2{zgjA~j6Xyv_;?O>*jDk^G}0En{lIp{~(^c#M5dt90Z1KB2kUUJf>mz*3w zMBd$Ngau<5bsziP_(V+S>95PzLi+I2<6q1tGcZYI4oyqSsB}6N7UC|300>z6SUgW_ zYJmUc3@F@&$a;Y^z+6_4#wN$hccG;SvK8#<%ci(ISR|Utpg5qF8Nq5z5=VmtLjup; zTy5|@BUHAVEJmt>Jya!vR}iCY-WbSxFACo*aY(R#b`1uN?HSSg(4roZ|L5S7PzJBJ z_7^UIzrTS^(aNe4jC=9+^<_bF9$avXW(E+M24D;=&4zv^fPnob0-na`&HW5Nb{3-f zhV^6IO*cYhfDck1&!9t6Y-7m82#jWeVxvp!BIArXJadZAG$H z_2JFK1ApV2UZ0B2KS~e-ZpaJRR@ube&$}j?o*$ba%J8g$a9iW`9_?I4JV*jFChhLpvaI`Q{-O*;>gj#hnk>GmvrDbx^lT9i z0n*4w?@~6BHc$TSoZcuHWNf?-Dl>aiQ zhNfo|2Xt6iSxioAGXVIQhn(&>>i<4|VEe~)Ii$8zG?N)!`{8)6`KE2)LCD+PS$pw0 zprSKC-ZouXW^IM_AEyaXk76Sc?b)2Onh(8%_jAn?-g3usF)IBz$`l}OT2!7)NVe}= zU08jm<9~ebosGW+0;>US8D!`sLutK9rhuy@2_CY}vz$WBTuf27DoB1NLEvVQFP=R8 zB!ucqQ)H;QP0=xmDEzqY!|RD>dyGEs=2Sj`Ue6rZK`Va8_xUb|o#OUi6NfKz0hlz$-GuW2iqM%91Kw0)3&8YP z$D$<_jLf(^4&2Gi;OXIC-b}$tr$RHY(Nr|EHG5*3E3&5oGM+D+-m?&YFmC$oOZKj+ zI7KsW2%l?Sk*Zyg8)=cT9+!U=4Cvyt2s?5t;bJ;e17oCDq$)tb$3V)FbgdL#4lc&A zCP@PiDLDCA;IAGUZu?q|Zp5=>W{Owspc+2IRuz5v9O7J;fma1A#G#oVt!291qLVmLaD%UcL#vSc1eu|wAJSSukP%lb0_z+ZD3Id}49_FxMP6v~ z7Zw7EMDwD;YpMY$<^?Gj9GGsL%20B{PgVRiK>^5#6B9T99ssID){b!K_g1-v2P}g}fWaWB zQ1n0WDJEKeqxOg~o|VMfTEH?)zG>yc&+ZGMv!-L>2xFvxnzOpSq5_ zYc35N#kurq2Ri|nUO~O!t9j`{f>)84woO=(O%AS2FwN>r=WM;lO<1OUN)>S!MdNqN>U8|G(b;%t|u7+7$RtOkpZLFq!M>I z+Zha|GVlCXHw_OI^$O{wFsPGx6$+8evq*LhK5;wLsX|nWx#puLR%=er?8syoxdPja% zyp@7a#@^tzGWqiv;Io50TsZSrNyT-%xZlDBM~t+qwJ*1|ukD+_!yi;WZv98HG9cq}F8Y43;(a&!`VJ|plcy3_4K7Mx*~w6A3d9uDL7U~mad4-E|A!!*0sf)i#m&IyC^7$|frh(Y zCSCcz8YpcZfa%l=Z*qu!C=~g%A=(~f1yHoVDY7q;5k$!@oG7>X26WVrJ@S)a@-tx+ z$^}fwJR-5)B&R4C^7+`I{R`2TX3_Fqz2^#hzg*_I647xr8Tu9@@?wLB_zCq|sp~kL zF@uxyXch)?1k3CLcwoi`VU2g7*P5bbGm})IjUFaRvorG0%3<5=krwA&m)a) zTcfvjo>&4;CG5E~`nWYXx-YwRFNSxs7j$2J)qVZFT}=|M~i?JCV5uo{RqZ?C~`tu~U&^ zGWKX%<)&7?l%`&!xbhVMP5~EK2s!NJtuJv6=wGv`y9-WOsXdcx)TVo-wNoH{mSIobNv{MojudTc{ti|GbIg&rqAu$^4k#@erTswl z3p4Rk#T2%ZTju(wd7G!_1u?O}5w^#C zK#*lxMM*^k)H93mQ0D%ZmB&L=RBul5b?;96gioY+wz86Fjk0NRiIT@lsiC=%&$Cup zFDL3>s{i{ED>QGUH*exGZ+30oqGta4oVfgkM9de3oWXfVF$pODnOhMGEt?_shx1PD z^PaY63capfE?!^|T3mv2`nE5KVb~6d?E4s~hX=m^XUC=2i(wuM{Oz-rO0zT`M>Q~R zDU@~T&_?Nk$8uvKhdVh=Jw*kUx17x?9r#35yk5%Kq1KZL7NCG3`Y5W2((@xR+|gO8xXo%9G`+OUsR-y3+-rc4;7VDboW^ z{pAL|wN3hNy~fxOgMwn*ttU&zDXRrSEAB7XUR=}8)e~=e**|-zShop@L-9SHg*Vi^ z+7qjPcWoWD$#=WG<-CMh7_V+^uS$myTPOwQe#BrEZuma$Rg2P6e+a(<)R_Pjx!YLV?8<``1@u{OWBSLhbeS6g#d(ibo>l9X8=HTk;U4^Z%X-`t!m_{@hpJ zbA@&Ay`wNj+_|uV2`C^4A>rDwcMLb9YyeqB40z#4hdJDGpA6`%b8G#vzE;aIVchVW z#OPNy>s#um{mz1U#v$ktVA!;E=5YIz_-<-y z2ufc69MA>Gp79M?Je=m9UQm{WcS5el+*p<=!RZ0p*S6*A0=)>HPq)MQ;ZdKOx6fbS ze9s-t6l3+X`2oY_2*pCtG9t_D3>tkZM_l70c!g;O38mS)za=Vv5Afuq2_8ocepIxb zcU%#V%mhv0Se%Y(qwa?N-VUqzSLN0s!x;?QM-1|24cPUZsY$OmGYQ_m9Y~5nj%`A+ z!`XYnhvM6B+~|suX%1qhL|*(9X%GLxnyOrS{iM=>;pUr@<3M)OnV;2WKkL+i=377S zoWZu=i;`}Re0V+ZQP_9Zv9I&WvLz28?gWVaM^5GZMboE;@)5D?RadsgD!tg1F4le& zrnlh2t=Mb>1rgW22<4O&uzfmW78sxtu<)xL!t>le9UuH>Hah+I(fcK`&oe8Xn=yyU zU16KgNAOExo=+0~1*2`26}?Atg^X*fZ=R`A)ahELCS7FSUlW%3p0w-jodkZkx^TxF z`KF~e@UauPGw&Ob$Q_&#CHKkMddvz0uZP_kFVNzM{7iyA zozVZCEl!q6MFphYR1YQ`-5*yM1GGeGC2o5Sd78={I`r&48Ov?}RWeDt*k&mTpcS_( z;r93`VMk%zEFw5DJe^lc7^yT?O6St64ejsy@tN8yrEBewibJ`c=I7?u?(xP^vLdo0 z%sFK=f49^#9BKLsMXt_T*U6B3p$x}c%wheXpI<$t_rYS$&P4bc)ZRiYFQH>%uWU-i zQo5;e&Pq;euE_e#fUl8_@^tRG4Alu0cD--mIS!IfyRbfRZhLYPs|KKXpOPz45GUZ)>Youy`%j?aEGGvB^y|^)z=# z3_t<+540L3>sgQuCh^_e7-E3g{fB5DdoQ-Ki|IJ81?Q8Z3hx{bKa#viaIZNMc;6OK z6to>~NObvX)B-YltAuvp-GB9H=|W1=SLzzdvu7hiytc>N*^8pNoA>MsuWuo@*cDK* zopa?okbC;cvTxw_LjTP%CtLsGI6Q%w$D0zBi8EE}=9hG*64qeDl+ip)g-=piDq%WQ zL<|yTzJs7+oNb2Uh=?rvOleMMzU|4h3OBO~xF*$I6mk@|!$K## zB@hA3;|X7$)1EwUcMGZ@bB{*<^6WcgGF^2uj^3QE@mRbLPn4`jY5w+1Ey6TML4K1n za{cAK^HVpYyWj$%dYAk!396>=Hu3kq zg30J!%C+18K|XtfjnJnMyqP&-CLDqrEEzr3e(X}O-mm`{)Oh-t-SK$74@UO6vOkL6@G^7-mqi$Jb`zI>{_knKAOn7^$i4Av@^> zr=5|eH%;~db@vEpU>bz!ly^S=%C4EJHd3l1rRJkeGE$m>11lX-{vl>b^iw zVUEHyj=!NM<;*webVAa_U}I)?PRncmu{$a3%k=im=r?fiRx+zPViVMg$b%8=ay%TG zD0sSOI$2Z(+4WN>l!kb&+t*l1OlziE+W3g6T!-T?s@&q64s`xfwIVU8-dAZVGhPtQ zxxu_#MoB>d*qEY;agf9();M2dMlKm5%t|yr3ZKF*8&|3sSC9oHhSEFPr~+^= zOi-=W`kJREht{JD5-G6pej)4Au8yTd&e-svMpNEar!MDxC>4_#rC?YB&Lr`?(aew| z6r;dMFYb553^iFh;oQyzEQ>|5&Rx-z?ongL{fR9zou77sevS`>VSC_FMK*scCpL>VoZpLW+wD|=;tKHa zdT?p5wX@mwFv#tfYt85RW@b`|jlb(l%GUQ-8X`#$Zclujfo|3pp#r~T2df?wUQht*(ac!XNH(EAl&n z9|wF?o{y3Wn`r~OgT(ePondy4ek^gOGEfCg^djph7a%bdSzJ=eiU#o~OAlp;h`tUY z`pACyFec2QT$pk9Zn^pi#D)R;x;mFb+FEPubk_rbeAh2GsuBWK53l(3>&-gj=P6iy zkWE}?s$|r_tVd5WTKz}AiFv+)dnk=4e?aMl2om_`3zV%<9(&CGrSlozXIfb$bRu6X z47a35hdK(qcihQHa|ux^cqSnJ;kCreo7>oC0}sGxCCH=GlicnSS68v+8u!E#8>c!Q zcrNAf^3%)e53a`&d3IGhzER*hSV|ISbEEzs*-RyinsIgN(b{TyY}1rrSp3qf^=&=i z@xpXytiadxHr?3ASI8e?I7i;l^m$8!XU5X3&7coSyAi)K!?QVe7r4jbMrr>ZWpc)j ztSrWT*JNYQx}LBkN>fUI)ZdoXu=cJ)|KypU{$-~&_aQk0&FLM#%Uahl9`cJUm!8nw!VZl|GI7JNu=Ne&=a*p*+kRCZd1dt$mTk_F_79C3(u@mq$Xsp7+0YfRWYd zeeuVl;JV(n;=RX+;aRqX7Gp$E7!L!{Ft4KzdTGdd3nKeR*n0>=;+{JQ zF91lVz*KwcfDF}OK=JQ8h&Z=4nQVDhGTEN~0`rPDl?i_onif#|r51keKou;DBvMdF z7pp$OuVceMJnwuHHBw=T@zU)~5&jlnu5%1RK$KC(gJCJ+wkRUh0wUZbfHr7JI)0nd zC-`&*%p!YMCB&4?nJYf4G7H?cU|RG0g&5_;#lXU>Lj*YVKW!vTfS6~4b_KI9VFeGl0SLDA_eL!!S;z6p00^Vxf+C;aSMFXE*-&@`Qa$k+~u@q9A?a zP8%mR=}iIjO2O?;Z+;GmfvSS1Ii#nNyXhVXFmxs3&<)T9;F`-|otgBI9`V#oWssKg z7q|M0n?m&fNQCZ;Rv0{ZRwmJ}*zYyS7GWe^Vl=cWEF7*n`A}EnWvy}n!x?;fR1br> zX7Wi2CwL|$3J)f0R%=jn9~DVZ2msS^pST602$QW@ZTxlxt}~3opCowr0-GrVe@u>r@*dp{BPn$^OSlZ|@@nMTw$=1Dshrsk|fhB?Dd7 zIbm@$hEPo~M^AcqMLO+$ili9F_A_BfT+;t%lctJ`gPywqZWZj`3WinPhs3GH3FGix zUKpBEs0?Ii;TY7`hOKjW{>rF$;IE8IzL`n}U#ft|TVw<-r=@EHoEGWNQ_Rk1sd-75 z`Iwmb`I`BsjxJ^ot2COa_m2kO%?LRd4PzO*#5;Dw0dd*HJlc0GKW;3pdMqAFygD`J z1Jb+qlJ7%SQehidwI!K$Ri`43s2mOt;f0Fe)6Z9_6^k?BXPC$Y7F@Vaz6J3KL=r0j z)ad54kl{G#EUs6SOe$N;13&<87&vE8QDRxuIdS~Ow5o+0jHUMv=)WhcCY;1G@J0@x zIFf^B|HWazW0Lt$PT2U1Am?$qBLS|AO}>at52lVtx?%_{!d|)XfFp1iE<+2G9*ARS z2~YnoVM^MEMD|QprGmEufd4^IEzsH0kjNtj_(9tFNa+Qnq5f{NT?O2fI!YQ$x8nuV zkAbZbV1NLOMonoQr3cP{iV~CtBy5IEY(^x2*9lYOxz!VQr_K-3Aybi-U?HlOXnk&!Fx&URr3*Z(h5k!cKOoh~Ho9|>+00p=x zoqh|KX2i*eqKL7G0`aFALM5KNkbR-CJ^uvbd9mlM(-r`n%^0gBuX$`}X9cSMzM z4PU=ne5|ZMO zse!yOFLJ8wQF{CC(*zRy5+OwdChXOjp=R0y>zPH7@{As_YK1tu)NuzJ8tLjSnKzx2 zUqArb9RE-{yb9^IvuvmIQer|3I9zZSYglp-jnWa(e%M?as0h}cTpiEG3QjtS6IU<)!A=(gTBhOf`3Cw7T7r&e@w$1(I zoy&7#%$#JxXlUT%xA1yMAhu?KW!m0B2B4#H9iJAO@)!voh<-$dy6R%|eV*RbY(8>^ zdPV951PzYGLIX21J%`ztDpId%rWWY&dU4q^2c(_bPZ9VJx*Fldslm7!rDx>N!NlUX ztWM$RTrHxzu1_%8N;1VN5nP{$C6CkO935V15Zt=c+K(|8crc z(c>DF1cv;$RPEN(?z;4BaNEZc};lu`DUT4SqhkTsszAN4R zudiuw@Z*UO^-S_L%VS&?5QhkSQg+mnwfelWz*h92_v%3iW3y8x`R;J5T7FwdpUZ^n zDInv#)i4*9?Ld(T#li!6jPG4>cbkMsDbD?+S;zS=fXeoJ@}Ew-|0d@=J6yXVKx-(r ztSn_DwO*;L=;ilVdDFhJ^az&z^U0gvdDK5O9{ufupr@~@j@D$wiOrUT);fN8Egzz* z@(-+#9S8L$Wc1rZxv6(B%(+5HHXaL*XEY-?6#vtKb<0k8JEFFZ{6IMlZeEeIol*=! zdZ%ID`q{O1wh94!!UhDx4@VmvUq|tbfNsme3&xsRnVQrB!O8oAhYRZ)md}8~v=H9z zoyzJ-FNAvjNC~EPn*$U~WZ)rk3nAUON%~Qw^mAM2(6qHKz2xo@J4-wuk0<`)n8_Y0 zHZ?2w+_0aO!E2@O9>emjKPMusd?YYG588^vV^ob708H}NOrOqjh0}a3%C=5@8%cTO zyD;JV{IzcpMii%C2jYFlZG_>eddIHBZQ7D*g@fW`?x4tZFf2rsnk<>=(Y*XNb|}wr zt?}$&`Z?Kqhe-nGHB%&yKmy0TakCOo`y4@*h5p~7kNAaBR&apO%*P&6h@2O_JiEYA3ZY-zvSY{aPTH*oech_|3f zaNksunk<2bJ#4>t<;vQ1wK-lYapb-jKV$JFGiAM?294FXmX=Zp-ctU_Qf-a5-SI%M zSbxZ4vE=$kB^O_c;IG? z86Vi%F|EnnfdzEJ;#3th18)=N3uRl2voH8Kl|O!br=Pza7ew?|eA=U(@uqP++a2gH zN@I4BxF6XHTHfr5EHXsM^s390w8>gpw0ZMFqIpgtBbc6O+xy9`@KDy66A~ns7b~9tkA;%=qj-o^Zbk$QW}i1d~kvIe~>R z^e!Zjm{-A{@hbABDpIDX>fl2af1`cdY4Y&1 zjYHpE4BbipKq&~_e=2bF{KGuC?ZcnY-m~COp>KDTX<>U-ANPyo4}!w>rQRQA^^afe z%NkNPK5LMgdGBn)y<53F+cS>J){Ni50KWi#S@0D010;{ib?sv=TUpBeLEb}{LP>(%C$io|IY;e z-jPq5$%nnIK<2I>=12Ao-R`kn9ybbI!pelZT9tr?TfBa+P-Q^MNwuKh@lJr)s{&lK0X0r{NyQFF4sJn{N*-p+A;EM}alCHbW{ z`xKwrN3Hao*h@c-OZ`vEVz1P{JT5Okscb%}%7`uPK8b(nav`j*`p1=#ly7-IC~-L_ z2P;3A%1>&>PHy@CTx5)|yZ-a4gM`DQ)tcGT&sS;L=N}sUc6-!zy+83f@?+4?gFUt~ z-uO!SpA*u*u6&B`47~c_h1P@opC#9?`qakz-ugAsV${t3Q$E+|qWQt&SgrdROKu{+ zg3kS-d^{PvarJgULZ89a%qk71_=Lwizwh2k7(V~gIrn!3-XdhnY*c^j3T2%2Avm%E zu0Hb@qlFCR6(yTrX&g)U9^O=Wx@{PRwoa&Wlb|^!#OWpEzy8;7_EDe4@6~8SjCx|3 z>D9qlyt(q9k@9kz_Mf|Bzqs@MtiDFRFOPbD?#|RwUO>|G=f9^MtDy_Nwo3`QUo_#~ zJ?SuP2A=daHpzCS(e~BwG=n1O3?fAd$c&tC)a9iSjxd8T1P>dYdL%lWi8^I}H{g@l zXf8LWneXzEsBwX?L6BVFXNk!Y+4B#A_fTTyIif20cLR^!jizaZEIMk=oqAEpm4aHx z`qF~SGSvgK_Ns0VH=EuV^p&;WIoHO{e=I-=`an?xn0m@CIaTA)F0uwOg=v5g zs9H`Jt}E4=FI@rbrK)nZ_It$5Xan$uJO{n)h8%{scnQZpe3lLQCLg*a9I;}(64%U> zqF0kW)sF34iXBxln88y<)y9_sEu4dJ9F!%!PbEG%EQ%%Xa575)941xAmJ=YnN>#2fO38TU$xo@+?tb*njJLC+9h0C*GA3{h>g z@rVc-Qt~ROY*VDSXrV|NVNiC+VqlqZfy6P4@8U0^i>5^IGJbcD4_%k`XJsYlth$c; zBa6bWyPcKZf%3OYzeOE3T6td;zrF8Nwp-fi;>RDbDck4o^FWIsNcP?GMw*yp)Vu$1 zr3DvQ|D@$m3}Jt8m4LY!o~L{P{CZ3PxGp`d26bz zu`NhO?@d1`;S0?GfQ&!95qHMS^im$Jil-C0y-Q3)Q9BpmGu;!&33-5(zbTGc&#RGl3 zLF5d5U78rGxWz<2DJ<)aNlRB||$EIi`;OT#)77 zX!aohC5;YE=b6BWsI*SlUYv8DS{Vd);nLpa<^qAcX58+I<2L~SiXjhcq>_>!ERU3u zOPT~7gj=b8(wWiMqFb777vfRXvsrnxA=enjEa))eF z5mab6*|4f5DHDcPWD6u*zT&hY#UE8bbSC)(vrr(KR5DB_z()WF0NQCh90!VQD5raW|Y#~II zgjywLXgJnE62I3Aos667?JF^%bx#2jilf@=j#E3h#RnBaW*{~Kjn%7UlCE`)Dd?x$ zPeDnb87!TmZaF2Uy9pl&GJe~$m&P^!R>+zP0KzlyR4EGL7m!M6IR$q3pG9ITjR&>4 z-Tm|_sd1+dJ)t2k^l}-d#Y0H;9bQB~Yzhipz2y0|t=siL@*{Dh4>*}ydU|ZSpZM$7 zg{G&eAVFRL@CFt|En+0urBie|l@KCw^tn03*OEzDBTRab`H7-ZI||4RnF3v6o*5WI zP;u^JCSzRk0q+pw3hm#=oa1Cb@PyN6xvYl)DNVRihZkeiFt`;RNRgQH%2JR9^Sk1) zAbxC8Fw_UGlv!-7NX-qPc(JWlFyzC(Z%Nb6X`xB0!&=}#B2WJ&(pk8}4~$KTo%fRa z@|6)_LK1h(Vg)_PCc0EbGojcNGJ`ZaPQVyown=99_`9Ax;0jsteOdD-s>Qqp8l3Bm z@SGWyOa=gkmdfeS^e;nlCn$%j{PXovVs*iTmmF#FpgW=8?)d1vckMoz)?^^ht({L( z2sdQ7u0IKxQ)JWZ!GOFe7c4&*L=c(i^Z;AWwG+wECd%BeNcGn7$R z&Vr2hfWVBam#b!DI5;rakXD*`$tw0z&@xCfNw5_^CW(h?g8`?O%QFm01YBe_)Os`k zq|AQF;p;=i0R?k3{=zu0ZHpJ&d^7#X@zH~?4aKg?Zec$?S{@Qx&OO;Lf2!T-!=Q}} zu4C2-?{&73$#;~(z^TqzYkQ$TE?-im9+;h#Mk+76DEJh(I8Pj4W1B#guP$s~K*IcpJ z4>1D@c7KboXc^@OenqAZ>)x!;u$V4aVmBm@JL|$P>hF5xm9cpQGKMc~_%+^o&v|P0 zoXD@}&l*2r^aWBp?bL%uJr}CK#h?9mou@Cl5%b5t;0@4x?%HZ>yO*k?A->|=grQRs z)BNk#Agauskb2Uj9N?fA$Eu%8|G0kk$v6CySrZ#gE15v9Xp0&367089%k+Z5f!}YI zF<>K0=&sgqtBJ9yO*M!>(HR~btH?r37LI#lu)9MlsI@)_EXX2Rl_iNO&QQC0fa5pc zcdlEuSx}8U!>k%ZGOh}+r%0@&+!_%AnT)48j;A?V6Xa9d14PsC7Orv>t|MWVg3PSD z1ZHIl^C)#}OJv=zE;ZMS=ff+b*$s3Ny|wA3+FHj(u45h~u_TKAe3a@GZOGGz8XSHN zig~r}cgutZr2-#>TIfI;!s`g}+VFsPV^+Y_h1xLwOtb^Oxi``pY^-dvwv~pdaxaR> zgNP^AmK78#N0S7qhDx|G zTG>8)F+#j=eKc=37^(s~kA3JZDuuZQ!7eCXW+1W=Fwqi=H5FUe69aV(jL~URy1z=y zK8*5KlTKm|$Lg_RMG_ii)6UzgKDR!+$*^w<;FInn&YK=D>WnkB7hwLww1UiOY=SMB zOszIm5`>|eY9Wj!)OJ-uO^vADXrzoxkr4B-R4ipB5?RBGF_{a-)a(vyYCAG#%8=o# zJcYerNKsX&iDS`~1W_~bGoveRcdhQ^Zg%R9Sx}nV$k}&zP3|GUED||iSpDq0U5(nH zo|8>?9CJM4hrRYCBaZykjwK1J*#w3pJM=|s5=i>wC0mc58P3}@e%n}(AnE;bc}7^9 zKA*;bt1#wLz%Z7om0QiRwf~sB)QsgXE~c8 zu3={)z>pw-AreQnpl5rDpsl3)zUwgqARdVUS^UPMC&N#_GP48z6x=g!<>92CImv%U zfCP61vf+6?Xte7N*mnm@kh0PZNcJ8|UVK+f3`hktq`9_?9Jogt4W$R|bl~vT5>*EO z7K=R=y5gF;_6f@rFsAEns8Ktd|2+UTH3UZ3fHJW%na0dda2-NKiMu#;(zH2tk4YX_ zow4Kdcwukz@+pXNW?@JdKP1M5EwG0!(NQ|y9WY%42SoqF!eTX)^Il?YUATzsUz0Uk zjOBOJj2ris$cA)?)8#?ReCduKCP_1VV4rw)g;vdKR1Ij(v(k667#@`x<_>5^88QJ_ z#+HKSs^X=)Daunx7?)@Bj&#MAl`-go^WC-u!nB~TC*-c}tA5)xMPlve)?47V+@djH zJm^jks{udk{#;x8+qPGf>B8?pxBj$Jpzp0pU@}dV*l^<3qnCgj+zY{eg?ge{`JEwYtH!_ilOyuW$ zutH-lBG7Y#pU?F@0-%_gfPW9V$nN_ku*jSY^F?p<-3E@v+2*_g{t#Uv^(gR0?D+)j zK*ucQ|M2#nUrmI4yY{39l0fJkLX}QHI*6f%A_AfY1Px8P1XQGnAyny2iXw!L3K)ur ziki@+2~tFC0UH8pR8+9#jrVi!XYaq@{gMxpwdTXD%v_V-b)3hkNE~W*!`{6L`eQeF zRRB>`V8cCS|8cY6xPIGU7q#wuVbZhBN%;sH$*Ae_0x3Dd!!OeV#@c|Tw)480m_C-z zplW}pPEh=aGeZNK`XQu7^s%Si;YpS`lZAM#KH^sVi}8Wp-_CE${5}5=^lU$9Yf0iz zM&6hXbf^9Ij38o3@b6kw*-Ye9wU0g7OqI4Y9Y))#!-}~x4V~?>)r_WT<Qx8?9jR--1GJ_~LYk~#eWoL9W03&37GW0UDy z1$sN&ay`L%Qgm7S_p4-O#S*0qnYG^@xaS*oWTtoe^_{oZ|9*^iXrf{xQ;!#v*yQCI z=!<-@v;(=+C(J+6%BXjRviqAPx$7O)yg0tpOGgqbIl)%58u{$_2vY3fo;RfxZZ1fX zX=F>dMgd}P42T4q2Wwl8HMmqd{kyz@`8A1aNxYSjPIf2sRH&FFImeH z{;6Pjx@0uiLW~f0`A~TKA@qB@dL5knwZ+z3;*k%;cky?nC)8M@7yB_lNP>7M_x$3K z;RRnmTR$fZ?QTV)H^HdK)?&=IzF?pC<%2%$3kXDvI0iUrRxv3@09_VKI#1BN{z4N zdYy}td{j^*MBn`@Z*8*L_BMW{)$+~f>iOu}vSnwHOIJHDUSnNsn_a%hHInIY+O0Iw zV|%A3iMe&<&z6w#rG0uw+`e|>Au+W};{uV^l9ATvMomvq+0o(QrJ}cB#%BmM8wUmT zPOBXbeGvI#XdJx%&b^JpIJ;rLYLDI<3#=o%{vIUjR}booW)@sZx%kvJ*>6CFDit65 zBIs%F>8CfRzBqhkn`S-P?ezlD^VBNTW;|JLRl4wTamH-w^B={2Aot5hLS*;ffd(sd zyC+|sW7Qrjdmhr@x8Z3|S>hL@E{Jauat+~cylYOnuO6DHzW96)AJ58=OLtODS344J zz&JDq9<|Pve}AD?3xDSQliD4=#X{J~+tc!r&8%nE0U)dNpT$CC)b~T&P!q6HT)-t? zLz=VdsX^Q(Q`B_Osb!0fMtyH8{Lyy|6dAXz!`wFe;d$m0aQ5cems%+sH-CRN`LlB- z^xBYh-+oZ?0vHXVMnEy*A-37OWdbXa!~r9sf(+X!HrlE12{1~@!`QJYhba&S8#jUr z>Fkpc4v_sw@qd!T$4`!H5I*zuvSjSaPcystQtKDgX1kgn6bwq{7qq+yVrd9b(xxVe zI7=?p6;^aTDr=Bm_LutUFI`T=b;Qfu^H;R$)@okZOqD&QfnkbsmUHNKn zN#eyH%BBUk&G~~Y?m1aoB{m-WN$kLjf4^<@!r=NlWkN?F<9>s+^Uuz?Tk%McZ8KAV zZY`+}YR8Qkhh*t8hbZ(1B6UQFCV!zq$iX)zU%DrE*r|SWur~v!S9K!f3iRRv_alW zZ(hsjYRwGaQ`M5bGVvpx`WwVSrFZPf39!vjvs%0#n)S9nqJoOqo)hM&;LS7X9q%0t zsVb)X^z%~9f%jt-G!y2a`S!qX`SiU;Lc*w_Tx5d008id zlIXOIoJ^EwqbUXW0ImUhwKA`M+jv-O%?Fo9dxr#1ROp8+_E((@;T<|xkVJpG2t$;p z$9xtE34N?3xa+9v*6HINm_RLoYdxo_9<8d|OESj2o8C7m(T_(qzX1Wyy~DI_CSMBu zIBYNYTfi!rC3J5U%ufC97= zAX6X^?UuqJUTNP|-v?d)Yiu|^Q3f-k%!GEgJgkMs2+uzdX`O8#D$~%n zysixJ2G)N5MP=_tL5Fr7f+c>da{=4m%#m~HZF|za3qjQ(k|&MiWoO3aHYhr*e=PC7 zbu2)YJ2Dd~)Wv4KXoGgbqr&n3Am^)b3s<+<2Ybl-Zd|)LeNjnHO2n-r(vw%(#Tt-5 zI`y+@5PnTJ!>#`mHB4y=x4Zd6kgT)>2%rAy5E!VL+8*C|1a7$DGS@mNOq^@aK`m(M z2|_mx?^>Sq$+zL|KU;D8^yYN%_ZzIHiE`LMpHID(=jefBC3jBDM~iD~hxw&WSrM~AKz5wzB{2>>LX3Kt*l z+~6u$0sTz5B0J-+jop(teEJiD>N4|HzNWb)o&cywcDtGNQjM{gr$cjjDz|m80IavQ zp;~wiL%|3?hSHB!ZssbW9U1DXhjig=Ud#Q4F7c(fej_@(G|0s(Zlr*yy7 zw|VYvepKdc*sdD7)Z+V=e*h)OC$KkN`TDZ4p9A_^&N(G?{#G0wFxZAvhR%F z*~8+D2WAv0LfRf0GH3vNPn(^g+6BE!fRr`{I!WXZ8ekCKK8LFzWpZW8Fh-Zl0UOWI zi${#Mb&3G|(@Fg-qvVjy(1s$d5ru|7LTpr%>?yNqKcp0Sz--7jkk7nxs1^J4#GuX( zFwBr(ZOq!lrlHR-=ihvhug5l9t1mrDUGxq4J7AGjsr&7-4va8paT-tU$Y>OjrQV1- zRr#T}d%~G{Y1AFSd?a%lO+G1diMWV?tamRXcP8=+AZksc#Ckr>c+xMQu4WV_hLAfM=iW z+j#1MBwM4y-Tf~orS8nMSbe|s=>(^=mT>#<{aaO>2-YlI z10m({h)HI&LaOMzidMn{*Pj>}unSfER_l<@MIN||%o26OI-8g&H z3{Hq2JmIZQ$ai-bAdSrrUJI)H8xy3U!6|Tuid1H#j~&!WUyv_`Y5xrZSpTu} z;!6J4&P%U@zJI@8#2)J|9^7ib=jt9`k83?0H`?{_c`p8%bN;f{joHtU|5wR7u%NC! zdB)(i?Q7DACA4!=Q?5mRhn{7m-t(xlbwYlz`n~@R51KIcG^ol}^VpzTMml$z`?V!^M_E(a=S_FnKkwGWanO~D*pIg!){8nplS+5xh zA>yiZeXy;xP@VF1vWE05uHip+URfsiDIxQG=8i?$aE|;Ib5wneS4Kk%y5@JI<=fDz zg0nu;aA3)S@2U&d>0X+s7ftJkq%TZu;X;Fb0wrC?euX4CF=8r^t9^4VEn;$|f|DO> z4Rr^ch=E8=j*a;=9#6!)zHENTIeMh%{%&@6l6gH-<)eq?mxFU$a63Irfh0qXv>Qwi z>0or0rysRc(lBzqIvuSTC<#bPvKlb73K?qF$+k4v4@6y3^9)H+nJ{_90d9Dl%weS2n z2Ug#7Q5S|u%MJ$X=dmigsYm%0nSIS?FKa*&aEl#f?@FA-lC*Xrs55DXWlWl{h3`#F z{K)ZjP(U&o0(_7^URl+pqrgZr4UnrSxScK#y7{;#nhST(t)x%MApz;f@_m{g{WeJ4 z9fzER8~i^b+m1`HpP*zhBsGuxT`02ngR>Z(o>$OvR+qh`w#~bpPcKOyjDOXf$y)^6z7D!9w14BP>9x5L{eoa$dH+- zRluI>z4<2Gr$9TOnOB7oy|4UBC)n73`Z-p1;l@TQnO#WX0 ztiz&Wu_`e66`1cg8ztzpC`OhVh^M;6(c*`9TNT`nxJ%kt2BI33q=pE;26^ZW&%?WO zS7->kZUcz41xR|TTY!WKdzQ|PnV_~oT(itNU&C|zOV;R_Um^^EkIg@kCv~m)RXPEk zHG%Eqxa%Kct|wh4H@)qdw3{e2Js zQ<1A2fk@mgaw_sJ6XAk~O;ZItajw@Wf*l0tSu)xNIJ1j_nPI{YvC;E0Jmy|zhyjb| z{mWnh|C1EAqau6BxSGf6nwetth(s-8aq@qXB9 zI2_n2p(ruKTK8XwoRc0^t}EE&vn&dc#in|1L#B&-)#_=ijO?7T?A_vSZ&r^&smTAv z$C>GDf9ZP^0iKN`N!|U_SCd-|z}Ou;k2vo$ma#8h^x0ZII~%iFdAbGe!7|NTaL*eZ z+kd>}++<&NeyGPRF?1uuCENpS$$SFSC)G_4qC9gOaPT3x@ECRdh3m&LbKU{U;$de zpd}1;MdM;K^@0zdco_9^4YvyVw5mK3bOao8=*Nfzlq`fQ5?UGnX*0n|633=Sz?#(B z1{DAb0C*;NS{Vxlj-Y``?t7v82Y82SUo{5^Nb4N^oEfuCzi^o=U)2MyqL zrjY4PY>G$x&ilYx6~J7-_SchIt`uGFXPuam>wQAXTa930f9=SO^Iy$`AQ!=}51lw= zuW-%4`QaBpp$Odk86-6hx)CBS&I02(jiPIoUaVR)RS0xdJu+BGvQrLBtxr@GI+7?P z6;rR_T4MO2xH?*z@ZLEZ?v&atamlhQnu0Jv*5dHFQ$qBUJN0|J+u zEVi0nq#n%TN;ep3D!V_ZlTXFQm_+XGQuvTbDqa>!x+NDHB7Kq}K;eJ{c3PXIgVlxV z&Uh4E{#lf{h5d8>Bq;zDM;8&-$IO%KYL*o$h(cGw_q3ZEH6>mhTE|gHZ3IPdDMO%_ z-Z=KNZDtwwG#(frV_p*fMb8t>cmurl7wfWR-`aPl%9JxpR}(wdd{inF!D)PgX17WW z%R9c9Uym)mvUOGX{#VfDGuMAFUsnamAJ?qS`~mvBejT2~Kwe_--(jGiG6a4xuri&( z7M-F;I=MMU;=e?WX=1rfqEf#)xA?_1=-{=wt9%h%nu=X2s4m}5nM>ncT8hAbljU3- z)M7zH!~AQ+TgT&l9f2bgvR?X1+vMJFRKXA3SWlN5ho1UIJnfyb z1}>(e9P#R(D11lnNRd&}2rTdvi-wtKcVX2K_#0o{Z_p?}Fd?R2>gF~_fWm5j0=^mR zauc>8fdjzh9E=U4tK)X}o?i!f#fmTRg3An~E)zWZMY?mP_4l$&BpckA8ldsC7GSi_ zq)A=3;On%wbK{N%(?z%+75t80{2&u{gb7b&!4ER(^jXFncT|oodWh?;ajpTKhDYjx zm7yF3kO2;6BPL4446)%ZOqdVdhEMl}ntK23 zFD0)o{w18CJFc&Sti7&PBLLqJ=PUsiX!!Qw#I>mKGyu`cofFRzQZ+0X)??$XF|F5U~zf`cQ+$-2nOM&ob*J$X`3}4{=Q}ic1 zQaG|CXA5Yl&}ar*oE;V@d@5ehKVsEo8yG=?{VGx(aM#t?6;_96O){zo4%n-a^}csd zyDr+~3!wZci?36A>WlUS!hMaW)xiOhir@vD42qz>PB3<+tEYML(r|?OH5uI*AcZM- zk)R)FAc&%<>DyZGKCl}%m*DP?K~W4e4#=Wr4D?F1zB><(td8Dq^W*cM9BZ4zGbg9f zQ;%GyW|F5e^mFdBrcj)zsO=L&Sw zF$R9vE6F6o7{6@a13t1s#?`kK6LDEVDxzf*wdRN1_{jOcccqpwg@PyaSWKDb80wq#Tut#qY`AJ z-eZy>*Rgm|;^-dc@%+W`sl^j=P>RmuQy+xl+D-H3Op=~IKKFd){DOq)OizTNNJ*K$ z){V1zMTzkh_m;_r;wFg&TcA%`C?6)|Fu}r3>1mIjDueP=7O%uh%HzXTueY<#ctj3= zP`G&+e9s+ybRK4b?^7#z3ggZYRVly<7VZC`4?zZ=0&0BLph+@m^T)u%^Jg;euxIfT z+s7Af)X9~lEi~R-Py>l0DBuRBfD3gHnd)+T$mQM^<=i}651a0ehqqn=Ck{DM-<^@J zLnO^Ro)aZ58wgm@kw2)YL5cmxL>&Ulnj2f(e>VEce+WQf-9xV!!4~4*y*)?(vB7R^ z_)%<1kf4CTim)zJ^xlfZ-!szk@Y{1MnX@vAR;z<&IyMv~Z@*hEIO3DiBbdQ-g_2Q^ z2(SH(4h3HT2OjC|^hAZ(sCeIdW3~Fmf&-#V2;!OrzDrCV{`hJy{f!q${HWV&1_P}d zj5^2^5Q#L?ReNj9#R9MVUf9V2aE?IXofp&W7yJGUY~$W-5#ANAW4l}z!c$*jGDLpu z%ToLBQr15;Wk%pLo?rP6Hkaoy7xNaD3+9^veFBid9Ml@t_#yS_BT5|~!T(|xuws1h zV1`&jHu58Lbi>epfUO5S)=2W(!=LDUkOMMU<0s zzL(M$Lkq>jNRDA$F=17`VRp?SsoAfL6@{*S+44{rlbrf?RPcMiF?^_AKnT}A>{vc1!^^o zxce4RF|Xuex4i$Tr{CYY$A1k<2Hx4ZB2!g`9e2dDz9|j=p2hPYtL48zMqlAr@Qh%9 zzTI$^Af61U1J|kuG`IquU@)E4oCgB19RPeE$&NMKYNi0#}!&Z7l6`(-fefm;Q zs2=_CZz3O171@NYC{;;Gy#Ic&v=Vy++3up(d(Y;5rRA1kgvooxj+gk6*r<5}`zzs6 zF2f#*gEWMQuI~(XWH4Jq>*y)z!ykQbpZ@Sdj_1afHncRw;u3Clh(#9A@Bo1~Ks`Ej zih;z6E;x}Eli`dm!FdQ+wEB&?K?&$tGcv?1Q+p?Xc4)p5%W(ORuz~C0*lSWny&jyq zzX1iK5`t^t25G`tQOMU4-;MQ(&YFJCnw8YR%f#6+o)@?IL3E|g1l5K+`%-Wp=GTaM z8_yBbq^&EnAHIEfIa+({oL_;gyg?{aFliZJUMVFoK-RlPgo3jF$fr|4;B>9v@+g4S zP!*;tMFsv7x-TG+LCThl80BmsZ&OXBf64ocqW%_qdmzxrhkJPaZX=bV@_6!v3J3E# zX=}1%`(UXG%OMwst_)Bhyy(^(===Fgj|oBrg%7|q`|*RA!k_pdv3+(P!(igjn7w2$ zdcfK2o#=jLnVBV}%==5FyFmv&J_*j#kMxE^fo$w9u9WFG87`-;vSoisHJJmsRM9qg zyu#Ry!OG05_Ce1EtuyL9)DJ~`FuL;_>g8^O5j*X1K+*5`fIjmU;?{GOrg={_tP)r@ zkX>2tp&?JA@|+odnTS5iiGt~mp0L9G-{2&QsNubMV#klwh7v2v%7lln`9pgXzd!td zzm)$yUR|4C&+Z}zuzYqazuf$rn=SzM7Q9ivBOxmv0cQY`aZp8Y@g)Q2_?67)@R(|y zOa{Fws;3WGd~}->z#lSGOmn`RZ1xT%uW6r!u0P6DlQ51{_irq9>w`8nSCs;<>5Wy3_Hmwy19tEdT<^23b6okL@+M=+sgj zx~}dHsFVvi${nlr1Aqg|S3=|?#5Jg`Fl|bvJnKEnvAn$^R_4U5_eXhliW2`7P9m@V zD+-V&T7JBxC*5%fK$GFMF8nz5$NqMY*N#iiQq(S=e*5ve%J{-@nJe(hD=_%d>%8}$ z#~z#rZ-l7qz-uDQDmUK6Ku%uwWxv@lPkg_iO3gkvo;rHbS$_G?a&Zwh3nkEYIb|9o zNgR-i2r>m#$Ct4;KY0#0jY?a25qbgC+w0tvpi#-JZ73luo>A^hyLqeJcDR^CVB`wrdR2#!udugM?y&%{(P zh4Dn;&t}$M@LJ(Hi{HlpGv1P4&q#LE-WT8r@EQ;8>=fKVD1j~jG>-vPR+-1_dH^vy z{aCy&ubY8@y)kJIfJu3xQcO@@wGdpjYr+ZY4yZeKMuqk(p}I)Q#-1Jk+{_6Cof$TX zeOQ5XrrzbbT!wOj3ev&U6CkIM%QUu`vib(nhZ8*u?RLaf z5Lal2%g0I(+_3<@ZE~SM@&F)`7GHT-9&;6oAt9=kp_kN_5aO5TT!Sp}cZlv2ph-rQ!2GZ>`O4d-tAN z9E4l5(GWvGKLpA)6qo!wj+`_eEu#LD=O6Z8zJY-7W zsH)p4lhM1-u5$2&pCkvkt<==p(~E#|h^RWYpXH>p7l7k*8SnTiaxxA1vXzSz@Z9~FM^j?ZH6;*IMWQIWwG zN0#ed;t@b2LF>qo(+{%tQ{O~UGVX}7F+8J`k!~=80%#4r;%|)6pp>E%U&2q_ew^Qa zsxwLV!wHjwM#~dwg(ZNp`*1>D@MgkXlHZ$7(ViC>?YCqly_;r!T)%@kdM0Unp>F-~ zUH>=HPA@0)I05PXZ~bb>{F~1~*CcLnqS}f1x5-{UH;;ij^iu9P`-6U&uz(ScgHE+B zq@OL@pq^y}_s>r<1>-?}O+6Po_%F zvC)(C)J#L)G|gJo;F{6?K#vsEWXb|1^^MCLr6a~nDo;9?+WZDT7xM7vA%iTLyCF^? zPX=@IPn9X&C3$;ZobmhBbHVv{fr$zr`PDPskY{%FDYNBmLlVlCIV(CL%X4Q_dSYYp z*Es68XBshvDStL1z=x$D)qFQ-xu3M*a%^d-viVdO*D~z*%h+Wh&CWf}XV;b2tYvWO zEQ9i5Fsb6qV$ZaGPci0A$d85F{l{P2IM=?>06)??Z`|GUcQSI%r;C$2Z_e!fF%`|# z@Ba02e@8FV_7C0TJ`3Ew1QYp5$3Qm8GGpw?)Q39HX}>JkGc}uc&TNGKnzrcv?)HY^ zGX4C*t6z)U;WI6KHJ|Kn{$83LiJ81>a`NJl+wYcAKvzV{QEH9SP!Q75P@wtqSEb(* zmW3uoQ39dXs5_4jnCt|4e0)(O^0fCrR$`H(TX64ce=AwgV$uY|Njc+v;ppDOy>bGd zm0d02s_OqKF1vz@AGiJ9SFoTPeRJUH`r+s^iwLb1>uc4VU#!HQbH{`66_+Padf!`A zxo0$+J>w67;g5U4IL3t7HaOW4N$K>j2{{UsirO|L}Kr$irVPBFo!yj`zAF6zU*Yf4ReyYHiXC;^1xq5Io~xUe3xo z?Nzt9u4cAraqP*g#NY2!#GkFx(MhcRiBD!tUf!=z*!H<@@-{_akNe!-l>_(x-nqYR z%S^jwwv&d{FHu6+mCRC6Tf|hbKggNC|C@;3nhR5tkHAR0-_sUGNF27>(u+HbXS{KF{kRXD1I$l|urQ&PJo#?;x z>dLIm!1V~L;?nkK4IM8J3~0{}=#-iLp0LqEcZ?3^3cemNFti;OvDJ08o%dvM^;HIU zrw^JAbr^dM8kG%N3=LY&5AJzAXtg!C7dk{pA1pCd&Wmr@GhSFq1c|M+E@%$HOzql) zhnyw_?L_yewduIHRwy(M{lwV`VYA|xh|}>m9C~uI+2A83Iv~K_L&e@{%g%MvZnp|G z^>Z%L$xJ4_-KRhENWYC}p3OueRk#(bOQLFKgB+?w^jFh@E3-~h;npPJ!TAP05LG+Y zHh)_h%SeyLf`f+&Vg3*aW?LAG7s<$KrtNlrGIHX#!+}T*ziRu&89t>@ICKz+W@aXj zWu2OY%Mr>Ob21Z%SphS&xDwRa*N*3)V}F&-!$}z~YpPsjs^MBDSCayYM{9KXGjExm zA7(oIda9mXM$9k6<_-}{=XDAqdmm_~sxn0kNq}xP$UKbdM9uQ4gqSG9l@m3<|6|8+ zcY0^WC%F!ZhC24&mX^y_eG`yRp+I!mG!sHiqP26AwzGV-Ba1CFYd2Lq+b*@u!4!5{+ zchjX5I;n|u9TQ=ZtJ^f!z`9u1$~JI#f8j7dw{{1uj0yeISyUqa=`6@XYq+cgGGq@2 zo+1gq_W!6YgaIl9pBhBD&7GcuL{Ad0Apv20=4l3>94QwcPIaDv$8+EZLx~bGslW1VgfV^G!`PK7Go%q3Pkh=#`q1&lv;~~>S zqJ#9wdVVUL3J+4JJ`@%Nt3xQ2kYHj~#0+#N4^?&wlrO`l6*~1eZ|aF}hss!H0*M+d znf~^-I8PHX@3k+FtQ$cBp3U!j;Y)q#<#vSZX8G0T4=*&JGn2GY1ofMNpPji?<<$`B^?BrcP8=2O`j zU?(s)QZhr41CHuT#{t0Kt=&K8oxd}IkZibVB_z5sGqeA}DMq@kJ2aFnncOKUXDZ}n zN^9S``Z4{yUH9CXAnR{mS*<56OzpA(%Kk>`{z3Pg9MLR%rMo^)y-}IFA4B8XDM=<} znG=P%Pd#`x4W6*&D8N^oJ;N0Bq~qoy7*InoJ`pDTSdo)U5sca>taBNv`^^(?Uv3mN zXOcf>HZ`~B!`$Ac*@F2(5al2#qxsv#1)sYMzTfti_3u9^!*@()j=XOUggX@I2LweC z)nx(o=iodnO*DS|50VP)GrgCXGm8Tbe4U1EKR%g_(92E@ZiS|EU=w62l$_2jKc5?Q zy0B4YKa-ukZ#z?m4X76j2PuMLB{OiQ&W$9jf_r)r6E;L0KSa&Mxuck^BKbNGIh!nv zNkYv9$LE3O``h7BB*bTnv0OU!pgY35H9d+7L6@NTC70{{G8?9@!toiDBAQ51rVa%Z z(>})2dr07cldq7skdrVxP1b4Y%r-5v9g0>$TO+}-8{%&iNohQSBby|8mE@%Z6vDiV zVbHuk-hY@r?QeXp@0l^g9_ccA3ML#G^E%SIk9v}Fr0>F!NcxdGZ}z_d1M(!`9*KJP z!(o|yKwJ?l71IsH3iDZ$_~Wz4t*Y|bseFoBwRBQK5x9i{;|?0-d=44{v5~>?nZoWM z$QqEe{T#tei=jaJ?Q*gS&@({(oS)4i6On?IJv{@K6MJZp;iwpRf2YgK+iQS+LE2Ih zsvAES7GDOUmYiIICojPbNmG3c;QQ%gQPh`hN>l-6rVCFk)|#nBea;p-nCD8BrRQ|} z0k4B|PO$68|CF6Oi|}uKE@@h6c{j_SL;JkajX3$df=W8?2RN|(9vB_f^*s6(cJ$D2 zAt28NTajbE3Sl+wh!YTGIlGBYX8Arvxlz$C*aC5xf&wc8Th~e%la^0XRZShHQ+bBc zO!As|t`Zf_Ogl+J#D-?^;5@Wi#$}~E>zP!pDksxtNuq_FN2!`$eO4Ume!G@QJ(CTa zCZzWq%z3$Eu-z$Sv(mR5G))vcOlPUcjZh!}#8(#SopELQy%4Z0qj=8m7*nx4qlTP8?jC#dtz4)L3qYH=S!*;;t&7egj^u=Rb+cr(fKYh&u704p=0?o-yZr6gx9HsH%zZI zzV&Y|N42P}w$`lYuVoh^`L^WRz-+J~?x865dGh7bHG00FIu)`64~(aZB1;rMFDl9B zbeiQTS*HzB>pu8iDP4Q*&yk`i3VtOB^7#wzQuNKEgCZ#z%928XHuDnai$%{r`9$mi zZEt?v_La4}FK>C@!jg~cVr8;YS)~1_r0E1TWQHt;1ghoX?nn7|-F6U=Jn`081L}c= z#}xJ6BYK~JOgiL1{dmM2Rd7cXLX8|^NbxBGGOXg^&eI+MNmwWGL}DRfCr2aE+9BGamdkQ&1;e!Ghw z_J+$~fie%N@|Y@EUl>e6z$zg%rU+(2qFwYT`TZp%mW{9m_Lq#6N3Ed2OsP;ZM2jtD z9rrwm0#jOp1Y*HKx1{rCB@1TFZJNtF2Fqh+#82$_0;eoSEFwxF&ge>h0${Sjwz446 z7G>aSrVU(YVwUp~O@b5T*};=Dd<|!BK0TyLxA8g)qKt`B4#)yZMPD>q%UX0}4w`-z z@CFJ&QpJi=rDg#3m{eqFRmJ(Ls+iB09#ma^FJ(TYZt+k;4mfhNIS2IV>t4aF&*gqL z2%aEtt4OM9`h+LrJxz7&@kEtKx3$c&29~G9;_t%cQD|{k*~dYJ2vaI%oi0eDeZ3_P zCek9Z;i}m;S2Su~eO47VP*mo-FI!MG#_c7nMUCZ;o1T=2$8V^BM4TE6;_Bqrp@PEq7S5SUtxzkI=1KWzb*acTU$5UJMqL9HOkebXGfXdN>rb9!>Li7`=|K14-fyD zQTSuLsvTle*jl@{(x8Din7L?9y)^Ld(@z(7Y0k0SzxwE=eBbvS+ANmfLBDd1i>tt; z0gcPjcW%z5JQzyZZ4Pu9X!Q*I_-pFKX4K_it?E|1#6 zzN$@EEASz)%v1)*7N7y+Xt zIuYNsmwUn=cSUV{*Ll_#`|eSD#1GvU{nAPN>8#aVpu7KOs;)wmQEq!?jMCfIc>SP4 zC^%XaXX9&;yq->>%kJ!6n~CDfyET%Q=Ga_sTVkAVu0OJX)U#6e$e5sMYIvgYSB+kM zT9ViW0XU`poEKmL6_f+v^Z2~auQ>_va@^^n$A^95L9~#aGk+ycMA^d2%S!45i2Z4+HDTJH@tHukBh=*!3?a>0u zqVi`3g#emhvX?O1`hgC^*Kis`T8%E%ClC}K2ahV|8!d0+3S#{2s3L4ev4X8AzI2~t zTD(Y}K;Va^SB1-}OUJ}C4g2aNU49~8m;QBnUwOzB%&Sh~`mY@t5DxYFtmGzfo}6R+ zwRLw6TH7}WX7-g)mu2!BJ@*uJh{V&`zIW|*;R9sJHU^)c*t~?OiQ`=?c3vOYzo&u0 z@dQ+uho|fDlK(FTnh05ok)s;tjIPANtjd+!qcPb3z@&r*X<@@E`S;p&HsAxBU*Qnv z)vH?9JEwTKC=U(g;i5b=H11+YH?tmPNoSwqlkD6tga1dUqBb8Ky)f}{S?dlQ1GM+d z`{YJNFUMYbebfCmPWRT^#e^1<^`AeVRD`B?4k9#`sF^Y_m87|Tan}JyiH6ArBnmG@ zW&Ic81n>mmr96Xl3GTA5FqAi_{69pfIrG{=#s5&3LpTT;Jmvd!%Iuu9pyAQ@hH2R$ z*RRY6`0>iC*Y2NgG0H_e{(0I5G{bR|L*v<-)}^d3uIQmSvY>kK`F{;z6DS?C7wW6_ zdYgopFS8VFcLsqiY;i*7t`ym3Ot~W2AoXO$+1|(am3EVtDG;}AcDf$flzCo{5&uf% zl5BJ+RFs)fsoChh!mI2e=sO(O;}NR!_!tj>uUe!~LSOs9gNl2k17HXro+co5?8KKT zaal>SHqIhqcn+j7!mDR{=(zWf6;*{ZU=hu}HzAZm6tN9~45`~)dl412`rAt&Xzvnt zr(%Jsu|kZY8TPWl^FocgX0ze&N{>9vep|6$8!x$FQ|1~cM3zEH1z|_`e5xBEz3>6b zPGt4H*!svzZqqoLsmlMc-KwzvXvVY9B@t;e8*D2p$$^>TnKQdw6;ss?Way@+qFmO% zLph~t&AbkuXXJsCU_$B{XrPH@HjjeG<-JlZ6#7yG{_zi%Y-Iw`Cx@rkvy{aARPq9~ zUrCLI2z~`Y&L++{@WnS=y%wBr@vZxaHK*mibxOwnQ`Q6H*C!jey5~pY-No>)a}sL(fB2PpR5C8KZ`_5gOu(4yM485D0iYCbkwu**Ro^a%%BM3OULXd8qJ zZvuA;M}^CBKjLi;B}){(t$_LE=J z_||h-PvklGq*T4?wn`;%3Rjm(jM-8j=%RbpQVadtdT_^_GmPT^05Zdr&yt)lmqU3! z@I2J&p%gB&zI>>EK+Jd8_1V7Rb(Jsa-#fo$Zw6_(6eVbg-MD&pE z<%)TT-;KMW>YozkeqAy9D{>Yx^~(OIJ4UsS3W?E)%v#$uHf*r1UJ6RZ;5O4^kO4@{ zU83-qB68~|OFoGD(Mw*|U&gg>fLk$1bV-nsbkZiE8X9T{_1|g%HmefOc^juCAtmHe zIUruMFWS?!L+s}E@Jg<}>2cpzvH1d{4;DU$xITfYXj)&z$)CqS*|{f^QxPf55%5@% zHpJCK%shVzHKK?z(XNBao3o7QEV!0wn)Ij35+F5effQu@VHEr5KaKN0i*speclGg}q;?s@haUMk4pB8yrVFIN2b}s7y0@3yAE}#&yMwk3j_7b53c~-~WT5yq2RPH~MTKDT*lZ-xaZlB0J}< zy7aw|vR~gd+iWv#|57~$*7eqxW2CExyy|(5TC`wQ8O$F(_ z|6!`;b_-6Vtqe=1ndjUCcJ&NCj~wgq_u&zyd!1wn66Uk{_kcz%zmj^fH5Ok04R`y*QB9$NiNV7 zLim#UF!>$g=-UoQf%UG+sMNMO@wn{=Z(FSM!9zg?ZO6uff^I0cg^fk*oXIYzdbxuk z`~0pws^S|Ug#%eZ!9R1rx;Ss`QSsAKLffo8zO+9&qL2Z8XdE4pf@3E3F8tDZ*b1h}Mh+2aO!}S`n*J^t2>LRKu}X z{lge5WR=HQ3TTL!N5E5~B3a^V4$tzTA8`;7_6sn{+Co&xiaod|)S4VAub@3>*#Mx$|3@(%h-ksozfK+Jl)exT=qmnmNr@m7pc7q~rzpDj;0jbIyNhC#nx<_CN7GJ;JshC(o81P zjJ0Vf2J`3nbRV

)S3SanKma(FjX>BX4EnaAgzeVG}sA51+lS>{W~;cNnt7EUUBL zbe)526HXdE7T&5OvI{;U10dB@L0*!%+S(BAU^HN>Gig1Kg?w=bjIoeQF{$EfuAoY= z83Q6kT~$qXkt#~lK@adac8hA33rWSnYR+Mm{g@eG+6R)Luw>Lzaev1--k7+&23_-v z;k-1%?Nu9eu!q1a8_@;y=76VbsPWh_)O6!G;_-aVPc@WF-*vEP*5`0Q*($S-r}J-l%8PC zKR;lh4MG4^au^>I7;uYCZ-I?Km`Wz9LoMm!t!3j^pbpKE&MifahVd)|uUzHO`3Af3 zMz8UvnTZ~+i5uDh|MK6}t2idrReXqN`MorB{tFK!Me^54^4F{LH>?fCCFY*75sIhf zv=-%Dp^xi_p<3|#J#8ULM+cKq_!^1)O(cG&MN~=vit{1lobKQ`xVkmE^%B$R&9?`& z&~duxIB$fU1y+v$2xvg=0#SZhglC4yn(XV&;icH=%Bi*3fxHX~t-p zpmy3zO5sBwe+E1Cu1w|a*3?TZm5-dJM^BX?(X`iGX(aSWa`4&a$?=}ai6L~$D!QXt zvB`OTt)JP%3z52FK^M^bXH zcjZkm^Cl^7Ed?7602RaFvaC{^GB&9j9D;D&gT}XRxMsG!SJ;6lhykfEI3P_s6eQ>i znr$u6)3zN~*RaTY4K4z9GY1V!X#&a2a#GNMZsee_u(pjY_P5_zR|jyVb<`@`8a5Kd zNi|Kkmij~8<0VKp=NYi@T+mRM`5dVx`fl0e?~lw6?$g8 z{|9hln|aM)jH@NnmkOvnUZ=AJD5;n=tz*l;QwRl z&ZD9H-~WN%vkx=F*v8n$zQ)*h#+p4u)@m$Cma>#&sTn)TmOYiV5E?rr%`k{cND}fM zTcspRMXTSuKi}VX`G3y2&zWXH7+I*)3EPvz; zfek-f_;*=ezgR6sAa^Z}2cJJBBCz#4$@0(QxxbtD&Axt_2GfA{YhNp;Kwn(JgQu1f zc)=o@V9PD22hBojlFx5zK7^*ma3LmcSrQw{2uB7~4xZsET1<9AALC@`8giQ0}yohfHBjN&p#Q5Qoj^?T4a_Ys(Ey*W#fLw z#_gf^!ApY*mM!};DL-^937YTqvOx{EsImGXh0bgXXi)OQj1xc5qW|u;hW8Z?esLH{ z$1?fNGq=Z{ce4~DvgkE=oeX<`^6w5lT}=dPmgKymed?u-84sx5JFeoWfl!gj(97%Q z^)iMKS&w>C5WidrMJ{?rRbD{h@fP^{j`Q}E;@uskk^9O=VJZRFbs~=*lIBIT+6-Ec zI><(=r9)51O`ci2_vzU^5d|Kk#3w%8J1b%b6!lpF+`XU@92Y1y645uRlXA{i{3w`a z!I;|&Q7sO&qh4j55IcR&42@$ZHtdx*i5#fgpm)qYwam5da?_GESXzjOJ zJEVd5Tg*K*PW~v;+MocWbo)#(c|cm=A)VlKM!T_>XLhXFJD~uCP=1Yi$!24mLr2d% z7DRV0#oQQsdv$eJBXCkhE7^!*egt`7wjN30ABCv*91YZ6B=jm~RyIjY%Vi?*RH51T z8UMkFB91sacOv* zvb^#Y&@|6)4^Ea~90UI$B`V>$N8;PBnwmG9gk}ZjAsA_1-h0-?%Zyo@JX@1$D$jE#bm1t@;SKHs=|U)UGXg?LUv>45RZ06v>~NaX;+O{7?Dnu|Vdt#XOMT?6UWZi6x?x+)NfSgOEp^XGE*1>IC6 z@rSawTx;g6PO`c%NqhsM;Cx!*JVZ3YjJI-II^7aPp(G?yxmk?Hx0(k9xd$fY&8?u8^|_1k z#7Yj~sv^3)vR*s8TsoVqphln~GOn&AZ)o zD9D{qgP*k>caI#ZH@s1A;giJoGzl?RDKOXQ6MyeCAWebaI#d?;CJ*-J>v!l2MXYnrj~ zBFJe z)JW>WlZG5vVUk{H<<6YG6Wc7xGlD+|V~B~GZS}oiW7?KMf=`?{DlMlp0|J}bY!Qgd z9?sKbu1`kZY)?WJT6Ood+g7v(|=~#PQH{BHL*zljJOsFGfc*Q&%xr0Ie(8OM4gRK^t@~nc zQ&c4VY(<}C*rIdkhE?8)tzW5YTb7`Run`&5mf+(!EXnvgl+}9s2$AGG9k6?&$-?%r z&lF)-{|e~R?R7KJJ%Hp~PbvTS+fn5(C*{g_4pR8(e?T3NzptF!xd-eS(IHys^#*`M zBQur;!N~|7Ik%oI0tf`wiy(uN0SFi^D5^(vS+Rsm2=QZ`dEnW($fLCro^4Bm8DN)2 zBj*B&MFuZIgC3km2Z5B_`U3r&>C}vWp@bl38MeycS+SsB^wIo=nxZ#zmgfNOktcar zLqYKsd8~-&y_j-oV#6WJBQk2DxBdN#O1(_sHHw?!%lsoXdDr9dt;G1f zFNRl3zhO=ogeJHdQ64|0CH(xp{WAA4h(l$94&iZdX1r(den{qq`bN;^>(#A9A zL5%*!#2~jB+J(tut1`*tb8&F75(u?3OWmoD`%qH=lwM78PdngP9(cM%N98rG&SGrS~~ItW#;5RRR0s7vW30%^ixx<|7ORjjag#S*4@ zk|0ub*+<8@zLAKhLt2DK-3s zn;k~@m@P!9HAp-hF7&Yjk`WwqTLwXL!|X6_(A9X*ibaT;{8Fwf&e07!vc9J4ce_g0*> z-x3uBCNN4bjI~-J@aWj33YW3gAZ+MnBvZ#Y?rq8X!K0Oq=;KVptNIjMz$)eRZ!+g` zLmNOCzy$IWj&U~FO6=4nLGgYPl+wa^q6GzkB?H_xB;_6k)Mx12)QPUX_f!*sW4v)KC_-Dpn|+>mudrqk}1^7x4HnHQgacM^N2zq`<&@z z=JnNPL@UMx42aU9qvYGaZt02g6ilQjxJg=j+L-=YdcmV!IE)UB9TZJElCY{N0lJK5 zO5(ONY<>OtUgC!ok8NjpEgbj@KE@BL7jI{OxmOub!3zg@@Px_3jv~`M`3b0Jh z203h9bE74rZGzz%Z6Ld}Ck}{MI$S9yA|DC_h(xo;Mt7=`L0Ew2b0xy~V^uCJSX~-N zqsr9Em)_iHD?!n$J9|7R@C<^;aE2K=(76RhrVC9{21WaLp6w4ogimax8N4H9pD|*& zLrLZL-4LY2%hr^050T<6Rj|DLJlD4<5~Sf9Nw}|u%Ov0l?4cx75MN0ZKYv3}`lU3u zB2V~mUXc0BoAHao(YLlgLi9`5l26efoUjQw-A5cwD@9b%MQ0{Bj^PSm>Co9Nm~bDN z!|MYpf$&UN;@|;Ux9=K}V-`Pz?W??I53RCZ)si3w_k^H8MhzQ57WM0Df9dRNaaK@;Zok3;(6J2BY z_G59j7$A)Is_U$nd@um;6T2i*0f#cSe1-^Yta&= zJmxb2c+#OKs-mk<=yg|JIQm3Jsvvf0&SCbjz-1bUh;GAN+#i&Tna>b(Ugo=73Q|5B z2|Cf|`&#N)>sc(_mw(q&?aGA4Cw``uwItd;Y(QkDQ=kaDl;a!wRSHV6y5Me4y_hoK z0HZOrV=9y6*J8SlHjLg3AY+YpFP-C97coPyTu&L*KpQ_(jD074<5xW&#IZJtaCFmz zsFLZ??alA>PsSG&YEVX$A;a;!MVZ}N$ZQXjC(rqs@s4(-#q17@VMQ)B5fHzSLL>6)^hW6xar*d8K4`*bli+I zSXo%lS$H}3Jmuoaql;g=F4kcp&Uk!ysE(#lV;<#T3p; zpdfD;>&8kwUja%lNDUTnBT;P^R3o$ZgHpeNRS4b|ae z5%IxSo(eKY-opO?Bxpg8HSG<%rd0>ve(#CUpUWR8cVNsAJ(F}psvy$|#v1{-R$f2K zemuYUcUKShXHGvfZu*GTH^Fs-BjWI1XP)R+JrfbOBn~x`=L7((BTO86XnbV7 zh2a5r?5;nnBu4|Q(H?iHsyzr-4>~1s&<`!zFcToX08=CdODQAgmqD8i^mnQV!#y3(w*cRP>0*9W((n0TMR(u%lsDgQNpYviHYNuz(17 zogV@U2-ll09sBZ&9r=@T`d%?m$H_uV+P_3zQD;YGO}fKkarmr zaT*y{CA>gDDp;Ut49o$JUI!g;K(byG5(Hm9<*q#(DV5DGnDz=8fJRtyZpch=l7&*1 zKvK(&ND?p243$KBT5;&>)F#q~d-^B&v@gB=JdvP2cv96)eGCv82QHtWi)$^p#YR5D z0njj#l!X;GjRbHYl2gba6_{8(XbOH>!?$r1Prew`!oj^PZgoV$G+ch`6iBQ7S7W5u z{I-~&^lzyg`4~G@G@Uz$OU4nV-NeAVvOs9U3jYeU(G2TQy}leI{;9P(EkK6dD4y7c zah@W$X-Mo%%02|i4TaD9W{JI3mpZ%iEsFOvk|KYfSK*VTLgti0eiTqxplIq}daFRZ z(?QdY%yFOvXM6mp;>z%!J8F<jshp$J-QAPL7uvsB;N2^3P=qP?h z7aLt~0SV92IV-5bwZK>2?2#5_H*2ap$$`+ox63)@X%HR!>*`(}X& zxG#rY5*j9RFeyS4AhEZ16cUz1n+CaQ)FHVw7)??TM~B-1BAGAs@{j7@Z8JGYT{79- zMSkaFK*#l8@tF;^0Z4v5dlm^&xR8-)CdO|rJMF75ZGH)`&_2)BW-ZLzEU)hBU2p%O zQA5<0JAa~%=y=#pZ3Xzv5)Guxx(=bhr*Tt$VmLYqodgmmqZ?BoZ2e)(OsKyWP^si+ zf=D|MHOK1^Cm250b{$UbF2Bo}%^ zzGhmdXRfwaXpm&7_EFtV?#gc`Uq*Pl4|un};>UHmgbpk{_~tYCihsD%_l34eqUG^7 zuZ}OdpICo&V%zWDO9YNrd0_p2xxA%Kl!u)yiCZ z%DfiAr6?T>t3j7rtx78{Ks*GyP+6E%@r6SGzBjxu( z#61pGSVf8K9U|z;8+AQNkc!@3_R{NCGkL9O-hIk&)mL?#^Fn_3)xD~WRR_4mrPp1T zZeBdUayC+N$j#0|-PuCha|Q5)==?opRr)&i?(6xOvA9dp@!h(5rSA-GUrc<{oyd6Y z$(9po7KZ&L25L%|hmtSX?Aau}O8m3O?jJk8H*ETG#bjsjidhB)Rx}OPrHJiQUwyAu zV45#@B%LlvlGIDqj!o9@NjBb3Hq%S7icPt(s&W9_huZ3;y2qw^&1w?clp3|g>906_ zdeXx8)Bag;aUPLE5@4}pl-?c9CQ5FAucp z6Ka6WH{&N9NOyI%KIK$@>ZfIa;+q0>p{!V%(s*gwpM7e3P$;1dX5`o?0KsIDF;BrtOo+16WSY#^y|=DNZ9b+_>|V#@HQOGtow zcjA11zdl~>)a^YvpnxQ{NapMCL&V~T43s$ssq8YnBG1txvhD4ge&e+D+YGzf;l^4$ ze&GoMrll$rXJKMGcLl>)RZtEz(S_~@#eTLNYl&+@t+d)$C){J553EGW4-!T}#^D5k zWe_&Ryx^1>Dwf50FkoAP2Z`lS;Cjz8HZ}+k4&!SjK^c{8s>xAoD9!v-g}W78LopL91>l(=VGg<=@*xn$z99sfppwEdB7q0_){9MjF<+UW!#UJ)}0z?#8h&xti&#Jyp zNM+=bOb?`F`kK`~(_R2EWRZbOHAM8a-G`T~9y;-o1qLKb@WLY805@yCBEabJ0ry!& z2viI1k1to}Pp0$q;->ws<9^rcd)J7Q*wKoFWjwYe<*pE*-6bi6q+!rlPRm0(mA;Y= zzc)l(gh>Qo0JH%oJZkMfdJ*YtDpTVp>|8cS%*=*#01k{FQRF3dJkK(%yC^H=&j-~u1fSGgGXgq^&s(P zQx!iE)*jzO$a_dvyGYVR8ZC4=^I6*zT&%)a(%~d6-~>=WwFZ*RU?93+haX1*U~x29 zz3#5UqP$y>TxPo2!ssK`2dwUeXh11`L{-EwoQh#q3O{+?{&fG%`<4iLd-CwhxFaMn znM3jo)QmjG_?DM_R>gL|fmGr?%w zqJNrv;oay8FiaPY5Pxx{7gXSUEK_{-;pNmbIzd-p!GaVB`IzRjdJ9Us7f0zUQjf8NVH`}uo_ zfxp7<--JdSAZ{A2Z~D#R!;zeBLMw+laVPcD-V^r&>Y2yiBX8?X>+W58yqEackowFZX|{}nwiP3?A@24E^SSPVx-*a)oY*R%h;D!*i-@~s`6@Fu?IhWKn_OT5G^?RzuLz7kvn7F1LO@A0s_ z$g`4kKPanjI!pJ%;G5p_&+iMy>R9=18I=OP~A6gbd*lh~_;)ar#V*H~V}Hg5jB>_BspQ==T$Kf_h{} zJc0|(OPl{j`tjw@Z!35(fYE?u%$)QfU`GHL-8tIw9L!|@fJH^i^pKOWCwl&|=j(Ty z#j$&4Sk6#SB8SBBY>iy0P98E?r@wNEt!RFrdtFr<)~gBT0NS66Mtf>r<3jOIP>^rf z$qcjxeXHmiam;4rtfarqnhSJaLo|^iWf0@a_rG)|KyvvXhI1$n2`D%m7S+;k|&V)yCaesZZH7*%^jN=UTKLJZV>nwMFx(c0HSbDj!=uSl(XGnwW zqt!K>Ul=<>B|ApPHasl1_Lf{8q9tW!1x- zsKxR0-hH~NR#C%aN!bI@$R-YHM|6}eKMDN4Z`TU_)XkNLV>O!>>WWseBz3&z@}kTA z6K3`g|1TrM^4<@PPdCA8l$}$+kiX1ms?WoyxhuB78jAv*(mLpQE7Q-)Ro=@e3eVs^tp+FcD z`}Vo_i+IX}cF|c}_n>;^b>e*C7SS@`I+z`P;-T2+x>Kg}7SJ3MB&|sx6o7wW7W}if zq}vfOXyU7xK0JMJyB}=hAN?PbF(Re#54R$MK4=JK2u*=#Zs_v_sFyC|+Hy~2G<6&|P~MSXCbB^R1Ms{7a1$-{4An|At&*LFW(T)~fZY(>pnxhp)pd+@ ztRu$wd$_dFUe=SnAS(l~Q6=|EJ$P;TyVlwV8KHUQ9FC2utV_l&rW}>j_OAqt?19a2 z#&p0xnpqyXD@*JxnObr76$vaw+a@V2ds=L_l*awN)lHl)iT7C-L@ULN!_)7!c6NRe z0#7(?sEk5ntsvq%Bol0UYe5=s;I%A}$XTShwhs}&M3w52TRiYVWEVQb3yQXFByl1TWLAOYuk+mA~w5i?<} zRA81pwiI>BhMi=5w*H?aBVQm{XmKgSD?GU3PL#aS8i!O|IVaTbLz1Z=HVx+6s$j4^ zq~A}6n35r4fq1mkE%h|n464wFyn#vVB+5j^k8`{}*dIE=WrhVE@O0%-l~8$EQL_wG zmIBC?_<(XP#*H(Rk!fOEtsSnyKyWsTS)plK?kO*dc^Pkvea%u#@D|8wTYL6A)U~#P zB|Je+7N{`8TvY|?2mAi{3C-g?R}V>A?o(v;r4%9vRaxd>me>&GndZhc{HSzm)=dLhpxK82@aS%}pGGD67zVOtr&Ow^YJM6_Dh>WY7f zeV@S_t6sRc*wXzdKJgs2;34}RTT=o%Q#s!ykJTI^0YDH#mQNZ0M*~HO;S!Q_gGK4I zJp*($V-i>Cb7Ssef}$7Yii>_#oA??4o{;1|X~mO!(-o#d`X>d|mj_0&a)@L`9g^hF zcYR2bV}FQvw$tw=L&h}d2H6{0Z6SsbGCvF_6A5ryLuSVBDtBe>^Ybn3H@{@RH#(Asd`@Eqe?xyDq2eY| zRL^@%?NGIa<0iAucGO5<)@|(LrV3j+4(imdtviI4Pa)XJb4A|kD8m%+J-jvcW20aB z!c+*hv8-`${)Wf7xEbzp?y~SZr?lsPs#tb@1vO!Fm05d|P@81ESOO1P<-@c_Z|2w7 zm5uAGbF#w6gu)`@LcK#pq@eggZGQJc?y(2sZ~u1H*BRGE&I>52=e;TY@kHHS#7`o8 zBC7SdJS%-#*Jr)uyUp}*jVrqd#g66?gQlvCuWmo)B;WWS>H1SyqD2-cA=o#KH1nCf zym4O5R$kZ_3!oz!*j1bs+gnuOLzY6XdGGcN8DBi795gSr((kSM1xfkg%f;$18)Yiz z_-8T`D&-pVUmQ;2&t0#O)KSB_<{dnO3OM-4kkkjjAFmZ?l^*%Hg^O*k5xZA^bL`W+ z-I#0nPGP-G%95~)1oPK!5iJ`wSKAut%uQc0U|;7j(1%Cxsx|fdm+t5$`Vdt>!V{E* zshW7#`HaJwGOgR7ykV_+m*?hVK#rNGwAc%UHG383YcU-b;(_&}{=&PZPZH|TQ(FTX zQ<2wF!cS}*yZ&c9b?5rly~SVs9OK`zlPi4gzt^8El<5|)Y-cb_tuQ4jSZ0_!W@XzZ|%Pq7iOD)`)M@*ONL>{E;00l*00Fd9seq**Bw>+#UCe zYAIVO>pOLD3ahRV;3{{zKrVDj?i{bYrMY}el6>5N{AI9$i|f@?UQ~L4!qq8-RCmgA z9mS$3#g*5gO*{ZMo*J)9Eyi+3kV9|JV$50KS6|f#v%HhRidr=~FAsAqPT(F*n5z}0 zit3#^9-H00Z|>9vI8UYMs7uL4NhwN++fpz!L}*SdhZz8%Y4V9X@>HqI7pOTR)u%Q- zcPJIynDfNKR!qW({lBBFCX+|f)$0KXY!B@d1(D?Zd+*{TR}w{G}sxY zPKOv`b*%Gooz7h5bP&fD#fGXfHlQ+IAaRBUNQ|iL0i0%3ehxDH+3OU^W6IsGVJpA^ z!bbuAwJXuRJxfLN(mlQ6!qisP4zJC=@Li&BjoRh>3hd0lYGvzOq&QuonR%ceV9RIB|q&v{{f zJzBC{@MYw_!d zA;tHtZ(Z2V3SHMG?-mCJ=mf9mhpp%+cVo|{t6hlJh3DzU^AY)f>QXiJQX%N{=u%gP zUJgXRz~cPE-P_-6E@v!3SJ}@aAlWNxavt12utt?${D*$(T-4^p9X zaEFhz+@Ly6^*ZT~y1|1&M*~jy#+^7ZChRwc40r&X%F+$?u83)c>6L*EX)vL7N6Scn zgNAvMC*iAS6d`kX40360fQw1;{$&fVpz`16Gi--cik1OUAz6(&2gQM-1}M9GM-K~z zlo`(-J&E=+UEYmdi#FkARBf!7Y)PAbwE!MvnWCj(zh0UuEAg-+hL98scnV2TI!2SH z>{I=<08-XztUoK(grCT<$5ODC1i+T0nR3#dGbWLC=a^Z_0{M5xvJQ>&89ln5b(hny z&BBmWd|03xy6B8DAT@nCDmqdwarJ?~x?MBuJn6GRs>SQZ?KzWOJDzV!4y^V|`<%vz zkGW7vSkrxUFFqb zf}mS5L2_gn>y!4@K~Q9Ht@VU3&sk=}la ze%-^#-FnJJO-grt+<0QM4oTZ_whx>)W10(R^4YX@s5+Y)NJnO4xwj+4?c1!5cXatN zX_$H79J42SwdeaNHUDbYb>+GuL+i5mKuu<^?ozKb^ns({gSz+!#JmR~zpR>mwGn;5 z_E=B*iTZm{?L>Q^E&|i&2b}ae2L(Lp&f674Cb24`p0#n8lN&ph5zpT9rB4{rtS7J$jzqC%^_9o$dIe56>!wu-J{5T7W(vOKADY6 z0uJ369{V$$$J{F+kIaC7HS7p8b(hhT97x1~hPZCdC6;|p^}xRrHjVS$$DSfV`;ehy zHr@}(y%Cl~t{P_;=h`4cl!>o%5nXdJZ5>6{Iiq1xi0h~@GDH?T(ANj_I~;v(SUZ|M z{;X;IJ*2Z;fo~K2V#jFMSJv%*l)!hDqd&6wel3jtT}YqBPC$-NxLug=ESd;>Ex
sxi4Vzx%dG!<~nuZ-7 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 M1@xxyEYR2X?qxTt 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 z zR4ZPM@IZYQ?r1ir4@ZSA$Gv1xB4~PipWd0hXW=U@V%DgnchLeGx^sxV`O5j_Cg*x4 zRN(Q@-l)Wt!%$%sZbt1xk1^zi1qiHPM;QW}0;2*jw&(>09<3^u$wA}9P^|aWBjO#t zj=?#L>yS463-<}HVwco@u1sp2ow-*m@6i=q@Lq%jm$|UTfA)l%sNs&d$d!UZl~Ct2 zeqmkRIsN0|=_(Lf3+I2CuNb5WQs7TP{;wFLoAhstA*6^Cu@i--LJ>@aP`k>MBte=W zMY1u;Y&`1WR<4;wV7_;+Js9jit(X_dv9D7TpWC6b7_ASzHBz(gMU1$$ikca^>qLKR zo)?RAT!Y7~Ih(`iQpX*co>Y12U1u)LtzvOC);$xEr+RD0NBn>`652eTkDDUxa(5aW zv`*BUzTtgpyfRc+v_S5nz+hRlu$bU-PkACt<@IG?X0)t>$54>}<@RnIb?oq=xx|Tw zegNO@GOL3qBBdV&6$-byKO9sa0y{y6+ufEOcYi9Ze;|A3g{;H9B8h9AMB=qmZ^>YtR=6W7Z#y0W5U|K{#r>Ea$uGq(B3KR%1=XQ$ci~K2(2E1$)AcZ~pD|}Q z%3neuG^4IxjqU93R`eV*nh$>4R+Z&2v`|i)9tcz6xZZ5}{%n1O3VzSK7Mk#!%y>$^ z%XfX(aor@NX(0qdc9j+K>SKgb-!*YA<>uxzMizJ;QfW{MPWonnuWemD< zC{;!~VjUH}-jglb)7o8YgBZGwVGYTh`yEiwCel&(9vfY=#FCauf6yDN|0^oehPyyN?K z0%14G&@%oi{yIg=@R(^UUemPaH}zSUHp_E9f%y#rg*6uww6dkK*x%|81rW4#}=O$3;R`kZ}4(_)?@}} zb9}flxPI6BS_u7a;({*(M8SYy(dEoK_@ufr43oO?sAk+HqP33su!F5U_tN#74EI0V zrL!r9DlMLXA_9oQUs{x3r0};+TI=j&s=CPS?1ozFtl(B4nlGyH)L@%5RR+G(`zC{t~q) z?#KSFMXG^|{Y{l-aY!R1zWm&(F)d_>b{}Z=?E}Lw{ZQqVPT;zLiMEyJgg88pfmg9T zz)pO$lAjSRGeMSNg94d;Q*)U<&y#2_L0Wdd!sZr%oeRxYkql=+N-80S@?@XavuXvI z9H{%_+M?*Yf(R?y4rQ0H_JXdT0%z(jq|aaEX>TIZoe@MwDZPk|V=OSLl1P~XJHXLM zUFp!y?(D7;8%Uwz(AIZ%8BY(Dj$G>WewqW1kX{9yPnBe60Q%;rI7YZsktWFQkS;JM zF`!#t+M{WCYnGSgae9Ob=?-^F%TWQ^p@;myj9#rjOqU$RT9BuE`~i`;PntV%fAslH z>90>2X_u}LLP?2G>m-qCA!Y?KstabVIo1VJ8J4oU(6aL4w8x8JmlDjPxv49WSjc2>nCw6vnqfXc< z_2R8p*I}sHu5)PlF^q$?8tW-WISwEnfah|p#65FDT+f?t%I7H+__F!tCImEwVY5!k zqC>FgqQSS474M7NyyErE@~2lzVl8J>KlHARxa{_9BPcj#Pznp>(0ZMcFlv12wkPW@ zwTWv?4a;!hFz#)>(KkN+THO%YhBiob;AH-*jp?yS2>#2F?7ocS@7M+g1JnTJ z6>KL5yHt3)OBp6d6ep>K=1~?mq^0W#x}lwrYz6=kdm%Y9&I+U!9N{A~E@1^QMqAbK zc6G)xA|?Bm=NxL1s)d^Uf}3NF#xu>hiT?N;xnxlV0juh*$!;=Oy|UE$`^vi>3M{rp zm*lMAbX8xutJT%>uG=FHU$kIZB=}X!*-Lban0$&SZxVY#41sL}${1C2;461e9Fc+0 zuqntC-LvdP3M0_~q1LCcnfc+DNHz3-089SXx&Q5h-+{~@=y`z_@$q12;Sc988Y1k+ zE@R?QPMas&@uySZU=c$rY?dfIjYXX;cUHNa7RRfki1#=77$RkiQp8<9i321SgbNk5 zYM6>*mu$H}??3>7kttD|Z$+4sSlG`Fwm(>=k{E>qh!lKPd>oJB>AoOzSGPS}#PEkP zYNiH*k{Z?su=fdJFOo=A1hAbAahzNjsNnqgPAnWoNkD-N$#L|;U5Ar8<(LUBg-wkx zs%YTKElm&tmDPQhxjg$b8#njE2nhkrG2~4u3^-Gqm+`|3gc9 z|EQkCKc$^=;Vu};3cz{DCP_;Uk&I$x#4XkdA1g3Pd@NfzSZXCTO@t*}I)KfmKMP>s zXkRW*hQLMKS`ISpB|u=9p}Rk;s4**x2x_0K1tNmPgcN7R2SvRG^Q`$XpO*n+1Y9M5 z5J2PWZRnVUbPovX>w{nxJ~a_-PxRn*@2hSI(gnlG#9;Ni?g1C-*x~*k0%=|UxExXI zpxYPUevtxD1=xe1w_HT6NYE)8X=-*eA-~LyWm7jS2=hNtidX-#n2Eo&7)T-ueNBO8 zR(Ee2okaM4tt}lW4ShVrxPL_)7t4AgAG$mSPC#+HkqeZt8-w|bLb{>&9*7M~045qW z5cWD23|EW_1-cBgxR72AK9`z_2o_U8veHBa3|zxrBBTjwOI{ESaSi$m-v?k6M{L7# zAy~nLR}I9G$>$XhV?|ybrIii=I~Ii^Drb|UQ*+DTrT5TBv5vt(y`VxJ!I$hZhdz#nTHq{qKME|60xY_gDO})y@CaR%f9}v8?KclTvGIL^#V-ow63XvOiA1s^Ui_9Tk&w z%zR!v^vzVQZLP~3matn*V^89d9>XMN&@m&D5~k`L&=z?-=|Xwmh?4!qDnX@Iw5qTjGKfR4+}?dg%U&ZH87$Na@a6i6NLtQF$Bq)HdZHI6v4qC2Z^OGZEmld%f5r5Cn(8`p#tdPq$o?xgkqS|&<)zCES|92bQ#9% z`tlhZYn|S-bk*^ME=#c25cnurk% z-|OZ^DaD(l3aBC1{WDk1;%V4@78g!@mcyXcATT$vK9CCtfqBr_SU$tjDM#WiTg`AH zOBA0d*$Yx`!pYXvlbj2r;0!gaIOGmOQ!fiNDLhS5d8?~!a$T{Y zQMg+X$OdCLa11Iwc$7%&)D4yJt{6bv{u%<6AWCHnufY`Zy}$5K8O(T-!NkBm*ilrR z1ZQ96(e~|UjOE}=cazkl-9&@6bE20~H1>tw7IXU$%}3QkZJK{QS9t!l@cjSz!u`Mf zzFugc^J5vYH((o09_YsG-uiPA3%RRDO9=@nRq|#V%fv1_WosDN#}k1rn`~IBmZP{cp;m2CXm{UULYB2=0Cehj^FA(8bmq||&i7AdxkZMFe@Pwv{<(h`Fs(j8 z%Ta_9X*mj~gOmLf5qK;LCR(hCiSI~YrWr7*3H_%5SN2?^Q%G^A<>)~G$g z3#1)RxzDj-MsGs=CR*s#YAs4tqLCPI$V=VY?OYz8Esoo4@oGO=CB~?ASH zgDqkpI@@bsaE-;5i--QPDA@!y8B{&1Qh`GOvuE-{we^=wyd2U=sPd}mK^5ZV=(^eS1JktCe7ayk>Mha;E6qiFLNY6ugmxX@?qR1uY$3&iMD|-2#S)rcut^yR1ngjN$fTos zcDz#K92;exi>ZdmVA5h=>pT^5-;_7%XVTtw+|vlv+{_~TJ89Rkw&wa5>rKj0`zQCs83ZjU}&*cq#_!FCnexqg+L;?@!bwaWPA|1r4HO(Y!o0G1-xZtL8eo5 zqllu}C?vv}GLPD?o*_Ynl!(TOkT@o`FQ%9F0VS}Ftk)1@)?F`u2%>Xm z3~Zl#+lP>kmL+H@%srq&DsP=jr<6n+3K-QgUBYIHdOMf0Af(4$>cn9QP8{&~EhW>L40sc}wL3 zz`VLHa%3Yj8n+7u19-lnvvCxaPr&4Ucyt{2Y=x17@n*#shFd}G+{_o;38t_aO=B{g({{1$A6kIo| z%+%T{R>r8!r(fjtut6XY^`kxfjV0|C`l8n7x05o?5h&V~Ji~M;!$4Wg*n@j-Aao3B zW95}t2pDRp$|KqWg45sy7hj%;qolPu8!1Y*v%HJhSu-W#^EbdBTXa=&zhwDsa$1Sv zTZUrSz1^Dza8{*J(a7EPt|)qyvb{tAG*U`zC&+w~?kqa<90>s869@!DVDar0aP!mB zi^9g2E*SN5Ls<|(Q7v_5%tq-Ph!E@J52y?gnk6dYacdBiC3COzC4hK#s1z#Vh%W=c zP*VS#8J>7S+w_GXtaSJ-f6OnE%-nTWkH2R4lWD^H!~c){pZk9bKqc~3=$2DI_U8!X z$(>Y0D`EaL%iefghAOubnz#(AGSk&m>k{>r7|LOx(~I?mt`0LI*373`aBy5Qhho3?q}Jl*KzRDW+t@ua>z+uHTbIT#jP0xRXNUH zvJ8Wzqq!xf7Aw~Oi?Or*iZcA#{WA$e4&5+xN=k=AH-aFkfOMzQ;*e6(NO$)j9qQ1a zfRqAafYM!33LM_=d(K(sm$T0K6P~s1=f1DK_q9Kl*oTDT!>$msK;gCj^IH19{x9jb zU;p32W&Hnk7ym!{IM8c@{@a%Y8o6>STvFMS)j8c7mLmJh)v`#L-Ikt0qQS!X9JD%{ zp5>~<{?v;4UX}XLQt~GRx2YIuVB(b5Uh{N5s{k0OmRA$TJ{3vIQWZy~vGgAa4xA%X zxV6I!@NXA;tha7;&xIfr6|1vljR&61E5krwIX#%w2)!JOHw3U-!pCVEHF2n2qD zUBW4r;J^m4I_$>Hb!H$_8mHB*&vlFKUcWy?#rxU~Qu2MB`2M%4VWd0og5XEISS6aU z_RB&S_$hi&bIykM39iUJ-1u9O6cv6Erp#po09m8ZksW^LvxrR@EIRDL=a{!svK?(h z)0Kve6}wZ|-JiG!LUq%CW(|(t9c8o=JyM!0AF@C7@Y@mQ+y;#|v;~_MhG{k6{I=X*(w4{LL zP&%;?^HmjB6vbS#kqb=+zz|?(a*$eCs)Dz&AiO3Ozrsl@Nv3oJm<8kAz0r@AI0z_> zBe^ouCzk)yqMRge*D_e!h|_wCCSMRpo)C?|9RPX{`IfFt%sZZt_x-!`&a>$wPPd%o3A4T1i!BK)sHrf5<#B>&5?iChjj*Q7>!-!|cg^d* zV-S{+B5KmJ8dE=)V}*VMyx_9_O-OHP$`((;P`L0);9WX55AM7xq9Nn-u zf(A>tK^1$2zKk{D#&p`QgLayQCM!t-tRp^Ex@4EM?8# z)R&Yk8nh4n)LeLy`rz&ML-yds?H^}-lJAbQNd4Yee82p9_U_ysKkQHZ5Aad@`|B$q zOu6jiEiZc22I0qe0@j(MhyVZq2+>D6Cp>z=3W?y3kW}6~O zr8r~pOl>HX9sU!B$h(AtjEQ0LO~K5|NEQ1>&9Rq_=@XJq8qS_8maD&^oD1=9v#{V`7y-#!opSlaX+ z6Qq>Ne?0(u_dp12JMI&F!ykrC;+P4OW0-$*00v*kR21|FkSD}b zA_+uORk@^!U=@lyFd}h^PlfDP9TUzX2cvWj#vccSOMsvdP z&IPtyzS&Mj>K5}LwY|Wbe5Sfx4rX(hWX%O2m(Wivj8Cd(L!G1=f`JS@ zsZw@s0j9s((pqA8^ysY#0_v0zYK$1y|B5{vXo8%2sj67BzM>sSxg|uavNKVlLT?lE zwkE+`8F-C%rMc2%KTmQ|WcOqj#Orl+RX@ncN%(u=!}ihk2a|Uy5N0gKirB!(0tV4m zMasya05zfqxM^A}2}C4A!^{<7m|f$^7~`cbn5a)z`LDJHvP&fLikFm^(i+6^7^i7? zIi@^aE*$XIWvb=F!^ZH3b$0HXW;0!wR{ElPu%h0hb#oJ|JtI`?XMR~4T+F*`acsmx zUis|>y8FefM!H*LU_a<0?Tt7)k*ig&H2gA-V(e$MxzV3Qap729|?+Ew6@SiB!{qvD))Fg0OINY18$SR!VBL9j)Z zb%Zc**mvcy*5$A+K8UDdb}X@sqCYIOy*5@nwYp{5?zhrWT`M=k7pkjYxO`3R2JcqF2#_&Aex?E zuSQ&4*=!MW9esEm{|#rM!40sfiNqE%{vxXlU=;8ZNyt^f+yZWjz@7i$wCsguf|j4F z3q)7y z09KQ5*&+ZKrbLsjVm-@QvlHCYhXF3^OFkJxW2yrY8^V7=D504S-um4MNkzj5#&GUp zu&&QQ1Ni1FDpNwWeSD>vN|BVOQGQhDhPXg~`_v zt%TO@ZZC2dl@Hq5+G7~K(}Lf!aNsyj@;vNGG2i#1+j~eSH}5Yp{EU7UPh5+|Q0}0J zoNiDQpvWTV?uIW&tExq_!!tmvLGFWQXD(c3glc1L$BB*(b43wq1vdUijM;w zpFc@IrpG}p^@$ZFzl)^e;3c_;7}K^-#EM?TgfSJaD@{2F1D3Xo0ya$!~xU*(Z&+~?OAI>L7vSa+td8~+eGlIFcx>YWBU9%)cjd!S>bl37jR*N zXXMM0p{p;6*V`@Otn4vf#Ay=nxdRfn0|A;5l5lJoQvfZ_I#OdJ%oD~(JxR2vFE!{7 z{8A4M#YAjK0zw$#P8?i9AN*(yGl>Pr=cT69Da2!<%&Q_`y%7pAT8dJU5dBE?kuW`{ zm%3SINup2#sVI~QqiaBf^hlH?ezeUzvz=2kg%d{PrplAh8lsH_w6TyaR=(7Em^KQU zAra0b0v14Bp`& zeSSdd0r5dHv6lX0g-OC+MI@~#nbAnTB@F2@4v!D>;!YMf@d2T3l;;S0!hlnPr=x4m zQ;pU(>$!O@2~mId3OTVX@?xntEK>GYDUv7|x@S!+>IGgxlD6@XQR@@@N+$V&enn;g z+|VXN%w7=+!$*5TU^H~Kh-ek0yRQ* zCAq<^BbD8pj5Ado+U$jp!GY2KiGkr%<|%Sg%day|o)@%Q#ifTobxaIJum&Av35;0( z%OHmy2=f(zT{QjhdI1evLFXxYYZ1SY6RHLfP_mFI*~AFO1bRjAWbgu++1ck{%|`~{ z5Np!@11etx@Ql|Ds!Y*Hm+M)b4e!lH{?3LOHdT6jW~7g=7;Z9FR&Q=u9_rA-DKi ze#<)FiY>PWQJb?eQPWop^k;d{S3c1KoKmtwj7&B8Dj5_j!Pb>5m`Y=U(u@ahs8+ zck!@DC8A|&8n!aaFKf^l<&#fqs)8y=rfV)@Yty!3*Vbxde_2t3E8cw7P#*Lb&8keG ztPLKnd$Luxj9>TOptenhe3BsibC*3&R?Yj%ib}1zK^Ju)scbN$L9D!X7gl2w*Fe+T z(B@tD!KEfvx>1ua@jc}BMHTjIuYTRdb{bT_eA&S7>*^;im&-xXmeY6@)VQQoze2!> z$ZdRUPJ_2C>-Dm(Ript}Y=A5_?GSJixHb_mG}6`jvEsd1pvNXwHwJOP;gEe;FZ0Hs zrkYIl&E8Fp!~kEj3TKu?aPyr*&`C0>l5C5LMT=T+i$-pXR(FfeVvFA27JY`+2ePe3 z7Olp?t){uH=H0E27F#Xw>jmu8EBI}cSfRX+q@XsePr98TD1EG zw+H05>vsb|i|wI*+c69s+8=N8RolXYJ7RJ>;<`H$7CRFEb|f=&rV_SA1|y?oJF{{- zbGke87CQ_6b`~*oJ+|m%WNq@KsV7*)hJC=sX*OP|CSw1-S?aC*UQ-|B*x;$uecahp z<*GrOTdm;Q^j5au;6oS9-&}g_Cg%W-mfYyUk2MYPwWHe29Spkf`Fj>!dPpC<-R%g! z$?2-U;qTt8?k<4!Y?``_E55_lygQLbz^(Csb#g_m0!MJvS=J8e0$SXyQhuY`AgoE!{$`_wnv+SuFi$wQ3LZf4sP*w z@oSyx=VmguL>8hxijdlGGR+IFeK3n=KApF}YI}JTa8hpX$@BUNiEuw;>sHKf;Zgl- zxmMf)^*2p@wKW6t^m41)@Bhly_W$jZuY146gR9x@KXk3{a%~_8d2hnl2dW!n+Toh~ z+QG+Xqz7KbEe2sBiN zG}P{d8xT=c3oxk`3_Gm{nt%MBkac zPMI}*HNv6@80AkQQaJOx2<&zfY;?a=>VF2OfTEFjWaz1ZMFgkC?NjIWAHUk7a-7wL@psxis167Px@EnE$*+p zT!gS!3u@Cys4xfxK$G;rb!6JN*zSq%)F12MkwsJ7yy#_APs$x=*#hU#ot>}D(Cp;0?A>8ID$Mh{w49_agAYK%|P`CHf#UHd=uO&VD9q-s^8ftYk!!zbOsEloXKo){p`$$heoBvjLv! z?(zKb^5_jrcIJ15P>Yls{0pvwz~TPuT>@ z!@w#-!LI28S5DMX+ zrk;PcnaHRyAXYEX9VD1S7}Oy0Q+EjDFv%i$czk?~ z|Hsn#^}Pcs%>L!Xeulxh)QfY;CwuLoTMaBH;Hla8B;b`$amB~?cgAp%bwU&jBMI4q zeTEcfshg!?Li7dr_c7qRH$Q&4>EbisHskd{_*gLbJtzr-kV=NljRWM?_)9|9r?0k1 zt*ue6lqNn9xHZ;C5XSQsBcJwzOo(8x{s5<$Bcd zMkS7b-_%~C*C?V=KoMcDY2K?>#sg<~7=T5C6)72zd`6c*l|EdaK_NlPpuwodxJhu* zT4Ow#`2LPYN9UVW?YAxqT~S@uIG~!5i!@nB`T^o&7M@+l3=cTsBffC}{Xcw)M1e?J zKwsF`c6bzWgGZyF1C(i`av8{Q&7Qh{N>1qlU*s5Vf+zh@WmI0)x6|Z^X~E$JBQ%7R zIT_WVSENivN+a0fBwCF7v-6RnW%^l}{3%?`TuN$c01-pTtfI5g*u9ryS`2_0Q9_Z| z(?A#oOzFlNi6l=aR3xQ5!SuEWcI#AsSL55{fKvm%QpjnAUr{gurbWdnUg%{~gGsPn zN2KWQ!y||Un?^)y!(j?LyBvT5 z`$*9;b-*+~)<~3Wkjm(vsJi6OfiZRI)g5m=$@PK5ak<;=v-?aJCn@(8@dlgql}F2L zZc5TaTs8=xr~`gj23zcM7=ed13=hg>U0x=;g#kJ#7|6pTD7^#5xnblPsB%V@c~mMV z5=D%EU(0l9S|`>#_9dyHyedQ!4~3S5Da9{^fmUwOc#IYa0|pw6sZYaf6qO|EEnmE4 zov$;pe>R2pMTz(&k>1g$@x)Wr3{14mQc+EPPbjx{sS5 zdr|4SKClX-`tn%o_a8zNN~vmA@WDz>-IO}}vyNFq^}ITj3H&Spu@vbI%{N5k(shD` z6883xNi@|TaUfy*%W|CVDniWRGp@n*wmn2B7kv0x(z^UE+D{sw&w?^|jA#Vj4DWXN z0zEb?9YrzJTmUHYrsgySe7czjZ9KYm}^F$H@ur0zya(Z zT>HZC2`3jJ@2=jFdPzOMl

$>I6m4%cNlDGy*hwTXX{H&u#C?s(|yJtx4xIafa|+ zoy?k?9ft8-OQo2WhU;jDdx)U}MN9X`{jKuk1Oo;_Zyswb6y{tReh(0FKl3k(D(pG> zD8?PAFHb7pq9@b-(;)aTID}DOc9^Cx_|Li_4k?iO(50L<^iTZ4J+2}b@cn`+%nWg2 zIUTDWWt6%c?v_F~Kruh_?#!J>o%k^0iVG=pB_C#xUSd`0;-5`~DZZf~%Sw23y;Mh% z;v_~l1~j~$=_R%F3O$;}#Tu%>zE*x)w!{NLK7dx;CDQJPqp3cF^%hnC5Fv`PDA zq{}3~^pawy5a}7NBBS#r)w#DqVjrh_^2ho4IzOF8?_5D*h38Y>5b86n%Z{YBrw=&w z>091OHL6}0J1y;4VJ}U^WaNHP-n(*UR7=kqhmPqsdp4s$9`DqQ0WDn2INSy__&Iv0M5 zS0C|{*@$;elIZYVA2xQi4oaLh)teWHXZn~G5m2cT;cKZBTM4ZX> z@}*|@sfm=^}Ju?C!`>vs9L?B8+r3PUp97Q^*8p$j!oT|Ew5 zZ}bU5KN)Guw+fsXKbkkbmEXglnjFOl9KN0bgptDr9HM$$0!nr>O;W?vYY%TufMgR~ z8U18a^P?i}_M!f)yrZ)wYs$IWxp)w=ki$Xyl$mQ=*v+3+h;^T$P14X-x8WsqcM|I1=`u$=CD)fwma?qysUvp81T=P%Ie+ zfU2l))wwclWrm>w04zDy>iIH{>xq?G0J;3|h3L2pqHFS>qvkFX==r*@8DVfqt>feA zds7&&UI~EkQX+vlJ>%d5!iZwt!WjXya!}DYH#FXoXsPp4hNv`O(Uk!bP$tN7NxaUR032Gjm-fVQ|~;L*wv5Dc-EB-$-WuiiFp# zc8l+Jk-Viw;SnVTP}>I{m45*8zBHR1CIiP1IvQ@T}gNW?Sy2YCT|Rahni5M9Ep;m zUMK@vB+1TkX>nmEW>b|CFEHbU1Tv4rV%hwenO6)_;g zg(h><;#tEVs2V>VR47ev@M+PL6CD(dl*Nyr>IhT2B}*^Gnz9aR==6l9fcSXIqEq6# zedsJ85Z)D#fN3O|HJSE+itdVvveba0a&zB@xag_~r@WyS+<(Z57?pqK)E07NUSk8g zD(ug8!Z??V4|k%XtJ-k7f-LMVsp z12K;ASPDZ3TtAV+DM}JQ9Ch&??+|n15)NOGJaP$da1I0ark%wkxohY3g(2~HFr9~q z0e>01j_FTOLU`zGgfuC)7r-vdNyz$v>pZEtdBkisXEX+^gre7UjnNBGQPzmWN7KEU zRrl?pW+xU1;?p8{GqyE)wvbJc@g&n~9w%y}Sy~B{rD{7L ztkVb^u&>*@Kh&KN-Mh=8lrBU6vA?prKQ&19$a}0DGH^6HRuwUJfs3WBx>tykK{`n= zbin8X0pu+T8#SB>0vTWgl(7+{0mB34b%w|&m5t&_#&Nv3$eXPAdtU~i1^}d-cYH=? zyoDAdd&@<~Mo#1fo?^_HNr9UueRup`Q-!mg;Bgm^q_fw_T^s4R?>Fm!H5_s>}3 z%FYBxTogL;>1yZGO-njNoCB#(fKa^;8z1_<6SLMlx!n>wCS@=uz}Z;^(o2@NPr1MF zDE@1YuC_B&PdHLFS#RsxBz$*Lu~}~}JuK2}5-uK@xPhpn&<8&*Rw*Vc6n@hxlU}JA z&V_^!2Lfar(??&Ee8k#w)6;B3!jJes^vH<5DcsHBFF`^aL>zWo&WAB#gcyK4Fppst zhh!mufgnNL7$Fu+!^qtx5bcAVZ%A+!qk+b2q3mV%`(MkR|ROnM! zn1%shH5OH2f2^ksizYwn3&87V>qPZA$_IDFjMh0G*qe=i;ZXnk2=ky^zZ~^i)!m31 z`sLFjl6_uEn{kpF$QS0Bu@52C=uV?2?xcc-^5sZ;*m+D^G6vt8>_DnjK&;$BHgz>Z zNkO^RP*&U!(ISv#Y?)64o~$f0RrY-d7`}N;)S<*WYRq1ZF-!ySM~v@=W1c_6h}3;? zRRvlY$Pmc+(5pF@@2P2%Em7xj&;B58qFUz+DKuS|jbS6An(V+vcZ=66jE%H-@aMZb z0K%6_t~1#LI;rR^u3ph$67%`XqlGyw8FKel$}o9xA%0U<#d-TcAW%FbU2N8ysecSu z2#b;z`iN241ko{No-xb`A?A~#F!*Sq+>E)_lh|5&b5$3!fIDW1gRQFpQ?&9j;E`Mq zv==6Y1KrgX{+RaV1Z$J?jk=*=@SL_XOW%LM}b+h06f=Gb{-M`rF8@L1?>BVVsR64G8Ur08j+ znr}JIAKx_{Lt#G4FMcfgc4PIYX({T9oB@li{TC9%z(h}zz5JJzK^=KU%fH9rh$4%A z&fL~Zjl!boaufs8!kf+V#dBFXMkyHIfvMl)M4QeZ1@oe=qd#`AKgyG>*dP6@G5DDa zSe=)bD+T_n3C8>*&anwp=6h?kyKCjzNq3>kaIR|&PZWmZz{0}F)7{wAg&!yN^q;OC zP38m2reU`a-temB9xou51&QfTYyA~Vc=`h%q3_gB!Y52BB{ZcxfQHc3y)?V;B>sc7 zpX3z3nwk0y(#S9S$1pa@x!?J?EJr$jt!4oN9S}VL za)030vU?jPldY0~?M<2N?@uRlOnb~9D65r3-jqH5t;~ugML%J6wK?bfRoZ{QAq#b% zy+q{4ZyAY|dlTVky7x%+0oZ87w*LX~AR{^y5jcaxJ-8QztZqjn|kj_1$&*f`_ z`|sDTJ6s;qiCZ&6drMr~eR{i}{sBQFFHem3|2XWQ`t6@Z@1MWkzo^*1Y}@}kwEyqR z{?*$4_38c%`~aXi0C69HB@ZC#2T+p(nBxK5|KMN6OH#ClI?4V-LE3TX0nyw6@%jPD z*#RluA(F-$c}t3xJfu+fCP_M2ZS&fT)*-roNSk>`S9wU^e#kI<$T)Y%w0`)<@(}T2 zjbMFSIqZ;G@`zpih{NQF)A5MQ--qMId#=HCmDw}U*piUNy&Hbi7P@kr=w%Dv`F2{R zaN>jT?7bN?Hr|wQzSXb7>pnY9#~eS_7Ej&RjAc@4wwTg-0ox^c_toPk@y~lo~%CWQb=;2lGs%`^B62x(OO^4OFU+T zuq^*@xcA=I;M7(A-cyC;6E&+pD~TDj-T z^~dbp3BTisO!khM(8~*Q6HozMYb0i}=H=f9Cc#}ajty3}?~+cU&w}&b1+*6i;qLwJHDMX}^sKrvpeHlnEq8Fa^*=k~ z5P^n&0_}f?B>xT3TufA6_Bi@=N_tMwoR2m(jkOCQF!$=)l|Ls1&q~_Q)4XwNkXLTH z`pNP9W^(=cWaU*$=2fTU^O&E{_b#5Fy5060epLGZYhbw!dH0V`;W~*vY{lwYsXBBO zZ)RivD$8?)o=FR`E=fuQNxABdtJYi!ZnhWO1!RWa<8+` z98U4@VMO=>JWdCwzDNQZQ5C7BW#uTAjj6wr2W)DQZ|YaR32Hp=;JP5o54a-f&sg)UKO8V z>cKYSh|e}(ipf=C+!QP^;r`^M15!9i|M0 zdw62iK=6D^r;DD~C7P7RstyZ&P$7(uW+8d^SHklLtXTWK#=kQq;uN{j>aBkqDYCJm z_AZpY2b0@MELg`R1?>=uxRe5JjwX{QbbTD+jmr)rLk;@Jl}w= zJ9dnLrjzPyVNyT1>|$v*O-o3${S+g})F!#M*DG?RNOZV9I77=xDt;Caq$)tuK38PZMa!`v$=XQSLoUcwH>oKq>9 zlm-Iy>TtS}BDhcTLL`lwUjb{*Rh;{OyzBXZ-L>SDnVVBE+)e7==obDJil&M7DmzwjXj+uESgMs$FFXO z4iNPoq*<)PRlD#2VNg1)G4FZUm7B*G1GjO@sacYkr_;*6+|Q;0_a8qlq}Mu+E&n;) z)Q?v$`gy;kAS8cObC>i@!vM3aCN zBY4SOmYIH7|2kI%#@+677Is=}sCVh}itt7SA#gz$8wg!Ue6)8%u%o9=Fk-IGsfJc( zUWqAzkAVAFs0ZV?i%4yg&=Ui+9hhC##^4XZ##>k@E*_KQ^UDGQB~q-ye+@*k5m64` z&YTFighau4CR_wDbu{FY7^rnIOj}9ecog*9Y1yLnlv<@18|O@?W4y*Q+^Ix&-&dLu zN0Kq$kB8C^bGHC!$-@}+kTztJBl=W^^Ed>vP@s%JA?5R!-irDLk09m8&%ef0;^Z|T z0(~F?xv5wUVgqBvCpakevp9M;3`JemSE$GB@CI0>xO4fLMIV()gj$CqO=p>8l<h&##Dgwyze%j)Ii#lvJ}3`sT4-X8nTsEcy^+qaJE_I7)=Sj{-7e!zK}0_ zILhjCQYPYix3GfsP&`!*#wqq>)bM$8a*r>$qJ0hP3&BY?uFig5uAw-K1Qgfx3JLli zf$opayVZUr)l)|PGkK|0G0z$JnJ7w_iX3i;DAvF!4`&gvDk?bTl9JEFSR_mx%nHse ziSYXtRKsurNXNz~Q~SM?2bNmuLLZgt-m9Levg61wMdQ%(?5qZM=+wGNK$=F?5P>O4lwp0Nx&(8*Cb=I}i2SncyS4oD zuTJU;?j7njT_sHnHYsy(W#UWI4Kx?ikJRy@+EV}wXh@MHYP!eai8<#Jy{|2q{P1oa zYb6k=bsc}T`)<7WX3MC=%}EYF#`WDNwp$*=Uh~1j z_$YObJa);O#Am>^+3W>)M77>r5#JP4Jc|Ky3n5d(Ug4}c(ah3zXpnrn1caWSDQQ@G z;!XZe5`C&x#LU;kI;T1Agl-=JA%Fkud3UaWz2n5OQMG=%r#6h+3||XmOrf0)UxKfX zGlxUgkh=EO@*JQC+a9emuQ>Z0`AoJNv?cVDAkcp%+ht;9VDfofr4-joFp#@^Vn$e8 zrcjI6d9+oF5+h_m!Xh7RS8${k}e$y&bl8ύ@onF} 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

Z98+$};5yBsp;AFH_t| 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

{8p5E% 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

@)Xr!_%G35(?LRPfBe8|Cs0s6|f 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 literal 0 HcmV?d00001 From 08fe603e81229636826aa78e71acf7ffb6014f48 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 23 Sep 2019 19:59:05 +1200 Subject: [PATCH 132/342] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2dfe463fd6..202c88f362 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A modern shell for the GitHub era -![Example of nushell](images/nushell-autocomplete4.gif "Example of nushell") +![Example of nushell](images/nushell-autocomplete.gif "Example of nushell") # Status From c720cc00e36547da1db7c8e15082dcfb734c7c56 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 24 Sep 2019 08:24:51 +1200 Subject: [PATCH 133/342] More 'did you mean?' errors --- src/errors.rs | 18 ------------------ src/evaluate/evaluator.rs | 20 +++++++++++++++----- 2 files changed, 15 insertions(+), 23 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index db70e13548..7e9c14b239 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -12,20 +12,6 @@ pub enum Description { Synthetic(String), } -impl Description { - pub fn from(value: Tagged>) -> Description { - let value_tag = value.tag(); - - match value_tag { - Tag { - span: crate::data::meta::Span { start: 0, end: 0 }, - .. - } => Description::Synthetic(value.item.into()), - _ => Description::Source(Tagged::from_item(value.item.into(), value_tag)), - } - } -} - impl Description { fn into_label(self) -> Result, String> { match self { @@ -114,10 +100,6 @@ impl ShellError { .start() } - pub(crate) fn missing_property(subpath: Description, expr: Description) -> ShellError { - ProximateShellError::MissingProperty { subpath, expr }.start() - } - pub(crate) fn missing_value(tag: Option, reason: impl Into) -> ShellError { ProximateShellError::MissingValue { tag, diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 8228c1035c..a111d3964d 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -1,5 +1,5 @@ use crate::data::base::Block; -use crate::errors::{ArgumentError, Description}; +use crate::errors::ArgumentError; use crate::parser::{ hir::{self, Expression, RawExpression}, CommandRegistry, Text, @@ -87,10 +87,20 @@ pub(crate) fn evaluate_baseline_expr( match next { None => { - return Err(ShellError::missing_property( - Description::from(item.tagged_type_name()), - Description::from(name.clone()), - )) + let possibilities = item.data_descriptors(); + + let mut possible_matches: Vec<_> = possibilities + .iter() + .map(|x| (natural::distance::levenshtein_distance(x, &name), x)) + .collect(); + + possible_matches.sort(); + + return Err(ShellError::labeled_error( + "Unknown column", + format!("did you mean '{}'?", possible_matches[0].1), + expr.tag(), + )); } Some(next) => { item = next.clone().item.tagged(expr.tag()); From 95ea3fcf4ef91bf3dc20d1591cb561d62b623af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Mon, 23 Sep 2019 17:01:40 -0500 Subject: [PATCH 134/342] Load plugin if and only if it hasn't been registered. --- src/cli.rs | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index d0c70876f6..b94f824dba 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -72,21 +72,23 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel trace!("processing {:?}", params); - if params.is_filter { - let fname = fname.to_string(); - let name = params.name.clone(); - context.add_commands(vec![whole_stream_command(PluginCommand::new( - name, fname, params, - ))]); - Ok(()) + let name = params.name.clone(); + let fname = fname.to_string(); + + if context.has_command(&name) { + trace!("plugin {:?} already loaded.", &name); } else { - let fname = fname.to_string(); - let name = params.name.clone(); - context.add_commands(vec![whole_stream_command(PluginSink::new( - name, fname, params, - ))]); - Ok(()) + if params.is_filter { + context.add_commands(vec![whole_stream_command( + PluginCommand::new(name, fname, params), + )]); + } else { + context.add_commands(vec![whole_stream_command(PluginSink::new( + name, fname, params, + ))]); + }; } + Ok(()) } Err(e) => Err(e), }, From 898b99d7c24723d06c2ed654d104adc8fbae8832 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Mon, 23 Sep 2019 17:27:18 -0500 Subject: [PATCH 135/342] Ignore incompatible plugins and continue plugin search. --- src/cli.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index b94f824dba..31017ac6aa 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -92,7 +92,10 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel } Err(e) => Err(e), }, - Err(e) => Err(ShellError::string(format!("Error: {:?}", e))), + Err(e) => { + trace!("incompatible plugin {:?}", input); + Err(ShellError::string(format!("Error: {:?}", e))) + } } } Err(e) => Err(ShellError::string(format!("Error: {:?}", e))), @@ -204,7 +207,9 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> { if is_valid_name && is_executable { trace!("Trying {:?}", bin.display()); - load_plugin(&bin, context)?; + + // we are ok if this plugin load fails + let _ = load_plugin(&bin, context); } } } From 2de77929399015b1f5bdd80d4b58dd7dbef62b81 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 24 Sep 2019 19:29:23 +1200 Subject: [PATCH 136/342] Bump version to 0.3.0 for release --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 45ab8ebe26..7cff7b22bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1504,7 +1504,7 @@ dependencies = [ [[package]] name = "nu" -version = "0.2.0" +version = "0.3.0" dependencies = [ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index e81bc0ee69..c59154eea5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nu" -version = "0.2.0" +version = "0.3.0" authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] description = "A shell for the GitHub era" license = "MIT" From 3dd48bf83149e9b5b5be126bcd0097ace8c1f80c Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 24 Sep 2019 19:29:54 +1200 Subject: [PATCH 137/342] Bump version to 0.3.0 for release --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 202c88f362..85b4dc30bf 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ We can pipeline this into a command that gets the contents of one of the columns ━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━┯━━━━━━┯━━━━━━━━━ authors │ description │ edition │ license │ name │ version ─────────────────┼────────────────────────────┼─────────┼─────────┼──────┼───────── - [table: 3 rows] │ A shell for the GitHub era │ 2018 │ ISC │ nu │ 0.2.0 + [table: 3 rows] │ A shell for the GitHub era │ 2018 │ ISC │ nu │ 0.3.0 ━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━┷━━━━━━┷━━━━━━━━━ ``` @@ -174,7 +174,7 @@ Finally, we can use commands outside of Nu once we have the data we want: ``` /home/jonathan/Source/nushell(master)> open Cargo.toml | get package.version | echo $it -0.2.0 +0.3.0 ``` Here we use the variable `$it` to refer to the value being piped to the external command. From 60b7da8ea71550651a77cf18ef856acf3e6281d9 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 24 Sep 2019 19:45:41 +1200 Subject: [PATCH 138/342] Fix help regression --- src/commands/help.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/commands/help.rs b/src/commands/help.rs index 4ddd147b42..c8b22898da 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -28,13 +28,6 @@ impl PerItemCommand for Help { ) -> Result { let tag = call_info.name_tag; - if call_info.args.len() == 0 { - return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell( - Value::nothing().tagged(tag), - )))] - .into()); - } - match call_info.args.expect_nth(0)? { Tagged { item: Value::Primitive(Primitive::String(document)), From 15481b7be14e4ba11035ed419ddcbf5450097ce9 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 24 Sep 2019 19:56:03 +1200 Subject: [PATCH 139/342] Fix nth regression --- src/commands/help.rs | 6 +++--- src/commands/nth.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/help.rs b/src/commands/help.rs index c8b22898da..e8c458caad 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -28,11 +28,11 @@ impl PerItemCommand for Help { ) -> Result { let tag = call_info.name_tag; - match call_info.args.expect_nth(0)? { - Tagged { + match call_info.args.nth(0) { + Some(Tagged { item: Value::Primitive(Primitive::String(document)), tag, - } => { + }) => { let mut help = VecDeque::new(); if document == "commands" { let mut sorted_names = registry.names(); diff --git a/src/commands/nth.rs b/src/commands/nth.rs index bf397e1bcf..18bb6f23af 100644 --- a/src/commands/nth.rs +++ b/src/commands/nth.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Nth { } fn signature(&self) -> Signature { - Signature::build("nth").required("amount", SyntaxShape::Any) + Signature::build("nth").required("row number", SyntaxShape::Any) } fn usage(&self) -> &str { From 8ce73d838efd11ba965ec97a8d3ffbd498624821 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 25 Sep 2019 04:39:18 +1200 Subject: [PATCH 140/342] Bump heim This bumps the heim dependency to fix an issue with sysinfo --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c59154eea5..132aad2393 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -79,7 +79,7 @@ neso = { version = "0.5.0", optional = true } crossterm = { version = "0.10.2", optional = true } syntect = {version = "3.2.0", optional = true } onig_sys = {version = "=69.1.0", optional = true } -heim = {version = "0.0.7", optional = true } +heim = {version = "0.0.8-alpha.1", optional = true } battery = {version = "0.7.4", optional = true } rawkey = {version = "0.1.2", optional = true } clipboard = {version = "0.5", optional = true } From ffa536bea340c3033b9de1a8bd53c8790b23c6db Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 25 Sep 2019 07:02:35 +1200 Subject: [PATCH 141/342] Add Cargo.lock --- Cargo.lock | 147 ++++++++++++++++++++++++++++------------------------- 1 file changed, 78 insertions(+), 69 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7cff7b22bb..8db4a18084 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -871,30 +871,31 @@ dependencies = [ [[package]] name = "heim" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-cpu 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-disk 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-host 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-memory 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-net 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-process 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-sensors 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-virt 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-cpu 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-disk 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-host 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-memory 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-net 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-process 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-sensors 0.0.3-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-virt 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-common" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -906,13 +907,13 @@ dependencies = [ [[package]] name = "heim-cpu" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -921,7 +922,7 @@ dependencies = [ [[package]] name = "heim-derive" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -931,15 +932,15 @@ dependencies = [ [[package]] name = "heim-disk" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -948,13 +949,13 @@ dependencies = [ [[package]] name = "heim-host" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -964,13 +965,13 @@ dependencies = [ [[package]] name = "heim-memory" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -979,15 +980,15 @@ dependencies = [ [[package]] name = "heim-net" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "macaddr 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -995,56 +996,58 @@ dependencies = [ [[package]] name = "heim-process" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "darwin-libproc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-cpu 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-net 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-cpu 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-host 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-net 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-runtime" -version = "0.0.3" +version = "0.0.4-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-sensors" -version = "0.0.2" +version = "0.0.3-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-virt" -version = "0.0.7" +version = "0.0.8-alpha.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", - "raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "raw-cpuid 7.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1052,6 +1055,11 @@ name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "hex" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "http" version = "0.1.18" @@ -1532,7 +1540,7 @@ dependencies = [ "getset 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heim 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)", + "heim 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2045,7 +2053,7 @@ dependencies = [ [[package]] name = "raw-cpuid" -version = "6.1.0" +version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3086,19 +3094,20 @@ dependencies = [ "checksum git2 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39f27186fbb5ec67ece9a56990292bc5aed3c3fc51b9b07b0b52446b1dfb4a82" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum heim 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "4e61bf22465c7f49852fd7e6044a395394962a2eaac0b5c1b87b5b0f010b0f48" -"checksum heim-common 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "81e38b3fc29d7888133d0ada8bc083487386fd930f3c8fd34a528a2aa4352a3a" -"checksum heim-cpu 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "307f12a429cfe56c92413f98a6e1a28f72d715b9f65fbfdf2e98f15bd38293c6" -"checksum heim-derive 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "addd10c94d06b172f816a1969253c2dd8a3f633e165d8e018e0be873d67f8cac" -"checksum heim-disk 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2ee4860d01ea623512bcd1d2d54e4566d482f2d4568789562b13d4b8cc294f00" -"checksum heim-host 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "1c6dee47910be9b5fb323ec6bf7462773a8bee67b65e5fe5d652f3e20b3ecab9" -"checksum heim-memory 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "30f5e88edcafd7ee6061997d171f84c153fabdd6459d739b45d7f05193d7f98c" -"checksum heim-net 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b265598f9d3ca525f54a394153e3e738af9795ac5be7c364d55a7be857e69" -"checksum heim-process 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "2165577ccfce4d038de4ca66cbb5c226e1691dff62c778cac6717455dc9ef28d" -"checksum heim-runtime 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4c23e20c02d9df62dbed41273e99ad70c9ebd8799f35ac672086f8cc584d09" -"checksum heim-sensors 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e641fab2e31c4b2039451a713dc92a5daacf84c617c803c946b8081fe8132142" -"checksum heim-virt 0.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "331b1486ed710843c551ac3a8ddb2721dd5345b3939f995ce0dbe453ba901b06" +"checksum heim 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "02692a4aa3bed77933da9ae7915aef7fcceb65eff9d9251be189b1acc0b77f65" +"checksum heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "559807533108e09863125eeccb38a7213cef5a7a7deadd3fac2674e1f8d3db70" +"checksum heim-cpu 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "60c237652eaa091b39f996deb41aa7baae67cab5f25204154c14414f46ef69c1" +"checksum heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3f326db96a03106afcea6839b13f7d95b09cffd063eaa94ef0fd3e796214a66" +"checksum heim-disk 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bd75c64f2d054ce1297ad08f2ca41bf7db7e9ca868221b2fb7427210579e85a1" +"checksum heim-host 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6401c858723568a09e0f09e09bda833e0019c34aa512ccdeba236fce45e4eeb1" +"checksum heim-memory 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "424a549b6c3faecc2492cd3d49f1f89ed9f191c7995741b89e674b85a262e303" +"checksum heim-net 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0ebbcbabe86dbc1c8713ecc1f54630549f82fa07520083cf9a0edcdd77d329a" +"checksum heim-process 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "564f0d9d123c708688721fb2c2aacc198bd5eec3d995eb8c25d369500c66ca7d" +"checksum heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "df59b2a6e00b7f4532dc00736d74bf721a4587d4dbf90793c524ed0a7eddfa19" +"checksum heim-sensors 0.0.3-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "512afc3c0562aa26ae4e236a4b371901fbf7ddac843c961b2ef201936e79a7cd" +"checksum heim-virt 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "95372a84d2a0a5709899449fbb8ed296a9ce5b9fc0ba4729f0c26f7d5ebdf155" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e" "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" @@ -3204,7 +3213,7 @@ dependencies = [ "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" "checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" "checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" -"checksum raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "30a9d219c32c9132f7be513c18be77c9881c7107d2ab5569d205a6a0f0e6dc7d" +"checksum raw-cpuid 7.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf" "checksum rawkey 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "33ec17a493dcb820725c002bc253f6f3ba4e4dc635e72c238540691b05e43897" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" "checksum readkey 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d98db94bb4f3e926c8d8186547cd9366d958d753aff5801214d93d38214e8f0f" From 837d12decdde53fd251670e3284831050f74dce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 24 Sep 2019 15:34:30 -0500 Subject: [PATCH 142/342] Filesystem shell can't cd into files. Ever. --- src/shell/filesystem_shell.rs | 8 ++++++++ tests/command_cd_tests.rs | 29 ++++++++++++++++++++++------- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index d28047745b..3c1ae79ea3 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -187,6 +187,14 @@ impl Shell for FilesystemShell { } else { let path = PathBuf::from(self.path()); + if target.exists() && !target.is_dir() { + return Err(ShellError::labeled_error( + "Can not change to directory", + "is not a directory", + v.tag().clone(), + )); + } + match dunce::canonicalize(path.join(&target)) { Ok(p) => p, Err(_) => { diff --git a/tests/command_cd_tests.rs b/tests/command_cd_tests.rs index e0cc60867f..8b6592c940 100644 --- a/tests/command_cd_tests.rs +++ b/tests/command_cd_tests.rs @@ -120,6 +120,21 @@ fn filesystem_change_to_a_directory_containing_spaces() { }) } +#[test] +fn filesystem_not_a_directory() { + Playground::setup("cd_test_8", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("ferris_did_it.txt")]); + + let actual = nu_error!( + cwd: dirs.test(), + "cd ferris_did_it.txt" + ); + + assert!(actual.contains("ferris_did_it.txt")); + assert!(actual.contains("is not a directory")); + }) +} + #[test] fn filesystem_directory_not_found() { let actual = nu_error!( @@ -133,7 +148,7 @@ fn filesystem_directory_not_found() { #[test] fn valuesystem_change_from_current_path_using_relative_path() { - Playground::setup("cd_test_8", |dirs, sandbox| { + Playground::setup("cd_test_9", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#" @@ -164,7 +179,7 @@ fn valuesystem_change_from_current_path_using_relative_path() { #[test] fn valuesystem_change_from_current_path_using_absolute_path() { - Playground::setup("cd_test_9", |dirs, sandbox| { + Playground::setup("cd_test_10", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#" @@ -198,7 +213,7 @@ fn valuesystem_change_from_current_path_using_absolute_path() { #[test] fn valuesystem_switch_back_to_previous_working_path() { - Playground::setup("cd_test_10", |dirs, sandbox| { + Playground::setup("cd_test_11", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#" @@ -234,7 +249,7 @@ fn valuesystem_switch_back_to_previous_working_path() { #[test] fn valuesystem_change_from_current_path_using_relative_path_and_dash() { - Playground::setup("cd_test_11", |dirs, sandbox| { + Playground::setup("cd_test_12", |dirs, sandbox| { sandbox .with_files(vec![FileWithContent( "sample.toml", @@ -268,7 +283,7 @@ fn valuesystem_change_from_current_path_using_relative_path_and_dash() { #[test] fn valuesystem_change_current_path_to_parent_path() { - Playground::setup("cd_test_12", |dirs, sandbox| { + Playground::setup("cd_test_13", |dirs, sandbox| { sandbox .with_files(vec![FileWithContent( "sample.toml", @@ -295,7 +310,7 @@ fn valuesystem_change_current_path_to_parent_path() { #[test] fn valuesystem_change_to_home_directory() { - Playground::setup("cd_test_13", |dirs, sandbox| { + Playground::setup("cd_test_14", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#" @@ -321,7 +336,7 @@ fn valuesystem_change_to_home_directory() { #[test] fn valuesystem_change_to_a_path_containing_spaces() { - Playground::setup("cd_test_14", |dirs, sandbox| { + Playground::setup("cd_test_15", |dirs, sandbox| { sandbox.with_files(vec![FileWithContent( "sample.toml", r#" From 3480cdb3b45dcc202dd8ef55481a4077396ad390 Mon Sep 17 00:00:00 2001 From: Pirmin Kalberer Date: Tue, 24 Sep 2019 23:33:30 +0200 Subject: [PATCH 143/342] Fix build without crossterm --- src/cli.rs | 3 +- src/fuzzysearch.rs | 129 +++++++++++++++++++++++---------------------- 2 files changed, 69 insertions(+), 63 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 31017ac6aa..3db0747d01 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -359,7 +359,8 @@ pub async fn cli() -> Result<(), Box> { // Register Ctrl-r for history fuzzy search // rustyline doesn't support custom commands, so we override Ctrl-D (EOF) - #[cfg(not(windows))] // https://github.com/nushell/nushell/issues/689 + // https://github.com/nushell/nushell/issues/689 + #[cfg(all(not(windows), feature = "crossterm"))] rl.bind_sequence(rustyline::KeyPress::Ctrl('R'), rustyline::Cmd::EndOfFile); // Redefine Ctrl-D to same command as Ctrl-C rl.bind_sequence(rustyline::KeyPress::Ctrl('D'), rustyline::Cmd::Interrupt); diff --git a/src/fuzzysearch.rs b/src/fuzzysearch.rs index b27745590e..5cb08dd3f5 100644 --- a/src/fuzzysearch.rs +++ b/src/fuzzysearch.rs @@ -1,4 +1,5 @@ use ansi_term::{ANSIString, ANSIStrings, Colour, Style}; +#[cfg(feature = "crossterm")] use crossterm::{cursor, terminal, ClearType, InputEvent, KeyEvent, RawScreen}; use std::io::Write; use sublime_fuzzy::best_match; @@ -18,76 +19,78 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select Edit(String), } let mut state = State::Selecting; - if let Ok(_raw) = RawScreen::into_raw_mode() { - // User input for search - let mut searchinput = String::new(); - let mut selected = 0; + #[cfg(feature = "crossterm")] + { + if let Ok(_raw) = RawScreen::into_raw_mode() { + // User input for search + let mut searchinput = String::new(); + let mut selected = 0; - let mut cursor = cursor(); - let _ = cursor.hide(); - let input = crossterm::input(); - let mut sync_stdin = input.read_sync(); + let mut cursor = cursor(); + let _ = cursor.hide(); + let input = crossterm::input(); + let mut sync_stdin = input.read_sync(); - while state == State::Selecting { - let mut selected_lines = fuzzy_search(&searchinput, &lines, max_results); - let num_lines = selected_lines.len(); - paint_selection_list(&selected_lines, selected); - if let Some(ev) = sync_stdin.next() { - match ev { - InputEvent::Keyboard(k) => match k { - KeyEvent::Esc | KeyEvent::Ctrl('c') => { - state = State::Quit; - } - KeyEvent::Up => { - if selected > 0 { - selected -= 1; + while state == State::Selecting { + let mut selected_lines = fuzzy_search(&searchinput, &lines, max_results); + let num_lines = selected_lines.len(); + paint_selection_list(&selected_lines, selected); + if let Some(ev) = sync_stdin.next() { + match ev { + InputEvent::Keyboard(k) => match k { + KeyEvent::Esc | KeyEvent::Ctrl('c') => { + state = State::Quit; } - } - KeyEvent::Down => { - if selected + 1 < selected_lines.len() { - selected += 1; + KeyEvent::Up => { + if selected > 0 { + selected -= 1; + } } - } - KeyEvent::Char('\n') => { - state = if selected_lines.len() > 0 { - State::Selected(selected_lines.remove(selected).text) - } else { - State::Edit("".to_string()) - }; - } - KeyEvent::Char('\t') | KeyEvent::Right => { - state = if selected_lines.len() > 0 { - State::Edit(selected_lines.remove(selected).text) - } else { - State::Edit("".to_string()) - }; - } - KeyEvent::Char(ch) => { - searchinput.push(ch); - selected = 0; - } - KeyEvent::Backspace => { - searchinput.pop(); - selected = 0; - } - _ => { - // println!("OTHER InputEvent: {:?}", k); - } - }, - _ => {} + KeyEvent::Down => { + if selected + 1 < selected_lines.len() { + selected += 1; + } + } + KeyEvent::Char('\n') => { + state = if selected_lines.len() > 0 { + State::Selected(selected_lines.remove(selected).text) + } else { + State::Edit("".to_string()) + }; + } + KeyEvent::Char('\t') | KeyEvent::Right => { + state = if selected_lines.len() > 0 { + State::Edit(selected_lines.remove(selected).text) + } else { + State::Edit("".to_string()) + }; + } + KeyEvent::Char(ch) => { + searchinput.push(ch); + selected = 0; + } + KeyEvent::Backspace => { + searchinput.pop(); + selected = 0; + } + _ => { + // println!("OTHER InputEvent: {:?}", k); + } + }, + _ => {} + } + } + if num_lines > 0 { + cursor.move_up(num_lines as u16); } } - if num_lines > 0 { - cursor.move_up(num_lines as u16); - } + let (_x, y) = cursor.pos(); + let _ = cursor.goto(0, y - 1); + let _ = cursor.show(); + let _ = RawScreen::disable_raw_mode(); } - let (_x, y) = cursor.pos(); - let _ = cursor.goto(0, y - 1); - let _ = cursor.show(); - let _ = RawScreen::disable_raw_mode(); + terminal().clear(ClearType::FromCursorDown).unwrap(); } - terminal().clear(ClearType::FromCursorDown).unwrap(); - match state { State::Selected(line) => SelectionResult::Selected(line), State::Edit(line) => SelectionResult::Edit(line), @@ -132,6 +135,7 @@ pub fn fuzzy_search(searchstr: &str, lines: &Vec<&str>, max_results: usize) -> V results } +#[cfg(feature = "crossterm")] fn highlight(textmatch: &Match, normal: Style, highlighted: Style) -> Vec { let text = &textmatch.text; let mut ansi_strings = vec![]; @@ -147,6 +151,7 @@ fn highlight(textmatch: &Match, normal: Style, highlighted: Style) -> Vec, selected: usize) { let terminal = terminal(); let size = terminal.terminal_size(); From 85cd03f899b6ff3d9caafc1dcbd75eb138335332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Szczygie=C5=82?= Date: Wed, 25 Sep 2019 00:15:53 +0200 Subject: [PATCH 144/342] Fix typo in echo usage message --- src/commands/echo.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/echo.rs b/src/commands/echo.rs index 453041bbcd..21188f54f2 100644 --- a/src/commands/echo.rs +++ b/src/commands/echo.rs @@ -16,7 +16,7 @@ impl PerItemCommand for Echo { } fn usage(&self) -> &str { - "Echo the argments back to the user." + "Echo the arguments back to the user." } fn run( From 0377efdc16335d2fdd741e0b740e7767dce2fbd6 Mon Sep 17 00:00:00 2001 From: Lander Brandt Date: Tue, 24 Sep 2019 18:01:38 -0700 Subject: [PATCH 145/342] feat(cli): add `ctrlc_exit` config option This feature allows a user to set `ctrlc_exit` to `true` or `false` in their config to override how multiple CTRL-C invocations are handled. Without this change pressing CTRL-C multiple times will exit nu. With this change applied the user can configure the behavior to behave like other shells where multiple invocations will essentially clear the line. This fixes #457. --- src/cli.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/cli.rs b/src/cli.rs index 31017ac6aa..b78c44c78f 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -405,6 +405,18 @@ pub async fn cli() -> Result<(), Box> { } LineResult::CtrlC => { + let config_ctrlc_exit = config::config(Tag::unknown())? + .get("ctrlc_exit") + .map(|s| match s.as_string().unwrap().as_ref() { + "true" => true, + _ => false, + }) + .unwrap_or(false); // default behavior is to allow CTRL-C spamming similar to other shells + + if !config_ctrlc_exit { + continue; + } + if ctrlcbreak { let _ = rl.save_history(&History::path()); std::process::exit(0); From f0b638063d27242c3c32df4bdc3a24182938ca46 Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Tue, 24 Sep 2019 20:42:18 -0700 Subject: [PATCH 146/342] Transfered Docker to a plugin instead of a Command. --- Cargo.toml | 4 ++ debian/install | 1 + src/cli.rs | 2 +- src/commands.rs | 4 +- src/commands/docker.rs | 116 --------------------------------------- src/plugins/docker.rs | 120 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 128 insertions(+), 119 deletions(-) delete mode 100644 src/commands/docker.rs create mode 100644 src/plugins/docker.rs diff --git a/Cargo.toml b/Cargo.toml index e81bc0ee69..e28674d7be 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -158,6 +158,10 @@ name = "nu_plugin_textview" path = "src/plugins/textview.rs" required-features = ["textview"] +[[bin]] +name = "nu_plugin_docker" +path = "src/plugins/docker.rs" + [[bin]] name = "nu" path = "src/main.rs" diff --git a/debian/install b/debian/install index 75cf2844d9..e9ebfc1232 100644 --- a/debian/install +++ b/debian/install @@ -8,3 +8,4 @@ target/release/nu_plugin_sum usr/bin target/release/nu_plugin_sys usr/bin target/release/nu_plugin_textview usr/bin target/release/nu_plugin_tree usr/bin +target/release/nu_plugin_docker usr/bin diff --git a/src/cli.rs b/src/cli.rs index 3bef636b2c..82413ec9a6 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -248,7 +248,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Next), whole_stream_command(Previous), whole_stream_command(Debug), - whole_stream_command(Docker), + // whole_stream_command(Docker), whole_stream_command(Lines), whole_stream_command(Shells), whole_stream_command(SplitColumn), diff --git a/src/commands.rs b/src/commands.rs index cc23cb7d19..5e4287c0af 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -11,7 +11,7 @@ pub(crate) mod config; pub(crate) mod cp; pub(crate) mod date; pub(crate) mod debug; -pub(crate) mod docker; +// pub(crate) mod docker; pub(crate) mod echo; pub(crate) mod enter; pub(crate) mod env; @@ -80,7 +80,7 @@ pub(crate) use config::Config; pub(crate) use cp::Cpy; pub(crate) use date::Date; pub(crate) use debug::Debug; -pub(crate) use docker::Docker; +// pub(crate) use docker::Docker; pub(crate) use echo::Echo; pub(crate) use enter::Enter; pub(crate) use env::Env; diff --git a/src/commands/docker.rs b/src/commands/docker.rs deleted file mode 100644 index 0149b4e04d..0000000000 --- a/src/commands/docker.rs +++ /dev/null @@ -1,116 +0,0 @@ -use crate::commands::WholeStreamCommand; -use crate::data::meta::Span; -use crate::data::Value; -use crate::errors::ShellError; -use crate::parser::registry::Signature; -use crate::prelude::*; -use indexmap::IndexMap; -use std::process::Command; -use std::str; - -pub struct Docker; - -#[derive(Deserialize)] -pub struct DockerArgs { - sub_command: Tagged, - rest: Vec>, -} - -impl WholeStreamCommand for Docker { - fn name(&self) -> &str { - "docker" - } - - fn signature(&self) -> Signature { - Signature::build(self.name()) - .required("sub_command", SyntaxShape::Member) - .rest(SyntaxShape::Member) - } - - fn usage(&self) -> &str { - "e.g. docker ps, docker images" - } - - fn run( - &self, - args: CommandArgs, - registry: &CommandRegistry, - ) -> Result { - args.process(registry, docker_arg)?.run() - // docker(args, registry) - } -} -pub fn docker_arg( - DockerArgs { - sub_command, - rest: _fields, - }: DockerArgs, - RunnableContext { input: _, name, .. }: RunnableContext, -) -> Result { - match sub_command.item().as_str() { - "ps" => docker_ps(name), - "images" => docker_images(name), - _ => Err(ShellError::labeled_error( - "Unsupported Docker command", - format!("'{}'?", sub_command.item()), - Span::unknown(), - )), - } -} - -fn process_docker_output(cmd_output: &str, tag: Tag) -> Result { - let mut docker_out = VecDeque::new(); - let columns: Vec<&str> = cmd_output.lines().collect(); - - let header: Vec<&str> = columns - .iter() - .take(1) - .next() - .unwrap() - .split_whitespace() - .collect(); - - for line in columns.iter().skip(1) { - let values: Vec<&str> = line - .trim_end() - .split(" ") // Some columns values contains spaces to split by two spaces - .filter(|s| s.trim() != "") - .collect(); - - let mut indexmap = IndexMap::new(); - for (i, v) in values.iter().enumerate() { - indexmap.insert( - header[i].to_string(), - Value::string(v.trim().to_string()).tagged(tag), - ); - } - - docker_out.push_back(Value::Row(indexmap.into()).tagged(tag)) - } - - Ok(docker_out.to_output_stream()) -} - -pub fn docker_images(tag: Tag) -> Result { - let output = Command::new("docker") - .arg("images") - .output() - .expect("failed to execute process."); - - let ps_output = str::from_utf8(&output.stdout).unwrap(); - let out = process_docker_output(ps_output, tag); - - out -} - -pub fn docker_ps(tag: Tag) -> Result { - let output = Command::new("docker") - .arg("ps") - .output() - .expect("failed to execute process."); - - let ps_output = str::from_utf8(&output.stdout).unwrap(); - let out = process_docker_output(ps_output, tag); - - out -} diff --git a/src/plugins/docker.rs b/src/plugins/docker.rs new file mode 100644 index 0000000000..9cb8a52e80 --- /dev/null +++ b/src/plugins/docker.rs @@ -0,0 +1,120 @@ +use futures::executor::block_on; +use nu::{ + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, + SyntaxShape, Tag, Tagged, TaggedDictBuilder, Value, +}; + +use std::process::Command; +use std::str; + +struct Docker; + +impl Docker { + fn new() -> Self { + Self + } +} + +async fn docker(sub_command: &String, name: Tag) -> Result>, ShellError> { + match sub_command.as_str() { + "ps" => docker_ps(name), + "images" => docker_images(name), + _ => Err(ShellError::labeled_error( + "Unsupported Docker command", + format!("'{}'?", sub_command), + name.span, + )), + } +} + +fn process_docker_output(cmd_output: &str, tag: Tag) -> Result>, ShellError> { + let columns: Vec<&str> = cmd_output.lines().collect(); + + let header: Vec<&str> = columns + .iter() + .take(1) + .next() + .unwrap() + .split_whitespace() + .collect(); + + let mut output = vec![]; + for line in columns.iter().skip(1) { + let values: Vec<&str> = line + .trim_end() + .split(" ") // Some columns values contains spaces to split by two spaces + .filter(|s| s.trim() != "") + .collect(); + + let mut dict = TaggedDictBuilder::new(tag); + for (i, v) in values.iter().enumerate() { + dict.insert(header[i].to_string(), Value::string(v.trim().to_string())); + } + + output.push(dict.into_tagged_value()); + } + + Ok(output) +} + +pub fn docker_images(tag: Tag) -> Result>, ShellError> { + let output = Command::new("docker") + .arg("images") + .output() + .expect("failed to execute process."); + + let ps_output = str::from_utf8(&output.stdout).unwrap(); + let out = process_docker_output(ps_output, tag); + + out +} + +pub fn docker_ps(tag: Tag) -> Result>, ShellError> { + let output = Command::new("docker") + .arg("ps") + .output() + .expect("failed to execute process."); + + let ps_output = str::from_utf8(&output.stdout).unwrap(); + let out = process_docker_output(ps_output, tag); + + out +} + +impl Plugin for Docker { + fn config(&mut self) -> Result { + Ok(Signature::build("docker") + .required("sub_command", SyntaxShape::Member) + .filter()) + } + + fn begin_filter(&mut self, callinfo: CallInfo) -> Result, ShellError> { + if let Some(args) = callinfo.args.positional { + match &args[0] { + Tagged { + item: Value::Primitive(Primitive::String(s)), + .. + } => match block_on(docker(&s, callinfo.name_tag)) { + Ok(v) => return Ok(v.into_iter().map(ReturnSuccess::value).collect()), + Err(e) => return Err(e), + }, + _ => { + return Err(ShellError::string(format!( + "Unrecognized type in params: {:?}", + args[0] + ))) + } + } + } + + Ok(vec![]) + } + + fn filter(&mut self, _: Tagged) -> Result, ShellError> { + Ok(vec![]) + } +} + +fn main() { + serve_plugin(&mut Docker::new()); +} From a492b019fe6e545273edd0332eb31d5fd7658314 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 25 Sep 2019 11:15:00 -0500 Subject: [PATCH 147/342] Commands documenting instructions. --- docs/commands/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/commands/README.md diff --git a/docs/commands/README.md b/docs/commands/README.md new file mode 100644 index 0000000000..d3834c3141 --- /dev/null +++ b/docs/commands/README.md @@ -0,0 +1,19 @@ +# How do I get started? + +Pick any command from the checklist and write a comment acknowleding you started work. + +# Instructions for documenting a Nu command of your choosing + +Name the file after the command, like so: + +`command.md` + +Example: If you want to add documentation for the Nu command `enter`, create a file named `enter.md`, write documentation, save and create your pull request. + +# What kind of documentation should I write? + +Anything you want that you believe it *best* documents the command and the way you would like to see it. Here are some of our ideas of documentation we would *love* to see (feel free to add yours): + +* Examples of using the command (max creativity welcomed!) +* Description of the command + From f85968aba41ea62ecae4cf255d17a6c112d72fd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Wed, 25 Sep 2019 11:35:58 -0500 Subject: [PATCH 148/342] More command documentation instructions. --- docs/commands/README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/commands/README.md b/docs/commands/README.md index d3834c3141..68ef658cae 100644 --- a/docs/commands/README.md +++ b/docs/commands/README.md @@ -1,6 +1,6 @@ # How do I get started? -Pick any command from the checklist and write a comment acknowleding you started work. +Pick any command from the checklist and write a comment acknowledging you started work. # Instructions for documenting a Nu command of your choosing @@ -8,12 +8,18 @@ Name the file after the command, like so: `command.md` -Example: If you want to add documentation for the Nu command `enter`, create a file named `enter.md`, write documentation, save and create your pull request. +Example: If you want to add documentation for the Nu command `enter`, create a file named `enter.md`, write documentation, save it at `/docs/commands/[your_command_picked].md` as and create your pull request. # What kind of documentation should I write? Anything you want that you believe it *best* documents the command and the way you would like to see it. Here are some of our ideas of documentation we would *love* to see (feel free to add yours): * Examples of using the command (max creativity welcomed!) -* Description of the command +* Description of the command. +* Command usage. +# Anything else? + +Of course! (These are drafts) so feel free to leave feedback and suggestions in the same file. + +Happy Documenting. From 9891e5ab81b117661f1ffee32605522acc66fa63 Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 26 Sep 2019 02:22:17 +0200 Subject: [PATCH 149/342] Use async-stream crate to replace most async_stream_block invocations --- Cargo.lock | 22 ++++++++++++++++++++++ Cargo.toml | 1 + src/commands/enter.rs | 2 +- src/commands/fetch.rs | 2 +- src/commands/from_bson.rs | 2 +- src/commands/from_csv.rs | 2 +- src/commands/from_ini.rs | 2 +- src/commands/from_json.rs | 2 +- src/commands/from_sqlite.rs | 2 +- src/commands/from_toml.rs | 2 +- src/commands/from_tsv.rs | 2 +- src/commands/from_url.rs | 2 +- src/commands/from_xml.rs | 2 +- src/commands/from_yaml.rs | 2 +- src/commands/last.rs | 2 +- src/commands/open.rs | 2 +- src/commands/pivot.rs | 2 +- src/commands/post.rs | 2 +- src/commands/sort_by.rs | 2 +- src/commands/to_bson.rs | 2 +- src/commands/to_csv.rs | 2 +- src/commands/to_json.rs | 2 +- src/commands/to_sqlite.rs | 2 +- src/commands/to_toml.rs | 2 +- src/commands/to_tsv.rs | 2 +- src/commands/to_url.rs | 2 +- src/commands/to_yaml.rs | 2 +- src/lib.rs | 1 + src/prelude.rs | 1 + 29 files changed, 50 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8db4a18084..c0730ef123 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -53,6 +53,25 @@ dependencies = [ "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "async-stream" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "async-stream-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "async-stream-impl" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "atty" version = "0.2.13" @@ -1516,6 +1535,7 @@ version = "0.3.0" dependencies = [ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "async-stream 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "battery 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "bigdecimal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3003,6 +3023,8 @@ dependencies = [ "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" "checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum async-stream 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "650be9b667e47506c42ee53034fb1935443cb2447a3a5c0a75e303d2e756fa73" +"checksum async-stream-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f0d8c5b411e36dcfb04388bacfec54795726b1f0148adcb0f377a96d6747e0e" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" "checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" "checksum backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "b5164d292487f037ece34ec0de2fcede2faa162f085dd96d2385ab81b12765ba" diff --git a/Cargo.toml b/Cargo.toml index 132aad2393..84b84ca416 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,6 +28,7 @@ byte-unit = "3.0.1" base64 = "0.10.1" futures-preview = { version = "=0.3.0-alpha.18", features = ["compat", "io-compat"] } futures-async-stream = "=0.1.0-alpha.5" +async-stream = "0.1.1" futures_codec = "0.2.5" num-traits = "0.2.8" term = "0.5.2" diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 9388abb941..923715d139 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -61,7 +61,7 @@ impl PerItemCommand for Enter { )))] .into()) } else { - let stream = async_stream_block! { + let stream = async_stream! { // If it's a file, attempt to open the file as a value and enter it let cwd = raw_args.shell_manager.path(); diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index 79806e76b2..886d6bd959 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -58,7 +58,7 @@ fn run( let registry = registry.clone(); let raw_args = raw_args.clone(); - let stream = async_stream_block! { + let stream = async_stream! { let result = fetch(&path_str, path_span).await; diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index a4171e3fec..7dd00983fc 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -201,7 +201,7 @@ fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result> = input.values.collect().await; for value in values { diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index 68296eb11b..ea90ab3de1 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -88,7 +88,7 @@ fn from_csv( ) -> Result { let name_tag = name; - let stream = async_stream_block! { + let stream = async_stream! { let values: Vec> = input.values.collect().await; let mut concat_string = String::new(); diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index 8409cf8489..d53ad67773 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -67,7 +67,7 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result> = input.values.collect().await; let mut concat_string = String::new(); diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index ab70685f2b..dae288a892 100644 --- a/src/commands/from_json.rs +++ b/src/commands/from_json.rs @@ -74,7 +74,7 @@ fn from_json( ) -> Result { let name_tag = name; - let stream = async_stream_block! { + let stream = async_stream! { let values: Vec> = input.values.collect().await; let mut concat_string = String::new(); diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index e880571911..20d087bd5c 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -131,7 +131,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result> = input.values.collect().await; for value in values { diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index 29db38a77e..c0098d9267 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -71,7 +71,7 @@ pub fn from_toml( let tag = args.name_tag(); let input = args.input; - let stream = async_stream_block! { + let stream = async_stream! { let values: Vec> = input.values.collect().await; let mut concat_string = String::new(); diff --git a/src/commands/from_tsv.rs b/src/commands/from_tsv.rs index 66f070a5df..bba532d17b 100644 --- a/src/commands/from_tsv.rs +++ b/src/commands/from_tsv.rs @@ -89,7 +89,7 @@ fn from_tsv( ) -> Result { let name_tag = name; - let stream = async_stream_block! { + let stream = async_stream! { let values: Vec> = input.values.collect().await; let mut concat_string = String::new(); diff --git a/src/commands/from_url.rs b/src/commands/from_url.rs index 81113a83d4..662508deb6 100644 --- a/src/commands/from_url.rs +++ b/src/commands/from_url.rs @@ -31,7 +31,7 @@ fn from_url(args: CommandArgs, registry: &CommandRegistry) -> Result> = input.values.collect().await; let mut concat_string = String::new(); diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index f80d428f43..5bba67b42a 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -86,7 +86,7 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result> = input.values.collect().await; let mut concat_string = String::new(); diff --git a/src/commands/from_yaml.rs b/src/commands/from_yaml.rs index 9bd2bf5629..9e156bbc75 100644 --- a/src/commands/from_yaml.rs +++ b/src/commands/from_yaml.rs @@ -100,7 +100,7 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result> = input.values.collect().await; let mut concat_string = String::new(); diff --git a/src/commands/last.rs b/src/commands/last.rs index fd7a3ecea2..4813c555a1 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -36,7 +36,7 @@ fn last( LastArgs { amount }: LastArgs, context: RunnableContext, ) -> Result { - let stream = async_stream_block! { + let stream = async_stream! { let v: Vec<_> = context.input.into_vec().await; let k = v.len() - (*amount as usize); for x in v[k..].iter() { diff --git a/src/commands/open.rs b/src/commands/open.rs index 603bb4da0b..78aa35db01 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -59,7 +59,7 @@ fn run( let registry = registry.clone(); let raw_args = raw_args.clone(); - let stream = async_stream_block! { + let stream = async_stream! { let result = fetch(&full_path, &path_str, path_span).await; diff --git a/src/commands/pivot.rs b/src/commands/pivot.rs index 0232f2d59e..1a6bb901fb 100644 --- a/src/commands/pivot.rs +++ b/src/commands/pivot.rs @@ -52,7 +52,7 @@ fn merge_descriptors(values: &[Tagged]) -> Vec { } pub fn pivot(args: PivotArgs, context: RunnableContext) -> Result { - let stream = async_stream_block! { + let stream = async_stream! { let input = context.input.into_vec().await; let descs = merge_descriptors(&input); diff --git a/src/commands/post.rs b/src/commands/post.rs index 6d5627a65f..1bff755d9e 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -73,7 +73,7 @@ fn run( let registry = registry.clone(); let raw_args = raw_args.clone(); - let stream = async_stream_block! { + let stream = async_stream! { let (file_extension, contents, contents_tag, span_source) = post(&path_str, &body, user, password, path_span, ®istry, &raw_args).await.unwrap(); diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index 8058b7889e..1e6b87491a 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -35,7 +35,7 @@ fn sort_by( SortByArgs { rest }: SortByArgs, mut context: RunnableContext, ) -> Result { - Ok(OutputStream::new(async_stream_block! { + Ok(OutputStream::new(async_stream! { let mut vec = context.input.drain_vec().await; let calc_key = |item: &Tagged| { diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index 0cec599265..a36d99c077 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -233,7 +233,7 @@ fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result, ShellError> { fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let name_tag = args.name_tag(); - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = args.input.values.collect().await; let to_process_input = if input.len() > 1 { diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index fd77fdcb6f..1897fb86b7 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -135,7 +135,7 @@ fn to_csv( RunnableContext { input, name, .. }: RunnableContext, ) -> Result { let name_tag = name; - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = input.values.collect().await; let to_process_input = if input.len() > 1 { diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index d8aaa96794..9c06299aad 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -81,7 +81,7 @@ fn json_list(input: &Vec>) -> Result, Shell fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let name_tag = args.name_tag(); - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = args.input.values.collect().await; let to_process_input = if input.len() > 1 { diff --git a/src/commands/to_sqlite.rs b/src/commands/to_sqlite.rs index c695667ca0..4f9181ec7c 100644 --- a/src/commands/to_sqlite.rs +++ b/src/commands/to_sqlite.rs @@ -201,7 +201,7 @@ fn sqlite_input_stream_to_bytes( fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let name_tag = args.name_tag(); - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = args.input.values.collect().await; match sqlite_input_stream_to_bytes(input) { diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index a30c9d3cf7..6c8904e0c2 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -76,7 +76,7 @@ fn collect_values(input: &Vec>) -> Result, ShellE fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let name_tag = args.name_tag(); - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = args.input.values.collect().await; let to_process_input = if input.len() > 1 { diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index 7bce174d01..4edc26face 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -134,7 +134,7 @@ fn to_tsv( RunnableContext { input, name, .. }: RunnableContext, ) -> Result { let name_tag = name; - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = input.values.collect().await; let to_process_input = if input.len() > 1 { diff --git a/src/commands/to_url.rs b/src/commands/to_url.rs index d98a765a29..dfba5faf4d 100644 --- a/src/commands/to_url.rs +++ b/src/commands/to_url.rs @@ -31,7 +31,7 @@ fn to_url(args: CommandArgs, registry: &CommandRegistry) -> Result> = input.values.collect().await; for value in input { diff --git a/src/commands/to_yaml.rs b/src/commands/to_yaml.rs index db54af6e89..a72ab9ffcc 100644 --- a/src/commands/to_yaml.rs +++ b/src/commands/to_yaml.rs @@ -77,7 +77,7 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result Result { let args = args.evaluate_once(registry)?; let name_tag = args.name_tag(); - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = args.input.values.collect().await; let to_process_input = if input.len() > 1 { diff --git a/src/lib.rs b/src/lib.rs index f4ccb4e4e3..ef7f3c3812 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ #![feature(generators)] #![feature(proc_macro_hygiene)] +#![recursion_limit = "512"] #[macro_use] mod prelude; diff --git a/src/prelude.rs b/src/prelude.rs index d58e7989a6..b80e21f034 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -72,6 +72,7 @@ pub(crate) use crate::shell::value_shell::ValueShell; pub(crate) use crate::stream::{InputStream, OutputStream}; pub(crate) use crate::traits::{HasTag, ToDebug}; pub(crate) use crate::Text; +pub(crate) use async_stream::stream as async_stream; pub(crate) use bigdecimal::BigDecimal; pub(crate) use futures::stream::BoxStream; pub(crate) use futures::{FutureExt, Stream, StreamExt}; From 6aad0b8443e1f750cfeff528ad01cc8283746fcc Mon Sep 17 00:00:00 2001 From: est31 Date: Thu, 26 Sep 2019 02:35:38 +0200 Subject: [PATCH 150/342] Remove async_stream_block from the prelude ... to indicate deprecation of its use --- src/commands/autoview.rs | 1 + src/commands/plugin.rs | 1 + src/commands/save.rs | 1 + src/prelude.rs | 1 - 4 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index b9b9d8941c..e715491e91 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -1,6 +1,7 @@ use crate::commands::{RawCommandArgs, WholeStreamCommand}; use crate::errors::ShellError; use crate::prelude::*; +use futures_async_stream::async_stream_block; pub struct Autoview; diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index ae9b2ec64a..4e30b68f45 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 futures_async_stream::async_stream_block; use log::trace; use serde::{self, Deserialize, Serialize}; use std::io::prelude::*; diff --git a/src/commands/save.rs b/src/commands/save.rs index 9c12fd2414..982e578a8a 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -2,6 +2,7 @@ use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand}; use crate::data::Value; use crate::errors::ShellError; use crate::prelude::*; +use futures_async_stream::async_stream_block; use std::path::{Path, PathBuf}; pub struct Save; diff --git a/src/prelude.rs b/src/prelude.rs index b80e21f034..56cd21b33a 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -76,7 +76,6 @@ pub(crate) use async_stream::stream as async_stream; pub(crate) use bigdecimal::BigDecimal; pub(crate) use futures::stream::BoxStream; pub(crate) use futures::{FutureExt, Stream, StreamExt}; -pub(crate) use futures_async_stream::async_stream_block; pub(crate) use num_bigint::BigInt; pub(crate) use num_traits::cast::{FromPrimitive, ToPrimitive}; pub(crate) use num_traits::identities::Zero; From def33206d989a142b2ab8e4e49d79eb5fccf6a6a Mon Sep 17 00:00:00 2001 From: BradyBromley <51128276+BradyBromley@users.noreply.github.com> Date: Fri, 27 Sep 2019 09:48:26 -0700 Subject: [PATCH 151/342] Changed wording in README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 85b4dc30bf..50f2e5100d 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Nu comes with a set of built-in commands (listed below). If a command is unknown # Learning more -There are a few good resources to learn about Nu. First, there is a [book](https://book.nushell.sh) about Nu, currently in progress. The book focuses on using Nu and its core concepts. +There are a few good resources to learn about Nu. There is a [book](https://book.nushell.sh) about Nu that is currently in progress. The book focuses on using Nu and its core concepts. If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-book/tree/master/en) to help get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. @@ -181,7 +181,7 @@ Here we use the variable `$it` to refer to the value being piped to the external ## Shells -By default, Nu will work inside of a single directory and allow you to navigate around your filesystem. Sometimes, you'll want to work in multiple directories at the same time. For this, Nu offers a way of adding additional working directories that you can jump between. +Nu will work inside of a single directory and allow you to navigate around your filesystem by default. Nu also offers a way of adding additional working directories that you can jump between, allowing you to work in multiple directories at the same time. To do so, use the `enter` command, which will allow you create a new "shell" and enter it at the specified path. You can toggle between this new shell and the original shell with the `p` (for previous) and `n` (for next), allowing you to navigate around a ring buffer of shells. Once you're done with a shell, you can `exit` it and remove it from the ring buffer. From 1da6ac8de7d9b851b56ef99ef94cf2d98b82a659 Mon Sep 17 00:00:00 2001 From: Blurryface05 Date: Fri, 27 Sep 2019 23:01:34 +0000 Subject: [PATCH 152/342] i new file: .gitpod.Dockerfile new file: .gitpod.yml --- .gitpod.Dockerfile | 12 ++++++++++++ .gitpod.yml | 11 +++++++++++ 2 files changed, 23 insertions(+) create mode 100644 .gitpod.Dockerfile create mode 100644 .gitpod.yml diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile new file mode 100644 index 0000000000..b795e6cac7 --- /dev/null +++ b/.gitpod.Dockerfile @@ -0,0 +1,12 @@ +FROM gitpod/workspace-full + +USER root + +# Install custom tools, runtime, etc. using apt-get +# For example, the command below would install "bastet" - a command line tetris clone: +# +# RUN apt-get update \ +# && apt-get install -y bastet \ +# && apt-get clean && rm -rf /var/cache/apt/* && rm -rf /var/lib/apt/lists/* && rm -rf /tmp/* +# +# More information: https://www.gitpod.io/docs/42_config_docker/ diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 0000000000..87dd64a333 --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,11 @@ +image: + file: docker/Dockerfile + +# List the ports you want to expose and what to do when they are served. See https://www.gitpod.io/docs/43_config_ports/ +ports: +- port: 3000 + +# List the start up tasks. You can start them in parallel in multiple terminals. See https://www.gitpod.io/docs/44_config_start_tasks/ +tasks: +- init: echo 'init script' # runs during prebuild + command: echo 'start script' From 1183d28b1519c2b5fe92d6375baf9e0abfd6c7a2 Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 28 Sep 2019 02:05:18 +0200 Subject: [PATCH 153/342] Remove uses of async_stream_block --- src/commands/autoview.rs | 8 +++- src/commands/clip.rs | 8 ++-- src/commands/plugin.rs | 8 +++- src/commands/save.rs | 79 +++++++++++++++++++++------------------- src/commands/table.rs | 7 +++- 5 files changed, 63 insertions(+), 47 deletions(-) diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index e715491e91..c3702b3f13 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -1,7 +1,6 @@ use crate::commands::{RawCommandArgs, WholeStreamCommand}; use crate::errors::ShellError; use crate::prelude::*; -use futures_async_stream::async_stream_block; pub struct Autoview; @@ -35,7 +34,7 @@ pub fn autoview( mut context: RunnableContext, raw: RawCommandArgs, ) -> Result { - Ok(OutputStream::new(async_stream_block! { + Ok(OutputStream::new(async_stream! { let input = context.input.drain_vec().await; if input.len() > 0 { @@ -89,6 +88,11 @@ pub fn autoview( result.collect::>().await; } } + + // Needed for async_stream to type check + if false { + yield ReturnSuccess::value(Value::nothing().tagged_unknown()); + } })) } diff --git a/src/commands/clip.rs b/src/commands/clip.rs index 2ef5bfac1d..ac3ded1d4b 100644 --- a/src/commands/clip.rs +++ b/src/commands/clip.rs @@ -5,7 +5,6 @@ pub mod clipboard { use crate::errors::ShellError; use crate::prelude::*; use futures::stream::StreamExt; - use futures_async_stream::async_stream_block; use clipboard::{ClipboardContext, ClipboardProvider}; @@ -40,10 +39,13 @@ pub mod clipboard { ClipArgs {}: ClipArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - let stream = async_stream_block! { + let stream = async_stream! { let values: Vec> = input.values.collect().await; - inner_clip(values, name).await; + let mut clip_stream = inner_clip(values, name).await; + while let Some(value) = clip_stream.next().await { + yield value; + } }; let stream: BoxStream<'static, ReturnValue> = stream.boxed(); diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index 4e30b68f45..e769a7b5c7 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -3,7 +3,6 @@ use crate::errors::ShellError; use crate::parser::registry; use crate::prelude::*; use derive_new::new; -use futures_async_stream::async_stream_block; use log::trace; use serde::{self, Deserialize, Serialize}; use std::io::prelude::*; @@ -298,7 +297,7 @@ pub fn sink_plugin( let args = args.evaluate_once(registry)?; let call_info = args.call_info.clone(); - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = args.input.values.collect().await; let request = JsonRpc::new("sink", (call_info.clone(), input)); @@ -313,6 +312,11 @@ pub fn sink_plugin( .expect("Failed to spawn child process"); let _ = child.wait(); + + // Needed for async_stream to type check + if false { + yield ReturnSuccess::value(Value::nothing().tagged_unknown()); + } }; Ok(OutputStream::new(stream)) } diff --git a/src/commands/save.rs b/src/commands/save.rs index 982e578a8a..e41116d682 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -2,13 +2,12 @@ use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand}; use crate::data::Value; use crate::errors::ShellError; use crate::prelude::*; -use futures_async_stream::async_stream_block; use std::path::{Path, PathBuf}; pub struct Save; macro_rules! process_string { - ($input:ident, $name_tag:ident) => {{ + ($scope:tt, $input:ident, $name_tag:ident) => {{ let mut result_string = String::new(); for res in $input { match res { @@ -19,11 +18,11 @@ macro_rules! process_string { result_string.push_str(&s); } _ => { - yield core::task::Poll::Ready(Err(ShellError::labeled_error( + break $scope Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", $name_tag, - ))); + )); } } } @@ -32,7 +31,7 @@ macro_rules! process_string { } macro_rules! process_string_return_success { - ($result_vec:ident, $name_tag:ident) => {{ + ($scope:tt, $result_vec:ident, $name_tag:ident) => {{ let mut result_string = String::new(); for res in $result_vec { match res { @@ -43,11 +42,11 @@ macro_rules! process_string_return_success { result_string.push_str(&s); } _ => { - yield core::task::Poll::Ready(Err(ShellError::labeled_error( + break $scope Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during text save", $name_tag, - ))); + )); } } } @@ -56,7 +55,7 @@ macro_rules! process_string_return_success { } macro_rules! process_binary_return_success { - ($result_vec:ident, $name_tag:ident) => {{ + ($scope:tt, $result_vec:ident, $name_tag:ident) => {{ let mut result_binary: Vec = Vec::new(); for res in $result_vec { match res { @@ -69,11 +68,11 @@ macro_rules! process_binary_return_success { } } _ => { - yield core::task::Poll::Ready(Err(ShellError::labeled_error( + break $scope Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during binary save", $name_tag, - ))); + )); } } } @@ -131,7 +130,7 @@ fn save( let name_tag = name; let source_map = source_map.clone(); - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = input.values.collect().await; if path.is_none() { // If there is no filename, check the metadata for the origin filename @@ -171,39 +170,43 @@ fn save( } } - let content : Result, ShellError> = if !save_raw { - if let Some(extension) = full_path.extension() { - let command_name = format!("to-{}", extension.to_str().unwrap()); - if let Some(converter) = registry.get_command(&command_name) { - let new_args = RawCommandArgs { - host, - shell_manager, - call_info: UnevaluatedCallInfo { - args: crate::parser::hir::Call { - head: raw_args.call_info.args.head, - positional: None, - named: None - }, - source: raw_args.call_info.source, - source_map: raw_args.call_info.source_map, - name_tag: raw_args.call_info.name_tag, + // TODO use label_break_value once it is stable: + // https://github.com/rust-lang/rust/issues/48594 + let content : Result, ShellError> = 'scope: loop { + break if !save_raw { + if let Some(extension) = full_path.extension() { + let command_name = format!("to-{}", extension.to_str().unwrap()); + if let Some(converter) = registry.get_command(&command_name) { + let new_args = RawCommandArgs { + host, + shell_manager, + call_info: UnevaluatedCallInfo { + args: crate::parser::hir::Call { + head: raw_args.call_info.args.head, + positional: None, + named: None + }, + source: raw_args.call_info.source, + source_map: raw_args.call_info.source_map, + name_tag: raw_args.call_info.name_tag, + } + }; + let mut result = converter.run(new_args.with_input(input), ®istry, false); + let result_vec: Vec> = result.drain_vec().await; + if converter.is_binary() { + process_binary_return_success!('scope, result_vec, name_tag) + } else { + process_string_return_success!('scope, result_vec, name_tag) } - }; - let mut result = converter.run(new_args.with_input(input), ®istry, false); - let result_vec: Vec> = result.drain_vec().await; - if converter.is_binary() { - process_binary_return_success!(result_vec, name_tag) } else { - process_string_return_success!(result_vec, name_tag) + process_string!('scope, input, name_tag) } } else { - process_string!(input, name_tag) + process_string!('scope, input, name_tag) } } else { - process_string!(input, name_tag) - } - } else { - Ok(string_from(&input).into_bytes()) + Ok(string_from(&input).into_bytes()) + }; }; match content { diff --git a/src/commands/table.rs b/src/commands/table.rs index 4efd6f821f..e9fbe35f2e 100644 --- a/src/commands/table.rs +++ b/src/commands/table.rs @@ -2,7 +2,6 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; use crate::format::TableView; use crate::prelude::*; -use futures_async_stream::async_stream_block; pub struct Table; @@ -32,7 +31,7 @@ impl WholeStreamCommand for Table { } pub fn table(_args: TableArgs, context: RunnableContext) -> Result { - let stream = async_stream_block! { + let stream = async_stream! { let input: Vec> = context.input.into_vec().await; if input.len() > 0 { let mut host = context.host.lock().unwrap(); @@ -41,6 +40,10 @@ pub fn table(_args: TableArgs, context: RunnableContext) -> Result Date: Sat, 28 Sep 2019 02:07:09 +0200 Subject: [PATCH 154/342] Remove use of nightly features --- src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ef7f3c3812..b17e6c0b38 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(generators)] -#![feature(proc_macro_hygiene)] #![recursion_limit = "512"] #[macro_use] From 1801c006ec893fbe6ad373b17987e3ab81401cf7 Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 28 Sep 2019 02:07:28 +0200 Subject: [PATCH 155/342] Remove futures-async-stream dependency --- Cargo.lock | 43 ------------------------------------------- Cargo.toml | 1 - 2 files changed, 44 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0730ef123..47348cd570 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -739,26 +739,6 @@ name = "futures" version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "futures-async-stream" -version = "0.1.0-alpha.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "futures-async-stream-macro 0.1.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", - "pin-project 0.4.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "futures-async-stream-macro" -version = "0.1.0-alpha.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "futures-channel-preview" version = "0.3.0-alpha.18" @@ -1553,7 +1533,6 @@ dependencies = [ "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "enum-utils 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "futures-async-stream 0.1.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1784,24 +1763,6 @@ dependencies = [ "ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "pin-project" -version = "0.4.0-alpha.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "pin-project-internal 0.4.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "pin-project-internal" -version = "0.4.0-alpha.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "pin-utils" version = "0.1.0-alpha.4" @@ -3100,8 +3061,6 @@ dependencies = [ "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" -"checksum futures-async-stream 0.1.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f6311b428f208a8e7294aad3ddfa695cd68163e49880f4a3c3705e94c613c99b" -"checksum futures-async-stream-macro 0.1.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c7665811c2ea29c7fd309e48b1c1f52538b50fda641616a11eedadcf23ad29da" "checksum futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "f477fd0292c4a4ae77044454e7f2b413207942ad405f759bb0b4698b7ace5b12" "checksum futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2f26f774b81b3847dcda0c81bd4b6313acfb4f69e5a0390c7cb12c058953e9" "checksum futures-executor-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "80705612926df8a1bc05f0057e77460e29318801f988bf7d803a734cf54e7528" @@ -3203,8 +3162,6 @@ dependencies = [ "checksum output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" -"checksum pin-project 0.4.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)" = "c6e7dd6a2ad14b55463a4b80ca7b6c3b373921310b61fcb3de5455ad2dea21f7" -"checksum pin-project-internal 0.4.0-alpha.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8cbe07d1ffd722968221af234aff370f5d02de3dea17decf536df93ee9af2fd3" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" "checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" "checksum platforms 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cfec0daac55b13af394ceaaad095d17c790f77bdc9329264f06e49d6cd3206c" diff --git a/Cargo.toml b/Cargo.toml index 84b84ca416..7899b2db8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ chrono-humanize = "0.0.11" byte-unit = "3.0.1" base64 = "0.10.1" futures-preview = { version = "=0.3.0-alpha.18", features = ["compat", "io-compat"] } -futures-async-stream = "=0.1.0-alpha.5" async-stream = "0.1.1" futures_codec = "0.2.5" num-traits = "0.2.8" From ba778eaff91071b6be5d12f7ed2860d10d27d1de Mon Sep 17 00:00:00 2001 From: Blurryface05 Date: Sat, 28 Sep 2019 00:31:16 +0000 Subject: [PATCH 156/342] modified: docker/Dockerfile --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index ffb1e5377d..e38096b7cf 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ ARG FROMTAG=latest FROM quay.io/nushell/nu-base:${FROMTAG} as base -FROM ubuntu:18.04 +FROM gitpod/workspace-full COPY --from=base /usr/local/bin/nu /usr/local/bin/nu ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y libssl-dev \ From f7d5ddbc07eb6c52ea9d8b780f4e47c76c4a3763 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 19:47:28 -0500 Subject: [PATCH 157/342] Update Dockerfile --- docker/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile b/docker/Dockerfile index e38096b7cf..ffb1e5377d 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ ARG FROMTAG=latest FROM quay.io/nushell/nu-base:${FROMTAG} as base -FROM gitpod/workspace-full +FROM ubuntu:18.04 COPY --from=base /usr/local/bin/nu /usr/local/bin/nu ENV DEBIAN_FRONTEND noninteractive RUN apt-get update && apt-get install -y libssl-dev \ From 6617731d5bdd299b9047ac0b0a6f48c8fce27b3f Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 19:51:42 -0500 Subject: [PATCH 158/342] Update .gitpod.Dockerfile --- .gitpod.Dockerfile | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index b795e6cac7..30258b27be 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -1,12 +1,15 @@ FROM gitpod/workspace-full USER root - -# Install custom tools, runtime, etc. using apt-get -# For example, the command below would install "bastet" - a command line tetris clone: -# -# RUN apt-get update \ -# && apt-get install -y bastet \ -# && apt-get clean && rm -rf /var/cache/apt/* && rm -rf /var/lib/apt/lists/* && rm -rf /tmp/* -# -# More information: https://www.gitpod.io/docs/42_config_docker/ +RUN apt-get update && apt-get install -y libssl-dev \ + libxcb-composite0-dev \ + pkg-config \ + curl +RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` +RUN echo "##vso[task.prependpath]/root/.cargo/bin" && \ + rustc -Vv && \ + if $RELEASE; then cargo build --release && cargo run --release; \ + cp target/release/nu /usr/local/bin; \ + else cargo build; \ + cp target/debug/nu /usr/local/bin; fi; +RUN cargo build From ac116f4f7cef51e9219da15fb1005cff5e3ff5cc Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 19:54:05 -0500 Subject: [PATCH 159/342] Update .gitpod.yml --- .gitpod.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.gitpod.yml b/.gitpod.yml index 87dd64a333..3cce5cbf76 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,11 +1,2 @@ image: - file: docker/Dockerfile - -# List the ports you want to expose and what to do when they are served. See https://www.gitpod.io/docs/43_config_ports/ -ports: -- port: 3000 - -# List the start up tasks. You can start them in parallel in multiple terminals. See https://www.gitpod.io/docs/44_config_start_tasks/ -tasks: -- init: echo 'init script' # runs during prebuild - command: echo 'start script' + file: .gitpod.dockerfile From 12f34cc698dc626241dedc43aa5615fdcf3c8e8c Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 19:54:42 -0500 Subject: [PATCH 160/342] Update .gitpod.yml --- .gitpod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.yml b/.gitpod.yml index 3cce5cbf76..527c76d392 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,2 +1,2 @@ image: - file: .gitpod.dockerfile + file: .gitpod.Dockerfile From 5b5c33a86f7e49a9e3d0dda4e90b93dcf55221ea Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 19:58:36 -0500 Subject: [PATCH 161/342] Update .gitpod.Dockerfile --- .gitpod.Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 30258b27be..ac88719d72 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -5,7 +5,6 @@ RUN apt-get update && apt-get install -y libssl-dev \ libxcb-composite0-dev \ pkg-config \ curl -RUN curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` RUN echo "##vso[task.prependpath]/root/.cargo/bin" && \ rustc -Vv && \ if $RELEASE; then cargo build --release && cargo run --release; \ From 0d076d97be3c2ca873225fa750065f0b915d87e2 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 19:59:30 -0500 Subject: [PATCH 162/342] Update .gitpod.Dockerfile --- .gitpod.Dockerfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index ac88719d72..792525b19a 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -4,7 +4,8 @@ USER root RUN apt-get update && apt-get install -y libssl-dev \ libxcb-composite0-dev \ pkg-config \ - curl + curl \ + rustc RUN echo "##vso[task.prependpath]/root/.cargo/bin" && \ rustc -Vv && \ if $RELEASE; then cargo build --release && cargo run --release; \ From 0d8768b82705d07cbc83125f33a94ce691dabecf Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 20:01:50 -0500 Subject: [PATCH 163/342] Update .gitpod.Dockerfile --- .gitpod.Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 792525b19a..73ca6c0b7f 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -1,5 +1,5 @@ FROM gitpod/workspace-full - +WORKDIR /workspace/nushell USER root RUN apt-get update && apt-get install -y libssl-dev \ libxcb-composite0-dev \ From aa495f4d743dcfdb0ba5f64195cb89f45c0abeaa Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 20:04:24 -0500 Subject: [PATCH 164/342] Update .gitpod.Dockerfile --- .gitpod.Dockerfile | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index 73ca6c0b7f..eaccc4be74 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -6,10 +6,4 @@ RUN apt-get update && apt-get install -y libssl-dev \ pkg-config \ curl \ rustc -RUN echo "##vso[task.prependpath]/root/.cargo/bin" && \ - rustc -Vv && \ - if $RELEASE; then cargo build --release && cargo run --release; \ - cp target/release/nu /usr/local/bin; \ - else cargo build; \ - cp target/debug/nu /usr/local/bin; fi; RUN cargo build From 48cbc5b23c4f0143519953cc2facf88a1a92ea88 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 20:06:15 -0500 Subject: [PATCH 165/342] Update .gitpod.Dockerfile --- .gitpod.Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index eaccc4be74..e7732651ed 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -6,4 +6,3 @@ RUN apt-get update && apt-get install -y libssl-dev \ pkg-config \ curl \ rustc -RUN cargo build From 9f352ace23beda5af7977e6db57a44d02efd578d Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 20:08:05 -0500 Subject: [PATCH 166/342] Update .gitpod.Dockerfile --- .gitpod.Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile index e7732651ed..b832017f62 100644 --- a/.gitpod.Dockerfile +++ b/.gitpod.Dockerfile @@ -1,5 +1,4 @@ FROM gitpod/workspace-full -WORKDIR /workspace/nushell USER root RUN apt-get update && apt-get install -y libssl-dev \ libxcb-composite0-dev \ From 20de0ea01f504a39c8805bf5d0ac43d2791fc3f8 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 20:09:21 -0500 Subject: [PATCH 167/342] Update .gitpod.yml --- .gitpod.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitpod.yml b/.gitpod.yml index 527c76d392..8adf00be3d 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,2 +1,4 @@ image: file: .gitpod.Dockerfile +tasks: + - command: cargo build From 02d6614ae2e11787d1c33d0eb9e3da91f8161479 Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 28 Sep 2019 02:59:04 +0200 Subject: [PATCH 168/342] Use language-reporting from git as it supports Rust stable --- Cargo.lock | 12 ++++++------ Cargo.toml | 3 ++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 47348cd570..267876726f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1214,12 +1214,12 @@ dependencies = [ [[package]] name = "language-reporting" version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/wycats/language-reporting#1e2100290fec96f69646e1e61482d80f7a8e7855" dependencies = [ "derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "render-tree 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "render-tree 0.1.1 (git+https://github.com/wycats/language-reporting)", "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1544,7 +1544,7 @@ dependencies = [ "image 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "language-reporting 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "language-reporting 0.3.1 (git+https://github.com/wycats/language-reporting)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "natural 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2117,7 +2117,7 @@ dependencies = [ [[package]] name = "render-tree" version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/wycats/language-reporting#1e2100290fec96f69646e1e61482d80f7a8e7855" dependencies = [ "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3105,7 +3105,7 @@ dependencies = [ "checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d" "checksum js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "1efc4f2a556c58e79c5500912e221dd826bec64ff4aabd8ce71ccef6da02d7d4" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum language-reporting 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "628912b84af4304e1e7e78ebb6a1f503f3a973cba79d072d12e6eb40e7f815db" +"checksum language-reporting 0.3.1 (git+https://github.com/wycats/language-reporting)" = "" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" @@ -3202,7 +3202,7 @@ dependencies = [ "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" "checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum render-tree 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "68ed587df09cfb7ce1bc6fe8f77e24db219f222c049326ccbfb948ec67e31664" +"checksum render-tree 0.1.1 (git+https://github.com/wycats/language-reporting)" = "" "checksum result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560" "checksum roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "153c367ce9fb8ef7afe637ef92bd083ba0f88b03ef3fcf0287d40be05ae0a61c" "checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051" diff --git a/Cargo.toml b/Cargo.toml index 7899b2db8f..a3a5514dd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,7 +41,8 @@ serde-hjson = "0.9.1" serde_yaml = "0.8" serde_bytes = "0.11.2" getset = "0.0.8" -language-reporting = "0.3.1" +#language-reporting = "0.3.1" +language-reporting = { git = "https://github.com/wycats/language-reporting" } app_dirs = "1.2.1" csv = "1.1" toml = "0.5.3" From b123f35d4b928c55e9f8e1bc5320e7a8ee917ff7 Mon Sep 17 00:00:00 2001 From: est31 Date: Sat, 28 Sep 2019 02:22:21 +0200 Subject: [PATCH 169/342] Switch pinned compiler to Rust beta --- rust-toolchain | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust-toolchain b/rust-toolchain index 8f93bd146e..c3a3f37794 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2019-09-11 +beta-2019-09-25 From 6f6d2abdacf5c5b77e7568bb6fbc3da3de173e4d Mon Sep 17 00:00:00 2001 From: Blurryface05 Date: Sat, 28 Sep 2019 01:18:00 +0000 Subject: [PATCH 170/342] modified: .gitpod.yml --- .gitpod.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitpod.yml b/.gitpod.yml index 8adf00be3d..aab5b5cb5f 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,4 +1,4 @@ image: file: .gitpod.Dockerfile tasks: - - command: cargo build + - init: cargo build From b4c783f23d3fc86d70d42ebb815b0db0121eddff Mon Sep 17 00:00:00 2001 From: Blurryface05 Date: Sat, 28 Sep 2019 01:18:55 +0000 Subject: [PATCH 171/342] modified: .gitpod.yml --- .gitpod.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitpod.yml b/.gitpod.yml index aab5b5cb5f..fce1f9b424 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -2,3 +2,4 @@ image: file: .gitpod.Dockerfile tasks: - init: cargo build + command: cargo run From 680aeb12c2cea69ee82c49f70a22baf38d02d625 Mon Sep 17 00:00:00 2001 From: Blurryface05 Date: Sat, 28 Sep 2019 01:46:57 +0000 Subject: [PATCH 172/342] modified: README.md --- README.md | 48 +++++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 50f2e5100d..f0d7968cd6 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ [![Crates.io](https://img.shields.io/crates/v/nu.svg)](https://crates.io/crates/nu) -[![Build Status](https://dev.azure.com/nushell/nushell/_apis/build/status/nushell.nushell?branchName=master)](https://dev.azure.com/nushell/nushell/_build/latest?definitionId=2&branchName=master) +[![Build Status](https://dev.azure.com/nushell/nushell/_apis/build/status/nushell.nushell?branchName=master)](https://dev.azure.com/nushell/nushell/_build/latest?definitionId=2&branchName=master) [![Discord](https://img.shields.io/discord/601130461678272522.svg?logo=discord)](https://discord.gg/NtAbbGn) - + # Nu Shell A modern shell for the GitHub era @@ -22,10 +22,12 @@ There are a few good resources to learn about Nu. There is a [book](https://book If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-book/tree/master/en) to help get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. We also have an active [discord](https://discord.gg/NtAbbGn) and [twitter](https://twitter.com/nu_shell) if you'd like to come chat with us. +Try it in gitpod +[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/nushell/nushell) # Installation -## Local +## Local Up-to-date installation instructions can be found in the [installation chapter of the book](https://book.nushell.sh/en/installation). @@ -71,13 +73,13 @@ To build the base image: ```bash $ docker build -f docker/Dockerfile.nu-base -t nushell/nu-base . -``` +``` And then to build the smaller container (using a Multistage build): ```bash $ docker build -f docker/Dockerfile -t nushell/nu . -``` +``` Either way, you can run either container as follows: @@ -112,17 +114,17 @@ Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing lef ``` /home/jonathan/Source/nushell(master)> ls | where type == "Directory" | autoview ━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ - # │ name │ type │ readonly │ size │ accessed │ modified + # │ name │ type │ readonly │ size │ accessed │ modified ────┼───────────┼───────────┼──────────┼────────┼──────────────┼──────────────── - 0 │ .azure │ Directory │ │ 4.1 KB │ 2 months ago │ a day ago - 1 │ target │ Directory │ │ 4.1 KB │ 3 days ago │ 3 days ago - 2 │ images │ Directory │ │ 4.1 KB │ 2 months ago │ 2 weeks ago - 3 │ tests │ Directory │ │ 4.1 KB │ 2 months ago │ 37 minutes ago - 4 │ tmp │ Directory │ │ 4.1 KB │ 2 weeks ago │ 2 weeks ago - 5 │ src │ Directory │ │ 4.1 KB │ 2 months ago │ 37 minutes ago - 6 │ assets │ Directory │ │ 4.1 KB │ a month ago │ a month ago + 0 │ .azure │ Directory │ │ 4.1 KB │ 2 months ago │ a day ago + 1 │ target │ Directory │ │ 4.1 KB │ 3 days ago │ 3 days ago + 2 │ images │ Directory │ │ 4.1 KB │ 2 months ago │ 2 weeks ago + 3 │ tests │ Directory │ │ 4.1 KB │ 2 months ago │ 37 minutes ago + 4 │ tmp │ Directory │ │ 4.1 KB │ 2 weeks ago │ 2 weeks ago + 5 │ src │ Directory │ │ 4.1 KB │ 2 months ago │ 37 minutes ago + 6 │ assets │ Directory │ │ 4.1 KB │ a month ago │ a month ago 7 │ docs │ Directory │ │ 4.1 KB │ 2 months ago │ 2 months ago -━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ +━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ ``` 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: @@ -136,12 +138,12 @@ Being able to use the same commands and compose them differently is an important ```text /home/jonathan/Source/nushell(master)> ps | where cpu > 0 ━━━┯━━━━━━━┯━━━━━━━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━ - # │ pid │ name │ status │ cpu + # │ pid │ name │ status │ cpu ───┼───────┼─────────────────┼──────────┼────────── - 0 │ 992 │ chrome │ Sleeping │ 6.988768 - 1 │ 4240 │ chrome │ Sleeping │ 5.645982 - 2 │ 13973 │ qemu-system-x86 │ Sleeping │ 4.996551 - 3 │ 15746 │ nu │ Sleeping │ 84.59905 + 0 │ 992 │ chrome │ Sleeping │ 6.988768 + 1 │ 4240 │ chrome │ Sleeping │ 5.645982 + 2 │ 13973 │ qemu-system-x86 │ Sleeping │ 4.996551 + 3 │ 15746 │ nu │ Sleeping │ 84.59905 ━━━┷━━━━━━━┷━━━━━━━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━ ``` @@ -153,9 +155,9 @@ Nu can load file and URL contents as raw text or as structured data (if it recog ``` /home/jonathan/Source/nushell(master)> open Cargo.toml ━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━ - bin │ dependencies │ dev-dependencies + bin │ dependencies │ dev-dependencies ──────────────────┼────────────────┼────────────────── - [table: 12 rows] │ [table: 1 row] │ [table: 1 row] + [table: 12 rows] │ [table: 1 row] │ [table: 1 row] ━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━ ``` @@ -164,7 +166,7 @@ We can pipeline this into a command that gets the contents of one of the columns ``` /home/jonathan/Source/nushell(master)> open Cargo.toml | get package ━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━┯━━━━━━┯━━━━━━━━━ - authors │ description │ edition │ license │ name │ version + authors │ description │ edition │ license │ name │ version ─────────────────┼────────────────────────────┼─────────┼─────────┼──────┼───────── [table: 3 rows] │ A shell for the GitHub era │ 2018 │ ISC │ nu │ 0.3.0 ━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━┷━━━━━━┷━━━━━━━━━ @@ -181,7 +183,7 @@ Here we use the variable `$it` to refer to the value being piped to the external ## Shells -Nu will work inside of a single directory and allow you to navigate around your filesystem by default. Nu also offers a way of adding additional working directories that you can jump between, allowing you to work in multiple directories at the same time. +Nu will work inside of a single directory and allow you to navigate around your filesystem by default. Nu also offers a way of adding additional working directories that you can jump between, allowing you to work in multiple directories at the same time. To do so, use the `enter` command, which will allow you create a new "shell" and enter it at the specified path. You can toggle between this new shell and the original shell with the `p` (for previous) and `n` (for next), allowing you to navigate around a ring buffer of shells. Once you're done with a shell, you can `exit` it and remove it from the ring buffer. From 78ccd4181c1dc2dca89c32492c0f913ab492a14a Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Fri, 27 Sep 2019 21:46:04 -0500 Subject: [PATCH 173/342] Update .gitpod.yml --- .gitpod.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/.gitpod.yml b/.gitpod.yml index fce1f9b424..adb894f2d3 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -3,3 +3,19 @@ image: tasks: - init: cargo build command: cargo run +github: + prebuilds: + # enable for the master/default branch (defaults to true) + master: true + # enable for all branches in this repo (defaults to false) + branches: true + # enable for pull requests coming from this repo (defaults to true) + pullRequests: true + # enable for pull requests coming from forks (defaults to false) + pullRequestsFromForks: true + # add a "Review in Gitpod" button as a comment to pull requests (defaults to true) + addComment: true + # add a "Review in Gitpod" button to pull requests (defaults to false) + addBadge: false + # add a label once the prebuild is ready to pull requests (defaults to false) + addLabel: prebuilt-in-gitpod From 4af0dbe44182a25807fc2e35f2194c409488b51c Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Fri, 27 Sep 2019 20:21:30 -0700 Subject: [PATCH 174/342] Removed commented code and added feature to Cargo.toml --- Cargo.toml | 1 + src/cli.rs | 1 - src/commands.rs | 2 -- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6da254c18b..073c213df0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -161,6 +161,7 @@ required-features = ["textview"] [[bin]] name = "nu_plugin_docker" path = "src/plugins/docker.rs" +required-features = ["docker"] [[bin]] name = "nu" diff --git a/src/cli.rs b/src/cli.rs index 7b5d49751e..3db0747d01 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -248,7 +248,6 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Next), whole_stream_command(Previous), whole_stream_command(Debug), - // whole_stream_command(Docker), whole_stream_command(Lines), whole_stream_command(Shells), whole_stream_command(SplitColumn), diff --git a/src/commands.rs b/src/commands.rs index 5e4287c0af..72c07e38e6 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -11,7 +11,6 @@ pub(crate) mod config; pub(crate) mod cp; pub(crate) mod date; pub(crate) mod debug; -// pub(crate) mod docker; pub(crate) mod echo; pub(crate) mod enter; pub(crate) mod env; @@ -80,7 +79,6 @@ pub(crate) use config::Config; pub(crate) use cp::Cpy; pub(crate) use date::Date; pub(crate) use debug::Debug; -// pub(crate) use docker::Docker; pub(crate) use echo::Echo; pub(crate) use enter::Enter; pub(crate) use env::Env; From 4f5c0314cf71e46a3779214471d5c66209554b58 Mon Sep 17 00:00:00 2001 From: Martin-Louis Bright Date: Sat, 28 Sep 2019 00:36:54 -0400 Subject: [PATCH 175/342] Fix 'Shell commands' table markdown formatting --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 50f2e5100d..d7ea7e2009 100644 --- a/README.md +++ b/README.md @@ -230,6 +230,8 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | version | Display Nu version | ## Shell commands +| command | description | +| ------- | ----------- | | exit (--now) | Exit the current shell (or all shells) | | enter (path) | Create a new shell and begin at this path | | p | Go to previous shell | From e12ba5be8f49fb06a1be182c8f940a54535b5ab3 Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Sat, 28 Sep 2019 19:03:10 -0700 Subject: [PATCH 176/342] Added support for more `post` headers. --- src/commands/post.rs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/commands/post.rs b/src/commands/post.rs index 1bff755d9e..ac41709eca 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -11,6 +11,11 @@ use std::path::PathBuf; use std::str::FromStr; use surf::mime; +pub enum HeaderKind { + ContentType(String), + ContentLength(String), +} + pub struct Post; impl PerItemCommand for Post { @@ -24,6 +29,8 @@ impl PerItemCommand for Post { .required("body", SyntaxShape::Any) .named("user", SyntaxShape::Any) .named("password", SyntaxShape::Any) + .named("content-type", SyntaxShape::Any) + .named("content-length", SyntaxShape::Any) .switch("raw") } @@ -73,9 +80,30 @@ fn run( let registry = registry.clone(); let raw_args = raw_args.clone(); + let content_type = call_info + .args + .get("content-type") + .map(|x| x.as_string().unwrap()); + + let content_length = call_info + .args + .get("content-length") + .map(|x| x.as_string().unwrap()); + + let mut headers = vec![]; + match content_type { + Some(ct) => headers.push(HeaderKind::ContentType(ct)), + None => {} + }; + + match content_length { + Some(cl) => headers.push(HeaderKind::ContentLength(cl)), + None => {} + }; + let stream = async_stream! { let (file_extension, contents, contents_tag, span_source) = - post(&path_str, &body, user, password, path_span, ®istry, &raw_args).await.unwrap(); + post(&path_str, &body, user, password, &headers, path_span, ®istry, &raw_args).await.unwrap(); let file_extension = if has_raw { None @@ -143,6 +171,7 @@ pub async fn post( body: &Tagged, user: Option, password: Option, + headers: &Vec, tag: Tag, registry: &CommandRegistry, raw_args: &RawCommandArgs, @@ -164,6 +193,13 @@ pub async fn post( if let Some(login) = login { s = s.set_header("Authorization", format!("Basic {}", login)); } + + for h in headers { + s = match h { + HeaderKind::ContentType(ct) => s.set_header("Content-Type", ct), + HeaderKind::ContentLength(cl) => s.set_header("Content-Length", cl), + }; + } s.await } Tagged { From caed87c12549f20b5eb50d5c8fa017d59a7513b0 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 29 Sep 2019 18:13:56 +1300 Subject: [PATCH 177/342] Rename origin to anchor --- src/commands/autoview.rs | 8 ++-- src/commands/enter.rs | 4 +- src/commands/fetch.rs | 22 +++++----- src/commands/open.rs | 20 ++++----- src/commands/post.rs | 4 +- src/commands/save.rs | 6 +-- src/commands/tags.rs | 8 ++-- src/data/meta.rs | 46 ++++++++++----------- src/parser.rs | 4 +- src/parser/parse/files.rs | 2 +- src/parser/parse/parser.rs | 10 ++--- src/parser/parse/token_tree_builder.rs | 56 +++++++++++++------------- src/plugins/binaryview.rs | 4 +- src/plugins/inc.rs | 10 ++--- src/plugins/str.rs | 6 +-- src/plugins/textview.rs | 4 +- src/shell/help_shell.rs | 4 +- src/shell/value_shell.rs | 4 +- 18 files changed, 111 insertions(+), 111 deletions(-) diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index c3702b3f13..a0e7e9a8a5 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -58,7 +58,7 @@ pub fn autoview( } } }; - } else if is_single_origined_text_value(&input) { + } else if is_single_anchored_text_value(&input) { let text = context.get_command("textview"); if let Some(text) = text { let result = text.run(raw.with_input(input), &context.commands, false); @@ -111,17 +111,17 @@ fn is_single_text_value(input: &Vec>) -> bool { } } -fn is_single_origined_text_value(input: &Vec>) -> bool { +fn is_single_anchored_text_value(input: &Vec>) -> bool { if input.len() != 1 { return false; } if let Tagged { item: Value::Primitive(Primitive::String(_)), - tag: Tag { origin, .. }, + tag: Tag { anchor, .. }, } = input[0] { - origin != uuid::Uuid::nil() + anchor != uuid::Uuid::nil() } else { false } diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 923715d139..5c451d3d5c 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -75,10 +75,10 @@ impl PerItemCommand for Enter { ) .await.unwrap(); - if contents_tag.origin != uuid::Uuid::nil() { + if contents_tag.anchor != uuid::Uuid::nil() { // If we have loaded something, track its source yield ReturnSuccess::action(CommandAction::AddSpanSource( - contents_tag.origin, + contents_tag.anchor, span_source, )); } diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index 886d6bd959..25810d6e17 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -76,10 +76,10 @@ fn run( file_extension.or(path_str.split('.').last().map(String::from)) }; - if contents_tag.origin != uuid::Uuid::nil() { + if contents_tag.anchor != uuid::Uuid::nil() { // If we have loaded something, track its source yield ReturnSuccess::action(CommandAction::AddSpanSource( - contents_tag.origin, + contents_tag.anchor, span_source, )); } @@ -158,7 +158,7 @@ pub async fn fetch( })?), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -173,7 +173,7 @@ pub async fn fetch( })?), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -190,7 +190,7 @@ pub async fn fetch( Value::binary(buf), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )) @@ -206,7 +206,7 @@ pub async fn fetch( })?), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -223,7 +223,7 @@ pub async fn fetch( Value::binary(buf), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )) @@ -239,7 +239,7 @@ pub async fn fetch( })?), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -266,7 +266,7 @@ pub async fn fetch( })?), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )) @@ -276,7 +276,7 @@ pub async fn fetch( Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), @@ -287,7 +287,7 @@ pub async fn fetch( Value::string(format!("No content type found")), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::Url(location.to_string()), )), diff --git a/src/commands/open.rs b/src/commands/open.rs index 78aa35db01..d80d8875ac 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -77,10 +77,10 @@ fn run( file_extension.or(path_str.split('.').last().map(String::from)) }; - if contents_tag.origin != uuid::Uuid::nil() { + if contents_tag.anchor != uuid::Uuid::nil() { // If we have loaded something, track its source yield ReturnSuccess::action(CommandAction::AddSpanSource( - contents_tag.origin, + contents_tag.anchor, span_source, )); } @@ -147,7 +147,7 @@ pub async fn fetch( Value::string(s), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -166,7 +166,7 @@ pub async fn fetch( Value::string(s), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -175,7 +175,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -186,7 +186,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )) @@ -204,7 +204,7 @@ pub async fn fetch( Value::string(s), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -213,7 +213,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), @@ -224,7 +224,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )) @@ -235,7 +235,7 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - origin: Uuid::new_v4(), + anchor: Uuid::new_v4(), }, SpanSource::File(cwd.to_string_lossy().to_string()), )), diff --git a/src/commands/post.rs b/src/commands/post.rs index 1bff755d9e..c0276fd6a4 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -85,10 +85,10 @@ fn run( file_extension.or(path_str.split('.').last().map(String::from)) }; - if contents_tag.origin != uuid::Uuid::nil() { + if contents_tag.anchor != uuid::Uuid::nil() { // If we have loaded something, track its source yield ReturnSuccess::action(CommandAction::AddSpanSource( - contents_tag.origin, + contents_tag.anchor, span_source, )); } diff --git a/src/commands/save.rs b/src/commands/save.rs index e41116d682..f0bf51f8d0 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -133,10 +133,10 @@ fn save( let stream = async_stream! { let input: Vec> = input.values.collect().await; if path.is_none() { - // If there is no filename, check the metadata for the origin filename + // If there is no filename, check the metadata for the anchor filename if input.len() > 0 { - let origin = input[0].origin(); - match source_map.get(&origin) { + let anchor = input[0].anchor(); + match source_map.get(&anchor) { Some(path) => match path { SpanSource::File(file) => { full_path.push(Path::new(file)); diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 9180ba2c61..d3eac91265 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -35,19 +35,19 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result { - tags.insert("origin", Value::string(source)); + tags.insert("anchor", Value::string(source)); } Some(SpanSource::Url(source)) => { - tags.insert("origin", Value::string(source)); + tags.insert("anchor", Value::string(source)); } _ => {} } diff --git a/src/data/meta.rs b/src/data/meta.rs index 010c98037f..4be3a6e1d9 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -39,7 +39,7 @@ pub trait TaggedItem: Sized { self, Tag { span: Span::unknown(), - origin: uuid::Uuid::nil(), + anchor: uuid::Uuid::nil(), }, ) } @@ -90,12 +90,12 @@ impl Tagged { self.tag.span } - pub fn origin(&self) -> uuid::Uuid { - self.tag.origin + pub fn anchor(&self) -> uuid::Uuid { + self.tag.anchor } - pub fn origin_name(&self, source_map: &SourceMap) -> Option { - match source_map.get(&self.tag.origin) { + pub fn anchor_name(&self, source_map: &SourceMap) -> Option { + match source_map.get(&self.tag.anchor) { Some(SpanSource::File(file)) => Some(file.clone()), Some(SpanSource::Url(url)) => Some(url.clone()), _ => None, @@ -167,14 +167,14 @@ impl From<&std::ops::Range> for Span { Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters, )] pub struct Tag { - pub origin: Uuid, + pub anchor: Uuid, pub span: Span, } impl From for Tag { fn from(span: Span) -> Self { Tag { - origin: uuid::Uuid::nil(), + anchor: uuid::Uuid::nil(), span, } } @@ -183,25 +183,25 @@ impl From for Tag { impl From<&Span> for Tag { fn from(span: &Span) -> Self { Tag { - origin: uuid::Uuid::nil(), + anchor: uuid::Uuid::nil(), span: *span, } } } impl From<(usize, usize, Uuid)> for Tag { - fn from((start, end, origin): (usize, usize, Uuid)) -> Self { + fn from((start, end, anchor): (usize, usize, Uuid)) -> Self { Tag { - origin, + anchor, span: Span { start, end }, } } } impl From<(usize, usize, Option)> for Tag { - fn from((start, end, origin): (usize, usize, Option)) -> Self { + fn from((start, end, anchor): (usize, usize, Option)) -> Self { Tag { - origin: if let Some(uuid) = origin { + anchor: if let Some(uuid) = anchor { uuid } else { uuid::Uuid::nil() @@ -214,7 +214,7 @@ impl From<(usize, usize, Option)> for Tag { impl From> for Tag { fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Tag { Tag { - origin: input.extra, + anchor: input.extra, span: Span { start: input.offset, end: input.offset + input.fragment.len(), @@ -236,23 +236,23 @@ impl From<&Tag> for Span { } impl Tag { - pub fn unknown_origin(span: Span) -> Tag { + pub fn unknown_anchor(span: Span) -> Tag { Tag { - origin: uuid::Uuid::nil(), + anchor: uuid::Uuid::nil(), span, } } - pub fn unknown_span(origin: Uuid) -> Tag { + pub fn unknown_span(anchor: Uuid) -> Tag { Tag { - origin, + anchor, span: Span::unknown(), } } pub fn unknown() -> Tag { Tag { - origin: uuid::Uuid::nil(), + anchor: uuid::Uuid::nil(), span: Span::unknown(), } } @@ -260,8 +260,8 @@ impl Tag { pub fn until(&self, other: impl Into) -> Tag { let other = other.into(); debug_assert!( - self.origin == other.origin, - "Can only merge two tags with the same origin" + self.anchor == other.anchor, + "Can only merge two tags with the same anchor" ); Tag { @@ -269,7 +269,7 @@ impl Tag { start: self.span.start, end: other.span.end, }, - origin: self.origin, + anchor: self.anchor, } } @@ -348,7 +348,7 @@ impl language_reporting::ReportingSpan for Tag { start, end: self.span.end, }, - origin: self.origin, + anchor: self.anchor, } } @@ -358,7 +358,7 @@ impl language_reporting::ReportingSpan for Tag { start: self.span.start, end, }, - origin: self.origin, + anchor: self.anchor, } } diff --git a/src/parser.rs b/src/parser.rs index 3b3ac1a136..138125769b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -21,10 +21,10 @@ pub(crate) use parse::unit::Unit; pub(crate) use parse_command::parse_command; pub(crate) use registry::CommandRegistry; -pub fn parse(input: &str, origin: uuid::Uuid) -> Result { +pub fn parse(input: &str, anchor: uuid::Uuid) -> Result { let _ = pretty_env_logger::try_init(); - match pipeline(nom_input(input, origin)) { + match pipeline(nom_input(input, anchor)) { Ok((_rest, val)) => Ok(val), Err(err) => Err(ShellError::parse_error(err)), } diff --git a/src/parser/parse/files.rs b/src/parser/parse/files.rs index 65a2620936..afe75ddb27 100644 --- a/src/parser/parse/files.rs +++ b/src/parser/parse/files.rs @@ -22,7 +22,7 @@ impl language_reporting::ReportingFiles for Files { } fn file_id(&self, tag: Self::Span) -> Self::FileId { - tag.origin + tag.anchor } fn file_name(&self, _file: Self::FileId) -> FileName { diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index f30eb2c11b..33903ba37c 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -26,8 +26,8 @@ use uuid::Uuid; pub type NomSpan<'a> = LocatedSpanEx<&'a str, Uuid>; -pub fn nom_input(s: &str, origin: Uuid) -> NomSpan<'_> { - LocatedSpanEx::new_extra(s, origin) +pub fn nom_input(s: &str, anchor: Uuid) -> NomSpan<'_> { + LocatedSpanEx::new_extra(s, anchor) } macro_rules! operator { @@ -149,7 +149,7 @@ impl Into for BigDecimal { } pub fn raw_number(input: NomSpan) -> IResult> { - let original = input; + let anchoral = input; let start = input.offset; trace_step(input, "raw_decimal", move |input| { let (input, neg) = opt(tag("-"))(input)?; @@ -1308,7 +1308,7 @@ mod tests { right: usize, ) -> TokenNode { let node = DelimitedNode::new(*delimiter, children); - let spanned = node.tagged((left, right, delimiter.tag.origin)); + let spanned = node.tagged((left, right, delimiter.tag.anchor)); TokenNode::Delimited(spanned) } @@ -1319,7 +1319,7 @@ mod tests { Box::new(head), tail.into_iter().map(TokenNode::Token).collect(), ); - let spanned = node.tagged((left, right, tag.origin)); + let spanned = node.tagged((left, right, tag.anchor)); TokenNode::Path(spanned) } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index 1ed8383f4c..9a2e6ab721 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -18,15 +18,15 @@ pub struct TokenTreeBuilder { #[new(default)] output: String, - origin: Uuid, + anchor: Uuid, } pub type CurriedToken = Box TokenNode + 'static>; pub type CurriedCall = Box Tagged + 'static>; impl TokenTreeBuilder { - pub fn build(origin: Uuid, block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { - let mut builder = TokenTreeBuilder::new(origin); + pub fn build(anchor: Uuid, block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { + let mut builder = TokenTreeBuilder::new(anchor); let node = block(&mut builder); (node, builder.output) } @@ -76,7 +76,7 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::tagged_pipeline((out, None), (start, end, b.origin)) + TokenTreeBuilder::tagged_pipeline((out, None), (start, end, b.anchor)) }) } @@ -95,7 +95,7 @@ impl TokenTreeBuilder { b.pos = end; - TokenTreeBuilder::tagged_op(input, (start, end, b.origin)) + TokenTreeBuilder::tagged_op(input, (start, end, b.anchor)) }) } @@ -113,8 +113,8 @@ impl TokenTreeBuilder { b.pos = end; TokenTreeBuilder::tagged_string( - (inner_start, inner_end, b.origin), - (start, end, b.origin), + (inner_start, inner_end, b.anchor), + (start, end, b.anchor), ) }) } @@ -130,7 +130,7 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_bare((start, end, b.origin)) + TokenTreeBuilder::tagged_bare((start, end, b.anchor)) }) } @@ -145,7 +145,7 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_pattern((start, end, b.origin)) + TokenTreeBuilder::tagged_pattern((start, end, b.anchor)) }) } @@ -160,7 +160,7 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_external_word((start, end, b.origin)) + TokenTreeBuilder::tagged_external_word((start, end, b.anchor)) }) } @@ -180,8 +180,8 @@ impl TokenTreeBuilder { b.pos = end; TokenTreeBuilder::tagged_number( - RawNumber::Int((start, end, b.origin).into()), - (start, end, b.origin), + RawNumber::Int((start, end, b.anchor).into()), + (start, end, b.anchor), ) }) } @@ -194,8 +194,8 @@ impl TokenTreeBuilder { b.pos = end; TokenTreeBuilder::tagged_number( - RawNumber::Decimal((start, end, b.origin).into()), - (start, end, b.origin), + RawNumber::Decimal((start, end, b.anchor).into()), + (start, end, b.anchor), ) }) } @@ -214,8 +214,8 @@ impl TokenTreeBuilder { b.pos = end_unit; TokenTreeBuilder::tagged_size( - (RawNumber::Int((start_int, end_int, b.origin).into()), unit), - (start_int, end_unit, b.origin), + (RawNumber::Int((start_int, end_int, b.anchor).into()), unit), + (start_int, end_unit, b.anchor), ) }) } @@ -244,7 +244,7 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::tagged_path((head, output), (start, end, b.origin)) + TokenTreeBuilder::tagged_path((head, output), (start, end, b.anchor)) }) } @@ -259,7 +259,7 @@ impl TokenTreeBuilder { let (start, _) = b.consume("$"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::tagged_var((inner_start, end, b.origin), (start, end, b.origin)) + TokenTreeBuilder::tagged_var((inner_start, end, b.anchor), (start, end, b.anchor)) }) } @@ -274,7 +274,7 @@ impl TokenTreeBuilder { let (start, _) = b.consume("--"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::tagged_flag((inner_start, end, b.origin), (start, end, b.origin)) + TokenTreeBuilder::tagged_flag((inner_start, end, b.anchor), (start, end, b.anchor)) }) } @@ -289,7 +289,7 @@ impl TokenTreeBuilder { let (start, _) = b.consume("-"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::tagged_shorthand((inner_start, end, b.origin), (start, end, b.origin)) + TokenTreeBuilder::tagged_shorthand((inner_start, end, b.anchor), (start, end, b.anchor)) }) } @@ -302,7 +302,7 @@ impl TokenTreeBuilder { Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::tagged_member((start, end, b.origin)) + TokenTreeBuilder::tagged_member((start, end, b.anchor)) }) } @@ -323,7 +323,7 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::tagged_call(nodes, (start, end, b.origin)) + TokenTreeBuilder::tagged_call(nodes, (start, end, b.anchor)) }) } @@ -350,7 +350,7 @@ impl TokenTreeBuilder { let (_, end) = b.consume(")"); - TokenTreeBuilder::tagged_parens(output, (start, end, b.origin)) + TokenTreeBuilder::tagged_parens(output, (start, end, b.anchor)) }) } @@ -368,7 +368,7 @@ impl TokenTreeBuilder { let (_, end) = b.consume("]"); - TokenTreeBuilder::tagged_square(output, (start, end, b.origin)) + TokenTreeBuilder::tagged_square(output, (start, end, b.anchor)) }) } @@ -386,7 +386,7 @@ impl TokenTreeBuilder { let (_, end) = b.consume(" }"); - TokenTreeBuilder::tagged_brace(output, (start, end, b.origin)) + TokenTreeBuilder::tagged_brace(output, (start, end, b.anchor)) }) } @@ -397,7 +397,7 @@ impl TokenTreeBuilder { pub fn sp() -> CurriedToken { Box::new(|b| { let (start, end) = b.consume(" "); - TokenNode::Whitespace(Tag::from((start, end, b.origin))) + TokenNode::Whitespace(Tag::from((start, end, b.anchor))) }) } @@ -406,7 +406,7 @@ impl TokenTreeBuilder { Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::tagged_ws((start, end, b.origin)) + TokenTreeBuilder::tagged_ws((start, end, b.anchor)) }) } @@ -425,6 +425,6 @@ impl TokenTreeBuilder { let start = self.pos; self.pos += input.len(); self.output.push_str(input); - (start, self.pos, self.origin).into() + (start, self.pos, self.anchor).into() } } diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index 895ec97fe2..d2d7a0aeb5 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -21,10 +21,10 @@ impl Plugin for BinaryView { fn sink(&mut self, call_info: CallInfo, input: Vec>) { for v in input { - let value_origin = v.origin(); + let value_anchor = v.anchor(); match v.item { Value::Primitive(Primitive::Binary(b)) => { - let source = call_info.source_map.get(&value_origin); + let source = call_info.source_map.get(&value_anchor); let _ = view_binary(&b, source, call_info.args.has("lores")); } _ => {} diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 4422195be8..ecab03dc97 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -186,15 +186,15 @@ mod tests { }; struct CallStub { - origin: uuid::Uuid, + anchor: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } impl CallStub { - fn new(origin: uuid::Uuid) -> CallStub { + fn new(anchor: uuid::Uuid) -> CallStub { CallStub { - origin, + anchor, positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -210,7 +210,7 @@ mod tests { fn with_parameter(&mut self, name: &str) -> &mut Self { self.positionals - .push(Value::string(name.to_string()).tagged(Tag::unknown_span(self.origin))); + .push(Value::string(name.to_string()).tagged(Tag::unknown_span(self.anchor))); self } @@ -218,7 +218,7 @@ mod tests { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), source_map: SourceMap::new(), - name_tag: Tag::unknown_span(self.origin), + name_tag: Tag::unknown_span(self.anchor), } } } diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 8030cc4bd3..4b74914f09 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -204,7 +204,7 @@ mod tests { use num_bigint::BigInt; struct CallStub { - origin: uuid::Uuid, + anchor: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } @@ -212,7 +212,7 @@ mod tests { impl CallStub { fn new() -> CallStub { CallStub { - origin: uuid::Uuid::nil(), + anchor: uuid::Uuid::nil(), positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -236,7 +236,7 @@ mod tests { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), source_map: SourceMap::new(), - name_tag: Tag::unknown_span(self.origin), + name_tag: Tag::unknown_span(self.anchor), } } } diff --git a/src/plugins/textview.rs b/src/plugins/textview.rs index cad2e16e62..2a2e3069fe 100644 --- a/src/plugins/textview.rs +++ b/src/plugins/textview.rs @@ -216,10 +216,10 @@ fn scroll_view(s: &str) { } fn view_text_value(value: &Tagged, source_map: &SourceMap) { - let value_origin = value.origin(); + let value_anchor = value.anchor(); match value.item { Value::Primitive(Primitive::String(ref s)) => { - let source = source_map.get(&value_origin); + let source = source_map.get(&value_anchor); if let Some(source) = source { let extension: Option = match source { diff --git a/src/shell/help_shell.rs b/src/shell/help_shell.rs index 25f1b9c428..0fedd9ad79 100644 --- a/src/shell/help_shell.rs +++ b/src/shell/help_shell.rs @@ -99,10 +99,10 @@ impl HelpShell { impl Shell for HelpShell { fn name(&self, source_map: &SourceMap) -> String { - let origin_name = self.value.origin_name(source_map); + let anchor_name = self.value.anchor_name(source_map); format!( "{}", - match origin_name { + match anchor_name { Some(x) => format!("{{{}}}", x), None => format!("<{}>", self.value.item.type_name(),), } diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index 175e232e7b..d95d07cb97 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -73,10 +73,10 @@ impl ValueShell { impl Shell for ValueShell { fn name(&self, source_map: &SourceMap) -> String { - let origin_name = self.value.origin_name(source_map); + let anchor_name = self.value.anchor_name(source_map); format!( "{}", - match origin_name { + match anchor_name { Some(x) => format!("{{{}}}", x), None => format!("<{}>", self.value.item.type_name(),), } From ce947d70b02ec9ec92b7d577af362cc3d43670b8 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 29 Sep 2019 18:18:59 +1300 Subject: [PATCH 178/342] Rename SpanSource to AnchorLocation --- src/commands/classified.rs | 4 ++-- src/commands/command.rs | 6 +++--- src/commands/enter.rs | 6 +++--- src/commands/fetch.rs | 28 ++++++++++++++-------------- src/commands/open.rs | 26 +++++++++++++------------- src/commands/post.rs | 26 +++++++++++++------------- src/commands/save.rs | 2 +- src/commands/tags.rs | 4 ++-- src/context.rs | 14 +++++++------- src/data/meta.rs | 6 +++--- src/lib.rs | 2 +- src/plugins/binaryview.rs | 10 +++++----- src/plugins/textview.rs | 8 ++++---- src/prelude.rs | 2 +- 14 files changed, 72 insertions(+), 72 deletions(-) diff --git a/src/commands/classified.rs b/src/commands/classified.rs index eb51f9c4c6..7e367b83d7 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -127,8 +127,8 @@ impl InternalCommand { CommandAction::ChangePath(path) => { context.shell_manager.set_path(path); } - CommandAction::AddSpanSource(uuid, span_source) => { - context.add_span_source(uuid, span_source); + CommandAction::AddAnchorLocation(uuid, anchor_location) => { + context.add_anchor_location(uuid, anchor_location); } CommandAction::Exit => std::process::exit(0), // TODO: save history.txt CommandAction::EnterHelpShell(value) => { diff --git a/src/commands/command.rs b/src/commands/command.rs index 8bbf2d7981..95732abac2 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,4 +1,4 @@ -use crate::context::{SourceMap, SpanSource}; +use crate::context::{AnchorLocation, SourceMap}; use crate::data::Value; use crate::errors::ShellError; use crate::evaluate::Scope; @@ -376,7 +376,7 @@ impl EvaluatedCommandArgs { #[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(String), - AddSpanSource(Uuid, SpanSource), + AddAnchorLocation(Uuid, AnchorLocation), Exit, EnterShell(String), EnterValueShell(Tagged), @@ -390,7 +390,7 @@ 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) => { + CommandAction::AddAnchorLocation(u, source) => { write!(f, "action:add-span-source={}@{:?}", u, source) } CommandAction::Exit => write!(f, "action:exit"), diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 5c451d3d5c..2d96fe865c 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -67,7 +67,7 @@ impl PerItemCommand for Enter { let full_path = std::path::PathBuf::from(cwd); - let (file_extension, contents, contents_tag, span_source) = + let (file_extension, contents, contents_tag, anchor_location) = crate::commands::open::fetch( &full_path, &location_clone, @@ -77,9 +77,9 @@ impl PerItemCommand for Enter { if contents_tag.anchor != uuid::Uuid::nil() { // If we have loaded something, track its source - yield ReturnSuccess::action(CommandAction::AddSpanSource( + yield ReturnSuccess::action(CommandAction::AddAnchorLocation( contents_tag.anchor, - span_source, + anchor_location, )); } diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index 25810d6e17..652ec77eb5 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -1,5 +1,5 @@ use crate::commands::UnevaluatedCallInfo; -use crate::context::SpanSource; +use crate::context::AnchorLocation; use crate::data::meta::Span; use crate::data::Value; use crate::errors::ShellError; @@ -66,7 +66,7 @@ fn run( yield Err(e); return; } - let (file_extension, contents, contents_tag, span_source) = result.unwrap(); + let (file_extension, contents, contents_tag, anchor_location) = result.unwrap(); let file_extension = if has_raw { None @@ -78,9 +78,9 @@ fn run( if contents_tag.anchor != uuid::Uuid::nil() { // If we have loaded something, track its source - yield ReturnSuccess::action(CommandAction::AddSpanSource( + yield ReturnSuccess::action(CommandAction::AddAnchorLocation( contents_tag.anchor, - span_source, + anchor_location, )); } @@ -132,7 +132,7 @@ fn run( pub async fn fetch( location: &str, span: Span, -) -> Result<(Option, Value, Tag, SpanSource), ShellError> { +) -> Result<(Option, Value, Tag, AnchorLocation), ShellError> { if let Err(_) = url::Url::parse(location) { return Err(ShellError::labeled_error( "Incomplete or incorrect url", @@ -160,7 +160,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( Some("json".to_string()), @@ -175,7 +175,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { let buf: Vec = r.body_bytes().await.map_err(|_| { @@ -192,7 +192,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )) } (mime::IMAGE, mime::SVG) => Ok(( @@ -208,7 +208,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), (mime::IMAGE, image_ty) => { let buf: Vec = r.body_bytes().await.map_err(|_| { @@ -225,7 +225,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )) } (mime::TEXT, mime::HTML) => Ok(( @@ -241,7 +241,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { let path_extension = url::Url::parse(location) @@ -268,7 +268,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )) } (ty, sub_ty) => Ok(( @@ -278,7 +278,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), } } @@ -289,7 +289,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), }, Err(_) => { diff --git a/src/commands/open.rs b/src/commands/open.rs index d80d8875ac..254b0bd7b9 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -1,5 +1,5 @@ use crate::commands::UnevaluatedCallInfo; -use crate::context::SpanSource; +use crate::context::AnchorLocation; use crate::data::meta::Span; use crate::data::Value; use crate::errors::ShellError; @@ -67,7 +67,7 @@ fn run( yield Err(e); return; } - let (file_extension, contents, contents_tag, span_source) = result.unwrap(); + let (file_extension, contents, contents_tag, anchor_location) = result.unwrap(); let file_extension = if has_raw { None @@ -79,9 +79,9 @@ fn run( if contents_tag.anchor != uuid::Uuid::nil() { // If we have loaded something, track its source - yield ReturnSuccess::action(CommandAction::AddSpanSource( + yield ReturnSuccess::action(CommandAction::AddAnchorLocation( contents_tag.anchor, - span_source, + anchor_location, )); } @@ -134,7 +134,7 @@ pub async fn fetch( cwd: &PathBuf, location: &str, span: Span, -) -> Result<(Option, Value, Tag, SpanSource), ShellError> { +) -> Result<(Option, Value, Tag, AnchorLocation), ShellError> { let mut cwd = cwd.clone(); cwd.push(Path::new(location)); @@ -149,7 +149,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::File(cwd.to_string_lossy().to_string()), + AnchorLocation::File(cwd.to_string_lossy().to_string()), )), Err(_) => { //Non utf8 data. @@ -168,7 +168,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::File(cwd.to_string_lossy().to_string()), + AnchorLocation::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, @@ -177,7 +177,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::File(cwd.to_string_lossy().to_string()), + AnchorLocation::File(cwd.to_string_lossy().to_string()), )), } } else { @@ -188,7 +188,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::File(cwd.to_string_lossy().to_string()), + AnchorLocation::File(cwd.to_string_lossy().to_string()), )) } } @@ -206,7 +206,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::File(cwd.to_string_lossy().to_string()), + AnchorLocation::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, @@ -215,7 +215,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::File(cwd.to_string_lossy().to_string()), + AnchorLocation::File(cwd.to_string_lossy().to_string()), )), } } else { @@ -226,7 +226,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::File(cwd.to_string_lossy().to_string()), + AnchorLocation::File(cwd.to_string_lossy().to_string()), )) } } @@ -237,7 +237,7 @@ pub async fn fetch( span, anchor: Uuid::new_v4(), }, - SpanSource::File(cwd.to_string_lossy().to_string()), + AnchorLocation::File(cwd.to_string_lossy().to_string()), )), } } diff --git a/src/commands/post.rs b/src/commands/post.rs index c0276fd6a4..5533e30652 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -1,5 +1,5 @@ use crate::commands::UnevaluatedCallInfo; -use crate::context::SpanSource; +use crate::context::AnchorLocation; use crate::data::Value; use crate::errors::ShellError; use crate::parser::hir::SyntaxShape; @@ -74,7 +74,7 @@ fn run( let raw_args = raw_args.clone(); let stream = async_stream! { - let (file_extension, contents, contents_tag, span_source) = + let (file_extension, contents, contents_tag, anchor_location) = post(&path_str, &body, user, password, path_span, ®istry, &raw_args).await.unwrap(); let file_extension = if has_raw { @@ -87,9 +87,9 @@ fn run( if contents_tag.anchor != uuid::Uuid::nil() { // If we have loaded something, track its source - yield ReturnSuccess::action(CommandAction::AddSpanSource( + yield ReturnSuccess::action(CommandAction::AddAnchorLocation( contents_tag.anchor, - span_source, + anchor_location, )); } @@ -146,7 +146,7 @@ pub async fn post( tag: Tag, registry: &CommandRegistry, raw_args: &RawCommandArgs, -) -> Result<(Option, Value, Tag, SpanSource), ShellError> { +) -> Result<(Option, Value, Tag, AnchorLocation), ShellError> { let registry = registry.clone(); let raw_args = raw_args.clone(); if location.starts_with("http:") || location.starts_with("https:") { @@ -248,7 +248,7 @@ pub async fn post( ) })?), tag, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( Some("json".to_string()), @@ -260,7 +260,7 @@ pub async fn post( ) })?), tag, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { let buf: Vec = r.body_bytes().await.map_err(|_| { @@ -274,7 +274,7 @@ pub async fn post( None, Value::binary(buf), tag, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )) } (mime::IMAGE, image_ty) => { @@ -289,7 +289,7 @@ pub async fn post( Some(image_ty.to_string()), Value::binary(buf), tag, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )) } (mime::TEXT, mime::HTML) => Ok(( @@ -302,7 +302,7 @@ pub async fn post( ) })?), tag, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { let path_extension = url::Url::parse(location) @@ -326,7 +326,7 @@ pub async fn post( ) })?), tag, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )) } (ty, sub_ty) => Ok(( @@ -336,7 +336,7 @@ pub async fn post( ty, sub_ty )), tag, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), } } @@ -344,7 +344,7 @@ pub async fn post( None, Value::string(format!("No content type found")), tag, - SpanSource::Url(location.to_string()), + AnchorLocation::Url(location.to_string()), )), }, Err(_) => { diff --git a/src/commands/save.rs b/src/commands/save.rs index f0bf51f8d0..47f1a17e95 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -138,7 +138,7 @@ fn save( let anchor = input[0].anchor(); match source_map.get(&anchor) { Some(path) => match path { - SpanSource::File(file) => { + AnchorLocation::File(file) => { full_path.push(Path::new(file)); } _ => { diff --git a/src/commands/tags.rs b/src/commands/tags.rs index d3eac91265..0cef300b0c 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -43,10 +43,10 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result { + Some(AnchorLocation::File(source)) => { tags.insert("anchor", Value::string(source)); } - Some(SpanSource::Url(source)) => { + Some(AnchorLocation::Url(source)) => { tags.insert("anchor", Value::string(source)); } _ => {} diff --git a/src/context.rs b/src/context.rs index 57dd1a841c..93f4c68b9a 100644 --- a/src/context.rs +++ b/src/context.rs @@ -11,21 +11,21 @@ use std::sync::Arc; use uuid::Uuid; #[derive(Clone, Debug, Serialize, Deserialize)] -pub enum SpanSource { +pub enum AnchorLocation { Url(String), File(String), Source(Text), } #[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SourceMap(HashMap); +pub struct SourceMap(HashMap); impl SourceMap { - pub fn insert(&mut self, uuid: Uuid, span_source: SpanSource) { - self.0.insert(uuid, span_source); + pub fn insert(&mut self, uuid: Uuid, anchor_location: AnchorLocation) { + self.0.insert(uuid, anchor_location); } - pub fn get(&self, uuid: &Uuid) -> Option<&SpanSource> { + pub fn get(&self, uuid: &Uuid) -> Option<&AnchorLocation> { self.0.get(uuid) } @@ -105,8 +105,8 @@ impl Context { } } - pub fn add_span_source(&mut self, uuid: Uuid, span_source: SpanSource) { - self.source_map.insert(uuid, span_source); + pub fn add_anchor_location(&mut self, uuid: Uuid, anchor_location: AnchorLocation) { + self.source_map.insert(uuid, anchor_location); } pub(crate) fn has_command(&self, name: &str) -> bool { diff --git a/src/data/meta.rs b/src/data/meta.rs index 4be3a6e1d9..0a56198e6c 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -1,4 +1,4 @@ -use crate::context::{SourceMap, SpanSource}; +use crate::context::{AnchorLocation, SourceMap}; use crate::prelude::*; use crate::Text; use derive_new::new; @@ -96,8 +96,8 @@ impl Tagged { pub fn anchor_name(&self, source_map: &SourceMap) -> Option { match source_map.get(&self.tag.anchor) { - Some(SpanSource::File(file)) => Some(file.clone()), - Some(SpanSource::Url(url)) => Some(url.clone()), + Some(AnchorLocation::File(file)) => Some(file.clone()), + Some(AnchorLocation::Url(url)) => Some(url.clone()), _ => None, } } diff --git a/src/lib.rs b/src/lib.rs index b17e6c0b38..e8e09aacdd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ mod traits; mod utils; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; -pub use crate::context::{SourceMap, SpanSource}; +pub use crate::context::{AnchorLocation, SourceMap}; pub use crate::env::host::BasicHost; pub use crate::parser::hir::SyntaxShape; pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder; diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index d2d7a0aeb5..d5488d3241 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -1,6 +1,6 @@ use crossterm::{cursor, terminal, Attribute, RawScreen}; use nu::{ - serve_plugin, CallInfo, Plugin, Primitive, ShellError, Signature, SpanSource, Tagged, Value, + serve_plugin, AnchorLocation, CallInfo, Plugin, Primitive, ShellError, Signature, Tagged, Value, }; use pretty_hex::*; @@ -35,7 +35,7 @@ impl Plugin for BinaryView { fn view_binary( b: &[u8], - source: Option<&SpanSource>, + source: Option<&AnchorLocation>, lores_mode: bool, ) -> Result<(), Box> { if b.len() > 3 { @@ -254,7 +254,7 @@ fn load_from_jpg_buffer(buffer: &[u8]) -> Option<(RawImageBuffer)> { pub fn view_contents( buffer: &[u8], - _source: Option<&SpanSource>, + _source: Option<&AnchorLocation>, lores_mode: bool, ) -> Result<(), Box> { let mut raw_image_buffer = load_from_png_buffer(buffer); @@ -341,12 +341,12 @@ pub fn view_contents( #[cfg(feature = "rawkey")] pub fn view_contents_interactive( buffer: &[u8], - source: Option<&SpanSource>, + source: Option<&AnchorLocation>, lores_mode: bool, ) -> Result<(), Box> { use rawkey::{KeyCode, RawKey}; - let sav_path = if let Some(SpanSource::File(f)) = source { + let sav_path = if let Some(AnchorLocation::File(f)) = source { let mut path = std::path::PathBuf::from(f); path.set_extension("sav"); Some(path) diff --git a/src/plugins/textview.rs b/src/plugins/textview.rs index 2a2e3069fe..456c73a78a 100644 --- a/src/plugins/textview.rs +++ b/src/plugins/textview.rs @@ -1,7 +1,7 @@ use crossterm::{cursor, terminal, RawScreen}; use crossterm::{InputEvent, KeyEvent}; use nu::{ - serve_plugin, CallInfo, Plugin, Primitive, ShellError, Signature, SourceMap, SpanSource, + serve_plugin, AnchorLocation, CallInfo, Plugin, Primitive, ShellError, Signature, SourceMap, Tagged, Value, }; @@ -223,11 +223,11 @@ fn view_text_value(value: &Tagged, source_map: &SourceMap) { if let Some(source) = source { let extension: Option = match source { - SpanSource::File(file) => { + AnchorLocation::File(file) => { let path = Path::new(file); path.extension().map(|x| x.to_string_lossy().to_string()) } - SpanSource::Url(url) => { + AnchorLocation::Url(url) => { let url = url::Url::parse(url); if let Ok(url) = url { let url = url.clone(); @@ -246,7 +246,7 @@ fn view_text_value(value: &Tagged, source_map: &SourceMap) { } } //FIXME: this probably isn't correct - SpanSource::Source(_source) => None, + AnchorLocation::Source(_source) => None, }; match extension { diff --git a/src/prelude.rs b/src/prelude.rs index 56cd21b33a..eabd778717 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -54,7 +54,7 @@ pub(crate) use crate::commands::command::{ pub(crate) use crate::commands::PerItemCommand; pub(crate) use crate::commands::RawCommandArgs; pub(crate) use crate::context::CommandRegistry; -pub(crate) use crate::context::{Context, SpanSource}; +pub(crate) use crate::context::{AnchorLocation, Context}; pub(crate) use crate::data::base as value; pub(crate) use crate::data::meta::{Tag, Tagged, TaggedItem}; pub(crate) use crate::data::types::ExtractType; From e1357a9541952f29243a3f1c1f1000d6fcdecbc8 Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Sun, 29 Sep 2019 01:29:43 -0700 Subject: [PATCH 179/342] Handle unexpected input and some cleanup. --- src/commands/post.rs | 67 +++++++++++++++++++++++++++++++------------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/src/commands/post.rs b/src/commands/post.rs index ac41709eca..db1a084f12 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -80,26 +80,7 @@ fn run( let registry = registry.clone(); let raw_args = raw_args.clone(); - let content_type = call_info - .args - .get("content-type") - .map(|x| x.as_string().unwrap()); - - let content_length = call_info - .args - .get("content-length") - .map(|x| x.as_string().unwrap()); - - let mut headers = vec![]; - match content_type { - Some(ct) => headers.push(HeaderKind::ContentType(ct)), - None => {} - }; - - match content_length { - Some(cl) => headers.push(HeaderKind::ContentLength(cl)), - None => {} - }; + let headers = get_headers(&call_info)?; let stream = async_stream! { let (file_extension, contents, contents_tag, span_source) = @@ -166,6 +147,52 @@ fn run( Ok(stream.to_output_stream()) } +fn get_headers(call_info: &CallInfo) -> Result, ShellError> { + let mut headers = vec![]; + + match extract_header_value(&call_info, "content-type") { + Ok(h) => match h { + Some(ct) => headers.push(HeaderKind::ContentType(ct)), + None => {} + }, + Err(e) => { + return Err(e); + } + }; + + match extract_header_value(&call_info, "content-length") { + Ok(h) => match h { + Some(cl) => headers.push(HeaderKind::ContentLength(cl)), + None => {} + }, + Err(e) => { + return Err(e); + } + }; + + Ok(headers) +} + +fn extract_header_value(call_info: &CallInfo, key: &str) -> Result, ShellError> { + if call_info.args.has(key) { + let val = match call_info.args.get(key) { + Some(Tagged { + item: Value::Primitive(Primitive::String(s)), + .. + }) => s.clone(), + _ => { + return Err(ShellError::string(format!( + "{} not in expected format. Expected string.", + key + ))); + } + }; + return Ok(Some(val)); + } + + Ok(None) +} + pub async fn post( location: &str, body: &Tagged, From 9c23d78513d2b0d7f5301da4bbe7d71b5c237be2 Mon Sep 17 00:00:00 2001 From: Jonah Snider Date: Sun, 29 Sep 2019 09:03:51 -1000 Subject: [PATCH 180/342] docs: use HTTPS where possible Signed-off-by: Jonah Snider --- Cargo.toml | 2 +- docs/docker.md | 6 +++--- src/commands/help.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 29814fef96..6e59dcaad7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" readme = "README.md" default-run = "nu" repository = "https://github.com/nushell/nushell" -homepage = "http://nushell.sh" +homepage = "https://www.nushell.sh" documentation = "https://book.nushell.sh" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/docs/docker.md b/docs/docker.md index b51f4e0cd0..6484cdfd8e 100644 --- a/docs/docker.md +++ b/docs/docker.md @@ -41,7 +41,7 @@ docker run -it . This image does not contain the common packages contained in the default tag and only contains the minimal packages needed to run `nu`. Unless you are working in an environment where only the `nu` image will be deployed and you have space constraints, it's highly recommended to use the alpine image if you aim for small image size. Only use this image if you really need **both** `glibc` and small image size. ### `nu:-alpine` -This image is based on the popular [Alpine Linux project](http://alpinelinux.org/), available in [the alpine official image][alpine]. Alpine Linux is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general. +This image is based on the popular [Alpine Linux project](https://alpinelinux.org/), available in [the alpine official image][alpine]. Alpine Linux is much smaller than most distribution base images (~5MB), and thus leads to much slimmer images in general. This variant is highly recommended when final image size being as small as possible is desired. The main caveat to note is that it does use `musl` libc instead of `glibc` and friends, so certain software might run into issues depending on the depth of their libc requirements. However, most software doesn't have an issue with this, so this variant is usually a very safe choice. See [this Hacker News comment thread](https://news.ycombinator.com/item?id=10782897) for more discussion of the issues that might arise and some pro/con comparisons of using Alpine-based images. @@ -104,7 +104,7 @@ ENTRYPOINT ["nu"] ### `nu:--busybox` -This image is based on [Busybox](http://www.busybox.net/) which is a very good ingredient to craft space-efficient distributions. It combines tiny versions of many common UNIX utilities into a single small executable. It also provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox generally have fewer options than their full-featured GNU cousins; however, the options that are included provide the expected functionality and behave very much like their GNU counterparts. Basically, this image provides a fairly complete environment for any small or embedded system. +This image is based on [Busybox](https://www.busybox.net/) which is a very good ingredient to craft space-efficient distributions. It combines tiny versions of many common UNIX utilities into a single small executable. It also provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc. The utilities in BusyBox generally have fewer options than their full-featured GNU cousins; however, the options that are included provide the expected functionality and behave very much like their GNU counterparts. Basically, this image provides a fairly complete environment for any small or embedded system. > Use this only if you need common utilities like `tar`, `awk`, and many more but don't want extra blob like nushell plugins and others. @@ -120,5 +120,5 @@ ENTRYPOINT ["nu"] ``` -[musl]: http://www.musl-libc.org/ +[musl]: https://www.musl-libc.org/ [alpine]: https://hub.docker.com/_/alpine/ \ No newline at end of file diff --git a/src/commands/help.rs b/src/commands/help.rs index e8c458caad..d780f13459 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -116,7 +116,7 @@ Here are some tips to help you get started. * help commands - list all available commands * help - display help about a particular command -You can also learn more at http://book.nushell.sh"#; +You can also learn more at https://book.nushell.sh"#; let mut output_stream = VecDeque::new(); From 64345b2985dfb795ac55f9336c561aee5265afd4 Mon Sep 17 00:00:00 2001 From: Vanessa Sochat Date: Sun, 29 Sep 2019 17:18:35 -0400 Subject: [PATCH 181/342] dont run nu at end of release build Signed-off-by: Vanessa Sochat --- docker/Dockerfile.nu-base | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/Dockerfile.nu-base b/docker/Dockerfile.nu-base index 206bb02369..1a9e83a11e 100644 --- a/docker/Dockerfile.nu-base +++ b/docker/Dockerfile.nu-base @@ -17,7 +17,7 @@ ENV PATH=/root/.cargo/bin:$PATH COPY . /code RUN echo "##vso[task.prependpath]/root/.cargo/bin" && \ rustc -Vv && \ - if $RELEASE; then cargo build --release && cargo run --release; \ + if $RELEASE; then cargo build --release; \ cp target/release/nu /usr/local/bin; \ else cargo build; \ cp target/debug/nu /usr/local/bin; fi; From 83d82a09b21bbf09e9be84aa292e2f626a697526 Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Sun, 29 Sep 2019 14:43:39 -0700 Subject: [PATCH 182/342] Better handling of unexpected error case. --- src/commands/post.rs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/commands/post.rs b/src/commands/post.rs index 9d08bd3a3a..5a77afd14b 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -175,16 +175,25 @@ fn get_headers(call_info: &CallInfo) -> Result, ShellError> { fn extract_header_value(call_info: &CallInfo, key: &str) -> Result, ShellError> { if call_info.args.has(key) { - let val = match call_info.args.get(key) { + let tagged = call_info.args.get(key); + let val = match tagged { Some(Tagged { item: Value::Primitive(Primitive::String(s)), .. }) => s.clone(), + Some(Tagged { tag, .. }) => { + return Err(ShellError::labeled_error( + format!("{} not in expected format. Expected string.", key), + "post error", + tag, + )); + } _ => { - return Err(ShellError::string(format!( - "{} not in expected format. Expected string.", - key - ))); + return Err(ShellError::labeled_error( + format!("{} not in expected format. Expected string.", key), + "post error", + Tag::unknown(), + )); } }; return Ok(Some(val)); From 00f0fd28730ae5a5a91d1cc417d4f30a93719972 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 30 Sep 2019 11:09:52 +1300 Subject: [PATCH 183/342] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a73932cb28..7180bccb92 100644 --- a/README.md +++ b/README.md @@ -43,13 +43,13 @@ Optional dependencies: * To use Nu with all possible optional features enabled, you'll also need the following: * on Linux (on Debian/Ubuntu): `apt install libxcb-composite0-dev libx11-dev` -To install Nu via cargo: +To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs/) the nightly compiler via `rustup install nightly`): ``` cargo +nightly install nu ``` -You can also install Nu with all the bells and whistles: +You can also install Nu with all the bells and whistles (be sure to have installed the [dependencies](https://book.nushell.sh/en/installation#dependencies] for your platform): ``` cargo +nightly install nu --all-features From c15b5df67423c0917273d86c2cfe59458753e790 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 30 Sep 2019 11:10:19 +1300 Subject: [PATCH 184/342] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7180bccb92..165321a266 100644 --- a/README.md +++ b/README.md @@ -49,7 +49,7 @@ To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs cargo +nightly install nu ``` -You can also install Nu with all the bells and whistles (be sure to have installed the [dependencies](https://book.nushell.sh/en/installation#dependencies] for your platform): +You can also install Nu with all the bells and whistles (be sure to have installed the [dependencies](https://book.nushell.sh/en/installation#dependencies) for your platform): ``` cargo +nightly install nu --all-features From c5fdbdb8a15cc86cb099d8610e5899152fee7e26 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 30 Sep 2019 11:29:59 +1300 Subject: [PATCH 185/342] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 165321a266..f5f8a1087b 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ There are a few good resources to learn about Nu. There is a [book](https://book If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-book/tree/master/en) to help get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. We also have an active [discord](https://discord.gg/NtAbbGn) and [twitter](https://twitter.com/nu_shell) if you'd like to come chat with us. + Try it in gitpod [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/nushell/nushell) @@ -43,7 +44,7 @@ Optional dependencies: * To use Nu with all possible optional features enabled, you'll also need the following: * on Linux (on Debian/Ubuntu): `apt install libxcb-composite0-dev libx11-dev` -To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs/) the nightly compiler via `rustup install nightly`): +To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs/) and the nightly compiler via `rustup install nightly`): ``` cargo +nightly install nu From 3812037e2a3914abcdd97ebe4517729911880a2d Mon Sep 17 00:00:00 2001 From: rnxypke Date: Mon, 30 Sep 2019 00:42:41 +0200 Subject: [PATCH 186/342] remove trailing newline after external command --- src/commands/classified.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 7e367b83d7..0e5cd95d8d 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -302,7 +302,6 @@ impl ExternalCommand { } } } - println!(""); Ok(ClassifiedInputStream::new()) } StreamNext::External => { From 093b9c1c5b73efb8f7a8f596f458501fc1f413e1 Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Sun, 29 Sep 2019 20:20:18 -0700 Subject: [PATCH 187/342] Fixed last command crash When the last command has an input value larger than the data its operating on it would crash. Added a check to ensure there are enough elements to take. --- src/commands/last.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/commands/last.rs b/src/commands/last.rs index 4813c555a1..321506846b 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -38,10 +38,13 @@ fn last( ) -> Result { let stream = async_stream! { let v: Vec<_> = context.input.into_vec().await; - let k = v.len() - (*amount as usize); - for x in v[k..].iter() { - let y: Tagged = x.clone(); - yield ReturnSuccess::value(y) + let count = (*amount as usize); + if count < v.len() { + let k = v.len() - count; + for x in v[k..].iter() { + let y: Tagged = x.clone(); + yield ReturnSuccess::value(y) + } } }; Ok(stream.to_output_stream()) From ce9e4a61e784fe07ae6d4c4ccf71fe2c5e5f65af Mon Sep 17 00:00:00 2001 From: Fahmi Akbar Wildana Date: Tue, 1 Oct 2019 03:39:16 +0700 Subject: [PATCH 188/342] Add repology.org badge to Packaging status --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index f5f8a1087b..7efa748464 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,8 @@ The second container is a bit smaller, if size is important to you. ## Packaging status +[![Packaging status](https://repology.org/badge/vertical-allrepos/nushell.svg)](https://repology.org/project/nushell/versions) + ### Fedora [COPR repo](https://copr.fedorainfracloud.org/coprs/atim/nushell/): `sudo dnf copr enable atim/nushell -y && sudo dnf install nushell -y` From 9d04a7cc406a98536bb5a1938b345903c55c6ab6 Mon Sep 17 00:00:00 2001 From: Isaac Goss Date: Mon, 30 Sep 2019 18:51:35 -0400 Subject: [PATCH 189/342] Adds a word to README for readabilty --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7efa748464..96e97a90d8 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A modern shell for the GitHub era # Status -This project has reached a minimum-viable product level of quality. While contributors dogfood it as their daily driver, it may be unstable for some commands. Future releases will work fill out missing features and improve stability. Its design is also subject to change as it matures. +This project has reached a minimum-viable product level of quality. While contributors dogfood it as their daily driver, it may be unstable for some commands. Future releases will work to fill out missing features and improve stability. Its design is also subject to change as it matures. Nu comes with a set 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. From 219da892b271bfe4fc3954078e3e52f6a4dde510 Mon Sep 17 00:00:00 2001 From: Marcelo Goncalves Date: Mon, 30 Sep 2019 22:30:17 -0300 Subject: [PATCH 190/342] Document date command --- docs/commands/date.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/commands/date.md diff --git a/docs/commands/date.md b/docs/commands/date.md new file mode 100644 index 0000000000..cc1c35bb65 --- /dev/null +++ b/docs/commands/date.md @@ -0,0 +1,34 @@ +# date + +Use `date` to get the current date and time. Defaults to local timezone but you can get it in UTC too. + +## Flags + + --utc + Returns da current date and time in UTC + + --local + Returns da current date and time in your local timezone + +## Examples + +```shell +> date +━━━━━━┯━━━━━━━┯━━━━━┯━━━━━━┯━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━ + year │ month │ day │ hour │ minute │ second │ timezone +──────┼───────┼─────┼──────┼────────┼────────┼────────── + 2019 │ 9 │ 30 │ 21 │ 52 │ 30 │ -03:00 +━━━━━━┷━━━━━━━┷━━━━━┷━━━━━━┷━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━ +> date --utc +━━━━━━┯━━━━━━━┯━━━━━┯━━━━━━┯━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━ + year │ month │ day │ hour │ minute │ second │ timezone +──────┼───────┼─────┼──────┼────────┼────────┼────────── + 2019 │ 10 │ 1 │ 0 │ 52 │ 32 │ UTC +━━━━━━┷━━━━━━━┷━━━━━┷━━━━━━┷━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━ +> date --local +━━━━━━┯━━━━━━━┯━━━━━┯━━━━━━┯━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━ + year │ month │ day │ hour │ minute │ second │ timezone +──────┼───────┼─────┼──────┼────────┼────────┼────────── + 2019 │ 9 │ 30 │ 21 │ 52 │ 34 │ -03:00 +━━━━━━┷━━━━━━━┷━━━━━┷━━━━━━┷━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━ +``` From 8ba917b704f6826f68ff8c665c386d8c30543e34 Mon Sep 17 00:00:00 2001 From: Noah AL-Shihabi Date: Tue, 1 Oct 2019 06:14:56 -0400 Subject: [PATCH 191/342] Add echo command documentation --- docs/commands/echo.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/commands/echo.md diff --git a/docs/commands/echo.md b/docs/commands/echo.md new file mode 100644 index 0000000000..d6ca3774c0 --- /dev/null +++ b/docs/commands/echo.md @@ -0,0 +1,12 @@ +# echo + +Use `echo` to repeat arguments back to the user + +## Examples + +```shell +> echo Hello world +Hello world +> echo "Hello, world!" +Hello, world! +``` \ No newline at end of file From 310897897e60790261ad3e183ee50d14710228b6 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Tue, 1 Oct 2019 05:28:54 -0500 Subject: [PATCH 192/342] Changed the location of the open in gitpod button In my opinion, this looks a lot better --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 96e97a90d8..43ce8e21c6 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,7 @@ If you're a developer who would like to contribute to Nu, we're also working on We also have an active [discord](https://discord.gg/NtAbbGn) and [twitter](https://twitter.com/nu_shell) if you'd like to come chat with us. Try it in gitpod + [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/nushell/nushell) # Installation From 91b4d27931fc1e580fae962ab599fe54f22b5827 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Tue, 1 Oct 2019 05:32:21 -0500 Subject: [PATCH 193/342] add capitalization in readme discord and twitter should be uppercase --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 43ce8e21c6..31cdba6bfb 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ There are a few good resources to learn about Nu. There is a [book](https://book If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-book/tree/master/en) to help get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. -We also have an active [discord](https://discord.gg/NtAbbGn) and [twitter](https://twitter.com/nu_shell) if you'd like to come chat with us. +We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come chat with us. Try it in gitpod From abf671da1b0e10995df889e88639ef6dafe409ed Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Tue, 1 Oct 2019 05:54:59 -0500 Subject: [PATCH 194/342] misc style changes --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 31cdba6bfb..0fcf71d8c1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ A modern shell for the GitHub era This project has reached a minimum-viable product level of quality. While contributors dogfood it as their daily driver, it may be unstable for some commands. Future releases will work to fill out missing features and improve stability. Its design is also subject to change as it matures. -Nu comes with a set 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. +Nu comes with a set 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. # Learning more @@ -21,7 +21,7 @@ There are a few good resources to learn about Nu. There is a [book](https://book If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-book/tree/master/en) to help get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. -We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come chat with us. +We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come and chat with us. Try it in gitpod @@ -91,7 +91,7 @@ $ docker run -it nushell/nu /> exit ``` -The second container is a bit smaller, if size is important to you. +The second container is a bit smaller, if the size is important to you. ## Packaging status @@ -103,7 +103,7 @@ The second container is a bit smaller, if size is important to you. # Philosophy -Nu draws inspiration from projects like PowerShell, functional programming languages, and modern cli tools. Rather than thinking of files 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 is a table of rows, where each row represents an item in that directory. These values can be piped through a series of steps, in a series of commands called a 'pipeline'. +Nu draws inspiration from projects like PowerShell, functional programming languages, and modern CLI tools. Rather than thinking of files 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 is a table of rows, where each row represents an item in that directory. These values can be piped through a series of steps, in a series of commands called a 'pipeline'. ## Pipelines From fe3753ea687ac726d8bfa10bfd0ca1d796ae3e64 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Tue, 1 Oct 2019 05:58:56 -0500 Subject: [PATCH 195/342] more style changes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0fcf71d8c1..5a56a8438e 100644 --- a/README.md +++ b/README.md @@ -91,7 +91,7 @@ $ docker run -it nushell/nu /> exit ``` -The second container is a bit smaller, if the size is important to you. +The second container is a bit smaller if the size is important to you. ## Packaging status From 08df76486d920cce0a04059c243a51a0066a32a1 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Tue, 1 Oct 2019 06:00:00 -0500 Subject: [PATCH 196/342] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5a56a8438e..5b47d5fad5 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # Nu Shell -A modern shell for the GitHub era +A modern shell for the GitHub era. ![Example of nushell](images/nushell-autocomplete.gif "Example of nushell") @@ -23,7 +23,7 @@ If you're a developer who would like to contribute to Nu, we're also working on We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come and chat with us. -Try it in gitpod +Try it in Gitpod, [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/nushell/nushell) From c81d20a0695bd7002ae1b3bd18dcf432372fdf03 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Tue, 1 Oct 2019 06:37:27 -0500 Subject: [PATCH 197/342] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5b47d5fad5..3cf3e13ecd 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ Nu comes with a set of built-in commands (listed below). If a command is unknown There are a few good resources to learn about Nu. There is a [book](https://book.nushell.sh) about Nu that is currently in progress. The book focuses on using Nu and its core concepts. -If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-book/tree/master/en) to help get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. +If you're a developer who would like to contribute to Nu, we're also working on a [book for developers](https://github.com/nushell/contributor-book/tree/master/en) to help you get started. There are also [good first issues](https://github.com/nushell/nushell/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) to help you dive in. We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come and chat with us. From ad53eb4e7685ff0c4423c667d0b86b5121402ece Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Tue, 1 Oct 2019 06:39:18 -0500 Subject: [PATCH 198/342] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cf3e13ecd..c1a25a004e 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ If you're a developer who would like to contribute to Nu, we're also working on We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come and chat with us. -Try it in Gitpod, +Try it in Gitpod. [![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://github.com/nushell/nushell) From 1bf0f7110a532fee949e5f58a462a948d3c9a380 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Tue, 1 Oct 2019 06:44:06 -0500 Subject: [PATCH 199/342] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c1a25a004e..17ba9a7323 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ Nu draws inspiration from projects like PowerShell, functional programming langu ## Pipelines -In Unix, it's common to pipe between commands to split up a sophisticated command over multiple steps. 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 +In Unix, it's common to pipe between commands to split up a sophisticated command over multiple steps. 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 type == "Directory"`) From a7a0f48286682b3a353a04c19a68bebeae545558 Mon Sep 17 00:00:00 2001 From: Sean Hellum Date: Tue, 1 Oct 2019 06:46:04 -0500 Subject: [PATCH 200/342] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 17ba9a7323..daa8db5e06 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ In Unix, it's common to pipe between commands to split up a sophisticated comman * Commands that produce a stream (eg, `ls`) * Commands that filter a stream (eg, `where type == "Directory"`) -* Commands that consumes the output of the pipeline (eg, `autoview`) +* Commands that consume the output of the pipeline (eg, `autoview`) Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right. From b7bf31df997cc0597fa68f34cba392baa41ff1cf Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Tue, 1 Oct 2019 18:45:38 +0530 Subject: [PATCH 201/342] Make docs for the cd command ; partially solves #711 --- docs/commands/cd.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 docs/commands/cd.md diff --git a/docs/commands/cd.md b/docs/commands/cd.md new file mode 100644 index 0000000000..1d08beaf33 --- /dev/null +++ b/docs/commands/cd.md @@ -0,0 +1,22 @@ +If you don't know about it alreaday, the `cd` command is very simple. It stands for 'change directory' and it does exactly that. It changes the current directory that you are in to the one specified. + +Example - + +``` +/home/username> cd Desktop +/home/username/Desktop> now your current directory has been changed +``` +Additionally, `..` takes you to the parent directory - + +``` +/home/username/Desktop/nested/folders> cd .. +/home/username/Desktop/nested>cd .. +/home/username/Desktop> cd ../Documents/school_related +/home/username/Documents/school_related> cd ../../.. +/home/> +``` + +And `/` takes you to the root of the filesystem, which is `/` on Linux and MacOS, and `C:\` on Windows. + + +If no directory is specified, it takes you to the home directory, which is `/home/your_username` on MacOS and Linux systems and `C:\Users\Your_username` on Windows. From 417ac4b69e2bf98a35b84afb9e5e86b6c9c6ceaf Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Tue, 1 Oct 2019 19:23:10 +0530 Subject: [PATCH 202/342] Improve cd docs Used format in PR#746 Added another example Removed unnecessary text --- docs/commands/cd.md | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/docs/commands/cd.md b/docs/commands/cd.md index 1d08beaf33..a227865981 100644 --- a/docs/commands/cd.md +++ b/docs/commands/cd.md @@ -1,22 +1,25 @@ -If you don't know about it alreaday, the `cd` command is very simple. It stands for 'change directory' and it does exactly that. It changes the current directory that you are in to the one specified. +# cd -Example - +If you don't know about it already, the `cd` command is very simple. It stands for 'change directory' and it does exactly that. It changes the current directory that you are in to the one specified. If no directory is specified, it takes you to the home directory. Additionally, `..` takes you to the parent directory -``` +## Examples - + +```shell /home/username> cd Desktop /home/username/Desktop> now your current directory has been changed ``` -Additionally, `..` takes you to the parent directory - -``` +```shell /home/username/Desktop/nested/folders> cd .. -/home/username/Desktop/nested>cd .. +/home/username/Desktop/nested> cd .. /home/username/Desktop> cd ../Documents/school_related /home/username/Documents/school_related> cd ../../.. /home/> ``` -And `/` takes you to the root of the filesystem, which is `/` on Linux and MacOS, and `C:\` on Windows. - - -If no directory is specified, it takes you to the home directory, which is `/home/your_username` on MacOS and Linux systems and `C:\Users\Your_username` on Windows. +```shell +/home/username/Desktop/super/duper/crazy/nested/folders> cd +/home/username> cd ../../usr +/usr> cd +/home/username> +``` From e62a2509ae65dae8bfcae7aaeadce5aad2ad52a0 Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Tue, 1 Oct 2019 19:40:16 +0530 Subject: [PATCH 203/342] Create exit command documentation Partial fix of issue #711 Some parts have been copied from the fish documentation --- docs/commands/exit.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 docs/commands/exit.md diff --git a/docs/commands/exit.md b/docs/commands/exit.md new file mode 100644 index 0000000000..7441d2ae51 --- /dev/null +++ b/docs/commands/exit.md @@ -0,0 +1,17 @@ +# exit + +Exits the nu shell. If STATUS is supplied, it will be converted to an integer and used as the exit code. Otherwise, the exit code will be that of the last command executed. + +## Examples + +```shell +> exit +``` + +```shell +> exit 1 +``` + +```shell +> exit 0 +``` From 94744c626c438070736be537f58161ab9ef17b2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcelo=20Gon=C3=A7alves?= Date: Tue, 1 Oct 2019 11:21:56 -0300 Subject: [PATCH 204/342] Fix typo in date.cmd --- docs/commands/date.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/commands/date.md b/docs/commands/date.md index cc1c35bb65..4263fd7e37 100644 --- a/docs/commands/date.md +++ b/docs/commands/date.md @@ -5,10 +5,10 @@ Use `date` to get the current date and time. Defaults to local timezone but you ## Flags --utc - Returns da current date and time in UTC + Returns the current date and time in UTC --local - Returns da current date and time in your local timezone + Returns the current date and time in your local timezone ## Examples From 94d81445eb66f782f91142c2dce9a03e89c2f4e0 Mon Sep 17 00:00:00 2001 From: Yahsin Huang Date: Tue, 1 Oct 2019 23:20:58 +0800 Subject: [PATCH 205/342] Add document for help --- docs/commands/help.md | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/commands/help.md diff --git a/docs/commands/help.md b/docs/commands/help.md new file mode 100644 index 0000000000..a232910c72 --- /dev/null +++ b/docs/commands/help.md @@ -0,0 +1,47 @@ +# help + +Use `help` for more information on a command. +Use `help commands` to list all availble commands. +Use `help ` to display help about a particular command. + +## Examples + +```shell +> help +Welcome to Nushell. + +Here are some tips to help you get started. + * help commands - list all available commands + * help - display help about a particular command + +You can also learn more at https://book.nushell.sh +``` + +```shell +> help commands +━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # │ name │ description +────┼──────────────┼──────────────────────────────────────────────────────────────────────────────────────── + 0 │ add │ Add a new field to the table. + 1 │ autoview │ View the contents of the pipeline as a table or list. + 2 │ cd │ Change to a new path. + 3 │ config │ Configuration management. + 4 │ cp │ Copy files. + 5 │ date │ Get the current datetime. +... + 70 │ trim │ Trim leading and following whitespace from text data. + 71 │ version │ Display Nu version + 72 │ where │ Filter table to match the condition. + 73 │ which │ Finds a program file. +━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` + +```shell +> help cd +Change to a new path. + +Usage: + > cd (directory) +``` + + From caf3015e66a922d86853d3011eb5913bbf3c4d8a Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Wed, 2 Oct 2019 06:55:30 +0530 Subject: [PATCH 206/342] Improved exit command docs --- docs/commands/exit.md | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/docs/commands/exit.md b/docs/commands/exit.md index 7441d2ae51..0238204f28 100644 --- a/docs/commands/exit.md +++ b/docs/commands/exit.md @@ -1,6 +1,6 @@ # exit -Exits the nu shell. If STATUS is supplied, it will be converted to an integer and used as the exit code. Otherwise, the exit code will be that of the last command executed. +Exits the nu shell. If you have multiple nu shells, use `exit --now` to exit all of them. ## Examples @@ -8,10 +8,23 @@ Exits the nu shell. If STATUS is supplied, it will be converted to an integer an > exit ``` -```shell -> exit 1 ``` - -```shell -> exit 0 +/home/username/stuff/books> shells +---+---+------------+---------------------------- + # | | name | path +---+---+------------+---------------------------- + 0 | | filesystem | /home/username/stuff/notes + 1 | | filesystem | /home/username/stuff/videos + 2 | X | filesystem | /home/username/stuff/books +---+---+------------+---------------------------- +/home/username/stuff/books> exit +/home/username/stuff/videos> shells +---+---+------------+---------------------------- + # | | name | path +---+---+------------+---------------------------- + 0 | | filesystem | /home/username/stuff/notes + 1 | X | filesystem | /home/username/stuff/videos +---+---+------------+---------------------------- +/home/username/stuff/videos> exit --now +exits both the shells ``` From a77c222db01702283246361dd0c47da4dfd3d1cc Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Wed, 2 Oct 2019 13:37:43 +0530 Subject: [PATCH 207/342] Created docs for shells command Partial fix of issue #711 The second example is taken from the book, specifically the section https://book.nushell.sh/en/shells_in_shells#going-beyond-directories --- docs/commands/shells.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 docs/commands/shells.md diff --git a/docs/commands/shells.md b/docs/commands/shells.md new file mode 100644 index 0000000000..b9fde457b3 --- /dev/null +++ b/docs/commands/shells.md @@ -0,0 +1,26 @@ +# shells + +Lists all the active nu shells with a number/index, a name and the path. Also marks the current nu shell. + +## Examples + +``` +> shells +---+---+------------+--------------- + # | | name | path +---+---+------------+--------------- + 0 | | filesystem | /usr + 1 | | filesystem | /home + 2 | X | filesystem | /home/username +---+---+------------+--------------- +``` + +``` +/> shells +---+---+-------------------------------------------------+------------------------------------ + # | | name | path +---+---+-------------------------------------------------+------------------------------------ + 0 | | filesystem | /Users/username/Code/nushell + 1 | X | {/Users/username/Code/nushell/Cargo.toml} | / +---+---+-------------------------------------------------+------------------------------------ +``` From 7e7eba8f4d834ba66e21d7cd784220922a0e24fc Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Wed, 2 Oct 2019 15:03:28 +0530 Subject: [PATCH 208/342] Create docs for reverse command Partial fix of issue #711 this command could be described better but I don't know how --- docs/commands/reverse.md | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 docs/commands/reverse.md diff --git a/docs/commands/reverse.md b/docs/commands/reverse.md new file mode 100644 index 0000000000..546f251568 --- /dev/null +++ b/docs/commands/reverse.md @@ -0,0 +1,51 @@ +# reverse + +This command reverses the order of the elements in a sorted table. + +## Examples + +```shell +> ls | sort-by name +━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼────────────────────────────┼──────┼──────────┼────────┼────────────────┼──────────────── + 0 │ abaracadabra.txt │ File │ │ 401 B │ 23 minutes ago │ 16 minutes ago + 1 │ coww.txt │ File │ │ 24 B │ 22 minutes ago │ 17 minutes ago + 2 │ randomweirdstuff.txt │ File │ │ 197 B │ 21 minutes ago │ 18 minutes ago + 3 │ youshouldeatmorecereal.txt │ File │ │ 768 B │ 30 seconds ago │ now + 4 │ zeusiscrazy.txt │ File │ │ 556 B │ 22 minutes ago │ 18 minutes ago +━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ +> ls | sort-by name | reverse +━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼────────────────────────────┼──────┼──────────┼────────┼────────────────┼──────────────── + 0 │ zeusiscrazy.txt │ File │ │ 556 B │ 22 minutes ago │ 19 minutes ago + 1 │ youshouldeatmorecereal.txt │ File │ │ 768 B │ 39 seconds ago │ 18 seconds ago + 2 │ randomweirdstuff.txt │ File │ │ 197 B │ 21 minutes ago │ 18 minutes ago + 3 │ coww.txt │ File │ │ 24 B │ 22 minutes ago │ 18 minutes ago + 4 │ abaracadabra.txt │ File │ │ 401 B │ 23 minutes ago │ 16 minutes ago +━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ +``` + +```shell +> ls | sort-by size +━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼────────────────────────────┼──────┼──────────┼────────┼────────────────┼──────────────── + 0 │ coww.txt │ File │ │ 24 B │ 22 minutes ago │ 18 minutes ago + 1 │ randomweirdstuff.txt │ File │ │ 197 B │ 21 minutes ago │ 18 minutes ago + 2 │ abaracadabra.txt │ File │ │ 401 B │ 23 minutes ago │ 16 minutes ago + 3 │ zeusiscrazy.txt │ File │ │ 556 B │ 22 minutes ago │ 19 minutes ago + 4 │ youshouldeatmorecereal.txt │ File │ │ 768 B │ a minute ago │ 26 seconds ago +━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ +> ls | sort-by size | reverse +━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼────────────────────────────┼──────┼──────────┼────────┼────────────────┼──────────────── + 0 │ youshouldeatmorecereal.txt │ File │ │ 768 B │ a minute ago │ 32 seconds ago + 1 │ zeusiscrazy.txt │ File │ │ 556 B │ 22 minutes ago │ 19 minutes ago + 2 │ abaracadabra.txt │ File │ │ 401 B │ 23 minutes ago │ 16 minutes ago + 3 │ randomweirdstuff.txt │ File │ │ 197 B │ 21 minutes ago │ 18 minutes ago + 4 │ coww.txt │ File │ │ 24 B │ 22 minutes ago │ 18 minutes ago +━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ +``` From 0b3c9b760e2c28457df8d805462da6dc3a863dc2 Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Wed, 2 Oct 2019 15:47:56 +0530 Subject: [PATCH 209/342] Create docs for version command Partial fix of #711 --- docs/commands/version.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docs/commands/version.md diff --git a/docs/commands/version.md b/docs/commands/version.md new file mode 100644 index 0000000000..d0b8828990 --- /dev/null +++ b/docs/commands/version.md @@ -0,0 +1,14 @@ +# version + +Outputs the nushell version. + +## Examples + +```shell +> version +━━━━━━━━━ + version +───────── + 0.3.0 +━━━━━━━━━ +``` From c78bce2af4a014893785701681337a83585df7d5 Mon Sep 17 00:00:00 2001 From: Jerod Santo Date: Wed, 2 Oct 2019 09:34:08 -0500 Subject: [PATCH 210/342] Add Changelog episode badge to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 96e97a90d8..af50ce7784 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ [![Crates.io](https://img.shields.io/crates/v/nu.svg)](https://crates.io/crates/nu) [![Build Status](https://dev.azure.com/nushell/nushell/_apis/build/status/nushell.nushell?branchName=master)](https://dev.azure.com/nushell/nushell/_build/latest?definitionId=2&branchName=master) [![Discord](https://img.shields.io/discord/601130461678272522.svg?logo=discord)](https://discord.gg/NtAbbGn) +[![The Changelog #363](https://img.shields.io/badge/The%20Changelog-%23363-61c192.svg)](https://changelog.com/podcast/363) # Nu Shell From ce771903e5ee2168bffef36eab947e33e0b6fff4 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 3 Oct 2019 04:46:49 +1300 Subject: [PATCH 211/342] Trying to fix Azure Pipelines --- .azure/azure-pipelines.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 851259b43e..99b034568c 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -18,6 +18,7 @@ steps: set -e curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` export PATH=$HOME/.cargo/bin:$PATH + apt-get -y install libxcb-composite0-dev libx11-dev rustc -Vv echo "##vso[task.prependpath]$HOME/.cargo/bin" rustup component add rustfmt --toolchain `cat rust-toolchain` From 03728c1868e92f4503c44bff98c3fa61b5a341ef Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 3 Oct 2019 04:55:29 +1300 Subject: [PATCH 212/342] Update azure-pipelines.yml --- .azure/azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 99b034568c..13c8c8ec59 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -18,7 +18,7 @@ steps: set -e curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` export PATH=$HOME/.cargo/bin:$PATH - apt-get -y install libxcb-composite0-dev libx11-dev + sudo apt-get -y install libxcb-composite0-dev libx11-dev rustc -Vv echo "##vso[task.prependpath]$HOME/.cargo/bin" rustup component add rustfmt --toolchain `cat rust-toolchain` From f689434bbc839ec115a2006af297a6e94f662c10 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 3 Oct 2019 05:06:28 +1300 Subject: [PATCH 213/342] Update azure-pipelines.yml --- .azure/azure-pipelines.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 13c8c8ec59..4a5669afb5 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -5,6 +5,8 @@ strategy: matrix: linux-nightly: image: ubuntu-16.04 + commands: + - sudo apt-get -y install libxcb-composite0-dev libx11-dev macos-nightly: image: macos-10.14 windows-nightly: @@ -18,7 +20,6 @@ steps: set -e curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` export PATH=$HOME/.cargo/bin:$PATH - sudo apt-get -y install libxcb-composite0-dev libx11-dev rustc -Vv echo "##vso[task.prependpath]$HOME/.cargo/bin" rustup component add rustfmt --toolchain `cat rust-toolchain` From 27272d37544e577b944c9725a70c322da9f782df Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 3 Oct 2019 05:27:03 +1300 Subject: [PATCH 214/342] Update azure-pipelines.yml --- .azure/azure-pipelines.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index 4a5669afb5..e1f9b93681 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -5,8 +5,6 @@ strategy: matrix: linux-nightly: image: ubuntu-16.04 - commands: - - sudo apt-get -y install libxcb-composite0-dev libx11-dev macos-nightly: image: macos-10.14 windows-nightly: @@ -18,6 +16,10 @@ pool: steps: - bash: | set -e + if [ -e /etc/debian_version ] + then + sudo apt-get -y install libxcb-composite0-dev libx11-dev + fi curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path --default-toolchain `cat rust-toolchain` export PATH=$HOME/.cargo/bin:$PATH rustc -Vv From 9fb9adb6b4e58296b1008724cd830fa9c190fff6 Mon Sep 17 00:00:00 2001 From: rnxypke Date: Wed, 2 Oct 2019 20:56:28 +0200 Subject: [PATCH 215/342] add regex match plugin --- Cargo.lock | 1 + Cargo.toml | 5 +++ src/plugins/match.rs | 100 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 src/plugins/match.rs diff --git a/Cargo.lock b/Cargo.lock index 267876726f..852fbd6103 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1561,6 +1561,7 @@ dependencies = [ "prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "ptree 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rawkey 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 6e59dcaad7..f51ea06d8a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -75,6 +75,7 @@ bigdecimal = { version = "0.1.0", features = ["serde"] } natural = "0.3.0" serde_urlencoded = "0.6.1" sublime_fuzzy = "0.5" +regex = "1" neso = { version = "0.5.0", optional = true } crossterm = { version = "0.10.2", optional = true } @@ -134,6 +135,10 @@ path = "src/plugins/str.rs" name = "nu_plugin_skip" path = "src/plugins/skip.rs" +[[bin]] +name = "nu_plugin_match" +path = "src/plugins/match.rs" + [[bin]] name = "nu_plugin_sys" path = "src/plugins/sys.rs" diff --git a/src/plugins/match.rs b/src/plugins/match.rs new file mode 100644 index 0000000000..eee79c6669 --- /dev/null +++ b/src/plugins/match.rs @@ -0,0 +1,100 @@ +use nu::{ + serve_plugin, CallInfo, CoerceInto, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, + Signature, SyntaxShape, Tagged, TaggedItem, Value, EvaluatedArgs, +}; +use indexmap::IndexMap; +use regex::Regex; + +struct Match { + column: String, + regex: Regex, +} + +impl Match { + fn new() -> Self { + Match { + column: String::new(), + regex: Regex::new("").unwrap(), + } + } +} + +impl Plugin for Match { + fn config(&mut self) -> Result { + Ok(Signature::build("match") + .desc("filter rows by regex") + .required("member", SyntaxShape::Member) + .required("regex", SyntaxShape::String) + .filter()) + } + fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { + if let Some(args) = call_info.args.positional { + match &args[0] { + Tagged { + item: Value::Primitive(Primitive::String(s)), + .. + } => { + self.column = s.clone(); + } + _ => { + return Err(ShellError::string(format!( + "Unrecognized type in params: {:?}", args[0]))); + } + } + match &args[1] { + Tagged { + item: Value::Primitive(Primitive::String(s)), + .. + } => { + self.regex = Regex::new(s).unwrap(); + } + _ => { + return Err(ShellError::string(format!( + "Unrecognized type in params: {:?}", args[0]))); + } + } + } + Ok(vec![]) + } + + fn filter(&mut self, input: Tagged) -> Result, ShellError> { + let flag: bool; + match &input { + Tagged { + item: Value::Row(dict), + .. + } => { + if let Some(val) = dict.entries.get(&self.column) { + match val { + Tagged { + item: Value::Primitive(Primitive::String(s)), + .. + } => { + flag = self.regex.is_match(s); + } + _ => { + return Err(ShellError::string(format!( + "value is not a string! {:?}", &val))); + } + } + } else { + return Err(ShellError::string(format!( + "column not in row! {:?} {:?}", &self.column, dict))); + } + } + _ => { + return Err(ShellError::string(format!( + "Not a row! {:?}", &input))); + } + } + if flag { + Ok(vec![Ok(ReturnSuccess::Value(input))]) + } else { + Ok(vec![]) + } + } +} + +fn main() { + serve_plugin(&mut Match::new()); +} From 9d84e47214597701207ca9e4c9cf55192bc1e395 Mon Sep 17 00:00:00 2001 From: Ryan Blecher Date: Wed, 2 Oct 2019 15:49:44 -0400 Subject: [PATCH 216/342] add documentation file for first command --- docs/commands/first.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 docs/commands/first.md diff --git a/docs/commands/first.md b/docs/commands/first.md new file mode 100644 index 0000000000..d295c8fd53 --- /dev/null +++ b/docs/commands/first.md @@ -0,0 +1,28 @@ +# first + +Use `first` to retrieve the first "n" rows of a table. `first` has a required amount parameter that indicates how many rows you would like returned. If more than one row is returned, an index column will be included showing the row number. + +## Examples + +```shell +> ps | first 1 +━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━ + pid │ name │ status │ cpu +───────┼──────────────┼─────────┼─────────────────── + 60358 │ nu_plugin_ps │ Running │ 5.399802999999999 +━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━ +``` + +```shell +> ps | first 5 +━━━┯━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━ + # │ pid │ name │ status │ cpu +───┼───────┼──────────────┼─────────┼─────────────────── + 0 │ 60754 │ nu_plugin_ps │ Running │ 4.024156000000000 + 1 │ 60107 │ quicklookd │ Running │ 0.000000000000000 + 2 │ 59356 │ nu │ Running │ 0.000000000000000 + 3 │ 59216 │ zsh │ Running │ 0.000000000000000 + 4 │ 59162 │ vim │ Running │ 0.000000000000000 +━━━┷━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━ +``` + From be51aad9ad448148e5fe7ecd3e12835ed1890787 Mon Sep 17 00:00:00 2001 From: rnxypke Date: Wed, 2 Oct 2019 22:24:37 +0200 Subject: [PATCH 217/342] remove unused imports on match plugin --- src/plugins/match.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/plugins/match.rs b/src/plugins/match.rs index eee79c6669..0e88c5981f 100644 --- a/src/plugins/match.rs +++ b/src/plugins/match.rs @@ -1,8 +1,7 @@ use nu::{ - serve_plugin, CallInfo, CoerceInto, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, - Signature, SyntaxShape, Tagged, TaggedItem, Value, EvaluatedArgs, + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, + Signature, SyntaxShape, Tagged, Value, }; -use indexmap::IndexMap; use regex::Regex; struct Match { From 36f2b09cadf04cd028b1a4a7d4aa1c526c1bc491 Mon Sep 17 00:00:00 2001 From: rnxypke Date: Wed, 2 Oct 2019 22:41:52 +0200 Subject: [PATCH 218/342] run rustfmt on match plugin --- src/plugins/match.rs | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/plugins/match.rs b/src/plugins/match.rs index 0e88c5981f..81008a726e 100644 --- a/src/plugins/match.rs +++ b/src/plugins/match.rs @@ -1,6 +1,6 @@ use nu::{ - serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, - Signature, SyntaxShape, Tagged, Value, + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, + SyntaxShape, Tagged, Value, }; use regex::Regex; @@ -11,8 +11,8 @@ struct Match { impl Match { fn new() -> Self { - Match { - column: String::new(), + Match { + column: String::new(), regex: Regex::new("").unwrap(), } } @@ -37,7 +37,9 @@ impl Plugin for Match { } _ => { return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", args[0]))); + "Unrecognized type in params: {:?}", + args[0] + ))); } } match &args[1] { @@ -48,8 +50,10 @@ impl Plugin for Match { self.regex = Regex::new(s).unwrap(); } _ => { - return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", args[0]))); + return Err(ShellError::string(format!( + "Unrecognized type in params: {:?}", + args[0] + ))); } } } @@ -73,21 +77,24 @@ impl Plugin for Match { } _ => { return Err(ShellError::string(format!( - "value is not a string! {:?}", &val))); + "value is not a string! {:?}", + &val + ))); } } } else { return Err(ShellError::string(format!( - "column not in row! {:?} {:?}", &self.column, dict))); + "column not in row! {:?} {:?}", + &self.column, dict + ))); } } _ => { - return Err(ShellError::string(format!( - "Not a row! {:?}", &input))); + return Err(ShellError::string(format!("Not a row! {:?}", &input))); } } if flag { - Ok(vec![Ok(ReturnSuccess::Value(input))]) + Ok(vec![Ok(ReturnSuccess::Value(input))]) } else { Ok(vec![]) } From 7d2747ea9aa2d325a900612819034731c245fd9f Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Wed, 2 Oct 2019 18:45:23 -0700 Subject: [PATCH 219/342] Added Vi support for scrolling in the textview command. --- src/plugins/textview.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/textview.rs b/src/plugins/textview.rs index 456c73a78a..cce8bd7084 100644 --- a/src/plugins/textview.rs +++ b/src/plugins/textview.rs @@ -150,7 +150,7 @@ fn scroll_view_lines_if_needed(draw_commands: Vec, use_color_buffer KeyEvent::Esc => { break; } - KeyEvent::Up => { + KeyEvent::Up | KeyEvent::Char('k') => { if starting_row > 0 { starting_row -= 1; max_bottom_line = paint_textview( @@ -160,19 +160,19 @@ fn scroll_view_lines_if_needed(draw_commands: Vec, use_color_buffer ); } } - KeyEvent::Down => { + KeyEvent::Down | KeyEvent::Char('j') => { if starting_row < (max_bottom_line - height) { starting_row += 1; } max_bottom_line = paint_textview(&draw_commands, starting_row, use_color_buffer); } - KeyEvent::PageUp => { + KeyEvent::PageUp | KeyEvent::Ctrl('b') => { starting_row -= std::cmp::min(height, starting_row); max_bottom_line = paint_textview(&draw_commands, starting_row, use_color_buffer); } - KeyEvent::PageDown | KeyEvent::Char(' ') => { + KeyEvent::PageDown | KeyEvent::Ctrl('f') | KeyEvent::Char(' ') => { if starting_row < (max_bottom_line - height) { starting_row += height; From 2e1670fcb899df1dbf588abdfb4dcc25303d9e6a Mon Sep 17 00:00:00 2001 From: gilesv Date: Wed, 2 Oct 2019 22:49:05 -0300 Subject: [PATCH 220/342] Add documentation for `where` command --- docs/commands/where.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/commands/where.md diff --git a/docs/commands/where.md b/docs/commands/where.md new file mode 100644 index 0000000000..be962726ee --- /dev/null +++ b/docs/commands/where.md @@ -0,0 +1,34 @@ +# where + +This command filters the content of a table based on a condition passed as a parameter, which must be a boolean expression making use of any of the table columns. Other commands such as `ls` are capable of feeding `where` with their output through pipelines. + +## Usage +```shell +> [input-command] | where [condition] +``` + +## Examples + +```shell +> ls | where size > 4kb +----+----------------+------+----------+----------+----------------+---------------- + # | name | type | readonly | size | accessed | modified +----+----------------+------+----------+----------+----------------+---------------- + 0 | IMG_1291.jpg | File | | 115.5 KB | a month ago | 4 months ago + 1 | README.md | File | | 11.1 KB | 2 days ago | 2 days ago + 2 | IMG_1291.png | File | | 589.0 KB | a month ago | a month ago + 3 | IMG_1381.jpg | File | | 81.0 KB | a month ago | 4 months ago + 4 | butterfly.jpeg | File | | 4.2 KB | a month ago | a month ago + 5 | Cargo.lock | File | | 199.6 KB | 22 minutes ago | 22 minutes ago +``` + +```shell +> ps | where cpu > 10 +---+-------+----------+-------+----------------------------- + # | pid | status | cpu | name +---+-------+----------+-------+----------------------------- + 0 | 1992 | Sleeping | 44.52 | /usr/bin/gnome-shell + 1 | 1069 | Sleeping | 16.15 | + 2 | 24116 | Sleeping | 13.70 | /opt/google/chrome/chrome + 3 | 21976 | Sleeping | 12.67 | /usr/share/discord/Discord +``` From f3eb4fb24e0eb14cf9eb0482654533f521878b24 Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Wed, 2 Oct 2019 20:16:27 -0700 Subject: [PATCH 221/342] Attempt at fixing `get` command panic. If possible matches are not found then check if the passed in `obj` parameter is a `string` or a `path`, if so then return it. I am not sure this is the right fix, but I figured I would make an attempt and get a conversation started about it. --- src/commands/get.rs | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/src/commands/get.rs b/src/commands/get.rs index 930392e5d6..3b9f578e26 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -58,11 +58,14 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result 0 { + return Err(ShellError::labeled_error( + "Unknown column", + format!("did you mean '{}'?", possible_matches[0].1), + path.tag(), + )); + } + None } } } @@ -70,6 +73,18 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result current = Some(obj), + Tagged { + item: Value::Primitive(Primitive::Path(_)), + .. + } => current = Some(obj), + _ => {} + }; + match current { Some(v) => Ok(v.clone()), None => Ok(Value::nothing().tagged(obj.tag)), From e54cd98a9cf31934773331f00acaec30bba2b4c7 Mon Sep 17 00:00:00 2001 From: Jonathan Rothberg Date: Wed, 2 Oct 2019 20:41:53 -0700 Subject: [PATCH 222/342] Put code into None case of last match. --- src/commands/get.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/commands/get.rs b/src/commands/get.rs index 3b9f578e26..afa550c72c 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -73,21 +73,20 @@ fn get_member(path: &Tagged, obj: &Tagged) -> Result current = Some(obj), - Tagged { - item: Value::Primitive(Primitive::Path(_)), - .. - } => current = Some(obj), - _ => {} - }; - match current { Some(v) => Ok(v.clone()), - None => Ok(Value::nothing().tagged(obj.tag)), + None => match obj { + // If its None check for certain values. + Tagged { + item: Value::Primitive(Primitive::String(_)), + .. + } => Ok(obj.clone()), + Tagged { + item: Value::Primitive(Primitive::Path(_)), + .. + } => Ok(obj.clone()), + _ => Ok(Value::nothing().tagged(obj.tag)), + }, } } From 1b0eaac470ee35e15b5461175143e60bf06bdc96 Mon Sep 17 00:00:00 2001 From: pema99 Date: Thu, 3 Oct 2019 06:09:01 +0200 Subject: [PATCH 223/342] Add documentation for lines --- docs/commands/lines.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 docs/commands/lines.md diff --git a/docs/commands/lines.md b/docs/commands/lines.md new file mode 100644 index 0000000000..7153900e3a --- /dev/null +++ b/docs/commands/lines.md @@ -0,0 +1,28 @@ +# lines +This command takes a string from a pipeline as input, and returns a table where each line of the input string is a row in the table. Empty lines are ignored. This command is capable of feeding other commands, such as `nth`, with its output. + +## Usage +```shell +> [input-command] | lines +``` + +## Examples +Basic usage: +```shell +> printf "Hello\nWorld!\nLove, nushell." | lines +━━━┯━━━━━━━━━━━━━━━━ + # │ value +───┼──────────────── + 0 │ Hello + 1 │ World! + 2 │ Love, nushell. +━━━┷━━━━━━━━━━━━━━━━ +``` + +One useful application is piping the contents of file into `lines`. This example extracts a certain line from a given file. +```shell +> cat lines.md | lines | nth 6 +## Examples +``` + +Similarly to this example, `lines` can be used to extract certain portions of or apply transformations to data returned by any program which returns a string. From 9181a046ec12ce3a6f9aa2510b1a73149373ee96 Mon Sep 17 00:00:00 2001 From: rnxypke Date: Thu, 3 Oct 2019 08:21:24 +0200 Subject: [PATCH 224/342] use correct argument for error message --- src/plugins/match.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/match.rs b/src/plugins/match.rs index 81008a726e..1f2aad83fc 100644 --- a/src/plugins/match.rs +++ b/src/plugins/match.rs @@ -52,7 +52,7 @@ impl Plugin for Match { _ => { return Err(ShellError::string(format!( "Unrecognized type in params: {:?}", - args[0] + args[1] ))); } } From 0505a9d6f7cf506d113854df939cdad8b59284e8 Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Thu, 3 Oct 2019 16:27:04 +0530 Subject: [PATCH 225/342] Create docs for add command Partial fix of issue #711 --- docs/commands/add.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 docs/commands/add.md diff --git a/docs/commands/add.md b/docs/commands/add.md new file mode 100644 index 0000000000..f3f080859b --- /dev/null +++ b/docs/commands/add.md @@ -0,0 +1,28 @@ +# add + +This command adds a column to any table output. The first parameter takes the heading, the second parameter takes the value for all the rows. + +## Examples + +```shell +> ls | add is_on_a_computer yes_obviously +━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified │ is_on_a_computer +───┼────────────────────────────┼──────┼──────────┼────────┼───────────┼───────────┼────────────────── + 0 │ zeusiscrazy.txt │ File │ │ 556 B │ a day ago │ a day ago │ yes_obviously + 1 │ coww.txt │ File │ │ 24 B │ a day ago │ a day ago │ yes_obviously + 2 │ randomweirdstuff.txt │ File │ │ 197 B │ a day ago │ a day ago │ yes_obviously + 3 │ abaracadabra.txt │ File │ │ 401 B │ a day ago │ a day ago │ yes_obviously + 4 │ youshouldeatmorecereal.txt │ File │ │ 768 B │ a day ago │ a day ago │ yes_obviously +━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━ +``` + +```shell +> shells | add os linux_on_this_machine +━━━┯━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━ + # │ │ name │ path │ os +───┼───┼────────────┼────────────────────────────────┼─────────────────────── + 0 │ X │ filesystem │ /home/shaurya/stuff/expr/stuff │ linux_on_this_machine + 1 │ │ filesystem │ / │ linux_on_this_machine +━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━ +``` From 5bfff0c39bfa593e60e83200f2f91db91d4ee7ae Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Thu, 3 Oct 2019 16:54:28 +0530 Subject: [PATCH 226/342] Create docs for edit command Partial fix of issue #711 --- docs/commands/edit.md | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 docs/commands/edit.md diff --git a/docs/commands/edit.md b/docs/commands/edit.md new file mode 100644 index 0000000000..5cfeeb55fe --- /dev/null +++ b/docs/commands/edit.md @@ -0,0 +1,45 @@ +# edit + +Edits an existing column on a table. First parameter is the column to edit and the second parameter is the value to put. + +## Examples + +```shell +> ls +━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼────────────────────────────┼──────┼──────────┼────────┼───────────┼─────────── + 0 │ zeusiscrazy.txt │ File │ │ 556 B │ a day ago │ a day ago + 1 │ coww.txt │ File │ │ 24 B │ a day ago │ a day ago + 2 │ randomweirdstuff.txt │ File │ │ 197 B │ a day ago │ a day ago + 3 │ abaracadabra.txt │ File │ │ 401 B │ a day ago │ a day ago + 4 │ youshouldeatmorecereal.txt │ File │ │ 768 B │ a day ago │ a day ago +━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━ +> ls | edit modified neverrrr +━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼────────────────────────────┼──────┼──────────┼────────┼───────────┼────────── + 0 │ zeusiscrazy.txt │ File │ │ 556 B │ a day ago │ neverrrr + 1 │ coww.txt │ File │ │ 24 B │ a day ago │ neverrrr + 2 │ randomweirdstuff.txt │ File │ │ 197 B │ a day ago │ neverrrr + 3 │ abaracadabra.txt │ File │ │ 401 B │ a day ago │ neverrrr + 4 │ youshouldeatmorecereal.txt │ File │ │ 768 B │ a day ago │ neverrrr +━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━ +``` + +```shell +> shells +━━━┯━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # │ │ name │ path +───┼───┼────────────┼──────────────────────────────── + 0 │ X │ filesystem │ /home/username/stuff/expr/stuff + 1 │ │ filesystem │ / +━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +> shells | edit " " X | edit path / +━━━┯━━━┯━━━━━━━━━━━━┯━━━━━━ + # │ │ name │ path +───┼───┼────────────┼────── + 0 │ X │ filesystem │ / + 1 │ X │ filesystem │ / +━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━ +``` From 872e26b524921ec9fd20e164b419f7c17a661438 Mon Sep 17 00:00:00 2001 From: Ryan Blecher Date: Thu, 3 Oct 2019 08:14:59 -0400 Subject: [PATCH 227/342] add documentation for the last command --- docs/commands/last.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/commands/last.md diff --git a/docs/commands/last.md b/docs/commands/last.md new file mode 100644 index 0000000000..2fd9100a78 --- /dev/null +++ b/docs/commands/last.md @@ -0,0 +1,29 @@ +# last + +Use `last` to retrieve the last "n" rows of a table. `last` has a required amount parameter that indicates how many rows you would like returned. If more than one row is returned, an index column will be included showing the row number. `last` does not alter the order the rows of the table. + +## Examples + +```shell +> ps | last 1 +━━━━━┯━━━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━ + pid │ name │ status │ cpu +─────┼─────────────┼─────────┼─────────────────── + 121 │ loginwindow │ Running │ 0.000000000000000 +━━━━━┷━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━ +``` + +```shell +> ps | last 5 +━━━┯━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━ + # │ pid │ name │ status │ cpu +───┼─────┼────────────────┼─────────┼─────────────────── + 0 │ 360 │ CommCenter │ Running │ 0.000000000000000 + 1 │ 358 │ distnoted │ Running │ 0.000000000000000 + 2 │ 356 │ UserEventAgent │ Running │ 0.000000000000000 + 3 │ 354 │ cfprefsd │ Running │ 0.000000000000000 + 4 │ 121 │ loginwindow │ Running │ 0.000000000000000 +━━━┷━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━ +``` + + From 9ed889ccbbd39caf30511d8d30fc2b986fa73e1c Mon Sep 17 00:00:00 2001 From: Ryan Blecher Date: Thu, 3 Oct 2019 08:18:51 -0400 Subject: [PATCH 228/342] fix grammar --- docs/commands/last.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands/last.md b/docs/commands/last.md index 2fd9100a78..bc7a55f12b 100644 --- a/docs/commands/last.md +++ b/docs/commands/last.md @@ -1,6 +1,6 @@ # last -Use `last` to retrieve the last "n" rows of a table. `last` has a required amount parameter that indicates how many rows you would like returned. If more than one row is returned, an index column will be included showing the row number. `last` does not alter the order the rows of the table. +Use `last` to retrieve the last "n" rows of a table. `last` has a required amount parameter that indicates how many rows you would like returned. If more than one row is returned, an index column will be included showing the row number. `last` does not alter the order of the rows of the table. ## Examples From 539e232f3c5a319019cf48c2feea629542d338db Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Thu, 3 Oct 2019 19:07:48 +0530 Subject: [PATCH 229/342] Added docs for most of the to-sth commands Partial fix of issue #711 Docs for the following commands were added - to-csv to-json to-toml to-tsv to-url to-yaml Docs for to-db , to-bson , to-sqlite have not been added as I don't recognize and understand those formats. --- docs/commands/to-csv.md | 21 +++++++++++++++++++++ docs/commands/to-json.md | 18 ++++++++++++++++++ docs/commands/to-toml.md | 32 ++++++++++++++++++++++++++++++++ docs/commands/to-tsv.md | 20 ++++++++++++++++++++ docs/commands/to-url.md | 24 ++++++++++++++++++++++++ docs/commands/to-yaml.md | 27 +++++++++++++++++++++++++++ 6 files changed, 142 insertions(+) create mode 100644 docs/commands/to-csv.md create mode 100644 docs/commands/to-json.md create mode 100644 docs/commands/to-toml.md create mode 100644 docs/commands/to-tsv.md create mode 100644 docs/commands/to-url.md create mode 100644 docs/commands/to-yaml.md diff --git a/docs/commands/to-csv.md b/docs/commands/to-csv.md new file mode 100644 index 0000000000..983f1cde95 --- /dev/null +++ b/docs/commands/to-csv.md @@ -0,0 +1,21 @@ +# to-csv + +Converts table data into csv text. + +## Example + +```shell +> shells +━━━┯━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━ + # │ │ name │ path +───┼───┼────────────┼──────────────────────── + 0 │ X │ filesystem │ /home/shaurya + 1 │ │ filesystem │ /home/shaurya/Pictures + 2 │ │ filesystem │ /home/shaurya/Desktop +━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━ +> shells | to-csv + ,name,path +X,filesystem,/home/shaurya + ,filesystem,/home/shaurya/Pictures + ,filesystem,/home/shaurya/Desktop +``` diff --git a/docs/commands/to-json.md b/docs/commands/to-json.md new file mode 100644 index 0000000000..f0e738d462 --- /dev/null +++ b/docs/commands/to-json.md @@ -0,0 +1,18 @@ +# to-json + +Converts table data into json text. + +## Example + +```shell +> shells +━━━┯━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━ + # │ │ name │ path +───┼───┼────────────┼──────────────────────── + 0 │ X │ filesystem │ /home/shaurya + 1 │ │ filesystem │ /home/shaurya/Pictures + 2 │ │ filesystem │ /home/shaurya/Desktop +━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━ +> shells | to-json +[{" ":"X","name":"filesystem","path":"/home/shaurya"},{" ":" ","name":"filesystem","path":"/home/shaurya/Pictures"},{" ":" ","name":"filesystem","path":"/home/shaurya/Desktop"}] +``` diff --git a/docs/commands/to-toml.md b/docs/commands/to-toml.md new file mode 100644 index 0000000000..0690156184 --- /dev/null +++ b/docs/commands/to-toml.md @@ -0,0 +1,32 @@ +# to-toml + +Converts table data into toml text. + +## Example + +```shell +> shells +━━━┯━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━ + # │ │ name │ path +───┼───┼────────────┼──────────────────────── + 0 │ X │ filesystem │ /home/shaurya + 1 │ │ filesystem │ /home/shaurya/Pictures + 2 │ │ filesystem │ /home/shaurya/Desktop +━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━ +> shells | to-toml +[[]] +" " = "X" +name = "filesystem" +path = "/home/shaurya" + +[[]] +" " = " " +name = "filesystem" +path = "/home/shaurya/Pictures" + +[[]] +" " = " " +name = "filesystem" +path = "/home/shaurya/Desktop" + +``` diff --git a/docs/commands/to-tsv.md b/docs/commands/to-tsv.md new file mode 100644 index 0000000000..ae2ade95c3 --- /dev/null +++ b/docs/commands/to-tsv.md @@ -0,0 +1,20 @@ +# to-tsv + +Converts table data into tsv text. + +## Example + +```shell +> shells +━━━┯━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━ + # │ │ name │ path +───┼───┼────────────┼──────────────────────── + 0 │ X │ filesystem │ /home/shaurya + 1 │ │ filesystem │ /home/shaurya/Pictures + 2 │ │ filesystem │ /home/shaurya/Desktop +━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━ +> shells |to-tsv + name path +X filesystem /home/shaurya + +``` diff --git a/docs/commands/to-url.md b/docs/commands/to-url.md new file mode 100644 index 0000000000..b66057ed67 --- /dev/null +++ b/docs/commands/to-url.md @@ -0,0 +1,24 @@ +# to-url + +Converts table data into url-formatted text. + +## Example + +```shell +> shells +━━━┯━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━ + # │ │ name │ path +───┼───┼────────────┼──────────────────────── + 0 │ X │ filesystem │ /home/shaurya + 1 │ │ filesystem │ /home/shaurya/Pictures + 2 │ │ filesystem │ /home/shaurya/Desktop +━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━ +> shells | to-url +━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + # │ value +───┼─────────────────────────────────────────────────────── + 0 │ +=X&name=filesystem&path=%2Fhome%2Fshaurya + 1 │ +=+&name=filesystem&path=%2Fhome%2Fshaurya%2FPictures + 2 │ +=+&name=filesystem&path=%2Fhome%2Fshaurya%2FDesktop +━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +``` diff --git a/docs/commands/to-yaml.md b/docs/commands/to-yaml.md new file mode 100644 index 0000000000..90d6283ce3 --- /dev/null +++ b/docs/commands/to-yaml.md @@ -0,0 +1,27 @@ +# to-yaml + +Converts table data into yaml text. + +## Example + +```shell +> shells +━━━┯━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━ + # │ │ name │ path +───┼───┼────────────┼──────────────────────── + 0 │ X │ filesystem │ /home/shaurya + 1 │ │ filesystem │ /home/shaurya/Pictures + 2 │ │ filesystem │ /home/shaurya/Desktop +━━━┷━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━ +> shells | to-yaml +--- +- " ": X + name: filesystem + path: /home/shaurya +- " ": " " + name: filesystem + path: /home/shaurya/Pictures +- " ": " " + name: filesystem + path: /home/shaurya/Desktop +``` From cf0fa3141ab524ecbcc276ed1d3495beea23db37 Mon Sep 17 00:00:00 2001 From: Charles Schleich Date: Thu, 3 Oct 2019 20:13:22 +0200 Subject: [PATCH 230/342] Created Docs for env command --- docs/commands/env.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 docs/commands/env.md diff --git a/docs/commands/env.md b/docs/commands/env.md new file mode 100644 index 0000000000..5dd08fac51 --- /dev/null +++ b/docs/commands/env.md @@ -0,0 +1,27 @@ +# env + +The `env` command prints to terminal the environment of nushell + +This includes +- cwd : the path to the current working the directory (`cwd`), +- home : the path to the home directory +- config : the path to the config file for nushell +- history : the path to the nushell command history +- temp : the path to the temp file +- vars : descriptor variable for the table + +`env` does not take any arguments, and ignores any arguments given. + + +## Examples - + + +```shell +/home/username/mynushell/docs/commands(master)> env +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━━━━━━━ + cwd │ home │ config │ history │ temp │ vars +────────────────────────────────────────┼────────────────┼───────────────────────────────────────┼────────────────────────────────────────────┼──────┼──────────────── + /home/username/mynushell/docs/commands │ /home/username │ /home/username/.config/nu/config.toml │ /home/username/.local/share/nu/history.txt │ /tmp │ [table: 1 row] +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━━━━━━━ +``` + From 2f7b1e4282e8c1ce9123711f8b6957d547b6f4ff Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Fri, 4 Oct 2019 06:40:16 +0530 Subject: [PATCH 231/342] Added improvements suggested by @andrasio Added `open file.sth | to-sth` type examples Also did a format conversion example with `open jonathon.xml | to-json` in to-json.md --- docs/commands/to-csv.md | 59 +++++++++++++++++++++++++++++ docs/commands/to-json.md | 22 +++++++++++ docs/commands/to-toml.md | 80 ++++++++++++++++++++++++++++++++++++++++ docs/commands/to-tsv.md | 60 ++++++++++++++++++++++++++++++ docs/commands/to-url.md | 11 ++++++ docs/commands/to-yaml.md | 33 +++++++++++++++++ 6 files changed, 265 insertions(+) diff --git a/docs/commands/to-csv.md b/docs/commands/to-csv.md index 983f1cde95..2be6390fa8 100644 --- a/docs/commands/to-csv.md +++ b/docs/commands/to-csv.md @@ -19,3 +19,62 @@ X,filesystem,/home/shaurya ,filesystem,/home/shaurya/Pictures ,filesystem,/home/shaurya/Desktop ``` + +```shell +> open caco3_plastics.csv +━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━━━━ + # │ importer │ shipper │ tariff_item │ name │ origin │ shipped_at │ arrived_at │ net_weight │ fob_price │ cif_price │ cif_per_net_ + │ │ │ │ │ │ │ │ │ │ │ weight +───┼──────────────┼──────────────┼─────────────┼──────────────┼──────────┼────────────┼────────────┼────────────┼───────────┼───────────┼────────────── + 0 │ PLASTICOS │ S A REVERTE │ 2509000000 │ CARBONATO DE │ SPAIN │ 18/03/2016 │ 17/04/2016 │ 81,000.00 │ 14,417.58 │ 18,252.34 │ 0.23 + │ RIVAL CIA │ │ │ CALCIO TIPO │ │ │ │ │ │ │ + │ LTDA │ │ │ CALCIPORE │ │ │ │ │ │ │ + │ │ │ │ 160 T AL │ │ │ │ │ │ │ + 1 │ MEXICHEM │ OMYA ANDINA │ 2836500000 │ CARBONATO │ COLOMBIA │ 07/07/2016 │ 10/07/2016 │ 26,000.00 │ 7,072.00 │ 8,127.18 │ 0.31 + │ ECUADOR S.A. │ S A │ │ │ │ │ │ │ │ │ + 2 │ PLASTIAZUAY │ SA REVERTE │ 2836500000 │ CARBONATO DE │ SPAIN │ 27/07/2016 │ 09/08/2016 │ 81,000.00 │ 8,100.00 │ 11,474.55 │ 0.14 + │ SA │ │ │ CALCIO │ │ │ │ │ │ │ + 3 │ PLASTICOS │ AND │ 2836500000 │ CALCIUM │ TURKEY │ 04/10/2016 │ 11/11/2016 │ 100,000.00 │ 17,500.00 │ 22,533.75 │ 0.23 + │ RIVAL CIA │ ENDUSTRIYEL │ │ CARBONATE │ │ │ │ │ │ │ + │ LTDA │ HAMMADDELER │ │ ANADOLU │ │ │ │ │ │ │ + │ │ DIS TCARET │ │ ANDCARB CT-1 │ │ │ │ │ │ │ + │ │ LTD.STI. │ │ │ │ │ │ │ │ │ + 4 │ QUIMICA │ SA REVERTE │ 2836500000 │ CARBONATO DE │ SPAIN │ 24/06/2016 │ 12/07/2016 │ 27,000.00 │ 3,258.90 │ 5,585.00 │ 0.21 + │ COMERCIAL │ │ │ CALCIO │ │ │ │ │ │ │ + │ QUIMICIAL │ │ │ │ │ │ │ │ │ │ + │ CIA. LTDA. │ │ │ │ │ │ │ │ │ │ + 5 │ PICA │ OMYA ANDINA │ 3824909999 │ CARBONATO DE │ COLOMBIA │ 01/01/1900 │ 18/01/2016 │ 66,500.00 │ 12,635.00 │ 18,670.52 │ 0.28 + │ PLASTICOS │ S.A │ │ CALCIO │ │ │ │ │ │ │ + │ INDUSTRIALES │ │ │ │ │ │ │ │ │ │ + │ C.A. │ │ │ │ │ │ │ │ │ │ + 6 │ PLASTIQUIM │ OMYA ANDINA │ 3824909999 │ CARBONATO DE │ COLOMBIA │ 01/01/1900 │ 25/10/2016 │ 33,000.00 │ 6,270.00 │ 9,999.00 │ 0.30 + │ S.A. │ S.A NIT │ │ CALCIO │ │ │ │ │ │ │ + │ │ 830.027.386- │ │ RECUBIERTO │ │ │ │ │ │ │ + │ │ 6 │ │ CON ACIDO │ │ │ │ │ │ │ + │ │ │ │ ESTEARICO │ │ │ │ │ │ │ + │ │ │ │ OMYA CARB 1T │ │ │ │ │ │ │ + │ │ │ │ CG BBS 1000 │ │ │ │ │ │ │ + 7 │ QUIMICOS │ SIBELCO │ 3824909999 │ CARBONATO DE │ COLOMBIA │ 01/11/2016 │ 03/11/2016 │ 52,000.00 │ 8,944.00 │ 13,039.05 │ 0.25 + │ ANDINOS │ COLOMBIA SAS │ │ CALCIO │ │ │ │ │ │ │ + │ QUIMANDI │ │ │ RECUBIERTO │ │ │ │ │ │ │ + │ S.A. │ │ │ │ │ │ │ │ │ │ + 8 │ TIGRE │ OMYA ANDINA │ 3824909999 │ CARBONATO DE │ COLOMBIA │ 01/01/1900 │ 28/10/2016 │ 66,000.00 │ 11,748.00 │ 18,216.00 │ 0.28 + │ ECUADOR S.A. │ S.A NIT │ │ CALCIO │ │ │ │ │ │ │ + │ ECUATIGRE │ 830.027.386- │ │ RECUBIERTO │ │ │ │ │ │ │ + │ │ 6 │ │ CON ACIDO │ │ │ │ │ │ │ + │ │ │ │ ESTEARICO │ │ │ │ │ │ │ + │ │ │ │ OMYACARB 1T │ │ │ │ │ │ │ + │ │ │ │ CG BPA 25 NO │ │ │ │ │ │ │ +━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━━━━ +> open caco3_plastics.csv | to-csv +importer,shipper,tariff_item,name,origin,shipped_at,arrived_at,net_weight,fob_price,cif_price,cif_per_net_weight +PLASTICOS RIVAL CIA LTDA,S A REVERTE,2509000000,CARBONATO DE CALCIO TIPO CALCIPORE 160 T AL,SPAIN,18/03/2016,17/04/2016,"81,000.00","14,417.58","18,252.34",0.23 +MEXICHEM ECUADOR S.A.,OMYA ANDINA S A,2836500000,CARBONATO,COLOMBIA,07/07/2016,10/07/2016,"26,000.00","7,072.00","8,127.18",0.31 +PLASTIAZUAY SA,SA REVERTE,2836500000,CARBONATO DE CALCIO,SPAIN,27/07/2016,09/08/2016,"81,000.00","8,100.00","11,474.55",0.14 +PLASTICOS RIVAL CIA LTDA,AND ENDUSTRIYEL HAMMADDELER DIS TCARET LTD.STI.,2836500000,CALCIUM CARBONATE ANADOLU ANDCARB CT-1,TURKEY,04/10/2016,11/11/2016,"100,000.00","17,500.00","22,533.75",0.23 +QUIMICA COMERCIAL QUIMICIAL CIA. LTDA.,SA REVERTE,2836500000,CARBONATO DE CALCIO,SPAIN,24/06/2016,12/07/2016,"27,000.00","3,258.90","5,585.00",0.21 +PICA PLASTICOS INDUSTRIALES C.A.,OMYA ANDINA S.A,3824909999,CARBONATO DE CALCIO,COLOMBIA,01/01/1900,18/01/2016,"66,500.00","12,635.00","18,670.52",0.28 +PLASTIQUIM S.A.,OMYA ANDINA S.A NIT 830.027.386-6,3824909999,CARBONATO DE CALCIO RECUBIERTO CON ACIDO ESTEARICO OMYA CARB 1T CG BBS 1000,COLOMBIA,01/01/1900,25/10/2016,"33,000.00","6,270.00","9,999.00",0.30 +QUIMICOS ANDINOS QUIMANDI S.A.,SIBELCO COLOMBIA SAS,3824909999,CARBONATO DE CALCIO RECUBIERTO,COLOMBIA,01/11/2016,03/11/2016,"52,000.00","8,944.00","13,039.05",0.25 +TIGRE ECUADOR S.A. ECUATIGRE,OMYA ANDINA S.A NIT 830.027.386-6,3824909999,CARBONATO DE CALCIO RECUBIERTO CON ACIDO ESTEARICO OMYACARB 1T CG BPA 25 NO,COLOMBIA,01/01/1900,28/10/2016,"66,000.00","11,748.00","18,216.00",0.28 +``` diff --git a/docs/commands/to-json.md b/docs/commands/to-json.md index f0e738d462..549e10d0f8 100644 --- a/docs/commands/to-json.md +++ b/docs/commands/to-json.md @@ -16,3 +16,25 @@ Converts table data into json text. > shells | to-json [{" ":"X","name":"filesystem","path":"/home/shaurya"},{" ":" ","name":"filesystem","path":"/home/shaurya/Pictures"},{" ":" ","name":"filesystem","path":"/home/shaurya/Desktop"}] ``` + +```shell +> open sgml_description.json +━━━━━━━━━━━━━━━━ + glossary +──────────────── + [table: 1 row] +━━━━━━━━━━━━━━━━ +> open sgml_description.json | to-json +{"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","Height":10,"GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"Sections":[101,102],"GlossSee":"markup"}}}}} +``` +We can also convert files ! +```shell +> open jonathan.xml +━━━━━━━━━━━━━━━━ + rss +──────────────── + [table: 1 row] +━━━━━━━━━━━━━━━━ +> open jonathan.xml | to-json +{"rss":[{"channel":[{"title":["Jonathan Turner"]},{"link":["http://www.jonathanturner.org"]},{"link":[]},{"item":[{"title":["Creating crossplatform Rust terminal apps"]},{"description":["

\"Pikachu

\n\n

Look Mom, Pikachu running in Windows CMD!

\n\n

Part of the adventure is not seeing the way ahead and going anyway.

\n"]},{"pubDate":["Mon, 05 Oct 2015 00:00:00 +0000"]},{"link":["http://www.jonathanturner.org/2015/10/off-to-new-adventures.html"]},{"guid":["http://www.jonathanturner.org/2015/10/off-to-new-adventures.html"]}]}]}]} +``` diff --git a/docs/commands/to-toml.md b/docs/commands/to-toml.md index 0690156184..a026520696 100644 --- a/docs/commands/to-toml.md +++ b/docs/commands/to-toml.md @@ -30,3 +30,83 @@ name = "filesystem" path = "/home/shaurya/Desktop" ``` + +```shell +> open cargo_sample.toml +━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + dependencies │ dev-dependencies │ package +────────────────┼──────────────────┼──────────────── + [table: 1 row] │ [table: 1 row] │ [table: 1 row] +━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ +> open cargo_sample.toml | to-toml +[dependencies] +ansi_term = "0.11.0" +app_dirs = "1.2.1" +byte-unit = "2.1.0" +bytes = "0.4.12" +chrono-humanize = "0.0.11" +chrono-tz = "0.5.1" +clap = "2.33.0" +conch-parser = "0.1.1" +derive-new = "0.5.6" +dunce = "1.0.0" +futures-sink-preview = "0.3.0-alpha.16" +futures_codec = "0.2.2" +getset = "0.0.7" +git2 = "0.8.0" +itertools = "0.8.0" +lalrpop-util = "0.17.0" +language-reporting = "0.3.0" +log = "0.4.6" +logos = "0.10.0-rc2" +logos-derive = "0.10.0-rc2" +nom = "5.0.0-beta1" +ordered-float = "1.0.2" +pretty_env_logger = "0.3.0" +prettyprint = "0.6.0" +prettytable-rs = "0.8.0" +regex = "1.1.6" +rustyline = "4.1.0" +serde = "1.0.91" +serde_derive = "1.0.91" +serde_json = "1.0.39" +subprocess = "0.1.18" +sysinfo = "0.8.4" +term = "0.5.2" +tokio-fs = "0.1.6" +toml = "0.5.1" +toml-query = "0.9.0" + +[dependencies.chrono] +features = ["serde"] +version = "0.4.6" + +[dependencies.cursive] +default-features = false +features = ["pancurses-backend"] +version = "0.12.0" + +[dependencies.futures-preview] +features = ["compat", "io-compat"] +version = "0.3.0-alpha.16" + +[dependencies.indexmap] +features = ["serde-1"] +version = "1.0.2" + +[dependencies.pancurses] +features = ["win32a"] +version = "0.16" + +[dev-dependencies] +pretty_assertions = "0.6.1" + +[package] +authors = ["Yehuda Katz "] +description = "A shell for the GitHub era" +edition = "2018" +license = "ISC" +name = "nu" +version = "0.1.1" + +``` diff --git a/docs/commands/to-tsv.md b/docs/commands/to-tsv.md index ae2ade95c3..b9e5f97d4f 100644 --- a/docs/commands/to-tsv.md +++ b/docs/commands/to-tsv.md @@ -18,3 +18,63 @@ Converts table data into tsv text. X filesystem /home/shaurya ``` + +```shell +> open caco3_plastics.tsv +━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━━━━ + # │ importer │ shipper │ tariff_item │ name │ origin │ shipped_at │ arrived_at │ net_weight │ fob_price │ cif_price │ cif_per_net_ + │ │ │ │ │ │ │ │ │ │ │ weight +───┼──────────────┼──────────────┼─────────────┼──────────────┼──────────┼────────────┼────────────┼────────────┼───────────┼───────────┼────────────── + 0 │ PLASTICOS │ S A REVERTE │ 2509000000 │ CARBONATO DE │ SPAIN │ 18/03/2016 │ 17/04/2016 │ 81,000.00 │ 14,417.58 │ 18,252.34 │ 0.23 + │ RIVAL CIA │ │ │ CALCIO TIPO │ │ │ │ │ │ │ + │ LTDA │ │ │ CALCIPORE │ │ │ │ │ │ │ + │ │ │ │ 160 T AL │ │ │ │ │ │ │ + 1 │ MEXICHEM │ OMYA ANDINA │ 2836500000 │ CARBONATO │ COLOMBIA │ 07/07/2016 │ 10/07/2016 │ 26,000.00 │ 7,072.00 │ 8,127.18 │ 0.31 + │ ECUADOR S.A. │ S A │ │ │ │ │ │ │ │ │ + 2 │ PLASTIAZUAY │ SA REVERTE │ 2836500000 │ CARBONATO DE │ SPAIN │ 27/07/2016 │ 09/08/2016 │ 81,000.00 │ 8,100.00 │ 11,474.55 │ 0.14 + │ SA │ │ │ CALCIO │ │ │ │ │ │ │ + 3 │ PLASTICOS │ AND │ 2836500000 │ CALCIUM │ TURKEY │ 04/10/2016 │ 11/11/2016 │ 100,000.00 │ 17,500.00 │ 22,533.75 │ 0.23 + │ RIVAL CIA │ ENDUSTRIYEL │ │ CARBONATE │ │ │ │ │ │ │ + │ LTDA │ HAMMADDELER │ │ ANADOLU │ │ │ │ │ │ │ + │ │ DIS TCARET │ │ ANDCARB CT-1 │ │ │ │ │ │ │ + │ │ LTD.STI. │ │ │ │ │ │ │ │ │ + 4 │ QUIMICA │ SA REVERTE │ 2836500000 │ CARBONATO DE │ SPAIN │ 24/06/2016 │ 12/07/2016 │ 27,000.00 │ 3,258.90 │ 5,585.00 │ 0.21 + │ COMERCIAL │ │ │ CALCIO │ │ │ │ │ │ │ + │ QUIMICIAL │ │ │ │ │ │ │ │ │ │ + │ CIA. LTDA. │ │ │ │ │ │ │ │ │ │ + 5 │ PICA │ OMYA ANDINA │ 3824909999 │ CARBONATO DE │ COLOMBIA │ 01/01/1900 │ 18/01/2016 │ 66,500.00 │ 12,635.00 │ 18,670.52 │ 0.28 + │ PLASTICOS │ S.A │ │ CALCIO │ │ │ │ │ │ │ + │ INDUSTRIALES │ │ │ │ │ │ │ │ │ │ + │ C.A. │ │ │ │ │ │ │ │ │ │ + 6 │ PLASTIQUIM │ OMYA ANDINA │ 3824909999 │ CARBONATO DE │ COLOMBIA │ 01/01/1900 │ 25/10/2016 │ 33,000.00 │ 6,270.00 │ 9,999.00 │ 0.30 + │ S.A. │ S.A NIT │ │ CALCIO │ │ │ │ │ │ │ + │ │ 830.027.386- │ │ RECUBIERTO │ │ │ │ │ │ │ + │ │ 6 │ │ CON ACIDO │ │ │ │ │ │ │ + │ │ │ │ ESTEARICO │ │ │ │ │ │ │ + │ │ │ │ OMYA CARB 1T │ │ │ │ │ │ │ + │ │ │ │ CG BBS 1000 │ │ │ │ │ │ │ + 7 │ QUIMICOS │ SIBELCO │ 3824909999 │ CARBONATO DE │ COLOMBIA │ 01/11/2016 │ 03/11/2016 │ 52,000.00 │ 8,944.00 │ 13,039.05 │ 0.25 + │ ANDINOS │ COLOMBIA SAS │ │ CALCIO │ │ │ │ │ │ │ + │ QUIMANDI │ │ │ RECUBIERTO │ │ │ │ │ │ │ + │ S.A. │ │ │ │ │ │ │ │ │ │ + 8 │ TIGRE │ OMYA ANDINA │ 3824909999 │ CARBONATO DE │ COLOMBIA │ 01/01/1900 │ 28/10/2016 │ 66,000.00 │ 11,748.00 │ 18,216.00 │ 0.28 + │ ECUADOR S.A. │ S.A NIT │ │ CALCIO │ │ │ │ │ │ │ + │ ECUATIGRE │ 830.027.386- │ │ RECUBIERTO │ │ │ │ │ │ │ + │ │ 6 │ │ CON ACIDO │ │ │ │ │ │ │ + │ │ │ │ ESTEARICO │ │ │ │ │ │ │ + │ │ │ │ OMYACARB 1T │ │ │ │ │ │ │ + │ │ │ │ CG BPA 25 NO │ │ │ │ │ │ │ +━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━━━━ +> open caco3_plastics.tsv | to-tsv +importer shipper tariff_item name origin shipped_at arrived_at net_weight fob_price cif_price cif_per_net_weight +PLASTICOS RIVAL CIA LTDA S A REVERTE 2509000000 CARBONATO DE CALCIO TIPO CALCIPORE 160 T AL SPAIN 18/03/2016 17/04/2016 81,000.00 14,417.58 18,252.34 0.23 +MEXICHEM ECUADOR S.A. OMYA ANDINA S A 2836500000 CARBONATO COLOMBIA 07/07/2016 10/07/2016 26,000.00 7,072.00 8,127.18 0.31 +PLASTIAZUAY SA SA REVERTE 2836500000 CARBONATO DE CALCIO SPAIN 27/07/2016 09/08/2016 81,000.00 8,100.00 11,474.55 0.14 +PLASTICOS RIVAL CIA LTDA AND ENDUSTRIYEL HAMMADDELER DIS TCARET LTD.STI. 2836500000 CALCIUM CARBONATE ANADOLU ANDCARB CT-1 TURKEY 04/10/2016 11/11/2016 100,000.00 17,500.00 22,533.75 0.23 +QUIMICA COMERCIAL QUIMICIAL CIA. LTDA. SA REVERTE 2836500000 CARBONATO DE CALCIO SPAIN 24/06/2016 12/07/2016 27,000.00 3,258.90 5,585.00 0.21 +PICA PLASTICOS INDUSTRIALES C.A. OMYA ANDINA S.A 3824909999 CARBONATO DE CALCIO COLOMBIA 01/01/1900 18/01/2016 66,500.00 12,635.00 18,670.52 0.28 +PLASTIQUIM S.A. OMYA ANDINA S.A NIT 830.027.386-6 3824909999 CARBONATO DE CALCIO RECUBIERTO CON ACIDO ESTEARICO OMYA CARB 1T CG BBS 1000 COLOMBIA 01/01/1900 25/10/2016 33,000.00 6,270.00 9,999.00 0.30 +QUIMICOS ANDINOS QUIMANDI S.A. SIBELCO COLOMBIA SAS 3824909999 CARBONATO DE CALCIO RECUBIERTO COLOMBIA 01/11/2016 03/11/2016 52,000.00 8,944.00 13,039.05 0.25 +TIGRE ECUADOR S.A. ECUATIGRE OMYA ANDINA S.A NIT 830.027.386-6 3824909999 CARBONATO DE CALCIO RECUBIERTO CON ACIDO ESTEARICO OMYACARB 1T CG BPA 25 NO COLOMBIA 01/01/1900 28/10/2016 66,000.00 11,748.00 18,216.00 0.28 + +``` diff --git a/docs/commands/to-url.md b/docs/commands/to-url.md index b66057ed67..ad11133760 100644 --- a/docs/commands/to-url.md +++ b/docs/commands/to-url.md @@ -22,3 +22,14 @@ Converts table data into url-formatted text. 2 │ +=+&name=filesystem&path=%2Fhome%2Fshaurya%2FDesktop ━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ``` + +```shell +> open sample.url +━━━━━━━━━━┯━━━━━━━━┯━━━━━━┯━━━━━━━━ + bread │ cheese │ meat │ fat +──────────┼────────┼──────┼──────── + baguette │ comté │ ham │ butter +━━━━━━━━━━┷━━━━━━━━┷━━━━━━┷━━━━━━━━ +> open sample.url | to-url +bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter +``` diff --git a/docs/commands/to-yaml.md b/docs/commands/to-yaml.md index 90d6283ce3..b2be3768ef 100644 --- a/docs/commands/to-yaml.md +++ b/docs/commands/to-yaml.md @@ -25,3 +25,36 @@ Converts table data into yaml text. name: filesystem path: /home/shaurya/Desktop ``` + +```shell +> open appveyor.yml +━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━┯━━━━━━━┯━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━ + image │ environment │ install │ build │ test_script │ cache +────────────────────┼────────────────┼─────────────────┼───────┼─────────────────┼───────────────── + Visual Studio 2017 │ [table: 1 row] │ [table: 5 rows] │ │ [table: 2 rows] │ [table: 2 rows] +━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━┷━━━━━━━┷━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━ +> open appveyor.yml | to-yaml +--- +image: Visual Studio 2017 +environment: + global: + PROJECT_NAME: nushell + RUST_BACKTRACE: 1 + matrix: + - TARGET: x86_64-pc-windows-msvc + CHANNEL: nightly + BITS: 64 +install: + - "set PATH=C:\\msys64\\mingw%BITS%\\bin;C:\\msys64\\usr\\bin;%PATH%" + - "curl -sSf -o rustup-init.exe https://win.rustup.rs" + - rustup-init.exe -y --default-host %TARGET% --default-toolchain %CHANNEL%-%TARGET% + - "set PATH=%PATH%;C:\\Users\\appveyor\\.cargo\\bin" + - "call \"C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\VC\\Auxiliary\\Build\\vcvars64.bat\"" +build: false +test_script: + - cargo build --verbose + - cargo test --all --verbose +cache: + - target -> Cargo.lock + - "C:\\Users\\appveyor\\.cargo\\registry -> Cargo.lock" +``` From 6aec03708f4d31e5c2202c47a23aae546d91676b Mon Sep 17 00:00:00 2001 From: Shaurya Shubham Date: Fri, 4 Oct 2019 06:44:45 +0530 Subject: [PATCH 232/342] Fix minor typo --- docs/commands/to-json.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands/to-json.md b/docs/commands/to-json.md index 549e10d0f8..eaf1cdb26a 100644 --- a/docs/commands/to-json.md +++ b/docs/commands/to-json.md @@ -27,7 +27,7 @@ Converts table data into json text. > open sgml_description.json | to-json {"glossary":{"title":"example glossary","GlossDiv":{"title":"S","GlossList":{"GlossEntry":{"ID":"SGML","SortAs":"SGML","GlossTerm":"Standard Generalized Markup Language","Acronym":"SGML","Abbrev":"ISO 8879:1986","Height":10,"GlossDef":{"para":"A meta-markup language, used to create markup languages such as DocBook.","GlossSeeAlso":["GML","XML"]},"Sections":[101,102],"GlossSee":"markup"}}}}} ``` -We can also convert files ! +We can also convert formats ! ```shell > open jonathan.xml ━━━━━━━━━━━━━━━━ From eb297d3b8f79e2e9548494f5db11c20564472058 Mon Sep 17 00:00:00 2001 From: Cristi Cismas Date: Fri, 4 Oct 2019 15:10:46 +0300 Subject: [PATCH 233/342] Update cd.md to look better --- docs/commands/cd.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands/cd.md b/docs/commands/cd.md index a227865981..377733ba8f 100644 --- a/docs/commands/cd.md +++ b/docs/commands/cd.md @@ -1,6 +1,6 @@ # cd -If you don't know about it already, the `cd` command is very simple. It stands for 'change directory' and it does exactly that. It changes the current directory that you are in to the one specified. If no directory is specified, it takes you to the home directory. Additionally, `..` takes you to the parent directory +If you didn't already know, the `cd` command is very simple. It stands for 'change directory' and it does exactly that. It changes the current directory to the one specified. If no directory is specified, it takes you to the home directory. Additionally, using `cd ..` takes you to the parent directory. ## Examples - From 20031861b91015de9cb3e9fdbee16446ce393efc Mon Sep 17 00:00:00 2001 From: Maya Farber Brodsky Date: Fri, 4 Oct 2019 17:37:11 +0300 Subject: [PATCH 234/342] Add documentation for nth command --- docs/commands/nth.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/commands/nth.md diff --git a/docs/commands/nth.md b/docs/commands/nth.md new file mode 100644 index 0000000000..0c8ce57f0c --- /dev/null +++ b/docs/commands/nth.md @@ -0,0 +1,31 @@ +# nth + +This command returns the nth row of a table, starting from 0. +If the number given is less than 0 or more than the number of rows, nothing is returned. + +## Usage +```shell +> [input-command] | nth [row-number] +``` + +## Examples +```shell +> ls +━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼────────────┼───────────┼──────────┼────────┼───────────────┼─────────────── + 0 │ Cargo.toml │ File │ │ 239 B │ 2 minutes ago │ 2 minutes ago + 1 │ .git │ Directory │ │ 4.1 KB │ 2 minutes ago │ 2 minutes ago + 2 │ .gitignore │ File │ │ 19 B │ 2 minutes ago │ 2 minutes ago + 3 │ src │ Directory │ │ 4.1 KB │ 2 minutes ago │ 2 minutes ago +━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━ + +> ls | nth 0 +━━━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━ + name │ type │ readonly │ size │ accessed │ modified +────────────┼──────┼──────────┼────────┼───────────────┼─────────────── + Cargo.toml │ File │ │ 239 B │ 2 minutes ago │ 2 minutes ago +━━━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━ + +> ls | nth 5 +``` \ No newline at end of file From 7d115da7825aee3532decb78682051f18d40d39c Mon Sep 17 00:00:00 2001 From: Pradeep Chhetri Date: Sun, 6 Oct 2019 22:35:38 +0800 Subject: [PATCH 235/342] Add documentation for the trim command --- docs/commands/trim.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/commands/trim.md diff --git a/docs/commands/trim.md b/docs/commands/trim.md new file mode 100644 index 0000000000..5f01a688f7 --- /dev/null +++ b/docs/commands/trim.md @@ -0,0 +1,12 @@ +# trim + +Trim leading and following whitespace from text data + +## Example + +```shell +> echo " Hello world" + Hello world +> echo " Hello world" | trim +Hello world +``` \ No newline at end of file From 1d19595996ab523547c369dced81aa8d3acd432e Mon Sep 17 00:00:00 2001 From: Pradeep Chhetri Date: Sun, 6 Oct 2019 23:20:48 +0800 Subject: [PATCH 236/342] Add documentation for the sys command --- docs/commands/sys.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/commands/sys.md diff --git a/docs/commands/sys.md b/docs/commands/sys.md new file mode 100644 index 0000000000..b21a0ef219 --- /dev/null +++ b/docs/commands/sys.md @@ -0,0 +1,32 @@ +# sys + +This command gives information about the system where nu is running on. + +## Examples + +```shell +> sys +━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + host │ cpu │ disks │ mem │ net │ battery +────────────────┼────────────────┼─────────────────┼────────────────┼──────────────────┼──────────────── + [table: 1 row] │ [table: 1 row] │ [table: 3 rows] │ [table: 1 row] │ [table: 18 rows] │ [table: 1 row] +━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ +> sys | get host +━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━ + name │ release │ hostname │ arch │ uptime │ users +────────┼─────────┼──────────────┼────────┼────────────────┼────────────────── + Darwin │ 18.7.0 │ C02Y437GJGH6 │ x86_64 │ [table: 1 row] │ [table: 17 rows] +━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━ +> sys | get cpu +━━━━━━━┯━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━ + cores │ current ghz │ min ghz │ max ghz +───────┼───────────────────┼───────────────────┼─────────────────── + 12 │ 2.600000000000000 │ 2.600000000000000 │ 2.600000000000000 +━━━━━━━┷━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━ +> sys | get mem +━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━ + total │ free │ swap total │ swap free +─────────┼──────────┼────────────┼─────────── + 34.4 GB │ 545.0 MB │ 2.1 GB │ 723.0 MB +━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━ +``` \ No newline at end of file From 7ff5734d5d4ef8497ec8a1af7ecb8d4536c440e7 Mon Sep 17 00:00:00 2001 From: Pradeep Chhetri Date: Sun, 6 Oct 2019 23:27:51 +0800 Subject: [PATCH 237/342] Add documentation for the inc command --- docs/commands/inc.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 docs/commands/inc.md diff --git a/docs/commands/inc.md b/docs/commands/inc.md new file mode 100644 index 0000000000..c6dcb8d806 --- /dev/null +++ b/docs/commands/inc.md @@ -0,0 +1,31 @@ +# inc + +This command increments the value of variable by one. + +## Examples + +```shell +> open rustfmt.toml +--------- + edition +--------- + 2018 +--------- +> open rustfmt.toml | inc edition +--------- + edition +--------- + 2019 +--------- +``` + +```shell +> open Cargo.toml | get package.version +0.1.3 +> open Cargo.toml | inc package.version --major | get package.version +1.0.0 +> open Cargo.toml | inc package.version --minor | get package.version +0.2.0 +> open Cargo.toml | inc package.version --patch | get package.version +0.1.4 +``` \ No newline at end of file From 81fec11f8881dc91b79b3f1781fd390cd1b81aa3 Mon Sep 17 00:00:00 2001 From: Pradeep Chhetri Date: Sun, 6 Oct 2019 22:53:17 +0800 Subject: [PATCH 238/342] Add documentation for the open command --- docs/commands/open.md | 95 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 docs/commands/open.md diff --git a/docs/commands/open.md b/docs/commands/open.md new file mode 100644 index 0000000000..61b5d1748b --- /dev/null +++ b/docs/commands/open.md @@ -0,0 +1,95 @@ +# open + +Loads a file into a cell, convert it to table if possible (avoid by appending `--raw` flag) + +## Example + +```shell +> cat user.yaml +- Name: Peter + Age: 30 + Telephone: 88204828 + Country: Singapore +- Name: Michael + Age: 42 + Telephone: 44002010 + Country: Spain +- Name: Will + Age: 50 + Telephone: 99521080 + Country: Germany +> open user.yaml +━━━┯━━━━━━━━━┯━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━ + # │ Name │ Age │ Telephone │ Country +───┼─────────┼─────┼───────────┼─────────── + 0 │ Peter │ 30 │ 88204828 │ Singapore + 1 │ Michael │ 42 │ 44002010 │ Spain + 2 │ Will │ 50 │ 99521080 │ Germany +━━━┷━━━━━━━━━┷━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━ +> open user.yaml --raw +- Name: Peter + Age: 30 + Telephone: 88204828 + Country: Singapore +- Name: Michael + Age: 42 + Telephone: 44002010 + Country: Spain +- Name: Will + Age: 50 + Telephone: 99521080 + Country: Germany +``` + +```shell +> cat user.json +[ + { + "Name": "Peter", + "Age": 30, + "Telephone": 88204828, + "Country": "Singapore" + }, + { + "Name": "Michael", + "Age": 42, + "Telephone": 44002010, + "Country": "Spain" + }, + { + "Name": "Will", + "Age": 50, + "Telephone": 99521080, + "Country": "Germany" + } +] +> open user.json +━━━┯━━━━━━━━━┯━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━ + # │ Name │ Age │ Telephone │ Country +───┼─────────┼─────┼───────────┼─────────── + 0 │ Peter │ 30 │ 88204828 │ Singapore + 1 │ Michael │ 42 │ 44002010 │ Spain + 2 │ Will │ 50 │ 99521080 │ Germany +━━━┷━━━━━━━━━┷━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━ +> open user.json --raw +[ + { + "Name": "Peter", + "Age": 30, + "Telephone": 88204828, + "Country": "Singapore" + }, + { + "Name": "Michael", + "Age": 42, + "Telephone": 44002010, + "Country": "Spain" + }, + { + "Name": "Will", + "Age": 50, + "Telephone": 99521080, + "Country": "Germany" + } +] +``` \ No newline at end of file From 9f15017032a398f36cf3f81de3d355f03658e900 Mon Sep 17 00:00:00 2001 From: Pradeep Chhetri Date: Sun, 6 Oct 2019 23:56:45 +0800 Subject: [PATCH 239/342] Add documentation for the fetch command --- docs/commands/fetch.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/commands/fetch.md diff --git a/docs/commands/fetch.md b/docs/commands/fetch.md new file mode 100644 index 0000000000..8d81e96044 --- /dev/null +++ b/docs/commands/fetch.md @@ -0,0 +1,32 @@ +# fetch + +This command loads from a URL into a cell, convert it to table if possible (avoid by appending `--raw` flag) + +## Examples + +```shell +> fetch http://headers.jsontest.com +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━ + X-Cloud-Trace-Context │ Accept │ Host │ Content-Length │ user-agent +───────────────────────────────────────────────────────┼────────┼──────────────────────┼────────────────┼───────────────────────── + aeee1a8abf08820f6fe19d114dc3bb87/16772233176633589121 │ */* │ headers.jsontest.com │ 0 │ curl/7.54.0 isahc/0.7.1 +━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━ +> fetch http://headers.jsontest.com --raw +{ + "X-Cloud-Trace-Context": "aeee1a8abf08820f6fe19d114dc3bb87/16772233176633589121", + "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3", + "Upgrade-Insecure-Requests": "1", + "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36", + "Host": "headers.jsontest.com", + "Accept-Language": "en-GB,en-US;q=0.9,en;q=0.8" +} +``` + +```shell +> fetch https://www.jonathanturner.org/feed.xml +━━━━━━━━━━━━━━━━ + rss +──────────────── + [table: 1 row] +━━━━━━━━━━━━━━━━ +``` \ No newline at end of file From c09d866a77b4b849461b6636b526787066312908 Mon Sep 17 00:00:00 2001 From: Pradeep Chhetri Date: Sun, 6 Oct 2019 23:12:38 +0800 Subject: [PATCH 240/342] Add documentation for the enter command --- docs/commands/enter.md | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/commands/enter.md diff --git a/docs/commands/enter.md b/docs/commands/enter.md new file mode 100644 index 0000000000..426fe0ec4b --- /dev/null +++ b/docs/commands/enter.md @@ -0,0 +1,39 @@ +# enter + +This command creates a new shell and begin at this path. + +## Examples + +```shell +/home/foobar> cat user.json +{ + "Name": "Peter", + "Age": 30, + "Telephone": 88204828, + "Country": "Singapore" +} +/home/foobar> enter user.json +/> ls +━━━━━━━┯━━━━━┯━━━━━━━━━━━┯━━━━━━━━━━━ + Name │ Age │ Telephone │ Country +───────┼─────┼───────────┼─────────── + Peter │ 30 │ 88204828 │ Singapore +━━━━━━━┷━━━━━┷━━━━━━━━━━━┷━━━━━━━━━━━ +/> exit +/home/foobar> +``` + +It also provides the ability to work with multiple directories at the same time. This command will allow you to create a new "shell" and enter it at the specified path. You can toggle between this new shell and the original shell with the `p` (for previous) and `n` (for next), allowing you to navigate around a ring buffer of shells. Once you're done with a shell, you can `exit` it and remove it from the ring buffer. + +```shell +/> enter /tmp +/tmp> enter /usr +/usr> enter /bin +/bin> enter /opt +/opt> p +/bin> p +/usr> p +/tmp> p +/> n +/tmp> +``` From e72bc8ea8b2a17a7fbc2944c28e4bc37938c01b9 Mon Sep 17 00:00:00 2001 From: Odin Dutton Date: Tue, 8 Oct 2019 10:16:25 +1100 Subject: [PATCH 241/342] Remove unneeded - --- docs/commands/cd.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands/cd.md b/docs/commands/cd.md index 377733ba8f..b964be50c9 100644 --- a/docs/commands/cd.md +++ b/docs/commands/cd.md @@ -2,7 +2,7 @@ If you didn't already know, the `cd` command is very simple. It stands for 'change directory' and it does exactly that. It changes the current directory to the one specified. If no directory is specified, it takes you to the home directory. Additionally, using `cd ..` takes you to the parent directory. -## Examples - +## Examples ```shell /home/username> cd Desktop From 77c34acb0318dab540b7e9677211f75e2a6b34c0 Mon Sep 17 00:00:00 2001 From: Odin Dutton Date: Tue, 8 Oct 2019 10:17:46 +1100 Subject: [PATCH 242/342] Whitespace --- docs/commands/cd.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands/cd.md b/docs/commands/cd.md index b964be50c9..535f1d16eb 100644 --- a/docs/commands/cd.md +++ b/docs/commands/cd.md @@ -21,5 +21,5 @@ If you didn't already know, the `cd` command is very simple. It stands for 'chan /home/username/Desktop/super/duper/crazy/nested/folders> cd /home/username> cd ../../usr /usr> cd -/home/username> +/home/username> ``` From 4d7025569603e20c446dbdf88bc86af4a5c78542 Mon Sep 17 00:00:00 2001 From: Odin Dutton Date: Tue, 8 Oct 2019 10:17:51 +1100 Subject: [PATCH 243/342] Add documentation for `cd -` --- docs/commands/cd.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/commands/cd.md b/docs/commands/cd.md index 535f1d16eb..2e5d933f47 100644 --- a/docs/commands/cd.md +++ b/docs/commands/cd.md @@ -23,3 +23,11 @@ If you didn't already know, the `cd` command is very simple. It stands for 'chan /usr> cd /home/username> ``` + +Using `cd -` will take you to the previous directory: + +```shell +/home/username/Desktop/super/duper/crazy/nested/folders> cd +/home/username> cd - +/home/username/Desktop/super/duper/crazy/nested/folders> cd +``` From 1ad9d6f199556c706812d992f22a7a91e8668259 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 17 Sep 2019 15:26:27 -0700 Subject: [PATCH 244/342] Overhaul the expansion system The main thrust of this (very large) commit is an overhaul of the expansion system. The parsing pipeline is: - Lightly parse the source file for atoms, basic delimiters and pipeline structure into a token tree - Expand the token tree into a HIR (high-level intermediate representation) based upon the baseline syntax rules for expressions and the syntactic shape of commands. Somewhat non-traditionally, nu doesn't have an AST at all. It goes directly from the token tree, which doesn't represent many important distinctions (like the difference between `hello` and `5KB`) directly into a high-level representation that doesn't have a direct correspondence to the source code. At a high level, nu commands work like macros, in the sense that the syntactic shape of the invocation of a command depends on the definition of a command. However, commands do not have the ability to perform unrestricted expansions of the token tree. Instead, they describe their arguments in terms of syntactic shapes, and the expander expands the token tree into HIR based upon that definition. For example, the `where` command says that it takes a block as its first required argument, and the description of the block syntactic shape expands the syntax `cpu > 10` into HIR that represents `{ $it.cpu > 10 }`. This commit overhauls that system so that the syntactic shapes are described in terms of a few new traits (`ExpandSyntax` and `ExpandExpression` are the primary ones) that are more composable than the previous system. The first big win of this new system is the addition of the `ColumnPath` shape, which looks like `cpu."max ghz"` or `package.version`. Previously, while a variable path could look like `$it.cpu."max ghz"`, the tail of a variable path could not be easily reused in other contexts. Now, that tail is its own syntactic shape, and it can be used as part of a command's signature. This cleans up commands like `inc`, `add` and `edit` as well as shorthand blocks, which can now look like `| where cpu."max ghz" > 10` --- Cargo.lock | 22 + Cargo.toml | 3 + src/cli.rs | 190 +- src/commands.rs | 1 + src/commands/autoview.rs | 33 +- src/commands/classified.rs | 35 +- src/commands/command.rs | 9 + src/commands/echo.rs | 9 +- src/commands/enter.rs | 10 +- src/commands/fetch.rs | 2 +- src/commands/first.rs | 2 +- src/commands/get.rs | 61 +- src/commands/open.rs | 2 +- src/commands/save.rs | 7 +- src/commands/skip_while.rs | 3 + src/commands/tags.rs | 4 +- src/context.rs | 24 +- src/data/base.rs | 133 +- src/data/meta.rs | 123 +- src/errors.rs | 31 +- src/evaluate/evaluator.rs | 30 +- src/lib.rs | 2 +- src/parser.rs | 6 +- src/parser/deserializer.rs | 9 +- src/parser/hir.rs | 138 +- src/parser/hir/baseline_parse.rs | 142 +- src/parser/hir/baseline_parse/tests.rs | 144 ++ src/parser/hir/baseline_parse_tokens.rs | 459 ----- src/parser/hir/binary.rs | 6 + src/parser/hir/expand_external_tokens.rs | 87 + src/parser/hir/external_command.rs | 2 +- src/parser/hir/path.rs | 34 +- src/parser/hir/syntax_shape.rs | 662 +++++++ src/parser/hir/syntax_shape/block.rs | 168 ++ src/parser/hir/syntax_shape/expression.rs | 188 ++ .../hir/syntax_shape/expression/delimited.rs | 38 + .../hir/syntax_shape/expression/file_path.rs | 59 + .../hir/syntax_shape/expression/list.rs | 43 + .../hir/syntax_shape/expression/number.rs | 97 + .../hir/syntax_shape/expression/pattern.rs | 86 + .../hir/syntax_shape/expression/string.rs | 60 + .../hir/syntax_shape/expression/unit.rs | 89 + .../syntax_shape/expression/variable_path.rs | 396 ++++ src/parser/hir/tokens_iterator.rs | 365 ++++ src/parser/hir/tokens_iterator/debug.rs | 30 + src/parser/parse/files.rs | 29 +- src/parser/parse/operator.rs | 3 + src/parser/parse/parser.rs | 1586 ++++++++--------- src/parser/parse/pipeline.rs | 27 +- src/parser/parse/token_tree.rs | 112 +- src/parser/parse/token_tree_builder.rs | 110 +- src/parser/parse/tokens.rs | 11 +- src/parser/parse_command.rs | 155 +- src/parser/registry.rs | 19 +- src/plugins/add.rs | 23 +- src/plugins/edit.rs | 18 +- src/plugins/inc.rs | 37 +- src/plugins/str.rs | 47 +- src/shell/helper.rs | 38 +- tests/command_open_tests.rs | 2 +- tests/helpers/mod.rs | 1 + 61 files changed, 4310 insertions(+), 1952 deletions(-) create mode 100644 src/parser/hir/baseline_parse/tests.rs delete mode 100644 src/parser/hir/baseline_parse_tokens.rs create mode 100644 src/parser/hir/expand_external_tokens.rs create mode 100644 src/parser/hir/syntax_shape.rs create mode 100644 src/parser/hir/syntax_shape/block.rs create mode 100644 src/parser/hir/syntax_shape/expression.rs create mode 100644 src/parser/hir/syntax_shape/expression/delimited.rs create mode 100644 src/parser/hir/syntax_shape/expression/file_path.rs create mode 100644 src/parser/hir/syntax_shape/expression/list.rs create mode 100644 src/parser/hir/syntax_shape/expression/number.rs create mode 100644 src/parser/hir/syntax_shape/expression/pattern.rs create mode 100644 src/parser/hir/syntax_shape/expression/string.rs create mode 100644 src/parser/hir/syntax_shape/expression/unit.rs create mode 100644 src/parser/hir/syntax_shape/expression/variable_path.rs create mode 100644 src/parser/hir/tokens_iterator.rs create mode 100644 src/parser/hir/tokens_iterator/debug.rs diff --git a/Cargo.lock b/Cargo.lock index 852fbd6103..af1d46aa0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1491,6 +1491,25 @@ dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "nom-tracable" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom-tracable-macros 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nom-tracable-macros" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nom_locate" version = "1.0.0" @@ -1550,6 +1569,7 @@ dependencies = [ "natural 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom-tracable 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3140,6 +3160,8 @@ dependencies = [ "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b" +"checksum nom-tracable 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "edaa64ad2837d831d4a17966c9a83aa5101cc320730f5b724811c8f7442a2528" +"checksum nom-tracable-macros 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd25f70877a9fe68bd406b3dd3ff99e94ce9de776cf2a96e0d99de90b53d4765" "checksum nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f932834fd8e391fc7710e2ba17e8f9f8645d846b55aa63207e17e110a1e1ce35" "checksum ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602" "checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a" diff --git a/Cargo.toml b/Cargo.toml index f51ea06d8a..66bd695c08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ surf = "1.0.2" url = "2.1.0" roxmltree = "0.7.0" nom_locate = "1.0.0" +nom-tracable = "0.4.0" enum-utils = "0.1.1" unicode-xid = "0.2.0" serde_ini = "0.2.0" @@ -95,6 +96,8 @@ textview = ["syntect", "onig_sys", "crossterm"] binaryview = ["image", "crossterm"] sys = ["heim", "battery"] ps = ["heim"] +trace = ["nom-tracable/trace"] +all = ["raw-key", "textview", "binaryview", "sys", "ps", "clipboard", "ptree"] [dependencies.rusqlite] version = "0.20.0" diff --git a/src/cli.rs b/src/cli.rs index 38e2474faf..6a35608d91 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,3 @@ -use crate::commands::autoview; use crate::commands::classified::{ ClassifiedCommand, ClassifiedInputStream, ClassifiedPipeline, ExternalCommand, InternalCommand, StreamNext, @@ -13,7 +12,12 @@ pub(crate) use crate::errors::ShellError; use crate::fuzzysearch::{interactive_fuzzy_search, SelectionResult}; use crate::git::current_branch; use crate::parser::registry::Signature; -use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode}; +use crate::parser::{ + hir, + hir::syntax_shape::{CommandHeadShape, CommandSignature, ExpandSyntax}, + hir::{expand_external_tokens::expand_external_tokens, tokens_iterator::TokensIterator}, + parse_command_tail, Pipeline, PipelineElement, TokenNode, +}; use crate::prelude::*; use log::{debug, trace}; @@ -25,6 +29,7 @@ use std::io::{BufRead, BufReader, Write}; use std::iter::Iterator; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; #[derive(Debug)] pub enum MaybeOwned<'a, T> { @@ -75,7 +80,7 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel let name = params.name.clone(); let fname = fname.to_string(); - if context.has_command(&name) { + if let Some(_) = context.get_command(&name) { trace!("plugin {:?} already loaded.", &name); } else { if params.is_filter { @@ -428,21 +433,11 @@ pub async fn cli() -> Result<(), Box> { } } - LineResult::Error(mut line, err) => { + LineResult::Error(line, err) => { rl.add_history_entry(line.clone()); - let diag = err.to_diagnostic(); + context.with_host(|host| { - let writer = host.err_termcolor(); - line.push_str(" "); - let files = crate::parser::Files::new(line); - let _ = std::panic::catch_unwind(move || { - let _ = language_reporting::emit( - &mut writer.lock(), - &files, - &diag, - &language_reporting::DefaultConfig, - ); - }); + print_err(err, host, &Text::from(line)); }) } @@ -459,6 +454,14 @@ pub async fn cli() -> Result<(), Box> { Ok(()) } +fn chomp_newline(s: &str) -> &str { + if s.ends_with('\n') { + &s[..s.len() - 1] + } else { + s + } +} + enum LineResult { Success(String), Error(String, ShellError), @@ -471,9 +474,11 @@ async fn process_line(readline: Result, ctx: &mut Context Ok(line) if line.trim() == "" => LineResult::Success(line.clone()), Ok(line) => { - let result = match crate::parser::parse(&line, uuid::Uuid::nil()) { + let line = chomp_newline(line); + + let result = match crate::parser::parse(&line, uuid::Uuid::new_v4()) { Err(err) => { - return LineResult::Error(line.clone(), err); + return LineResult::Error(line.to_string(), err); } Ok(val) => val, @@ -484,7 +489,7 @@ async fn process_line(readline: Result, ctx: &mut Context let mut pipeline = match classify_pipeline(&result, ctx, &Text::from(line)) { Ok(pipeline) => pipeline, - Err(err) => return LineResult::Error(line.clone(), err), + Err(err) => return LineResult::Error(line.to_string(), err), }; match pipeline.commands.last() { @@ -492,7 +497,7 @@ async fn process_line(readline: Result, ctx: &mut Context _ => pipeline .commands .push(ClassifiedCommand::Internal(InternalCommand { - command: whole_stream_command(autoview::Autoview), + name: "autoview".to_string(), name_tag: Tag::unknown(), args: hir::Call::new( Box::new(hir::Expression::synthetic_string("autoview")), @@ -514,16 +519,24 @@ async fn process_line(readline: Result, ctx: &mut Context input = match (item, next) { (None, _) => break, + (Some(ClassifiedCommand::Dynamic(_)), _) + | (_, Some(ClassifiedCommand::Dynamic(_))) => { + return LineResult::Error( + line.to_string(), + ShellError::unimplemented("Dynamic commands"), + ) + } + (Some(ClassifiedCommand::Expr(_)), _) => { return LineResult::Error( - line.clone(), + line.to_string(), ShellError::unimplemented("Expression-only commands"), ) } (_, Some(ClassifiedCommand::Expr(_))) => { return LineResult::Error( - line.clone(), + line.to_string(), ShellError::unimplemented("Expression-only commands"), ) } @@ -536,7 +549,7 @@ async fn process_line(readline: Result, ctx: &mut Context .await { Ok(val) => ClassifiedInputStream::from_input_stream(val), - Err(err) => return LineResult::Error(line.clone(), err), + Err(err) => return LineResult::Error(line.to_string(), err), }, (Some(ClassifiedCommand::Internal(left)), Some(_)) => { @@ -545,7 +558,7 @@ async fn process_line(readline: Result, ctx: &mut Context .await { Ok(val) => ClassifiedInputStream::from_input_stream(val), - Err(err) => return LineResult::Error(line.clone(), err), + Err(err) => return LineResult::Error(line.to_string(), err), } } @@ -555,7 +568,7 @@ async fn process_line(readline: Result, ctx: &mut Context .await { Ok(val) => ClassifiedInputStream::from_input_stream(val), - Err(err) => return LineResult::Error(line.clone(), err), + Err(err) => return LineResult::Error(line.to_string(), err), } } @@ -564,20 +577,20 @@ async fn process_line(readline: Result, ctx: &mut Context Some(ClassifiedCommand::External(_)), ) => match left.run(ctx, input, StreamNext::External).await { Ok(val) => val, - Err(err) => return LineResult::Error(line.clone(), err), + Err(err) => return LineResult::Error(line.to_string(), err), }, (Some(ClassifiedCommand::External(left)), Some(_)) => { match left.run(ctx, input, StreamNext::Internal).await { Ok(val) => val, - Err(err) => return LineResult::Error(line.clone(), err), + Err(err) => return LineResult::Error(line.to_string(), err), } } (Some(ClassifiedCommand::External(left)), None) => { match left.run(ctx, input, StreamNext::Last).await { Ok(val) => val, - Err(err) => return LineResult::Error(line.clone(), err), + Err(err) => return LineResult::Error(line.to_string(), err), } } }; @@ -585,7 +598,7 @@ async fn process_line(readline: Result, ctx: &mut Context is_first_command = false; } - LineResult::Success(line.clone()) + LineResult::Success(line.to_string()) } Err(ReadlineError::Interrupted) => LineResult::CtrlC, Err(ReadlineError::Eof) => LineResult::Break, @@ -616,80 +629,91 @@ fn classify_pipeline( } fn classify_command( - command: &PipelineElement, + command: &Tagged, context: &Context, source: &Text, ) -> Result { - let call = command.call(); + let mut iterator = TokensIterator::new(&command.tokens.item, command.tag, true); + + let head = CommandHeadShape + .expand_syntax(&mut iterator, &context.expand_context(source, command.tag))?; + + match &head { + CommandSignature::Expression(_) => Err(ShellError::syntax_error( + "Unexpected expression in command position".tagged(command.tag), + )), - match call { // If the command starts with `^`, treat it as an external command no matter what - call if call.head().is_external() => { - let name_tag = call.head().expect_external(); - let name = name_tag.slice(source); + CommandSignature::External(name) => { + let name_str = name.slice(source); - Ok(external_command(call, source, name.tagged(name_tag))) + external_command(&mut iterator, source, name_str.tagged(name)) } - // Otherwise, if the command is a bare word, we'll need to triage it - call if call.head().is_bare() => { - let head = call.head(); - let name = head.source(source); + CommandSignature::LiteralExternal { outer, inner } => { + let name_str = inner.slice(source); - match context.has_command(name) { - // if the command is in the registry, it's an internal command - true => { - let command = context.get_command(name); - let config = command.signature(); - - trace!(target: "nu::build_pipeline", "classifying {:?}", config); - - let args: hir::Call = config.parse_args(call, &context, source)?; - - trace!(target: "nu::build_pipeline", "args :: {}", args.debug(source)); - - Ok(ClassifiedCommand::Internal(InternalCommand { - command, - name_tag: head.tag(), - args, - })) - } - - // otherwise, it's an external command - false => Ok(external_command(call, source, name.tagged(head.tag()))), - } + external_command(&mut iterator, source, name_str.tagged(outer)) } - // If the command is something else (like a number or a variable), that is currently unsupported. - // We might support `$somevar` as a curried command in the future. - call => Err(ShellError::invalid_command(call.head().tag())), + CommandSignature::Internal(command) => { + let tail = parse_command_tail( + &command.signature(), + &context.expand_context(source, command.tag), + &mut iterator, + command.tag, + )?; + + let (positional, named) = match tail { + None => (None, None), + Some((positional, named)) => (positional, named), + }; + + let call = hir::Call { + head: Box::new(head.to_expression()), + positional, + named, + }; + + Ok(ClassifiedCommand::Internal(InternalCommand::new( + command.name().to_string(), + command.tag, + call, + ))) + } } } // Classify this command as an external command, which doesn't give special meaning // to nu syntactic constructs, and passes all arguments to the external command as // strings. -fn external_command( - call: &Tagged, +pub(crate) fn external_command( + tokens: &mut TokensIterator, source: &Text, name: Tagged<&str>, -) -> ClassifiedCommand { - let arg_list_strings: Vec> = match call.children() { - Some(args) => args - .iter() - .filter_map(|i| match i { - TokenNode::Whitespace(_) => None, - other => Some(other.as_external_arg(source).tagged(other.tag())), - }) - .collect(), - None => vec![], - }; +) -> Result { + let arg_list_strings = expand_external_tokens(tokens, source)?; - let (name, tag) = name.into_parts(); - - ClassifiedCommand::External(ExternalCommand { + Ok(ClassifiedCommand::External(ExternalCommand { name: name.to_string(), - name_tag: tag, + name_tag: name.tag(), args: arg_list_strings, - }) + })) +} + +pub fn print_err(err: ShellError, host: &dyn Host, source: &Text) { + let diag = err.to_diagnostic(); + + let writer = host.err_termcolor(); + let mut source = source.to_string(); + source.push_str(" "); + let files = crate::parser::Files::new(source); + let _ = std::panic::catch_unwind(move || { + let _ = language_reporting::emit( + &mut writer.lock(), + &files, + &diag, + &language_reporting::DefaultConfig, + ); + }); } diff --git a/src/commands.rs b/src/commands.rs index 72c07e38e6..4eb733edd4 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -75,6 +75,7 @@ pub(crate) use command::{ UnevaluatedCallInfo, WholeStreamCommand, }; +pub(crate) use classified::ClassifiedCommand; pub(crate) use config::Config; pub(crate) use cp::Cpy; pub(crate) use date::Date; diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index a0e7e9a8a5..57ab6269b3 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -58,21 +58,21 @@ pub fn autoview( } } }; - } else if is_single_anchored_text_value(&input) { - let text = context.get_command("textview"); - if let Some(text) = text { - let result = text.run(raw.with_input(input), &context.commands, false); - result.collect::>().await; - } else { - for i in input { - match i.item { - Value::Primitive(Primitive::String(s)) => { - println!("{}", s); - } - _ => {} - } - } - } + // } else if is_single_origined_text_value(&input) { + // let text = context.get_command("textview"); + // if let Some(text) = text { + // let result = text.run(raw.with_input(input), &context.commands); + // result.collect::>().await; + // } else { + // for i in input { + // match i.item { + // Value::Primitive(Primitive::String(s)) => { + // println!("{}", s); + // } + // _ => {} + // } + // } + // } } else if is_single_text_value(&input) { for i in input { match i.item { @@ -111,7 +111,8 @@ fn is_single_text_value(input: &Vec>) -> bool { } } -fn is_single_anchored_text_value(input: &Vec>) -> bool { +#[allow(unused)] +fn is_single_origined_text_value(input: &Vec>) -> bool { if input.len() != 1 { return false; } diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 0e5cd95d8d..d30025b944 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -1,12 +1,11 @@ -use crate::commands::Command; use crate::parser::{hir, TokenNode}; use crate::prelude::*; use bytes::{BufMut, BytesMut}; +use derive_new::new; use futures::stream::StreamExt; use futures_codec::{Decoder, Encoder, Framed}; use log::{log_enabled, trace}; use std::io::{Error, ErrorKind}; -use std::sync::Arc; use subprocess::Exec; /// A simple `Codec` implementation that splits up data into lines. @@ -77,19 +76,28 @@ pub(crate) struct ClassifiedPipeline { pub(crate) commands: Vec, } +#[derive(Debug, Eq, PartialEq)] pub(crate) enum ClassifiedCommand { #[allow(unused)] Expr(TokenNode), Internal(InternalCommand), + #[allow(unused)] + Dynamic(hir::Call), External(ExternalCommand), } +#[derive(new, Debug, Eq, PartialEq)] pub(crate) struct InternalCommand { - pub(crate) command: Arc, + pub(crate) name: String, pub(crate) name_tag: Tag, pub(crate) args: hir::Call, } +#[derive(new, Debug, Eq, PartialEq)] +pub(crate) struct DynamicCommand { + pub(crate) args: hir::Call, +} + impl InternalCommand { pub(crate) async fn run( self, @@ -100,15 +108,17 @@ impl InternalCommand { ) -> Result { if log_enabled!(log::Level::Trace) { trace!(target: "nu::run::internal", "->"); - trace!(target: "nu::run::internal", "{}", self.command.name()); + trace!(target: "nu::run::internal", "{}", self.name); trace!(target: "nu::run::internal", "{}", self.args.debug(&source)); } let objects: InputStream = trace_stream!(target: "nu::trace_stream::internal", "input" = input.objects); + let command = context.expect_command(&self.name); + let result = context.run_command( - self.command, + command, self.name_tag.clone(), context.source_map.clone(), self.args, @@ -185,6 +195,7 @@ impl InternalCommand { } } +#[derive(Debug, Eq, PartialEq)] pub(crate) struct ExternalCommand { pub(crate) name: String, @@ -192,6 +203,7 @@ pub(crate) struct ExternalCommand { pub(crate) args: Vec>, } +#[derive(Debug)] pub(crate) enum StreamNext { Last, External, @@ -221,6 +233,8 @@ impl ExternalCommand { process = Exec::cmd(&self.name); + trace!(target: "nu::run::external", "command = {:?}", process); + if arg_string.contains("$it") { let mut first = true; @@ -275,6 +289,8 @@ impl ExternalCommand { process = process.cwd(context.shell_manager.path()); + trace!(target: "nu::run::external", "cwd = {:?}", context.shell_manager.path()); + let mut process = match stream_next { StreamNext::Last => process, StreamNext::External | StreamNext::Internal => { @@ -282,11 +298,18 @@ impl ExternalCommand { } }; + trace!(target: "nu::run::external", "set up stdout pipe"); + if let Some(stdin) = stdin { process = process.stdin(stdin); } - let mut popen = process.popen()?; + trace!(target: "nu::run::external", "set up stdin pipe"); + trace!(target: "nu::run::external", "built process {:?}", process); + + let mut popen = process.popen().unwrap(); + + trace!(target: "nu::run::external", "next = {:?}", stream_next); match stream_next { StreamNext::Last => { diff --git a/src/commands/command.rs b/src/commands/command.rs index 95732abac2..7fb08bcefa 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -507,6 +507,15 @@ pub enum Command { PerItem(Arc), } +impl std::fmt::Debug for Command { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Command::WholeStream(command) => write!(f, "WholeStream({})", command.name()), + Command::PerItem(command) => write!(f, "PerItem({})", command.name()), + } + } +} + impl Command { pub fn name(&self) -> &str { match self { diff --git a/src/commands/echo.rs b/src/commands/echo.rs index 21188f54f2..5bfc12efb7 100644 --- a/src/commands/echo.rs +++ b/src/commands/echo.rs @@ -54,11 +54,10 @@ fn run( output.push_str(&s); } _ => { - return Err(ShellError::labeled_error( - "Expect a string from pipeline", - "not a string-compatible value", - i.tag(), - )); + return Err(ShellError::type_error( + "a string-compatible value", + i.tagged_type_name(), + )) } } } diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 2d96fe865c..94688acd56 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -15,7 +15,7 @@ impl PerItemCommand for Enter { } fn signature(&self) -> registry::Signature { - Signature::build("enter").required("location", SyntaxShape::Block) + Signature::build("enter").required("location", SyntaxShape::Path) } fn usage(&self) -> &str { @@ -33,14 +33,14 @@ impl PerItemCommand for Enter { let raw_args = raw_args.clone(); match call_info.args.expect_nth(0)? { Tagged { - item: Value::Primitive(Primitive::String(location)), + item: Value::Primitive(Primitive::Path(location)), .. } => { - let location = location.to_string(); - let location_clone = location.to_string(); + let location_string = location.display().to_string(); + let location_clone = location_string.clone(); if location.starts_with("help") { - let spec = location.split(":").collect::>(); + let spec = location_string.split(":").collect::>(); let (_, command) = (spec[0], spec[1]); diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index 652ec77eb5..21ef7fbfd9 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -53,7 +53,7 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_span = path.span(); + let path_span = path.tag.span; let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); diff --git a/src/commands/first.rs b/src/commands/first.rs index e39b5155d0..71d05be7e1 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for First { } fn signature(&self) -> Signature { - Signature::build("first").required("amount", SyntaxShape::Literal) + Signature::build("first").required("amount", SyntaxShape::Int) } fn usage(&self) -> &str { diff --git a/src/commands/get.rs b/src/commands/get.rs index afa550c72c..4b0916c5d1 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -1,14 +1,16 @@ use crate::commands::WholeStreamCommand; +use crate::data::meta::tag_for_tagged_list; use crate::data::Value; use crate::errors::ShellError; use crate::prelude::*; +use log::trace; pub struct Get; #[derive(Deserialize)] pub struct GetArgs { - member: Tagged, - rest: Vec>, + member: ColumnPath, + rest: Vec, } impl WholeStreamCommand for Get { @@ -18,8 +20,8 @@ impl WholeStreamCommand for Get { fn signature(&self) -> Signature { Signature::build("get") - .required("member", SyntaxShape::Member) - .rest(SyntaxShape::Member) + .required("member", SyntaxShape::ColumnPath) + .rest(SyntaxShape::ColumnPath) } fn usage(&self) -> &str { @@ -35,39 +37,34 @@ impl WholeStreamCommand for Get { } } -fn get_member(path: &Tagged, obj: &Tagged) -> Result, ShellError> { +pub type ColumnPath = Vec>; + +pub fn get_column_path( + path: &ColumnPath, + obj: &Tagged, +) -> Result, ShellError> { let mut current = Some(obj); - for p in path.split(".") { + for p in path.iter() { if let Some(obj) = current { - current = match obj.get_data_by_key(p) { + 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 => { - let possibilities = obj.data_descriptors(); + let possibilities = obj.data_descriptors(); - let mut possible_matches: Vec<_> = possibilities - .iter() - .map(|x| { - (natural::distance::levenshtein_distance(x, &path.item), x) - }) - .collect(); + let mut possible_matches: Vec<_> = possibilities + .iter() + .map(|x| (natural::distance::levenshtein_distance(x, &p), x)) + .collect(); - possible_matches.sort(); + possible_matches.sort(); - if possible_matches.len() > 0 { - return Err(ShellError::labeled_error( - "Unknown column", - format!("did you mean '{}'?", possible_matches[0].1), - path.tag(), - )); - } - None - } - } + return Err(ShellError::labeled_error( + "Unknown column", + format!("did you mean '{}'?", possible_matches[0].1), + tag_for_tagged_list(path.iter().map(|p| p.tag())), + )); } } } @@ -97,6 +94,8 @@ pub fn get( }: GetArgs, RunnableContext { input, .. }: RunnableContext, ) -> Result { + trace!("get {:?} {:?}", member, fields); + let stream = input .values .map(move |item| { @@ -107,10 +106,10 @@ pub fn get( let fields = vec![&member, &fields] .into_iter() .flatten() - .collect::>>(); + .collect::>(); - for field in &fields { - match get_member(field, &item) { + for column_path in &fields { + match get_column_path(column_path, &item) { Ok(Tagged { item: Value::Table(l), .. diff --git a/src/commands/open.rs b/src/commands/open.rs index 254b0bd7b9..97b0df2744 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -54,7 +54,7 @@ fn run( }; let path_buf = path.as_path()?; let path_str = path_buf.display().to_string(); - let path_span = path.span(); + let path_span = path.tag.span; let has_raw = call_info.args.has("raw"); let registry = registry.clone(); let raw_args = raw_args.clone(); diff --git a/src/commands/save.rs b/src/commands/save.rs index 47f1a17e95..44e07da5ed 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -143,15 +143,16 @@ fn save( } _ => { yield Err(ShellError::labeled_error( - "Save requires a filepath", + "Save requires a filepath (1)", "needs path", name_tag, )); } }, None => { + eprintln!("{:?} {:?}", anchor, source_map); yield Err(ShellError::labeled_error( - "Save requires a filepath", + "Save requires a filepath (2)", "needs path", name_tag, )); @@ -159,7 +160,7 @@ fn save( } } else { yield Err(ShellError::labeled_error( - "Save requires a filepath", + "Save requires a filepath (3)", "needs path", name_tag, )); diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index 041caf300e..a768ae6133 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -1,6 +1,7 @@ use crate::commands::WholeStreamCommand; use crate::errors::ShellError; use crate::prelude::*; +use log::trace; pub struct SkipWhile; @@ -38,7 +39,9 @@ pub fn skip_while( RunnableContext { input, .. }: RunnableContext, ) -> Result { let objects = input.values.skip_while(move |item| { + trace!("ITEM = {:?}", item); let result = condition.invoke(&item); + trace!("RESULT = {:?}", result); let return_value = match result { Ok(ref v) if v.is_true() => true, diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 0cef300b0c..2b710d1b61 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -38,8 +38,8 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result Arc { + self.get_command(name).unwrap() + } + pub(crate) fn has(&self, name: &str) -> bool { let registry = self.registry.lock().unwrap(); registry.contains_key(name) } - fn insert(&mut self, name: impl Into, command: Arc) { + pub(crate) fn insert(&mut self, name: impl Into, command: Arc) { let mut registry = self.registry.lock().unwrap(); registry.insert(name.into(), command); } @@ -83,6 +87,14 @@ impl Context { &self.registry } + pub(crate) fn expand_context<'context>( + &'context self, + source: &'context Text, + tag: Tag, + ) -> ExpandContext<'context> { + ExpandContext::new(&self.registry, tag, source, self.shell_manager.homedir()) + } + pub(crate) fn basic() -> Result> { let registry = CommandRegistry::new(); Ok(Context { @@ -109,12 +121,12 @@ impl Context { self.source_map.insert(uuid, anchor_location); } - pub(crate) fn has_command(&self, name: &str) -> bool { - self.registry.has(name) + pub(crate) fn get_command(&self, name: &str) -> Option> { + self.registry.get_command(name) } - pub(crate) fn get_command(&self, name: &str) -> Arc { - self.registry.get_command(name).unwrap() + pub(crate) fn expect_command(&self, name: &str) -> Arc { + self.registry.expect_command(name) } pub(crate) fn run_command<'a>( diff --git a/src/data/base.rs b/src/data/base.rs index 04465181a3..176560137f 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -8,6 +8,7 @@ use crate::Text; use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use derive_new::new; +use log::trace; use serde::{Deserialize, Serialize}; use std::fmt; use std::path::PathBuf; @@ -217,6 +218,14 @@ impl Block { let mut last = None; + trace!( + "EXPRS = {:?}", + self.expressions + .iter() + .map(|e| format!("{}", e)) + .collect::>() + ); + for expr in self.expressions.iter() { last = Some(evaluate_baseline_expr( &expr, @@ -394,13 +403,34 @@ impl Tagged { pub(crate) fn debug(&self) -> ValueDebug<'_> { ValueDebug { value: self } } + + pub fn as_column_path(&self) -> Result>>, ShellError> { + let mut out: Vec> = vec![]; + + match &self.item { + Value::Table(table) => { + for item in table { + out.push(item.as_string()?.tagged(item.tag)); + } + } + + other => { + return Err(ShellError::type_error( + "column name", + other.type_name().tagged(self.tag), + )) + } + } + + Ok(out.tagged(self.tag)) + } } impl Value { pub(crate) fn type_name(&self) -> String { match self { Value::Primitive(p) => p.type_name(), - Value::Row(_) => format!("object"), + Value::Row(_) => format!("row"), Value::Table(_) => format!("list"), Value::Block(_) => format!("block"), } @@ -443,6 +473,22 @@ impl Value { } } + pub fn get_data_by_column_path( + &self, + tag: Tag, + path: &Vec>, + ) -> Option> { + let mut current = self; + for p in path { + match current.get_data_by_key(p) { + Some(v) => current = v, + None => return None, + } + } + + Some(Tagged::from_item(current, tag)) + } + pub fn get_data_by_path(&self, tag: Tag, path: &str) -> Option> { let mut current = self; for p in path.split(".") { @@ -508,6 +554,58 @@ impl Value { None } + pub fn insert_data_at_column_path( + &self, + tag: Tag, + split_path: &Vec>, + new_value: Value, + ) -> Option> { + let mut new_obj = self.clone(); + + if let Value::Row(ref mut o) = new_obj { + let mut current = o; + + if split_path.len() == 1 { + // Special case for inserting at the top level + current.entries.insert( + split_path[0].item.clone(), + Tagged::from_item(new_value, tag), + ); + return Some(Tagged::from_item(new_obj, tag)); + } + + for idx in 0..split_path.len() { + match current.entries.get_mut(&split_path[idx].item) { + Some(next) => { + if idx == (split_path.len() - 2) { + match &mut next.item { + Value::Row(o) => { + o.entries.insert( + split_path[idx + 1].to_string(), + Tagged::from_item(new_value, tag), + ); + } + _ => {} + } + + return Some(Tagged::from_item(new_obj, tag)); + } else { + match next.item { + Value::Row(ref mut o) => { + current = o; + } + _ => return None, + } + } + } + _ => return None, + } + } + } + + None + } + pub fn replace_data_at_path( &self, tag: Tag, @@ -543,6 +641,39 @@ impl Value { None } + pub fn replace_data_at_column_path( + &self, + tag: Tag, + split_path: &Vec>, + replaced_value: Value, + ) -> Option> { + let mut new_obj = self.clone(); + + if let Value::Row(ref mut o) = new_obj { + let mut current = o; + for idx in 0..split_path.len() { + match current.entries.get_mut(&split_path[idx].item) { + Some(next) => { + if idx == (split_path.len() - 1) { + *next = Tagged::from_item(replaced_value, tag); + return Some(Tagged::from_item(new_obj, tag)); + } else { + match next.item { + Value::Row(ref mut o) => { + current = o; + } + _ => return None, + } + } + } + _ => return None, + } + } + } + + None + } + pub fn get_data(&self, desc: &String) -> MaybeOwned<'_, Value> { match self { p @ Value::Primitive(_) => MaybeOwned::Borrowed(p), diff --git a/src/data/meta.rs b/src/data/meta.rs index 0a56198e6c..b66b009cc2 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -1,4 +1,5 @@ use crate::context::{AnchorLocation, SourceMap}; +use crate::parser::parse::parser::TracableContext; use crate::prelude::*; use crate::Text; use derive_new::new; @@ -119,10 +120,7 @@ impl From<&Tag> for Tag { impl From> for Span { fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Span { - Span { - start: input.offset, - end: input.offset + input.fragment.len(), - } + Span::new(input.offset, input.offset + input.fragment.len()) } } @@ -147,10 +145,7 @@ impl impl From<(usize, usize)> for Span { fn from(input: (usize, usize)) -> Span { - Span { - start: input.0, - end: input.1, - } + Span::new(input.0, input.1) } } @@ -164,7 +159,7 @@ impl From<&std::ops::Range> for Span { } #[derive( - Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters, + Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters, new, )] pub struct Tag { pub anchor: Uuid, @@ -189,11 +184,20 @@ impl From<&Span> for Tag { } } +impl From<(usize, usize, TracableContext)> for Tag { + fn from((start, end, context): (usize, usize, TracableContext)) -> Self { + Tag { + anchor: context.origin, + span: Span::new(start, end), + } + } +} + impl From<(usize, usize, Uuid)> for Tag { fn from((start, end, anchor): (usize, usize, Uuid)) -> Self { Tag { anchor, - span: Span { start, end }, + span: Span::new(start, end), } } } @@ -201,24 +205,17 @@ impl From<(usize, usize, Uuid)> for Tag { impl From<(usize, usize, Option)> for Tag { fn from((start, end, anchor): (usize, usize, Option)) -> Self { Tag { - anchor: if let Some(uuid) = anchor { - uuid - } else { - uuid::Uuid::nil() - }, - span: Span { start, end }, + anchor: anchor.unwrap_or(uuid::Uuid::nil()), + span: Span::new(start, end), } } } -impl From> for Tag { - fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Tag { +impl From> for Tag { + fn from(input: nom_locate::LocatedSpanEx<&str, TracableContext>) -> Tag { Tag { - anchor: input.extra, - span: Span { - start: input.offset, - end: input.offset + input.fragment.len(), - }, + anchor: input.extra.origin, + span: Span::new(input.offset, input.offset + input.fragment.len()), } } } @@ -265,10 +262,7 @@ impl Tag { ); Tag { - span: Span { - start: self.span.start, - end: other.span.end, - }, + span: Span::new(self.span.start, other.span.end), anchor: self.anchor, } } @@ -276,18 +270,46 @@ impl Tag { pub fn slice<'a>(&self, source: &'a str) -> &'a str { self.span.slice(source) } + + pub fn string<'a>(&self, source: &'a str) -> String { + self.span.slice(source).to_string() + } + + pub fn tagged_slice<'a>(&self, source: &'a str) -> Tagged<&'a str> { + self.span.slice(source).tagged(self) + } + + pub fn tagged_string<'a>(&self, source: &'a str) -> Tagged { + self.span.slice(source).to_string().tagged(self) + } +} + +pub fn tag_for_tagged_list(mut iter: impl Iterator) -> Tag { + let first = iter.next(); + + let first = match first { + None => return Tag::unknown(), + Some(first) => first, + }; + + let last = iter.last(); + + match last { + None => first, + Some(last) => first.until(last), + } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)] pub struct Span { - pub(crate) start: usize, - pub(crate) end: usize, + start: usize, + end: usize, } impl From> for Span { fn from(input: Option) -> Span { match input { - None => Span { start: 0, end: 0 }, + None => Span::new(0, 0), Some(span) => span, } } @@ -295,7 +317,18 @@ impl From> for Span { impl Span { pub fn unknown() -> Span { - Span { start: 0, end: 0 } + Span::new(0, 0) + } + + pub fn new(start: usize, end: usize) -> Span { + assert!( + end >= start, + "Can't create a Span whose end < start, start={}, end={}", + start, + end + ); + + Span { start, end } } /* @@ -308,6 +341,14 @@ impl Span { } */ + pub fn start(&self) -> usize { + self.start + } + + pub fn end(&self) -> usize { + self.end + } + pub fn is_unknown(&self) -> bool { self.start == 0 && self.end == 0 } @@ -319,17 +360,11 @@ impl Span { impl language_reporting::ReportingSpan for Span { fn with_start(&self, start: usize) -> Self { - Span { - start, - end: self.end, - } + Span::new(start, self.end) } fn with_end(&self, end: usize) -> Self { - Span { - start: self.start, - end, - } + Span::new(self.start, end) } fn start(&self) -> usize { @@ -344,20 +379,14 @@ impl language_reporting::ReportingSpan for Span { impl language_reporting::ReportingSpan for Tag { fn with_start(&self, start: usize) -> Self { Tag { - span: Span { - start, - end: self.span.end, - }, + span: Span::new(start, self.span.end), anchor: self.anchor, } } fn with_end(&self, end: usize) -> Self { Tag { - span: Span { - start: self.span.start, - end, - }, + span: Span::new(self.span.start, end), anchor: self.anchor, } } diff --git a/src/errors.rs b/src/errors.rs index 7e9c14b239..a070f6f54e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,5 +1,6 @@ use crate::prelude::*; +use crate::parser::parse::parser::TracableContext; use ansi_term::Color; use derive_new::new; use language_reporting::{Diagnostic, Label, Severity}; @@ -62,6 +63,14 @@ impl ShellError { .start() } + pub(crate) fn unexpected_eof(expected: impl Into, tag: Tag) -> ShellError { + ProximateShellError::UnexpectedEof { + expected: expected.into(), + tag, + } + .start() + } + pub(crate) fn range_error( expected: impl Into, actual: &Tagged, @@ -82,6 +91,7 @@ impl ShellError { .start() } + #[allow(unused)] pub(crate) fn invalid_command(problem: impl Into) -> ShellError { ProximateShellError::InvalidCommand { command: problem.into(), @@ -133,7 +143,7 @@ impl ShellError { pub(crate) fn parse_error( error: nom::Err<( - nom_locate::LocatedSpanEx<&str, uuid::Uuid>, + nom_locate::LocatedSpanEx<&str, TracableContext>, nom::error::ErrorKind, )>, ) -> ShellError { @@ -235,7 +245,6 @@ impl ShellError { Label::new_primary(tag) .with_message(format!("Expected {}, found {}", expected, actual)), ), - ProximateShellError::TypeError { expected, actual: @@ -246,6 +255,11 @@ impl ShellError { } => Diagnostic::new(Severity::Error, "Type Error") .with_label(Label::new_primary(tag).with_message(expected)), + ProximateShellError::UnexpectedEof { + expected, tag + } => Diagnostic::new(Severity::Error, format!("Unexpected end of input")) + .with_label(Label::new_primary(tag).with_message(format!("Expected {}", expected))), + ProximateShellError::RangeError { kind, operation, @@ -267,10 +281,10 @@ impl ShellError { problem: Tagged { tag, - .. + item }, } => Diagnostic::new(Severity::Error, "Syntax Error") - .with_label(Label::new_primary(tag).with_message("Unexpected external command")), + .with_label(Label::new_primary(tag).with_message(item)), ProximateShellError::MissingProperty { subpath, expr } => { let subpath = subpath.into_label(); @@ -340,6 +354,10 @@ impl ShellError { pub(crate) fn unexpected(title: impl Into) -> ShellError { ShellError::string(&format!("Unexpected: {}", title.into())) } + + pub(crate) fn unreachable(title: impl Into) -> ShellError { + ShellError::string(&format!("BUG: Unreachable: {}", title.into())) + } } #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] @@ -387,6 +405,10 @@ pub enum ProximateShellError { SyntaxError { problem: Tagged, }, + UnexpectedEof { + expected: String, + tag: Tag, + }, InvalidCommand { command: Tag, }, @@ -473,6 +495,7 @@ impl std::fmt::Display for ShellError { ProximateShellError::MissingValue { .. } => write!(f, "MissingValue"), ProximateShellError::InvalidCommand { .. } => write!(f, "InvalidCommand"), ProximateShellError::TypeError { .. } => write!(f, "TypeError"), + ProximateShellError::UnexpectedEof { .. } => write!(f, "UnexpectedEof"), ProximateShellError::RangeError { .. } => write!(f, "RangeError"), ProximateShellError::SyntaxError { .. } => write!(f, "SyntaxError"), ProximateShellError::MissingProperty { .. } => write!(f, "MissingProperty"), diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index a111d3964d..248d2a0816 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -7,6 +7,8 @@ use crate::parser::{ use crate::prelude::*; use derive_new::new; use indexmap::IndexMap; +use log::trace; +use std::fmt; #[derive(new)] pub struct Scope { @@ -15,6 +17,15 @@ pub struct Scope { vars: IndexMap>, } +impl fmt::Display for Scope { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_map() + .entry(&"$it", &format!("{:?}", self.it.item)) + .entries(self.vars.iter().map(|(k, v)| (k, &v.item))) + .finish() + } +} + impl Scope { pub(crate) fn empty() -> Scope { Scope { @@ -48,12 +59,15 @@ pub(crate) fn evaluate_baseline_expr( RawExpression::Synthetic(hir::Synthetic::String(s)) => { Ok(Value::string(s).tagged_unknown()) } - RawExpression::Variable(var) => evaluate_reference(var, scope, source), + RawExpression::Variable(var) => evaluate_reference(var, scope, source, expr.tag()), + RawExpression::Command(_) => evaluate_command(expr.tag(), scope, source), RawExpression::ExternalCommand(external) => evaluate_external(external, scope, source), RawExpression::Binary(binary) => { let left = evaluate_baseline_expr(binary.left(), registry, scope, source)?; let right = evaluate_baseline_expr(binary.right(), registry, scope, source)?; + trace!("left={:?} right={:?}", left.item, right.item); + match left.compare(binary.op(), &*right) { Ok(result) => Ok(Value::boolean(result).tagged(expr.tag())), Err((left_type, right_type)) => Err(ShellError::coerce_error( @@ -130,14 +144,16 @@ fn evaluate_reference( name: &hir::Variable, scope: &Scope, source: &Text, + tag: Tag, ) -> Result, ShellError> { + trace!("Evaluating {} with Scope {}", name, scope); match name { - hir::Variable::It(tag) => Ok(scope.it.item.clone().tagged(*tag)), - hir::Variable::Other(tag) => Ok(scope + hir::Variable::It(_) => Ok(scope.it.item.clone().tagged(tag)), + hir::Variable::Other(inner) => Ok(scope .vars - .get(tag.slice(source)) + .get(inner.slice(source)) .map(|v| v.clone()) - .unwrap_or_else(|| Value::nothing().tagged(*tag))), + .unwrap_or_else(|| Value::nothing().tagged(tag))), } } @@ -150,3 +166,7 @@ fn evaluate_external( "Unexpected external command".tagged(*external.name()), )) } + +fn evaluate_command(tag: Tag, _scope: &Scope, _source: &Text) -> Result, ShellError> { + Err(ShellError::syntax_error("Unexpected command".tagged(tag))) +} diff --git a/src/lib.rs b/src/lib.rs index e8e09aacdd..b955f426e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,7 +31,7 @@ pub use cli::cli; pub use data::base::{Primitive, Value}; pub use data::config::{config_path, APP_INFO}; pub use data::dict::{Dictionary, TaggedDictBuilder}; -pub use data::meta::{Tag, Tagged, TaggedItem}; +pub use data::meta::{Span, Tag, Tagged, TaggedItem}; pub use errors::{CoerceInto, ShellError}; pub use num_traits::cast::ToPrimitive; pub use parser::parse::text::Text; diff --git a/src/parser.rs b/src/parser.rs index 138125769b..5fcfaaa27e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -7,7 +7,7 @@ pub(crate) mod registry; use crate::errors::ShellError; pub(crate) use deserializer::ConfigDeserializer; -pub(crate) use hir::baseline_parse_tokens::baseline_parse_tokens; +pub(crate) use hir::TokensIterator; pub(crate) use parse::call_node::CallNode; pub(crate) use parse::files::Files; pub(crate) use parse::flag::Flag; @@ -15,10 +15,10 @@ pub(crate) use parse::operator::Operator; pub(crate) use parse::parser::{nom_input, pipeline}; pub(crate) use parse::pipeline::{Pipeline, PipelineElement}; pub(crate) use parse::text::Text; -pub(crate) use parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode}; +pub(crate) use parse::token_tree::{DelimitedNode, Delimiter, TokenNode}; pub(crate) use parse::tokens::{RawToken, Token}; pub(crate) use parse::unit::Unit; -pub(crate) use parse_command::parse_command; +pub(crate) use parse_command::parse_command_tail; pub(crate) use registry::CommandRegistry; pub fn parse(input: &str, anchor: uuid::Uuid) -> Result { diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs index f9b9146e50..43409fc4df 100644 --- a/src/parser/deserializer.rs +++ b/src/parser/deserializer.rs @@ -310,9 +310,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { return Ok(r); } trace!( - "deserializing struct {:?} {:?} (stack={:?})", + "deserializing struct {:?} {:?} (saw_root={} stack={:?})", name, fields, + self.saw_root, self.stack ); @@ -326,6 +327,12 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut ConfigDeserializer<'de> { let type_name = std::any::type_name::(); let tagged_val_name = std::any::type_name::>(); + trace!( + "type_name={} tagged_val_name={}", + type_name, + tagged_val_name + ); + if type_name == tagged_val_name { return visit::, _>(value.val, name, fields, visitor); } diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 96eb7272a6..4fd0a71b3d 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -1,11 +1,13 @@ pub(crate) mod baseline_parse; -pub(crate) mod baseline_parse_tokens; pub(crate) mod binary; +pub(crate) mod expand_external_tokens; pub(crate) mod external_command; pub(crate) mod named; pub(crate) mod path; +pub(crate) mod syntax_shape; +pub(crate) mod tokens_iterator; -use crate::parser::{registry, Unit}; +use crate::parser::{registry, Operator, Unit}; use crate::prelude::*; use derive_new::new; use getset::Getters; @@ -14,27 +16,18 @@ use std::fmt; use std::path::PathBuf; use crate::evaluate::Scope; +use crate::parser::parse::tokens::RawNumber; +use crate::traits::ToDebug; -pub(crate) use self::baseline_parse::{ - baseline_parse_single_token, baseline_parse_token_as_number, baseline_parse_token_as_path, - baseline_parse_token_as_pattern, baseline_parse_token_as_string, -}; -pub(crate) use self::baseline_parse_tokens::{baseline_parse_next_expr, TokensIterator}; pub(crate) use self::binary::Binary; pub(crate) use self::external_command::ExternalCommand; pub(crate) use self::named::NamedArguments; pub(crate) use self::path::Path; +pub(crate) use self::syntax_shape::ExpandContext; +pub(crate) use self::tokens_iterator::debug::debug_tokens; +pub(crate) use self::tokens_iterator::TokensIterator; -pub use self::baseline_parse_tokens::SyntaxShape; - -pub fn path(head: impl Into, tail: Vec>>) -> Path { - Path::new( - head.into(), - tail.into_iter() - .map(|item| item.map(|string| string.into())) - .collect(), - ) -} +pub use self::syntax_shape::SyntaxShape; #[derive(Debug, Clone, Eq, PartialEq, Getters, Serialize, Deserialize, new)] pub struct Call { @@ -93,6 +86,7 @@ pub enum RawExpression { FilePath(PathBuf), ExternalCommand(ExternalCommand), + Command(Tag), Boolean(bool), } @@ -115,13 +109,14 @@ impl RawExpression { match self { RawExpression::Literal(literal) => literal.type_name(), RawExpression::Synthetic(synthetic) => synthetic.type_name(), - RawExpression::ExternalWord => "externalword", - RawExpression::FilePath(..) => "filepath", + RawExpression::Command(..) => "command", + RawExpression::ExternalWord => "external word", + RawExpression::FilePath(..) => "file path", RawExpression::Variable(..) => "variable", RawExpression::List(..) => "list", RawExpression::Binary(..) => "binary", RawExpression::Block(..) => "block", - RawExpression::Path(..) => "path", + RawExpression::Path(..) => "variable path", RawExpression::Boolean(..) => "boolean", RawExpression::ExternalCommand(..) => "external", } @@ -130,6 +125,39 @@ impl RawExpression { pub type Expression = Tagged; +impl std::fmt::Display for Expression { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let span = self.tag.span; + + match &self.item { + RawExpression::Literal(literal) => write!(f, "{}", literal.tagged(self.tag)), + RawExpression::Synthetic(Synthetic::String(s)) => write!(f, "{}", s), + RawExpression::Command(_) => write!(f, "Command{{ {}..{} }}", span.start(), span.end()), + RawExpression::ExternalWord => { + write!(f, "ExternalWord{{ {}..{} }}", span.start(), span.end()) + } + RawExpression::FilePath(file) => write!(f, "Path{{ {} }}", file.display()), + RawExpression::Variable(variable) => write!(f, "{}", variable), + RawExpression::List(list) => f + .debug_list() + .entries(list.iter().map(|e| format!("{}", e))) + .finish(), + RawExpression::Binary(binary) => write!(f, "{}", binary), + RawExpression::Block(items) => { + write!(f, "Block")?; + f.debug_set() + .entries(items.iter().map(|i| format!("{}", i))) + .finish() + } + RawExpression::Path(path) => write!(f, "{}", path), + RawExpression::Boolean(b) => write!(f, "${}", b), + RawExpression::ExternalCommand(..) => { + write!(f, "ExternalComment{{ {}..{} }}", span.start(), span.end()) + } + } + } +} + impl Expression { pub(crate) fn number(i: impl Into, tag: impl Into) -> Expression { RawExpression::Literal(Literal::Number(i.into())).tagged(tag.into()) @@ -151,10 +179,50 @@ impl Expression { RawExpression::Literal(Literal::String(inner.into())).tagged(outer.into()) } + pub(crate) fn path( + head: Expression, + tail: Vec>>, + tag: impl Into, + ) -> Expression { + let tail = tail.into_iter().map(|t| t.map(|s| s.into())).collect(); + RawExpression::Path(Box::new(Path::new(head, tail))).tagged(tag.into()) + } + + pub(crate) fn dot_member(head: Expression, next: Tagged>) -> Expression { + let Tagged { item, tag } = head; + let new_tag = head.tag.until(next.tag); + + match item { + RawExpression::Path(path) => { + let (head, mut tail) = path.parts(); + + tail.push(next.map(|i| i.into())); + Expression::path(head, tail, new_tag) + } + + other => Expression::path(other.tagged(tag), vec![next], new_tag), + } + } + + pub(crate) fn infix( + left: Expression, + op: Tagged>, + right: Expression, + ) -> Expression { + let new_tag = left.tag.until(right.tag); + + RawExpression::Binary(Box::new(Binary::new(left, op.map(|o| o.into()), right))) + .tagged(new_tag) + } + pub(crate) fn file_path(path: impl Into, outer: impl Into) -> Expression { RawExpression::FilePath(path.into()).tagged(outer) } + pub(crate) fn list(list: Vec, tag: impl Into) -> Expression { + RawExpression::List(list).tagged(tag) + } + pub(crate) fn bare(tag: impl Into) -> Expression { RawExpression::Literal(Literal::Bare).tagged(tag) } @@ -182,6 +250,7 @@ impl ToDebug for Expression { RawExpression::Literal(l) => l.tagged(self.tag()).fmt_debug(f, source), RawExpression::FilePath(p) => write!(f, "{}", p.display()), RawExpression::ExternalWord => write!(f, "{}", self.tag().slice(source)), + RawExpression::Command(tag) => write!(f, "{}", tag.slice(source)), 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)), @@ -232,6 +301,26 @@ pub enum Literal { Bare, } +impl std::fmt::Display for Tagged { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", Tagged::new(self.tag, &self.item)) + } +} + +impl std::fmt::Display for Tagged<&Literal> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let span = self.tag.span; + + match &self.item { + Literal::Number(number) => write!(f, "{}", number), + Literal::Size(number, unit) => write!(f, "{}{}", number, unit.as_str()), + Literal::String(_) => write!(f, "String{{ {}..{} }}", span.start(), span.end()), + Literal::GlobPattern => write!(f, "Glob{{ {}..{} }}", span.start(), span.end()), + Literal::Bare => write!(f, "Bare{{ {}..{} }}", span.start(), span.end()), + } + } +} + impl ToDebug for Tagged<&Literal> { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { match self.item() { @@ -261,3 +350,12 @@ pub enum Variable { It(Tag), Other(Tag), } + +impl std::fmt::Display for Variable { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Variable::It(_) => write!(f, "$it"), + Variable::Other(tag) => write!(f, "${{ {}..{} }}", tag.span.start(), tag.span.end()), + } + } +} diff --git a/src/parser/hir/baseline_parse.rs b/src/parser/hir/baseline_parse.rs index 267494f27c..87c2771955 100644 --- a/src/parser/hir/baseline_parse.rs +++ b/src/parser/hir/baseline_parse.rs @@ -1,140 +1,2 @@ -use crate::context::Context; -use crate::errors::ShellError; -use crate::parser::{hir, RawToken, Token}; -use crate::TaggedItem; -use crate::Text; -use std::path::PathBuf; - -pub fn baseline_parse_single_token( - token: &Token, - source: &Text, -) -> Result { - Ok(match *token.item() { - RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()), - RawToken::Size(int, unit) => { - hir::Expression::size(int.to_number(source), unit, token.tag()) - } - RawToken::String(tag) => hir::Expression::string(tag, token.tag()), - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) - } - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::GlobPattern => hir::Expression::pattern(token.tag()), - RawToken::Bare => hir::Expression::bare(token.tag()), - }) -} - -pub fn baseline_parse_token_as_number( - token: &Token, - source: &Text, -) -> Result { - Ok(match *token.item() { - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) - } - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()), - RawToken::Size(number, unit) => { - hir::Expression::size(number.to_number(source), unit, token.tag()) - } - RawToken::Bare => hir::Expression::bare(token.tag()), - RawToken::GlobPattern => { - return Err(ShellError::type_error( - "Number", - "glob pattern".to_string().tagged(token.tag()), - )) - } - RawToken::String(tag) => hir::Expression::string(tag, token.tag()), - }) -} - -pub fn baseline_parse_token_as_string( - token: &Token, - source: &Text, -) -> Result { - Ok(match *token.item() { - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) - } - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::Number(_) => hir::Expression::bare(token.tag()), - RawToken::Size(_, _) => hir::Expression::bare(token.tag()), - RawToken::Bare => hir::Expression::bare(token.tag()), - RawToken::GlobPattern => { - return Err(ShellError::type_error( - "String", - "glob pattern".tagged(token.tag()), - )) - } - RawToken::String(tag) => hir::Expression::string(tag, token.tag()), - }) -} - -pub fn baseline_parse_token_as_path( - token: &Token, - context: &Context, - source: &Text, -) -> Result { - Ok(match *token.item() { - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) - } - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::Number(_) => hir::Expression::bare(token.tag()), - RawToken::Size(_, _) => hir::Expression::bare(token.tag()), - RawToken::Bare => { - hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag()) - } - RawToken::GlobPattern => { - return Err(ShellError::type_error( - "Path", - "glob pattern".tagged(token.tag()), - )) - } - RawToken::String(tag) => { - hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag()) - } - }) -} - -pub fn baseline_parse_token_as_pattern( - token: &Token, - context: &Context, - source: &Text, -) -> Result { - Ok(match *token.item() { - RawToken::Variable(tag) if tag.slice(source) == "it" => { - hir::Expression::it_variable(tag, token.tag()) - } - RawToken::ExternalCommand(_) => { - return Err(ShellError::syntax_error( - "Invalid external command".to_string().tagged(token.tag()), - )) - } - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())), - RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()), - RawToken::Number(_) => hir::Expression::bare(token.tag()), - RawToken::Size(_, _) => hir::Expression::bare(token.tag()), - RawToken::GlobPattern => hir::Expression::pattern(token.tag()), - RawToken::Bare => { - hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag()) - } - RawToken::String(tag) => { - hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag()) - } - }) -} - -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()) -} +#[cfg(test)] +mod tests; diff --git a/src/parser/hir/baseline_parse/tests.rs b/src/parser/hir/baseline_parse/tests.rs new file mode 100644 index 0000000000..badb177513 --- /dev/null +++ b/src/parser/hir/baseline_parse/tests.rs @@ -0,0 +1,144 @@ +use crate::commands::classified::InternalCommand; +use crate::commands::ClassifiedCommand; +use crate::env::host::BasicHost; +use crate::parser::hir; +use crate::parser::hir::syntax_shape::*; +use crate::parser::hir::TokensIterator; +use crate::parser::parse::token_tree_builder::{CurriedToken, TokenTreeBuilder as b}; +use crate::parser::TokenNode; +use crate::{Span, Tag, Tagged, TaggedItem, Text}; +use pretty_assertions::assert_eq; +use std::fmt::Debug; +use uuid::Uuid; + +#[test] +fn test_parse_string() { + parse_tokens(StringShape, vec![b::string("hello")], |tokens| { + hir::Expression::string(inner_string_tag(tokens[0].tag()), tokens[0].tag()) + }); +} + +#[test] +fn test_parse_path() { + parse_tokens( + VariablePathShape, + vec![b::var("it"), b::op("."), b::bare("cpu")], + |tokens| { + let (outer_var, inner_var) = tokens[0].expect_var(); + let bare = tokens[2].expect_bare(); + hir::Expression::path( + hir::Expression::it_variable(inner_var, outer_var), + vec!["cpu".tagged(bare)], + outer_var.until(bare), + ) + }, + ); + + parse_tokens( + VariablePathShape, + vec![ + b::var("cpu"), + b::op("."), + b::bare("amount"), + b::op("."), + b::string("max ghz"), + ], + |tokens| { + let (outer_var, inner_var) = tokens[0].expect_var(); + let amount = tokens[2].expect_bare(); + let (outer_max_ghz, _) = tokens[4].expect_string(); + + hir::Expression::path( + hir::Expression::variable(inner_var, outer_var), + vec!["amount".tagged(amount), "max ghz".tagged(outer_max_ghz)], + outer_var.until(outer_max_ghz), + ) + }, + ); +} + +#[test] +fn test_parse_command() { + parse_tokens( + ClassifiedCommandShape, + vec![b::bare("ls"), b::sp(), b::pattern("*.txt")], + |tokens| { + let bare = tokens[0].expect_bare(); + let pat = tokens[2].tag(); + + ClassifiedCommand::Internal(InternalCommand::new( + "ls".to_string(), + bare, + hir::Call { + head: Box::new(hir::RawExpression::Command(bare).tagged(bare)), + positional: Some(vec![hir::Expression::pattern(pat)]), + named: None, + }, + )) + // hir::Expression::path( + // hir::Expression::variable(inner_var, outer_var), + // vec!["cpu".tagged(bare)], + // outer_var.until(bare), + // ) + }, + ); + + parse_tokens( + VariablePathShape, + vec![ + b::var("cpu"), + b::op("."), + b::bare("amount"), + b::op("."), + b::string("max ghz"), + ], + |tokens| { + let (outer_var, inner_var) = tokens[0].expect_var(); + let amount = tokens[2].expect_bare(); + let (outer_max_ghz, _) = tokens[4].expect_string(); + + hir::Expression::path( + hir::Expression::variable(inner_var, outer_var), + vec!["amount".tagged(amount), "max ghz".tagged(outer_max_ghz)], + outer_var.until(outer_max_ghz), + ) + }, + ); +} + +fn parse_tokens( + shape: impl ExpandSyntax, + tokens: Vec, + expected: impl FnOnce(Tagged<&[TokenNode]>) -> T, +) { + let tokens = b::token_list(tokens); + let (tokens, source) = b::build(test_origin(), tokens); + + ExpandContext::with_empty(&Text::from(source), |context| { + let tokens = tokens.expect_list(); + let mut iterator = TokensIterator::all(tokens.item, *context.tag()); + + let expr = expand_syntax(&shape, &mut iterator, &context); + + let expr = match expr { + Ok(expr) => expr, + Err(err) => { + crate::cli::print_err(err, &BasicHost, context.source().clone()); + panic!("Parse failed"); + } + }; + + assert_eq!(expr, expected(tokens)); + }) +} + +fn test_origin() -> Uuid { + Uuid::nil() +} + +fn inner_string_tag(tag: Tag) -> Tag { + Tag { + span: Span::new(tag.span.start() + 1, tag.span.end() - 1), + anchor: tag.anchor, + } +} diff --git a/src/parser/hir/baseline_parse_tokens.rs b/src/parser/hir/baseline_parse_tokens.rs deleted file mode 100644 index 8413bd07e1..0000000000 --- a/src/parser/hir/baseline_parse_tokens.rs +++ /dev/null @@ -1,459 +0,0 @@ -use crate::context::Context; -use crate::errors::ShellError; -use crate::parser::{ - hir, - hir::{ - baseline_parse_single_token, baseline_parse_token_as_number, baseline_parse_token_as_path, - baseline_parse_token_as_pattern, baseline_parse_token_as_string, - }, - DelimitedNode, Delimiter, PathNode, RawToken, TokenNode, -}; -use crate::{Tag, Tagged, TaggedItem, Text}; -use derive_new::new; -use log::trace; -use serde::{Deserialize, Serialize}; - -pub fn baseline_parse_tokens( - token_nodes: &mut TokensIterator<'_>, - context: &Context, - source: &Text, - syntax_type: SyntaxShape, -) -> Result, ShellError> { - let mut exprs: Vec = vec![]; - - loop { - if token_nodes.at_end() { - break; - } - - let expr = baseline_parse_next_expr(token_nodes, context, source, syntax_type)?; - exprs.push(expr); - } - - Ok(exprs) -} - -#[derive(Debug, Copy, Clone, Serialize, Deserialize)] -pub enum SyntaxShape { - Any, - List, - Literal, - String, - Member, - Variable, - Number, - Path, - Pattern, - Binary, - Block, - Boolean, -} - -impl std::fmt::Display for SyntaxShape { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - match self { - SyntaxShape::Any => write!(f, "Any"), - SyntaxShape::List => write!(f, "List"), - SyntaxShape::Literal => write!(f, "Literal"), - SyntaxShape::String => write!(f, "String"), - SyntaxShape::Member => write!(f, "Member"), - SyntaxShape::Variable => write!(f, "Variable"), - SyntaxShape::Number => write!(f, "Number"), - SyntaxShape::Path => write!(f, "Path"), - SyntaxShape::Pattern => write!(f, "Pattern"), - SyntaxShape::Binary => write!(f, "Binary"), - SyntaxShape::Block => write!(f, "Block"), - SyntaxShape::Boolean => write!(f, "Boolean"), - } - } -} - -pub fn baseline_parse_next_expr( - tokens: &mut TokensIterator, - context: &Context, - source: &Text, - syntax_type: SyntaxShape, -) -> Result { - let next = tokens - .next() - .ok_or_else(|| ShellError::string("Expected token, found none"))?; - - trace!(target: "nu::parser::parse_one_expr", "syntax_type={:?}, token={:?}", syntax_type, next); - - match (syntax_type, next) { - (SyntaxShape::Path, TokenNode::Token(token)) => { - return baseline_parse_token_as_path(token, context, source) - } - - (SyntaxShape::Path, token) => { - return Err(ShellError::type_error( - "Path", - token.type_name().tagged(token.tag()), - )) - } - - (SyntaxShape::Pattern, TokenNode::Token(token)) => { - return baseline_parse_token_as_pattern(token, context, source) - } - - (SyntaxShape::Pattern, token) => { - return Err(ShellError::type_error( - "Path", - token.type_name().tagged(token.tag()), - )) - } - - (SyntaxShape::String, TokenNode::Token(token)) => { - return baseline_parse_token_as_string(token, source); - } - - (SyntaxShape::String, token) => { - return Err(ShellError::type_error( - "String", - token.type_name().tagged(token.tag()), - )) - } - - (SyntaxShape::Number, TokenNode::Token(token)) => { - return Ok(baseline_parse_token_as_number(token, source)?); - } - - (SyntaxShape::Number, token) => { - return Err(ShellError::type_error( - "Numeric", - token.type_name().tagged(token.tag()), - )) - } - - // TODO: More legit member processing - (SyntaxShape::Member, TokenNode::Token(token)) => { - return baseline_parse_token_as_string(token, source); - } - - (SyntaxShape::Member, token) => { - return Err(ShellError::type_error( - "member", - token.type_name().tagged(token.tag()), - )) - } - - (SyntaxShape::Any, _) => {} - (SyntaxShape::List, _) => {} - (SyntaxShape::Literal, _) => {} - (SyntaxShape::Variable, _) => {} - (SyntaxShape::Binary, _) => {} - (SyntaxShape::Block, _) => {} - (SyntaxShape::Boolean, _) => {} - }; - - let first = baseline_parse_semantic_token(next, context, source)?; - - let possible_op = tokens.peek(); - - let op = match possible_op { - Some(TokenNode::Operator(op)) => op.clone(), - _ => return Ok(first), - }; - - tokens.next(); - - let second = match tokens.next() { - None => { - return Err(ShellError::labeled_error( - "Expected something after an operator", - "operator", - op.tag(), - )) - } - 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 - - match syntax_type { - SyntaxShape::Any => { - let tag = first.tag().until(second.tag()); - let binary = hir::Binary::new(first, op, second); - let binary = hir::RawExpression::Binary(Box::new(binary)); - let binary = binary.tagged(tag); - - Ok(binary) - } - - SyntaxShape::Block => { - let tag = first.tag().until(second.tag()); - - let path: Tagged = match first { - Tagged { - item: hir::RawExpression::Literal(hir::Literal::Bare), - tag, - } => { - let string = tag.slice(source).to_string().tagged(tag); - let path = hir::Path::new( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Tag::unknown())) - .tagged(Tag::unknown()), - vec![string], - ); - let path = hir::RawExpression::Path(Box::new(path)); - path.tagged(first.tag()) - } - Tagged { - item: hir::RawExpression::Literal(hir::Literal::String(inner)), - tag, - } => { - let string = inner.slice(source).to_string().tagged(tag); - let path = hir::Path::new( - // TODO: Deal with synthetic nodes that have no representation at all in source - hir::RawExpression::Variable(hir::Variable::It(Tag::unknown())) - .tagged_unknown(), - vec![string], - ); - let path = hir::RawExpression::Path(Box::new(path)); - path.tagged(first.tag()) - } - Tagged { - item: hir::RawExpression::Variable(..), - .. - } => first, - Tagged { tag, item } => { - return Err(ShellError::labeled_error( - "The first part of an un-braced block must be a column name", - item.type_name(), - tag, - )) - } - }; - - let binary = hir::Binary::new(path, op, second); - let binary = hir::RawExpression::Binary(Box::new(binary)); - let binary = binary.tagged(tag); - - let block = hir::RawExpression::Block(vec![binary]); - let block = block.tagged(tag); - - Ok(block) - } - - other => Err(ShellError::unimplemented(format!( - "coerce hint {:?}", - other - ))), - } -} - -pub fn baseline_parse_semantic_token( - token: &TokenNode, - context: &Context, - source: &Text, -) -> Result { - match token { - TokenNode::Token(token) => baseline_parse_single_token(token, source), - TokenNode::Call(_call) => unimplemented!(), - TokenNode::Delimited(delimited) => baseline_parse_delimited(delimited, context, source), - TokenNode::Pipeline(_pipeline) => unimplemented!(), - TokenNode::Operator(op) => Err(ShellError::syntax_error( - "Unexpected operator".tagged(op.tag), - )), - TokenNode::Flag(flag) => Err(ShellError::syntax_error("Unexpected flag".tagged(flag.tag))), - TokenNode::Member(tag) => Err(ShellError::syntax_error( - "BUG: Top-level member".tagged(*tag), - )), - TokenNode::Whitespace(tag) => Err(ShellError::syntax_error( - "BUG: Whitespace found during parse".tagged(*tag), - )), - TokenNode::Error(error) => Err(*error.item.clone()), - TokenNode::Path(path) => baseline_parse_path(path, context, source), - } -} - -pub fn baseline_parse_delimited( - token: &Tagged, - context: &Context, - source: &Text, -) -> Result { - match token.delimiter() { - Delimiter::Brace => { - let children = token.children(); - let exprs = baseline_parse_tokens( - &mut TokensIterator::new(children), - context, - source, - SyntaxShape::Any, - )?; - - let expr = hir::RawExpression::Block(exprs); - Ok(expr.tagged(token.tag())) - } - Delimiter::Paren => unimplemented!(), - Delimiter::Square => { - let children = token.children(); - let exprs = baseline_parse_tokens( - &mut TokensIterator::new(children), - context, - source, - SyntaxShape::Any, - )?; - - let expr = hir::RawExpression::List(exprs); - Ok(expr.tagged(token.tag())) - } - } -} - -pub fn baseline_parse_path( - token: &Tagged, - context: &Context, - source: &Text, -) -> Result { - let head = baseline_parse_semantic_token(token.head(), context, source)?; - - let mut tail = vec![]; - - for part in token.tail() { - let string = match part { - TokenNode::Token(token) => match token.item() { - RawToken::Bare => token.tag().slice(source), - RawToken::String(tag) => tag.slice(source), - RawToken::Number(_) - | RawToken::Size(..) - | RawToken::Variable(_) - | RawToken::ExternalCommand(_) - | RawToken::GlobPattern - | RawToken::ExternalWord => { - return Err(ShellError::type_error( - "String", - token.type_name().tagged(part.tag()), - )) - } - }, - - TokenNode::Member(tag) => tag.slice(source), - - // TODO: Make this impossible - other => { - return Err(ShellError::syntax_error( - format!("{} in path", other.type_name()).tagged(other.tag()), - )) - } - } - .to_string(); - - tail.push(string.tagged(part.tag())); - } - - Ok(hir::path(head, tail).tagged(token.tag()).into()) -} - -#[derive(Debug, new)] -pub struct TokensIterator<'a> { - tokens: &'a [TokenNode], - #[new(default)] - index: usize, - #[new(default)] - seen: indexmap::IndexSet, -} - -impl TokensIterator<'_> { - pub fn remove(&mut self, position: usize) { - self.seen.insert(position); - } - - pub fn len(&self) -> usize { - self.tokens.len() - } - - pub fn at_end(&self) -> bool { - for index in self.index..self.tokens.len() { - if !self.seen.contains(&index) { - return false; - } - } - - true - } - - pub fn advance(&mut self) { - self.seen.insert(self.index); - self.index += 1; - } - - pub fn extract(&mut self, f: impl Fn(&TokenNode) -> Option) -> Option<(usize, T)> { - for (i, item) in self.tokens.iter().enumerate() { - if self.seen.contains(&i) { - continue; - } - - match f(item) { - None => { - continue; - } - Some(value) => { - self.seen.insert(i); - return Some((i, value)); - } - } - } - - None - } - - pub fn move_to(&mut self, pos: usize) { - self.index = pos; - } - - pub fn restart(&mut self) { - self.index = 0; - } - - pub fn clone(&self) -> TokensIterator { - TokensIterator { - tokens: self.tokens, - index: self.index, - seen: self.seen.clone(), - } - } - - pub fn peek(&self) -> Option<&TokenNode> { - let mut tokens = self.clone(); - - tokens.next() - } - - pub fn debug_remaining(&self) -> Vec { - let mut tokens = self.clone(); - tokens.restart(); - tokens.cloned().collect() - } -} - -impl<'a> Iterator for TokensIterator<'a> { - type Item = &'a TokenNode; - - fn next(&mut self) -> Option<&'a TokenNode> { - loop { - if self.index >= self.tokens.len() { - return None; - } - - if self.seen.contains(&self.index) { - self.advance(); - continue; - } - - if self.index >= self.tokens.len() { - return None; - } - - match &self.tokens[self.index] { - TokenNode::Whitespace(_) => { - self.advance(); - } - other => { - self.advance(); - return Some(other); - } - } - } - } -} diff --git a/src/parser/hir/binary.rs b/src/parser/hir/binary.rs index 02a4d416e4..a44c41d63a 100644 --- a/src/parser/hir/binary.rs +++ b/src/parser/hir/binary.rs @@ -16,6 +16,12 @@ pub struct Binary { right: Expression, } +impl fmt::Display for Binary { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "({} {} {})", self.op.as_str(), self.left, self.right) + } +} + impl ToDebug for Binary { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { write!(f, "{}", self.left.debug(source))?; diff --git a/src/parser/hir/expand_external_tokens.rs b/src/parser/hir/expand_external_tokens.rs new file mode 100644 index 0000000000..30a2a90aaf --- /dev/null +++ b/src/parser/hir/expand_external_tokens.rs @@ -0,0 +1,87 @@ +use crate::errors::ShellError; +use crate::parser::{TokenNode, TokensIterator}; +use crate::{Tag, Tagged, Text}; + +pub fn expand_external_tokens( + token_nodes: &mut TokensIterator<'_>, + source: &Text, +) -> Result>, ShellError> { + let mut out: Vec> = vec![]; + + loop { + if let Some(tag) = expand_next_expression(token_nodes)? { + out.push(tag.tagged_string(source)); + } else { + break; + } + } + + Ok(out) +} + +pub fn expand_next_expression( + token_nodes: &mut TokensIterator<'_>, +) -> Result, ShellError> { + let first = token_nodes.next_non_ws(); + + let first = match first { + None => return Ok(None), + Some(v) => v, + }; + + let first = triage_external_head(first)?; + let mut last = first; + + loop { + let continuation = triage_continuation(token_nodes)?; + + if let Some(continuation) = continuation { + last = continuation; + } else { + break; + } + } + + Ok(Some(first.until(last))) +} + +fn triage_external_head(node: &TokenNode) -> Result { + Ok(match node { + TokenNode::Token(token) => token.tag(), + TokenNode::Call(_call) => unimplemented!(), + TokenNode::Nodes(_nodes) => unimplemented!(), + TokenNode::Delimited(_delimited) => unimplemented!(), + TokenNode::Pipeline(_pipeline) => unimplemented!(), + TokenNode::Flag(flag) => flag.tag(), + TokenNode::Member(member) => *member, + TokenNode::Whitespace(_whitespace) => { + unreachable!("This function should be called after next_non_ws()") + } + TokenNode::Error(_error) => unimplemented!(), + }) +} + +fn triage_continuation<'a, 'b>( + nodes: &'a mut TokensIterator<'b>, +) -> Result, ShellError> { + let mut peeked = nodes.peek_any(); + + let node = match peeked.node { + None => return Ok(None), + Some(node) => node, + }; + + match &node { + node if node.is_whitespace() => return Ok(None), + TokenNode::Token(..) | TokenNode::Flag(..) | TokenNode::Member(..) => {} + TokenNode::Call(..) => unimplemented!("call"), + TokenNode::Nodes(..) => unimplemented!("nodes"), + TokenNode::Delimited(..) => unimplemented!("delimited"), + TokenNode::Pipeline(..) => unimplemented!("pipeline"), + TokenNode::Whitespace(..) => unimplemented!("whitespace"), + TokenNode::Error(..) => unimplemented!("error"), + } + + peeked.commit(); + Ok(Some(node.tag())) +} diff --git a/src/parser/hir/external_command.rs b/src/parser/hir/external_command.rs index 28865330d5..2dd42c1312 100644 --- a/src/parser/hir/external_command.rs +++ b/src/parser/hir/external_command.rs @@ -9,7 +9,7 @@ use std::fmt; )] #[get = "pub(crate)"] pub struct ExternalCommand { - name: Tag, + pub(crate) name: Tag, } impl ToDebug for ExternalCommand { diff --git a/src/parser/hir/path.rs b/src/parser/hir/path.rs index f43edf1762..a1925102fb 100644 --- a/src/parser/hir/path.rs +++ b/src/parser/hir/path.rs @@ -2,19 +2,49 @@ use crate::parser::hir::Expression; use crate::prelude::*; use crate::Tagged; use derive_new::new; -use getset::Getters; +use getset::{Getters, MutGetters}; use serde::{Deserialize, Serialize}; use std::fmt; #[derive( - Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Getters, Serialize, Deserialize, new, + Debug, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + Hash, + Getters, + MutGetters, + Serialize, + Deserialize, + new, )] #[get = "pub(crate)"] pub struct Path { head: Expression, + #[get_mut = "pub(crate)"] tail: Vec>, } +impl fmt::Display for Path { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.head)?; + + for entry in &self.tail { + write!(f, ".{}", entry.item)?; + } + + Ok(()) + } +} + +impl Path { + pub(crate) fn parts(self) -> (Expression, Vec>) { + (self.head, self.tail) + } +} + impl ToDebug for Path { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { write!(f, "{}", self.head.debug(source))?; diff --git a/src/parser/hir/syntax_shape.rs b/src/parser/hir/syntax_shape.rs new file mode 100644 index 0000000000..5dcbd0fb76 --- /dev/null +++ b/src/parser/hir/syntax_shape.rs @@ -0,0 +1,662 @@ +mod block; +mod expression; + +use crate::cli::external_command; +use crate::commands::{classified::InternalCommand, ClassifiedCommand, Command}; +use crate::parser::hir::syntax_shape::block::AnyBlockShape; +use crate::parser::hir::tokens_iterator::Peeked; +use crate::parser::parse_command::parse_command_tail; +use crate::parser::{ + hir, + hir::{debug_tokens, TokensIterator}, + Operator, RawToken, TokenNode, +}; +use crate::prelude::*; +use derive_new::new; +use getset::Getters; +use log::trace; +use serde::{Deserialize, Serialize}; +use std::path::{Path, PathBuf}; + +pub(crate) use self::expression::file_path::FilePathShape; +pub(crate) use self::expression::list::ExpressionListShape; +pub(crate) use self::expression::number::{IntShape, NumberShape}; +pub(crate) use self::expression::pattern::PatternShape; +pub(crate) use self::expression::string::StringShape; +pub(crate) use self::expression::unit::UnitShape; +pub(crate) use self::expression::variable_path::{ + ColumnPathShape, DotShape, ExpressionContinuation, ExpressionContinuationShape, MemberShape, + PathTailShape, VariablePathShape, +}; +pub(crate) use self::expression::{continue_expression, AnyExpressionShape}; + +#[derive(Debug, Copy, Clone, Serialize, Deserialize)] +pub enum SyntaxShape { + Any, + List, + String, + Member, + ColumnPath, + Number, + Int, + Path, + Pattern, + Binary, + Block, + Boolean, +} + +impl ExpandExpression for SyntaxShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + match self { + SyntaxShape::Any => expand_expr(&AnyExpressionShape, token_nodes, context), + SyntaxShape::List => Err(ShellError::unimplemented("SyntaxShape:List")), + SyntaxShape::Int => expand_expr(&IntShape, token_nodes, context), + SyntaxShape::String => expand_expr(&StringShape, token_nodes, context), + SyntaxShape::Member => { + let syntax = expand_syntax(&MemberShape, token_nodes, context)?; + Ok(syntax.to_expr()) + } + SyntaxShape::ColumnPath => { + let Tagged { item: members, tag } = + expand_syntax(&ColumnPathShape, token_nodes, context)?; + + Ok(hir::Expression::list( + members.into_iter().map(|s| s.to_expr()).collect(), + tag, + )) + } + SyntaxShape::Number => expand_expr(&NumberShape, token_nodes, context), + SyntaxShape::Path => expand_expr(&FilePathShape, token_nodes, context), + SyntaxShape::Pattern => expand_expr(&PatternShape, token_nodes, context), + SyntaxShape::Binary => Err(ShellError::unimplemented("SyntaxShape:Binary")), + SyntaxShape::Block => expand_expr(&AnyBlockShape, token_nodes, context), + SyntaxShape::Boolean => Err(ShellError::unimplemented("SyntaxShape:Boolean")), + } + } +} + +impl std::fmt::Display for SyntaxShape { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + SyntaxShape::Any => write!(f, "Any"), + SyntaxShape::List => write!(f, "List"), + SyntaxShape::String => write!(f, "String"), + SyntaxShape::Int => write!(f, "Integer"), + SyntaxShape::Member => write!(f, "Member"), + SyntaxShape::ColumnPath => write!(f, "ColumnPath"), + SyntaxShape::Number => write!(f, "Number"), + SyntaxShape::Path => write!(f, "Path"), + SyntaxShape::Pattern => write!(f, "Pattern"), + SyntaxShape::Binary => write!(f, "Binary"), + SyntaxShape::Block => write!(f, "Block"), + SyntaxShape::Boolean => write!(f, "Boolean"), + } + } +} + +#[derive(Getters, new)] +pub struct ExpandContext<'context> { + #[get = "pub(crate)"] + registry: &'context CommandRegistry, + #[get = "pub(crate)"] + tag: Tag, + #[get = "pub(crate)"] + source: &'context Text, + homedir: Option, +} + +impl<'context> ExpandContext<'context> { + pub(crate) fn homedir(&self) -> Option<&Path> { + self.homedir.as_ref().map(|h| h.as_path()) + } + + #[cfg(test)] + pub fn with_empty(source: &Text, callback: impl FnOnce(ExpandContext)) { + let mut registry = CommandRegistry::new(); + registry.insert( + "ls", + crate::commands::whole_stream_command(crate::commands::LS), + ); + + callback(ExpandContext { + registry: ®istry, + tag: Tag::unknown(), + source, + homedir: None, + }) + } +} + +pub trait TestSyntax: std::fmt::Debug + Copy { + fn test<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Option>; +} + +pub trait ExpandExpression: std::fmt::Debug + Copy { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result; +} + +pub(crate) trait ExpandSyntax: std::fmt::Debug + Copy { + type Output: std::fmt::Debug; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result; +} + +pub(crate) fn expand_syntax<'a, 'b, T: ExpandSyntax>( + shape: &T, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, +) -> Result { + trace!(target: "nu::expand_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + let result = shape.expand_syntax(token_nodes, context); + + match result { + Err(err) => { + trace!(target: "nu::expand_syntax", "error :: {} :: {:?}", err, debug_tokens(token_nodes, context.source)); + Err(err) + } + + Ok(result) => { + trace!(target: "nu::expand_syntax", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes, context.source)); + Ok(result) + } + } +} + +pub(crate) fn expand_expr<'a, 'b, T: ExpandExpression>( + shape: &T, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, +) -> Result { + trace!(target: "nu::expand_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + let result = shape.expand_syntax(token_nodes, context); + + match result { + Err(err) => { + trace!(target: "nu::expand_syntax", "error :: {} :: {:?}", err, debug_tokens(token_nodes, context.source)); + Err(err) + } + + Ok(result) => { + trace!(target: "nu::expand_syntax", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes, context.source)); + Ok(result) + } + } +} + +impl ExpandSyntax for T { + type Output = hir::Expression; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + ExpandExpression::expand_expr(self, token_nodes, context) + } +} + +pub trait SkipSyntax: std::fmt::Debug + Copy { + fn skip<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError>; +} + +enum BarePathState { + Initial, + Seen(Tag, Tag), + Error(ShellError), +} + +impl BarePathState { + pub fn seen(self, tag: Tag) -> BarePathState { + match self { + BarePathState::Initial => BarePathState::Seen(tag, tag), + BarePathState::Seen(start, _) => BarePathState::Seen(start, tag), + BarePathState::Error(err) => BarePathState::Error(err), + } + } + + pub fn end(self, peeked: Peeked, reason: impl Into) -> BarePathState { + match self { + BarePathState::Initial => BarePathState::Error(peeked.type_error(reason)), + BarePathState::Seen(start, end) => BarePathState::Seen(start, end), + BarePathState::Error(err) => BarePathState::Error(err), + } + } + + pub fn into_bare(self) -> Result { + match self { + BarePathState::Initial => unreachable!("into_bare in initial state"), + BarePathState::Seen(start, end) => Ok(start.until(end)), + BarePathState::Error(err) => Err(err), + } + } +} + +pub fn expand_bare<'a, 'b>( + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + predicate: impl Fn(&TokenNode) -> bool, +) -> Result { + let mut state = BarePathState::Initial; + + loop { + // Whitespace ends a word + let mut peeked = token_nodes.peek_any(); + + match peeked.node { + None => { + state = state.end(peeked, "word"); + break; + } + Some(node) => { + if predicate(node) { + state = state.seen(node.tag()); + peeked.commit(); + } else { + state = state.end(peeked, "word"); + break; + } + } + } + } + + state.into_bare() +} + +#[derive(Debug, Copy, Clone)] +pub struct BarePathShape; + +impl ExpandSyntax for BarePathShape { + type Output = Tag; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + expand_bare(token_nodes, context, |token| match token { + TokenNode::Token(Tagged { + item: RawToken::Bare, + .. + }) + | TokenNode::Token(Tagged { + item: RawToken::Operator(Operator::Dot), + .. + }) => true, + + _ => false, + }) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct BareShape; + +impl ExpandSyntax for BareShape { + type Output = Tagged; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + let peeked = token_nodes.peek_any().not_eof("word")?; + + match peeked.node { + TokenNode::Token(Tagged { + item: RawToken::Bare, + tag, + }) => { + peeked.commit(); + Ok(tag.tagged_string(context.source)) + } + + other => Err(ShellError::type_error("word", other.tagged_type_name())), + } + } +} + +impl TestSyntax for BareShape { + fn test<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Option> { + let peeked = token_nodes.peek_any(); + + match peeked.node { + Some(TokenNode::Token(token)) => match token.item { + RawToken::Bare => Some(peeked), + _ => None, + }, + + _ => None, + } + } +} + +#[derive(Debug)] +pub enum CommandSignature { + Internal(Tagged>), + LiteralExternal { outer: Tag, inner: Tag }, + External(Tag), + Expression(hir::Expression), +} + +impl CommandSignature { + pub fn to_expression(&self) -> hir::Expression { + match self { + CommandSignature::Internal(command) => { + let tag = command.tag; + hir::RawExpression::Command(tag).tagged(tag) + } + CommandSignature::LiteralExternal { outer, inner } => { + hir::RawExpression::ExternalCommand(hir::ExternalCommand::new(*inner)).tagged(outer) + } + CommandSignature::External(tag) => { + hir::RawExpression::ExternalCommand(hir::ExternalCommand::new(*tag)).tagged(tag) + } + CommandSignature::Expression(expr) => expr.clone(), + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct CommandHeadShape; + +impl ExpandSyntax for CommandHeadShape { + type Output = CommandSignature; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + let node = + parse_single_node_skipping_ws(token_nodes, "command head1", |token, token_tag| { + Ok(match token { + RawToken::ExternalCommand(tag) => CommandSignature::LiteralExternal { + outer: token_tag, + inner: tag, + }, + RawToken::Bare => { + let name = token_tag.slice(context.source); + if context.registry.has(name) { + let command = context.registry.expect_command(name); + CommandSignature::Internal(command.tagged(token_tag)) + } else { + CommandSignature::External(token_tag) + } + } + _ => { + return Err(ShellError::type_error( + "command head2", + token.type_name().tagged(token_tag), + )) + } + }) + }); + + match node { + Ok(expr) => return Ok(expr), + Err(_) => match expand_expr(&AnyExpressionShape, token_nodes, context) { + Ok(expr) => return Ok(CommandSignature::Expression(expr)), + Err(_) => Err(token_nodes.peek_non_ws().type_error("command head3")), + }, + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct ClassifiedCommandShape; + +impl ExpandSyntax for ClassifiedCommandShape { + type Output = ClassifiedCommand; + + fn expand_syntax<'a, 'b>( + &self, + iterator: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + let head = expand_syntax(&CommandHeadShape, iterator, context)?; + + match &head { + CommandSignature::Expression(expr) => Err(ShellError::syntax_error( + "Unexpected expression in command position".tagged(expr.tag), + )), + + // If the command starts with `^`, treat it as an external command no matter what + CommandSignature::External(name) => { + let name_str = name.slice(&context.source); + + external_command(iterator, &context.source, name_str.tagged(name)) + } + + CommandSignature::LiteralExternal { outer, inner } => { + let name_str = inner.slice(&context.source); + + external_command(iterator, &context.source, name_str.tagged(outer)) + } + + CommandSignature::Internal(command) => { + let tail = + parse_command_tail(&command.signature(), &context, iterator, command.tag)?; + + let (positional, named) = match tail { + None => (None, None), + Some((positional, named)) => (positional, named), + }; + + let call = hir::Call { + head: Box::new(head.to_expression()), + positional, + named, + }; + + Ok(ClassifiedCommand::Internal(InternalCommand::new( + command.item.name().to_string(), + command.tag, + call, + ))) + } + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct InternalCommandHeadShape; + +impl ExpandExpression for InternalCommandHeadShape { + fn expand_expr( + &self, + token_nodes: &mut TokensIterator<'_>, + _context: &ExpandContext, + ) -> Result { + let peeked_head = token_nodes.peek_non_ws().not_eof("command head4")?; + + let expr = match peeked_head.node { + TokenNode::Token( + spanned @ Tagged { + item: RawToken::Bare, + .. + }, + ) => spanned.map(|_| hir::RawExpression::Literal(hir::Literal::Bare)), + + TokenNode::Token(Tagged { + item: RawToken::String(inner_tag), + tag, + }) => hir::RawExpression::Literal(hir::Literal::String(*inner_tag)).tagged(*tag), + + node => { + return Err(ShellError::type_error( + "command head5", + node.tagged_type_name(), + )) + } + }; + + peeked_head.commit(); + + Ok(expr) + } +} + +fn parse_single_node<'a, 'b, T>( + token_nodes: &'b mut TokensIterator<'a>, + expected: &'static str, + callback: impl FnOnce(RawToken, Tag) -> Result, +) -> Result { + let peeked = token_nodes.peek_any().not_eof(expected)?; + + let expr = match peeked.node { + TokenNode::Token(token) => callback(token.item, token.tag())?, + + other => return Err(ShellError::type_error(expected, other.tagged_type_name())), + }; + + peeked.commit(); + + Ok(expr) +} + +fn parse_single_node_skipping_ws<'a, 'b, T>( + token_nodes: &'b mut TokensIterator<'a>, + expected: &'static str, + callback: impl FnOnce(RawToken, Tag) -> Result, +) -> Result { + let peeked = token_nodes.peek_non_ws().not_eof(expected)?; + + let expr = match peeked.node { + TokenNode::Token(token) => callback(token.item, token.tag())?, + + other => return Err(ShellError::type_error(expected, other.tagged_type_name())), + }; + + peeked.commit(); + + Ok(expr) +} + +#[derive(Debug, Copy, Clone)] +pub struct WhitespaceShape; + +impl ExpandSyntax for WhitespaceShape { + type Output = Tag; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Result { + let peeked = token_nodes.peek_any().not_eof("whitespace")?; + + let tag = match peeked.node { + TokenNode::Whitespace(tag) => *tag, + + other => { + return Err(ShellError::type_error( + "whitespace", + other.tagged_type_name(), + )) + } + }; + + peeked.commit(); + + Ok(tag) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct SpacedExpression { + inner: T, +} + +impl ExpandExpression for SpacedExpression { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + // TODO: Make the name part of the trait + let peeked = token_nodes.peek_any().not_eof("whitespace")?; + + match peeked.node { + TokenNode::Whitespace(_) => { + peeked.commit(); + expand_expr(&self.inner, token_nodes, context) + } + + other => Err(ShellError::type_error( + "whitespace", + other.tagged_type_name(), + )), + } + } +} + +pub fn maybe_spaced(inner: T) -> MaybeSpacedExpression { + MaybeSpacedExpression { inner } +} + +#[derive(Debug, Copy, Clone)] +pub struct MaybeSpacedExpression { + inner: T, +} + +impl ExpandExpression for MaybeSpacedExpression { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + // TODO: Make the name part of the trait + let peeked = token_nodes.peek_any().not_eof("whitespace")?; + + match peeked.node { + TokenNode::Whitespace(_) => { + peeked.commit(); + expand_expr(&self.inner, token_nodes, context) + } + + _ => { + peeked.rollback(); + expand_expr(&self.inner, token_nodes, context) + } + } + } +} + +pub fn spaced(inner: T) -> SpacedExpression { + SpacedExpression { inner } +} + +fn expand_variable(tag: Tag, token_tag: Tag, source: &Text) -> hir::Expression { + if tag.slice(source) == "it" { + hir::Expression::it_variable(tag, token_tag) + } else { + hir::Expression::variable(tag, token_tag) + } +} diff --git a/src/parser/hir/syntax_shape/block.rs b/src/parser/hir/syntax_shape/block.rs new file mode 100644 index 0000000000..a78292b34e --- /dev/null +++ b/src/parser/hir/syntax_shape/block.rs @@ -0,0 +1,168 @@ +use crate::errors::ShellError; +use crate::parser::{ + hir, + hir::syntax_shape::{ + continue_expression, expand_expr, expand_syntax, ExpandContext, ExpandExpression, + ExpressionListShape, PathTailShape, VariablePathShape, + }, + hir::tokens_iterator::TokensIterator, + RawToken, TokenNode, +}; +use crate::{Tag, Tagged, TaggedItem}; + +#[derive(Debug, Copy, Clone)] +pub struct AnyBlockShape; + +impl ExpandExpression for AnyBlockShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + let block = token_nodes.peek_non_ws().not_eof("block")?; + + // is it just a block? + let block = block.node.as_block(); + + match block { + Some(block) => { + let mut iterator = TokensIterator::new(&block.item, context.tag, false); + + let exprs = expand_syntax(&ExpressionListShape, &mut iterator, context)?; + + return Ok(hir::RawExpression::Block(exprs).tagged(block.tag)); + } + _ => {} + } + + expand_syntax(&ShorthandBlock, token_nodes, context) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct ShorthandBlock; + +impl ExpandExpression for ShorthandBlock { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + let path = expand_expr(&ShorthandPath, token_nodes, context)?; + let start = path.tag; + let expr = continue_expression(path, token_nodes, context)?; + let end = expr.tag; + let block = hir::RawExpression::Block(vec![expr]).tagged(start.until(end)); + + Ok(block) + } +} + +/// A shorthand for `$it.foo."bar"`, used inside of a shorthand block +#[derive(Debug, Copy, Clone)] +pub struct ShorthandPath; + +impl ExpandExpression for ShorthandPath { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + // if it's a variable path, that's the head part + let path = expand_expr(&VariablePathShape, token_nodes, context); + + match path { + Ok(path) => return Ok(path), + Err(_) => {} + } + + // Synthesize the head of the shorthand path (`` -> `$it.`) + let mut head = expand_expr(&ShorthandHeadShape, token_nodes, context)?; + + // Now that we've synthesized the head, of the path, proceed to expand the tail of the path + // like any other path. + let tail = expand_syntax(&PathTailShape, token_nodes, context); + + match tail { + Err(_) => return Ok(head), + Ok((tail, _)) => { + // For each member that `PathTailShape` expanded, join it onto the existing expression + // to form a new path + for member in tail { + head = hir::Expression::dot_member(head, member); + } + + println!("{:?}", head); + + Ok(head) + } + } + } +} + +/// A shorthand for `$it.foo."bar"`, used inside of a shorthand block +#[derive(Debug, Copy, Clone)] +pub struct ShorthandHeadShape; + +impl ExpandExpression for ShorthandHeadShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + // A shorthand path must not be at EOF + let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?; + + match peeked.node { + // If the head of a shorthand path is a bare token, it expands to `$it.bare` + TokenNode::Token(Tagged { + item: RawToken::Bare, + tag, + }) => { + // Commit the peeked token + peeked.commit(); + + // Synthesize an `$it` expression + let it = synthetic_it(token_nodes.anchor()); + + // Make a path out of `$it` and the bare token as a member + Ok(hir::Expression::path( + it, + vec![tag.tagged_string(context.source)], + tag, + )) + } + + // If the head of a shorthand path is a string, it expands to `$it."some string"` + TokenNode::Token(Tagged { + item: RawToken::String(inner), + tag: outer, + }) => { + // Commit the peeked token + peeked.commit(); + + // Synthesize an `$it` expression + let it = synthetic_it(token_nodes.anchor()); + + // Make a path out of `$it` and the bare token as a member + Ok(hir::Expression::path( + it, + vec![inner.string(context.source).tagged(outer)], + outer, + )) + } + + // Any other token is not a valid bare head + other => { + return Err(ShellError::type_error( + "shorthand path", + other.tagged_type_name(), + )) + } + } + } +} + +fn synthetic_it(origin: uuid::Uuid) -> hir::Expression { + hir::Expression::it_variable(Tag::unknown_span(origin), Tag::unknown_span(origin)) +} diff --git a/src/parser/hir/syntax_shape/expression.rs b/src/parser/hir/syntax_shape/expression.rs new file mode 100644 index 0000000000..58cfa4a1a5 --- /dev/null +++ b/src/parser/hir/syntax_shape/expression.rs @@ -0,0 +1,188 @@ +pub(crate) mod delimited; +pub(crate) mod file_path; +pub(crate) mod list; +pub(crate) mod number; +pub(crate) mod pattern; +pub(crate) mod string; +pub(crate) mod unit; +pub(crate) mod variable_path; + +use crate::parser::hir::syntax_shape::{ + expand_expr, expand_syntax, expand_variable, expression::delimited::expand_delimited_expr, + BareShape, DotShape, ExpandContext, ExpandExpression, ExpandSyntax, ExpressionContinuation, + ExpressionContinuationShape, UnitShape, +}; +use crate::parser::{ + hir, + hir::{Expression, Operator, TokensIterator}, + RawToken, Token, TokenNode, +}; +use crate::prelude::*; +use std::path::PathBuf; + +#[derive(Debug, Copy, Clone)] +pub struct AnyExpressionShape; + +impl ExpandExpression for AnyExpressionShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + // Look for an expression at the cursor + let head = expand_expr(&AnyExpressionStartShape, token_nodes, context)?; + + continue_expression(head, token_nodes, context) + } +} + +pub(crate) fn continue_expression( + mut head: hir::Expression, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, +) -> Result { + loop { + // Check to see whether there's any continuation after the head expression + let continuation = expand_syntax(&ExpressionContinuationShape, token_nodes, context); + + match continuation { + // If there's no continuation, return the head + Err(_) => return Ok(head), + // Otherwise, form a new expression by combining the head with the continuation + Ok(continuation) => match continuation { + // If the continuation is a `.member`, form a path with the new member + ExpressionContinuation::DotSuffix(_dot, member) => { + head = Expression::dot_member(head, member); + } + + // Otherwise, if the continuation is an infix suffix, form an infix expression + ExpressionContinuation::InfixSuffix(op, expr) => { + head = Expression::infix(head, op, expr); + } + }, + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct AnyExpressionStartShape; + +impl ExpandExpression for AnyExpressionStartShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + let size = expand_expr(&UnitShape, token_nodes, context); + + match size { + Ok(expr) => return Ok(expr), + Err(_) => {} + } + + let peek_next = token_nodes.peek_any().not_eof("expression")?; + + let head = match peek_next.node { + TokenNode::Token(token) => match token.item { + RawToken::Bare | RawToken::Operator(Operator::Dot) => { + let start = token.tag; + peek_next.commit(); + + let end = expand_syntax(&BareTailShape, token_nodes, context)?; + + match end { + Some(end) => return Ok(hir::Expression::bare(start.until(end))), + None => return Ok(hir::Expression::bare(start)), + } + } + _ => { + peek_next.commit(); + expand_one_context_free_token(*token, context) + } + }, + node @ TokenNode::Call(_) + | node @ TokenNode::Nodes(_) + | node @ TokenNode::Pipeline(_) + | node @ TokenNode::Flag(_) + | node @ TokenNode::Member(_) + | node @ TokenNode::Whitespace(_) => { + return Err(ShellError::type_error( + "expression", + node.tagged_type_name(), + )) + } + TokenNode::Delimited(delimited) => { + peek_next.commit(); + expand_delimited_expr(delimited, context) + } + + TokenNode::Error(error) => return Err(*error.item.clone()), + }?; + + Ok(head) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct BareTailShape; + +impl ExpandSyntax for BareTailShape { + type Output = Option; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result, ShellError> { + let mut end: Option = None; + + loop { + match expand_syntax(&BareShape, token_nodes, context) { + Ok(bare) => { + end = Some(bare.tag); + continue; + } + + Err(_) => match expand_syntax(&DotShape, token_nodes, context) { + Ok(dot) => { + end = Some(dot); + continue; + } + + Err(_) => break, + }, + } + } + + Ok(end) + } +} + +fn expand_one_context_free_token<'a, 'b>( + token: Token, + context: &ExpandContext, +) -> Result { + Ok(match token.item { + RawToken::Number(number) => { + hir::Expression::number(number.to_number(context.source), token.tag) + } + RawToken::Operator(..) => { + return Err(ShellError::syntax_error( + "unexpected operator, expected an expression".tagged(token.tag), + )) + } + RawToken::Size(..) => unimplemented!("size"), + RawToken::String(tag) => hir::Expression::string(tag, token.tag), + RawToken::Variable(tag) => expand_variable(tag, token.tag, &context.source), + RawToken::ExternalCommand(_) => unimplemented!(), + RawToken::ExternalWord => unimplemented!(), + RawToken::GlobPattern => hir::Expression::pattern(token.tag), + RawToken::Bare => hir::Expression::string(token.tag, token.tag), + }) +} + +pub fn expand_file_path(string: &str, context: &ExpandContext) -> PathBuf { + let expanded = shellexpand::tilde_with_context(string, || context.homedir()); + + PathBuf::from(expanded.as_ref()) +} diff --git a/src/parser/hir/syntax_shape/expression/delimited.rs b/src/parser/hir/syntax_shape/expression/delimited.rs new file mode 100644 index 0000000000..0a01b0fc26 --- /dev/null +++ b/src/parser/hir/syntax_shape/expression/delimited.rs @@ -0,0 +1,38 @@ +use crate::parser::hir::syntax_shape::{expand_syntax, ExpandContext, ExpressionListShape}; +use crate::parser::{hir, hir::TokensIterator}; +use crate::parser::{DelimitedNode, Delimiter}; +use crate::prelude::*; + +pub fn expand_delimited_expr( + delimited: &Tagged, + context: &ExpandContext, +) -> Result { + match &delimited.item { + DelimitedNode { + delimiter: Delimiter::Square, + children, + } => { + let mut tokens = TokensIterator::new(&children, delimited.tag, false); + + let list = expand_syntax(&ExpressionListShape, &mut tokens, context); + + Ok(hir::Expression::list(list?, delimited.tag)) + } + + DelimitedNode { + delimiter: Delimiter::Paren, + .. + } => Err(ShellError::type_error( + "expression", + "unimplemented call expression".tagged(delimited.tag), + )), + + DelimitedNode { + delimiter: Delimiter::Brace, + .. + } => Err(ShellError::type_error( + "expression", + "unimplemented block expression".tagged(delimited.tag), + )), + } +} diff --git a/src/parser/hir/syntax_shape/expression/file_path.rs b/src/parser/hir/syntax_shape/expression/file_path.rs new file mode 100644 index 0000000000..c0e5c7c2ab --- /dev/null +++ b/src/parser/hir/syntax_shape/expression/file_path.rs @@ -0,0 +1,59 @@ +use crate::parser::hir::syntax_shape::{ + expand_syntax, expression::expand_file_path, parse_single_node, BarePathShape, ExpandContext, + ExpandExpression, +}; +use crate::parser::{hir, hir::TokensIterator, RawToken}; +use crate::prelude::*; + +#[derive(Debug, Copy, Clone)] +pub struct FilePathShape; + +impl ExpandExpression for FilePathShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + let bare = expand_syntax(&BarePathShape, token_nodes, context); + + match bare { + Ok(tag) => { + let string = tag.slice(context.source); + let path = expand_file_path(string, context); + return Ok(hir::Expression::file_path(path, tag)); + } + Err(_) => {} + } + + parse_single_node(token_nodes, "Path", |token, token_tag| { + Ok(match token { + RawToken::GlobPattern => { + return Err(ShellError::type_error( + "Path", + "glob pattern".tagged(token_tag), + )) + } + RawToken::Operator(..) => { + return Err(ShellError::type_error("Path", "operator".tagged(token_tag))) + } + RawToken::Variable(tag) if tag.slice(context.source) == "it" => { + hir::Expression::it_variable(tag, token_tag) + } + RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), + RawToken::Number(_) => hir::Expression::bare(token_tag), + RawToken::Size(_, _) => hir::Expression::bare(token_tag), + RawToken::Bare => hir::Expression::file_path( + expand_file_path(token_tag.slice(context.source), context), + token_tag, + ), + + RawToken::String(tag) => hir::Expression::file_path( + expand_file_path(tag.slice(context.source), context), + token_tag, + ), + }) + }) + } +} diff --git a/src/parser/hir/syntax_shape/expression/list.rs b/src/parser/hir/syntax_shape/expression/list.rs new file mode 100644 index 0000000000..9d28f44141 --- /dev/null +++ b/src/parser/hir/syntax_shape/expression/list.rs @@ -0,0 +1,43 @@ +use crate::errors::ShellError; +use crate::parser::{ + hir, + hir::syntax_shape::{ + expand_expr, maybe_spaced, spaced, AnyExpressionShape, ExpandContext, ExpandSyntax, + }, + hir::{debug_tokens, TokensIterator}, +}; + +#[derive(Debug, Copy, Clone)] +pub struct ExpressionListShape; + +impl ExpandSyntax for ExpressionListShape { + type Output = Vec; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result, ShellError> { + let mut exprs = vec![]; + + if token_nodes.at_end_possible_ws() { + return Ok(exprs); + } + + let expr = expand_expr(&maybe_spaced(AnyExpressionShape), token_nodes, context)?; + + exprs.push(expr); + + println!("{:?}", debug_tokens(token_nodes, context.source)); + + loop { + if token_nodes.at_end_possible_ws() { + return Ok(exprs); + } + + let expr = expand_expr(&spaced(AnyExpressionShape), token_nodes, context)?; + + exprs.push(expr); + } + } +} diff --git a/src/parser/hir/syntax_shape/expression/number.rs b/src/parser/hir/syntax_shape/expression/number.rs new file mode 100644 index 0000000000..5b77044a2d --- /dev/null +++ b/src/parser/hir/syntax_shape/expression/number.rs @@ -0,0 +1,97 @@ +use crate::parser::hir::syntax_shape::{parse_single_node, ExpandContext, ExpandExpression}; +use crate::parser::{ + hir, + hir::{RawNumber, TokensIterator}, + RawToken, +}; +use crate::prelude::*; + +#[derive(Debug, Copy, Clone)] +pub struct NumberShape; + +impl ExpandExpression for NumberShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + parse_single_node(token_nodes, "Number", |token, token_tag| { + Ok(match token { + RawToken::GlobPattern => { + return Err(ShellError::type_error( + "Number", + "glob pattern".to_string().tagged(token_tag), + )) + } + RawToken::Operator(..) => { + return Err(ShellError::type_error( + "Number", + "operator".to_string().tagged(token_tag), + )) + } + RawToken::Variable(tag) if tag.slice(context.source) == "it" => { + hir::Expression::it_variable(tag, token_tag) + } + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), + RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), + RawToken::Number(number) => { + hir::Expression::number(number.to_number(context.source), token_tag) + } + RawToken::Size(number, unit) => { + hir::Expression::size(number.to_number(context.source), unit, token_tag) + } + RawToken::Bare => hir::Expression::bare(token_tag), + RawToken::String(tag) => hir::Expression::string(tag, token_tag), + }) + }) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct IntShape; + +impl ExpandExpression for IntShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + parse_single_node(token_nodes, "Integer", |token, token_tag| { + Ok(match token { + RawToken::GlobPattern => { + return Err(ShellError::type_error( + "Integer", + "glob pattern".to_string().tagged(token_tag), + )) + } + RawToken::Operator(..) => { + return Err(ShellError::type_error( + "Integer", + "operator".to_string().tagged(token_tag), + )) + } + RawToken::Variable(tag) if tag.slice(context.source) == "it" => { + hir::Expression::it_variable(tag, token_tag) + } + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), + RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), + RawToken::Number(number @ RawNumber::Int(_)) => { + hir::Expression::number(number.to_number(context.source), token_tag) + } + token @ RawToken::Number(_) => { + return Err(ShellError::type_error( + "Integer", + token.type_name().tagged(token_tag), + )); + } + RawToken::Size(number, unit) => { + hir::Expression::size(number.to_number(context.source), unit, token_tag) + } + RawToken::Bare => hir::Expression::bare(token_tag), + RawToken::String(tag) => hir::Expression::string(tag, token_tag), + }) + }) + } +} diff --git a/src/parser/hir/syntax_shape/expression/pattern.rs b/src/parser/hir/syntax_shape/expression/pattern.rs new file mode 100644 index 0000000000..4105b79b4f --- /dev/null +++ b/src/parser/hir/syntax_shape/expression/pattern.rs @@ -0,0 +1,86 @@ +use crate::parser::hir::syntax_shape::{ + expand_bare, expand_syntax, expression::expand_file_path, parse_single_node, ExpandContext, + ExpandExpression, ExpandSyntax, +}; +use crate::parser::{hir, hir::TokensIterator, Operator, RawToken, TokenNode}; +use crate::prelude::*; + +#[derive(Debug, Copy, Clone)] +pub struct PatternShape; + +impl ExpandExpression for PatternShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + let pattern = expand_syntax(&BarePatternShape, token_nodes, context); + + match pattern { + Ok(tag) => { + return Ok(hir::Expression::pattern(tag)); + } + Err(_) => {} + } + + parse_single_node(token_nodes, "Pattern", |token, token_tag| { + Ok(match token { + RawToken::GlobPattern => { + return Err(ShellError::unreachable( + "glob pattern after glob already returned", + )) + } + RawToken::Operator(..) => { + return Err(ShellError::unreachable("dot after glob already returned")) + } + RawToken::Bare => { + return Err(ShellError::unreachable("bare after glob already returned")) + } + + RawToken::Variable(tag) if tag.slice(context.source) == "it" => { + hir::Expression::it_variable(tag, token_tag) + } + RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), + RawToken::Number(_) => hir::Expression::bare(token_tag), + RawToken::Size(_, _) => hir::Expression::bare(token_tag), + + RawToken::String(tag) => hir::Expression::file_path( + expand_file_path(tag.slice(context.source), context), + token_tag, + ), + }) + }) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct BarePatternShape; + +impl ExpandSyntax for BarePatternShape { + type Output = Tag; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + expand_bare(token_nodes, context, |token| match token { + TokenNode::Token(Tagged { + item: RawToken::Bare, + .. + }) + | TokenNode::Token(Tagged { + item: RawToken::Operator(Operator::Dot), + .. + }) + | TokenNode::Token(Tagged { + item: RawToken::GlobPattern, + .. + }) => true, + + _ => false, + }) + } +} diff --git a/src/parser/hir/syntax_shape/expression/string.rs b/src/parser/hir/syntax_shape/expression/string.rs new file mode 100644 index 0000000000..6a4973febe --- /dev/null +++ b/src/parser/hir/syntax_shape/expression/string.rs @@ -0,0 +1,60 @@ +use crate::parser::hir::syntax_shape::{ + expand_variable, parse_single_node, ExpandContext, ExpandExpression, TestSyntax, +}; +use crate::parser::hir::tokens_iterator::Peeked; +use crate::parser::{hir, hir::TokensIterator, RawToken, TokenNode}; +use crate::prelude::*; + +#[derive(Debug, Copy, Clone)] +pub struct StringShape; + +impl ExpandExpression for StringShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + parse_single_node(token_nodes, "String", |token, token_tag| { + Ok(match token { + RawToken::GlobPattern => { + return Err(ShellError::type_error( + "String", + "glob pattern".tagged(token_tag), + )) + } + RawToken::Operator(..) => { + return Err(ShellError::type_error( + "String", + "operator".tagged(token_tag), + )) + } + RawToken::Variable(tag) => expand_variable(tag, token_tag, &context.source), + RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), + RawToken::Number(_) => hir::Expression::bare(token_tag), + RawToken::Size(_, _) => hir::Expression::bare(token_tag), + RawToken::Bare => hir::Expression::bare(token_tag), + RawToken::String(tag) => hir::Expression::string(tag, token_tag), + }) + }) + } +} + +impl TestSyntax for StringShape { + fn test<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Option> { + let peeked = token_nodes.peek_any(); + + match peeked.node { + Some(TokenNode::Token(token)) => match token.item { + RawToken::String(_) => Some(peeked), + _ => None, + }, + + _ => None, + } + } +} diff --git a/src/parser/hir/syntax_shape/expression/unit.rs b/src/parser/hir/syntax_shape/expression/unit.rs new file mode 100644 index 0000000000..cc3642bda5 --- /dev/null +++ b/src/parser/hir/syntax_shape/expression/unit.rs @@ -0,0 +1,89 @@ +use crate::parser::hir::syntax_shape::{ExpandContext, ExpandExpression}; +use crate::parser::parse::tokens::RawNumber; +use crate::parser::parse::unit::Unit; +use crate::parser::{hir, hir::TokensIterator, RawToken, TokenNode}; +use crate::prelude::*; +use nom::branch::alt; +use nom::bytes::complete::tag; +use nom::character::complete::digit1; +use nom::combinator::{all_consuming, opt, value}; +use nom::IResult; + +#[derive(Debug, Copy, Clone)] +pub struct UnitShape; + +impl ExpandExpression for UnitShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + let peeked = token_nodes.peek_any().not_eof("unit")?; + + let tag = match peeked.node { + TokenNode::Token(Tagged { + item: RawToken::Bare, + tag, + }) => tag, + _ => return Err(peeked.type_error("unit")), + }; + + let unit = unit_size(tag.slice(context.source), *tag); + + let (_, (number, unit)) = match unit { + Err(_) => { + return Err(ShellError::type_error( + "unit", + "word".tagged(Tag::unknown()), + )) + } + Ok((number, unit)) => (number, unit), + }; + + Ok(hir::Expression::size( + number.to_number(context.source), + unit, + tag, + )) + } +} + +fn unit_size(input: &str, bare_tag: Tag) -> IResult<&str, (Tagged, Unit)> { + let (input, digits) = digit1(input)?; + + let (input, dot) = opt(tag("."))(input)?; + + let (input, number) = match dot { + Some(dot) => { + let (input, rest) = digit1(input)?; + ( + input, + RawNumber::decimal(( + bare_tag.span.start(), + bare_tag.span.start() + digits.len() + dot.len() + rest.len(), + bare_tag.anchor, + )), + ) + } + + None => ( + input, + RawNumber::int(( + bare_tag.span.start(), + bare_tag.span.start() + digits.len(), + bare_tag.anchor, + )), + ), + }; + + let (input, unit) = all_consuming(alt(( + value(Unit::B, alt((tag("B"), tag("b")))), + value(Unit::KB, alt((tag("KB"), tag("kb"), tag("Kb")))), + value(Unit::MB, alt((tag("MB"), tag("mb"), tag("Mb")))), + value(Unit::MB, alt((tag("GB"), tag("gb"), tag("Gb")))), + value(Unit::MB, alt((tag("TB"), tag("tb"), tag("Tb")))), + value(Unit::MB, alt((tag("PB"), tag("pb"), tag("Pb")))), + )))(input)?; + + Ok((input, (number, unit))) +} diff --git a/src/parser/hir/syntax_shape/expression/variable_path.rs b/src/parser/hir/syntax_shape/expression/variable_path.rs new file mode 100644 index 0000000000..afea1b1499 --- /dev/null +++ b/src/parser/hir/syntax_shape/expression/variable_path.rs @@ -0,0 +1,396 @@ +use crate::parser::hir::syntax_shape::{ + expand_expr, expand_syntax, parse_single_node, AnyExpressionShape, BareShape, ExpandContext, + ExpandExpression, ExpandSyntax, Peeked, SkipSyntax, StringShape, TestSyntax, WhitespaceShape, +}; +use crate::parser::{hir, hir::Expression, hir::TokensIterator, Operator, RawToken}; +use crate::prelude::*; + +#[derive(Debug, Copy, Clone)] +pub struct VariablePathShape; + +impl ExpandExpression for VariablePathShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + // 1. let the head be the first token, expecting a variable + // 2. let the tail be an empty list of members + // 2. while the next token (excluding ws) is a dot: + // 1. consume the dot + // 2. consume the next token as a member and push it onto tail + + let head = expand_expr(&VariableShape, token_nodes, context)?; + let start = head.tag(); + let mut end = start; + let mut tail: Vec> = vec![]; + + loop { + match DotShape.skip(token_nodes, context) { + Err(_) => break, + Ok(_) => {} + } + + let syntax = expand_syntax(&MemberShape, token_nodes, context)?; + let member = syntax.to_tagged_string(context.source); + + end = member.tag(); + tail.push(member); + } + + Ok(hir::Expression::path(head, tail, start.until(end))) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct PathTailShape; + +impl ExpandSyntax for PathTailShape { + type Output = (Vec>, Tag); + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + let mut end: Option = None; + let mut tail = vec![]; + + loop { + match DotShape.skip(token_nodes, context) { + Err(_) => break, + Ok(_) => {} + } + + let syntax = expand_syntax(&MemberShape, token_nodes, context)?; + let member = syntax.to_tagged_string(context.source); + end = Some(member.tag()); + tail.push(member); + } + + match end { + None => { + return Err(ShellError::type_error( + "path tail", + token_nodes.typed_tag_at_cursor(), + )) + } + + Some(end) => Ok((tail, end)), + } + } +} + +#[derive(Debug)] +pub enum ExpressionContinuation { + DotSuffix(Tag, Tagged), + InfixSuffix(Tagged, Expression), +} + +/// An expression continuation +#[derive(Debug, Copy, Clone)] +pub struct ExpressionContinuationShape; + +impl ExpandSyntax for ExpressionContinuationShape { + type Output = ExpressionContinuation; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + // Try to expand a `.` + let dot = expand_syntax(&DotShape, token_nodes, context); + + match dot { + // If a `.` was matched, it's a `Path`, and we expect a `Member` next + Ok(dot) => { + let syntax = expand_syntax(&MemberShape, token_nodes, context)?; + let member = syntax.to_tagged_string(context.source); + + Ok(ExpressionContinuation::DotSuffix(dot, member)) + } + + // Otherwise, we expect an infix operator and an expression next + Err(_) => { + let (_, op, _) = expand_syntax(&InfixShape, token_nodes, context)?; + let next = expand_expr(&AnyExpressionShape, token_nodes, context)?; + + Ok(ExpressionContinuation::InfixSuffix(op, next)) + } + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct VariableShape; + +impl ExpandExpression for VariableShape { + fn expand_expr<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + parse_single_node(token_nodes, "variable", |token, token_tag| { + Ok(match token { + RawToken::Variable(tag) => { + if tag.slice(context.source) == "it" { + hir::Expression::it_variable(tag, token_tag) + } else { + hir::Expression::variable(tag, token_tag) + } + } + _ => { + return Err(ShellError::type_error( + "variable", + token.type_name().tagged(token_tag), + )) + } + }) + }) + } +} + +#[derive(Debug, Clone, Copy)] +pub enum Member { + String(/* outer */ Tag, /* inner */ Tag), + Bare(Tag), +} + +impl Member { + pub(crate) fn to_expr(&self) -> hir::Expression { + match self { + Member::String(outer, inner) => hir::Expression::string(inner, outer), + Member::Bare(tag) => hir::Expression::string(tag, tag), + } + } + + pub(crate) fn tag(&self) -> Tag { + match self { + Member::String(outer, _inner) => *outer, + Member::Bare(tag) => *tag, + } + } + + pub(crate) fn to_tagged_string(&self, source: &str) -> Tagged { + match self { + Member::String(outer, inner) => inner.string(source).tagged(outer), + Member::Bare(tag) => tag.tagged_string(source), + } + } + + pub(crate) fn tagged_type_name(&self) -> Tagged<&'static str> { + match self { + Member::String(outer, _inner) => "string".tagged(outer), + Member::Bare(tag) => "word".tagged(tag), + } + } +} + +enum ColumnPathState { + Initial, + LeadingDot(Tag), + Dot(Tag, Vec, Tag), + Member(Tag, Vec), + Error(ShellError), +} + +impl ColumnPathState { + pub fn dot(self, dot: Tag) -> ColumnPathState { + match self { + ColumnPathState::Initial => ColumnPathState::LeadingDot(dot), + ColumnPathState::LeadingDot(_) => { + ColumnPathState::Error(ShellError::type_error("column", "dot".tagged(dot))) + } + ColumnPathState::Dot(..) => { + ColumnPathState::Error(ShellError::type_error("column", "dot".tagged(dot))) + } + ColumnPathState::Member(tag, members) => ColumnPathState::Dot(tag, members, dot), + ColumnPathState::Error(err) => ColumnPathState::Error(err), + } + } + + pub fn member(self, member: Member) -> ColumnPathState { + match self { + ColumnPathState::Initial => ColumnPathState::Member(member.tag(), vec![member]), + ColumnPathState::LeadingDot(tag) => { + ColumnPathState::Member(tag.until(member.tag()), vec![member]) + } + + ColumnPathState::Dot(tag, mut tags, _) => { + ColumnPathState::Member(tag.until(member.tag()), { + tags.push(member); + tags + }) + } + ColumnPathState::Member(..) => { + ColumnPathState::Error(ShellError::type_error("column", member.tagged_type_name())) + } + ColumnPathState::Error(err) => ColumnPathState::Error(err), + } + } + + pub fn into_path(self, next: Peeked) -> Result>, ShellError> { + match self { + ColumnPathState::Initial => Err(next.type_error("column path")), + ColumnPathState::LeadingDot(dot) => { + Err(ShellError::type_error("column", "dot".tagged(dot))) + } + ColumnPathState::Dot(_tag, _members, dot) => { + Err(ShellError::type_error("column", "dot".tagged(dot))) + } + ColumnPathState::Member(tag, tags) => Ok(tags.tagged(tag)), + ColumnPathState::Error(err) => Err(err), + } + } +} + +pub fn expand_column_path<'a, 'b>( + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, +) -> Result>, ShellError> { + let mut state = ColumnPathState::Initial; + + loop { + let member = MemberShape.expand_syntax(token_nodes, context); + + match member { + Err(_) => break, + Ok(member) => state = state.member(member), + } + + let dot = DotShape.expand_syntax(token_nodes, context); + + match dot { + Err(_) => break, + Ok(dot) => state = state.dot(dot), + } + } + + state.into_path(token_nodes.peek_non_ws()) +} + +#[derive(Debug, Copy, Clone)] +pub struct ColumnPathShape; + +impl ExpandSyntax for ColumnPathShape { + type Output = Tagged>; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + expand_column_path(token_nodes, context) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct MemberShape; + +impl ExpandSyntax for MemberShape { + type Output = Member; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result { + let bare = BareShape.test(token_nodes, context); + if let Some(peeked) = bare { + let node = peeked.not_eof("column")?.commit(); + return Ok(Member::Bare(node.tag())); + } + + let string = StringShape.test(token_nodes, context); + + if let Some(peeked) = string { + let node = peeked.not_eof("column")?.commit(); + let (outer, inner) = node.expect_string(); + + return Ok(Member::String(outer, inner)); + } + + Err(token_nodes.peek_any().type_error("column")) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct DotShape; + +impl SkipSyntax for DotShape { + fn skip<'a, 'b>( + &self, + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + expand_syntax(self, token_nodes, context)?; + + Ok(()) + } +} + +impl ExpandSyntax for DotShape { + type Output = Tag; + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Result { + parse_single_node(token_nodes, "dot", |token, token_tag| { + Ok(match token { + RawToken::Operator(Operator::Dot) => token_tag, + _ => { + return Err(ShellError::type_error( + "dot", + token.type_name().tagged(token_tag), + )) + } + }) + }) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct InfixShape; + +impl ExpandSyntax for InfixShape { + type Output = (Tag, Tagged, Tag); + + fn expand_syntax<'a, 'b>( + &self, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + let checkpoint = token_nodes.checkpoint(); + + // An infix operator must be prefixed by whitespace + let start = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?; + + // Parse the next TokenNode after the whitespace + let operator = + parse_single_node(checkpoint.iterator, "infix operator", |token, token_tag| { + Ok(match token { + // If it's an operator (and not `.`), it's a match + RawToken::Operator(operator) if operator != Operator::Dot => { + operator.tagged(token_tag) + } + + // Otherwise, it's not a match + _ => { + return Err(ShellError::type_error( + "infix operator", + token.type_name().tagged(token_tag), + )) + } + }) + })?; + + // An infix operator must be followed by whitespace + let end = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?; + + checkpoint.commit(); + + Ok((start, operator, end)) + } +} diff --git a/src/parser/hir/tokens_iterator.rs b/src/parser/hir/tokens_iterator.rs new file mode 100644 index 0000000000..c0dd9c50fd --- /dev/null +++ b/src/parser/hir/tokens_iterator.rs @@ -0,0 +1,365 @@ +pub(crate) mod debug; + +use crate::errors::ShellError; +use crate::parser::TokenNode; +use crate::{Tag, Tagged, TaggedItem}; +use derive_new::new; + +#[derive(Debug, new)] +pub struct TokensIterator<'a> { + tokens: &'a [TokenNode], + tag: Tag, + skip_ws: bool, + #[new(default)] + index: usize, + #[new(default)] + seen: indexmap::IndexSet, +} + +#[derive(Debug)] +pub struct Checkpoint<'content, 'me> { + pub(crate) iterator: &'me mut TokensIterator<'content>, + index: usize, + seen: indexmap::IndexSet, + committed: bool, +} + +impl<'content, 'me> Checkpoint<'content, 'me> { + pub(crate) fn commit(mut self) { + self.committed = true; + } +} + +impl<'content, 'me> std::ops::Drop for Checkpoint<'content, 'me> { + fn drop(&mut self) { + if !self.committed { + self.iterator.index = self.index; + self.iterator.seen = self.seen.clone(); + } + } +} + +#[derive(Debug)] +pub struct Peeked<'content, 'me> { + pub(crate) node: Option<&'content TokenNode>, + iterator: &'me mut TokensIterator<'content>, + from: usize, + to: usize, +} + +impl<'content, 'me> Peeked<'content, 'me> { + pub fn commit(&mut self) -> Option<&'content TokenNode> { + let Peeked { + node, + iterator, + from, + to, + } = self; + + let node = (*node)?; + iterator.commit(*from, *to); + Some(node) + } + + pub fn not_eof( + self, + expected: impl Into, + ) -> Result, ShellError> { + match self.node { + None => Err(ShellError::unexpected_eof( + expected, + self.iterator.eof_tag(), + )), + Some(node) => Ok(PeekedNode { + node, + iterator: self.iterator, + from: self.from, + to: self.to, + }), + } + } + + pub fn type_error(&self, expected: impl Into) -> ShellError { + peek_error(&self.node, self.iterator.eof_tag(), expected) + } +} + +#[derive(Debug)] +pub struct PeekedNode<'content, 'me> { + pub(crate) node: &'content TokenNode, + iterator: &'me mut TokensIterator<'content>, + from: usize, + to: usize, +} + +impl<'content, 'me> PeekedNode<'content, 'me> { + pub fn commit(self) -> &'content TokenNode { + let PeekedNode { + node, + iterator, + from, + to, + } = self; + + iterator.commit(from, to); + node + } + + pub fn rollback(self) {} + + pub fn type_error(&self, expected: impl Into) -> ShellError { + peek_error(&Some(self.node), self.iterator.eof_tag(), expected) + } +} + +pub fn peek_error( + node: &Option<&TokenNode>, + eof_tag: Tag, + expected: impl Into, +) -> ShellError { + match node { + None => ShellError::unexpected_eof(expected, eof_tag), + Some(node) => ShellError::type_error(expected, node.tagged_type_name()), + } +} + +impl<'content> TokensIterator<'content> { + #[cfg(test)] + pub fn all(tokens: &'content [TokenNode], tag: Tag) -> TokensIterator<'content> { + TokensIterator::new(tokens, tag, false) + } + + /// Use a checkpoint when you need to peek more than one token ahead, but can't be sure + /// that you'll succeed. + pub fn checkpoint<'me>(&'me mut self) -> Checkpoint<'content, 'me> { + let index = self.index; + let seen = self.seen.clone(); + + Checkpoint { + iterator: self, + index, + seen, + committed: false, + } + } + + pub fn anchor(&self) -> uuid::Uuid { + self.tag.anchor + } + + fn eof_tag(&self) -> Tag { + Tag::from((self.tag.span.end(), self.tag.span.end(), self.tag.anchor)) + } + + pub fn typed_tag_at_cursor(&mut self) -> Tagged<&'static str> { + let next = self.peek_any(); + + match next.node { + None => "end".tagged(self.eof_tag()), + Some(node) => node.tagged_type_name(), + } + } + + pub fn remove(&mut self, position: usize) { + self.seen.insert(position); + } + + pub fn at_end(&self) -> bool { + peek(self, self.skip_ws).is_none() + } + + pub fn at_end_possible_ws(&self) -> bool { + peek(self, true).is_none() + } + + pub fn advance(&mut self) { + self.seen.insert(self.index); + self.index += 1; + } + + pub fn extract(&mut self, f: impl Fn(&TokenNode) -> Option) -> Option<(usize, T)> { + for (i, item) in self.tokens.iter().enumerate() { + if self.seen.contains(&i) { + continue; + } + + match f(item) { + None => { + continue; + } + Some(value) => { + self.seen.insert(i); + return Some((i, value)); + } + } + } + + None + } + + pub fn move_to(&mut self, pos: usize) { + self.index = pos; + } + + pub fn restart(&mut self) { + self.index = 0; + } + + pub fn clone(&self) -> TokensIterator<'content> { + TokensIterator { + tokens: self.tokens, + tag: self.tag, + index: self.index, + seen: self.seen.clone(), + skip_ws: self.skip_ws, + } + } + + // Get the next token, not including whitespace + pub fn next_non_ws(&mut self) -> Option<&TokenNode> { + let mut peeked = start_next(self, true); + peeked.commit() + } + + // Peek the next token, not including whitespace + pub fn peek_non_ws<'me>(&'me mut self) -> Peeked<'content, 'me> { + start_next(self, true) + } + + // Peek the next token, including whitespace + pub fn peek_any<'me>(&'me mut self) -> Peeked<'content, 'me> { + start_next(self, false) + } + + fn commit(&mut self, from: usize, to: usize) { + for index in from..to { + self.seen.insert(index); + } + + self.index = to; + } + + pub fn debug_remaining(&self) -> Vec { + let mut tokens = self.clone(); + tokens.restart(); + tokens.cloned().collect() + } +} + +impl<'a> Iterator for TokensIterator<'a> { + type Item = &'a TokenNode; + + fn next(&mut self) -> Option<&'a TokenNode> { + next(self, self.skip_ws) + } +} + +fn peek<'content, 'me>( + iterator: &TokensIterator<'content>, + skip_ws: bool, +) -> Option<&'content TokenNode> { + let mut to = iterator.index; + + loop { + if to >= iterator.tokens.len() { + return None; + } + + if iterator.seen.contains(&to) { + to += 1; + continue; + } + + if to >= iterator.tokens.len() { + return None; + } + + let node = &iterator.tokens[to]; + + match node { + TokenNode::Whitespace(_) if skip_ws => { + to += 1; + } + _ => { + return Some(node); + } + } + } +} + +fn start_next<'content, 'me>( + iterator: &'me mut TokensIterator<'content>, + skip_ws: bool, +) -> Peeked<'content, 'me> { + let from = iterator.index; + let mut to = iterator.index; + + loop { + if to >= iterator.tokens.len() { + return Peeked { + node: None, + iterator, + from, + to, + }; + } + + if iterator.seen.contains(&to) { + to += 1; + continue; + } + + if to >= iterator.tokens.len() { + return Peeked { + node: None, + iterator, + from, + to, + }; + } + + let node = &iterator.tokens[to]; + + match node { + TokenNode::Whitespace(_) if skip_ws => { + to += 1; + } + _ => { + to += 1; + return Peeked { + node: Some(node), + iterator, + from, + to, + }; + } + } + } +} + +fn next<'a>(iterator: &mut TokensIterator<'a>, skip_ws: bool) -> Option<&'a TokenNode> { + loop { + if iterator.index >= iterator.tokens.len() { + return None; + } + + if iterator.seen.contains(&iterator.index) { + iterator.advance(); + continue; + } + + if iterator.index >= iterator.tokens.len() { + return None; + } + + match &iterator.tokens[iterator.index] { + TokenNode::Whitespace(_) if skip_ws => { + iterator.advance(); + } + other => { + iterator.advance(); + return Some(other); + } + } + } +} diff --git a/src/parser/hir/tokens_iterator/debug.rs b/src/parser/hir/tokens_iterator/debug.rs new file mode 100644 index 0000000000..2e26720154 --- /dev/null +++ b/src/parser/hir/tokens_iterator/debug.rs @@ -0,0 +1,30 @@ +use crate::parser::hir::tokens_iterator::TokensIterator; +use crate::traits::ToDebug; + +#[derive(Debug)] +pub(crate) enum DebugIteratorToken { + Seen(String), + Unseen(String), + Cursor, +} + +pub(crate) fn debug_tokens(iterator: &TokensIterator, source: &str) -> Vec { + let mut out = vec![]; + + for (i, token) in iterator.tokens.iter().enumerate() { + if iterator.index == i { + out.push(DebugIteratorToken::Cursor); + } + + if iterator.seen.contains(&i) { + out.push(DebugIteratorToken::Seen(format!("{}", token.debug(source)))); + } else { + out.push(DebugIteratorToken::Unseen(format!( + "{}", + token.debug(source) + ))); + } + } + + out +} diff --git a/src/parser/parse/files.rs b/src/parser/parse/files.rs index afe75ddb27..3c28237f5d 100644 --- a/src/parser/parse/files.rs +++ b/src/parser/parse/files.rs @@ -1,6 +1,7 @@ use crate::Tag; use derive_new::new; use language_reporting::{FileName, Location}; +use log::trace; use uuid::Uuid; #[derive(new, Debug, Clone)] @@ -18,7 +19,7 @@ impl language_reporting::ReportingFiles for Files { from_index: usize, to_index: usize, ) -> Option { - Some(Tag::from((from_index, to_index, file))) + Some(Tag::new(file, (from_index, to_index).into())) } fn file_id(&self, tag: Self::Span) -> Self::FileId { @@ -38,8 +39,18 @@ impl language_reporting::ReportingFiles for Files { let mut seen_lines = 0; let mut seen_bytes = 0; - for (pos, _) in source.match_indices('\n') { - if pos > byte_index { + for (pos, slice) in source.match_indices('\n') { + trace!( + "SEARCH={} SEEN={} POS={} SLICE={:?} LEN={} ALL={:?}", + byte_index, + seen_bytes, + pos, + slice, + source.len(), + source + ); + + if pos >= byte_index { return Some(language_reporting::Location::new( seen_lines, byte_index - seen_bytes, @@ -53,7 +64,7 @@ impl language_reporting::ReportingFiles for Files { if seen_lines == 0 { Some(language_reporting::Location::new(0, byte_index)) } else { - None + panic!("byte index {} wasn't valid", byte_index); } } @@ -64,7 +75,7 @@ impl language_reporting::ReportingFiles for Files { for (pos, _) in source.match_indices('\n') { if seen_lines == lineno { - return Some(Tag::from((seen_bytes, pos, file))); + return Some(Tag::new(file, (seen_bytes, pos + 1).into())); } else { seen_lines += 1; seen_bytes = pos + 1; @@ -72,16 +83,18 @@ impl language_reporting::ReportingFiles for Files { } if seen_lines == 0 { - Some(Tag::from((0, self.snippet.len() - 1, file))) + Some(Tag::new(file, (0, self.snippet.len() - 1).into())) } else { None } } fn source(&self, tag: Self::Span) -> Option { - if tag.span.start > tag.span.end { + trace!("source(tag={:?}) snippet={:?}", tag, self.snippet); + + if tag.span.start() > tag.span.end() { return None; - } else if tag.span.end >= self.snippet.len() { + } else if tag.span.end() > self.snippet.len() { return None; } Some(tag.slice(&self.snippet).to_string()) diff --git a/src/parser/parse/operator.rs b/src/parser/parse/operator.rs index 82a04ed796..7b5a5c77d8 100644 --- a/src/parser/parse/operator.rs +++ b/src/parser/parse/operator.rs @@ -11,6 +11,7 @@ pub enum Operator { GreaterThan, LessThanOrEqual, GreaterThanOrEqual, + Dot, } impl ToDebug for Operator { @@ -32,6 +33,7 @@ impl Operator { Operator::GreaterThan => ">", Operator::LessThanOrEqual => "<=", Operator::GreaterThanOrEqual => ">=", + Operator::Dot => ".", } } } @@ -52,6 +54,7 @@ impl FromStr for Operator { ">" => Ok(Operator::GreaterThan), "<=" => Ok(Operator::LessThanOrEqual), ">=" => Ok(Operator::GreaterThanOrEqual), + "." => Ok(Operator::Dot), _ => Err(()), } } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 33903ba37c..93ba043ba1 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -14,24 +14,54 @@ use nom::combinator::*; use nom::multi::*; use nom::sequence::*; +use derive_new::new; use log::trace; use nom::dbg; use nom::*; use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice}; use nom_locate::{position, LocatedSpanEx}; +use nom_tracable::{tracable_parser, HasTracableInfo, TracableInfo}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::str::FromStr; use uuid::Uuid; -pub type NomSpan<'a> = LocatedSpanEx<&'a str, Uuid>; +pub type NomSpan<'a> = LocatedSpanEx<&'a str, TracableContext>; + +#[derive(Debug, Clone, Copy, PartialEq, new)] +pub struct TracableContext { + pub(crate) origin: Uuid, + pub(crate) info: TracableInfo, +} + +impl HasTracableInfo for TracableContext { + fn get_tracable_info(&self) -> TracableInfo { + self.info + } + + fn set_tracable_info(mut self, info: TracableInfo) -> Self { + TracableContext { + origin: self.origin, + info, + } + } +} + +impl std::ops::Deref for TracableContext { + type Target = TracableInfo; + + fn deref(&self) -> &TracableInfo { + &self.info + } +} pub fn nom_input(s: &str, anchor: Uuid) -> NomSpan<'_> { - LocatedSpanEx::new_extra(s, anchor) + LocatedSpanEx::new_extra(s, TracableContext::new(anchor, TracableInfo::new())) } macro_rules! operator { ($name:tt : $token:tt ) => { + #[tracable_parser] pub fn $name(input: NomSpan) -> IResult { let start = input.offset; let (input, tag) = tag(stringify!($token))(input)?; @@ -51,25 +81,7 @@ operator! { gte: >= } operator! { lte: <= } operator! { eq: == } operator! { neq: != } - -fn trace_step<'a, T: Debug>( - input: NomSpan<'a>, - name: &str, - block: impl FnOnce(NomSpan<'a>) -> IResult, T>, -) -> IResult, T> { - trace!(target: "nu::lite_parse", "+ before {} @ {:?}", name, input); - match block(input) { - Ok((input, result)) => { - trace!(target: "nu::lite_parse", "after {} @ {:?} -> {:?}", name, input, result); - Ok((input, result)) - } - - Err(e) => { - trace!(target: "nu::lite_parse", "- failed {} :: {:?}", name, e); - Err(e) - } - } -} +operator! { dot: . } #[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)] pub enum Number { @@ -77,6 +89,15 @@ pub enum Number { Decimal(BigDecimal), } +impl std::fmt::Display for Number { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Number::Int(int) => write!(f, "{}", int), + Number::Decimal(decimal) => write!(f, "{}", decimal), + } + } +} + macro_rules! primitive_int { ($($ty:ty)*) => { $( @@ -148,540 +169,479 @@ impl Into for BigDecimal { } } +#[tracable_parser] +pub fn number(input: NomSpan) -> IResult { + let (input, number) = raw_number(input)?; + + Ok(( + input, + TokenTreeBuilder::tagged_number(number.item, number.tag), + )) +} + +#[tracable_parser] pub fn raw_number(input: NomSpan) -> IResult> { let anchoral = input; let start = input.offset; - trace_step(input, "raw_decimal", move |input| { - let (input, neg) = opt(tag("-"))(input)?; - let (input, head) = digit1(input)?; - let dot: IResult = tag(".")(input); + let (input, neg) = opt(tag("-"))(input)?; + let (input, head) = digit1(input)?; - let input = match dot { - Ok((input, dot)) => input, + match input.fragment.chars().next() { + None => return Ok((input, RawNumber::int((start, input.offset, input.extra)))), + Some('.') => (), + Some(other) if other.is_whitespace() => { + return Ok((input, RawNumber::int((start, input.offset, input.extra)))) + } + _ => { + return Err(nom::Err::Error(nom::error::make_error( + input, + nom::error::ErrorKind::Tag, + ))) + } + } - // it's just an integer - Err(_) => return Ok((input, RawNumber::int((start, input.offset, input.extra)))), - }; + let dot: IResult = tag(".")(input); - let (input, tail) = digit1(input)?; + let input = match dot { + Ok((input, dot)) => input, - let end = input.offset; + // it's just an integer + Err(_) => return Ok((input, RawNumber::int((start, input.offset, input.extra)))), + }; - Ok((input, RawNumber::decimal((start, end, input.extra)))) - }) + let (input, tail) = digit1(input)?; + + let end = input.offset; + + let next = input.fragment.chars().next(); + + if let Some(next) = next { + if !next.is_whitespace() { + return Err(nom::Err::Error(nom::error::make_error( + input, + nom::error::ErrorKind::Tag, + ))); + } + } + + Ok((input, RawNumber::decimal((start, end, input.extra)))) } +#[tracable_parser] pub fn operator(input: NomSpan) -> IResult { - trace_step(input, "operator", |input| { - let (input, operator) = alt((gte, lte, neq, gt, lt, eq))(input)?; + let (input, operator) = alt((gte, lte, neq, gt, lt, eq))(input)?; - Ok((input, operator)) - }) + Ok((input, operator)) } +#[tracable_parser] pub fn dq_string(input: NomSpan) -> IResult { - trace_step(input, "dq_string", |input| { - let start = input.offset; - let (input, _) = char('"')(input)?; - let start1 = input.offset; - let (input, _) = many0(none_of("\""))(input)?; - let end1 = input.offset; - let (input, _) = char('"')(input)?; - let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), - )) - }) + let start = input.offset; + let (input, _) = char('"')(input)?; + let start1 = input.offset; + let (input, _) = many0(none_of("\""))(input)?; + let end1 = input.offset; + let (input, _) = char('"')(input)?; + let end = input.offset; + Ok(( + input, + TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), + )) } +#[tracable_parser] pub fn sq_string(input: NomSpan) -> IResult { - trace_step(input, "sq_string", move |input| { - let start = input.offset; - let (input, _) = char('\'')(input)?; - let start1 = input.offset; - let (input, _) = many0(none_of("\'"))(input)?; - let end1 = input.offset; - let (input, _) = char('\'')(input)?; - let end = input.offset; + let start = input.offset; + let (input, _) = char('\'')(input)?; + let start1 = input.offset; + let (input, _) = many0(none_of("\'"))(input)?; + let end1 = input.offset; + let (input, _) = char('\'')(input)?; + let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), - )) - }) + Ok(( + input, + TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), + )) } +#[tracable_parser] pub fn string(input: NomSpan) -> IResult { - trace_step(input, "string", move |input| { - alt((sq_string, dq_string))(input) - }) + alt((sq_string, dq_string))(input) } +#[tracable_parser] pub fn external(input: NomSpan) -> IResult { - trace_step(input, "external", move |input| { - let start = input.offset; - let (input, _) = tag("^")(input)?; - let (input, bare) = take_while(is_bare_char)(input)?; - let end = input.offset; + let start = input.offset; + let (input, _) = tag("^")(input)?; + let (input, bare) = take_while(is_bare_char)(input)?; + let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_external(bare, (start, end, input.extra)), - )) - }) + Ok(( + input, + TokenTreeBuilder::tagged_external_command(bare, (start, end, input.extra)), + )) } +#[tracable_parser] pub fn pattern(input: NomSpan) -> IResult { - trace_step(input, "bare", move |input| { - let start = input.offset; - let (input, _) = take_while1(is_start_glob_char)(input)?; - let (input, _) = take_while(is_glob_char)(input)?; + let start = input.offset; + let (input, _) = take_while1(is_start_glob_char)(input)?; + let (input, _) = take_while(is_glob_char)(input)?; - let next_char = &input.fragment.chars().nth(0); + let next_char = &input.fragment.chars().nth(0); - if let Some(next_char) = next_char { - if is_external_word_char(*next_char) { - return Err(nom::Err::Error(nom::error::make_error( - input, - nom::error::ErrorKind::TakeWhile1, - ))); - } + if let Some(next_char) = next_char { + if is_external_word_char(*next_char) { + return Err(nom::Err::Error(nom::error::make_error( + input, + nom::error::ErrorKind::TakeWhile1, + ))); } + } - let end = input.offset; + let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_pattern((start, end, input.extra)), - )) - }) + Ok(( + input, + TokenTreeBuilder::tagged_pattern((start, end, input.extra)), + )) } +#[tracable_parser] pub fn bare(input: NomSpan) -> IResult { - trace_step(input, "bare", move |input| { - let start = input.offset; - let (input, _) = take_while1(is_start_bare_char)(input)?; - let (input, _) = take_while(is_bare_char)(input)?; + let start = input.offset; + let (input, _) = take_while1(is_start_bare_char)(input)?; + let (input, last) = take_while(is_bare_char)(input)?; - let next_char = &input.fragment.chars().nth(0); + let next_char = &input.fragment.chars().nth(0); + let prev_char = last.fragment.chars().nth(0); - if let Some(next_char) = next_char { - if is_external_word_char(*next_char) || is_glob_specific_char(*next_char) { - return Err(nom::Err::Error(nom::error::make_error( - input, - nom::error::ErrorKind::TakeWhile1, - ))); - } + // if let (Some(prev), Some(next)) = (prev_char, next_char) { + // if prev == '.' && is_member_start(*next) { + // return Err(nom::Err::Error(nom::error::make_error( + // input, + // nom::error::ErrorKind::TakeWhile1, + // ))); + // } + // } + + if let Some(next_char) = next_char { + if is_external_word_char(*next_char) || is_glob_specific_char(*next_char) { + return Err(nom::Err::Error(nom::error::make_error( + input, + nom::error::ErrorKind::TakeWhile1, + ))); } + } - let end = input.offset; + let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_bare((start, end, input.extra)), - )) - }) + Ok(( + input, + TokenTreeBuilder::tagged_bare((start, end, input.extra)), + )) } +#[tracable_parser] pub fn external_word(input: NomSpan) -> IResult { - trace_step(input, "bare", move |input| { - let start = input.offset; - let (input, _) = take_while1(is_external_word_char)(input)?; - let end = input.offset; + let start = input.offset; + let (input, _) = take_while1(is_external_word_char)(input)?; + let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_external_word((start, end, input.extra)), - )) - }) + Ok(( + input, + TokenTreeBuilder::tagged_external_word((start, end, input.extra)), + )) } +#[tracable_parser] pub fn var(input: NomSpan) -> IResult { - trace_step(input, "var", move |input| { - let start = input.offset; - let (input, _) = tag("$")(input)?; - let (input, bare) = member(input)?; - let end = input.offset; + let start = input.offset; + let (input, _) = tag("$")(input)?; + let (input, bare) = ident(input)?; + let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_var(bare.tag(), (start, end, input.extra)), - )) - }) + Ok(( + input, + TokenTreeBuilder::tagged_var(bare, (start, end, input.extra)), + )) } -pub fn member(input: NomSpan) -> IResult { - trace_step(input, "identifier", move |input| { - let start = input.offset; - let (input, _) = take_while1(is_id_start)(input)?; - let (input, _) = take_while(is_id_continue)(input)?; +#[tracable_parser] +pub fn ident(input: NomSpan) -> IResult { + let start = input.offset; + let (input, _) = take_while1(is_start_bare_char)(input)?; + let (input, _) = take_while(is_bare_char)(input)?; + let end = input.offset; - let end = input.offset; - - Ok(( - input, - TokenTreeBuilder::tagged_member((start, end, input.extra)), - )) - }) + Ok((input, Tag::from((start, end, input.extra.origin)))) } +#[tracable_parser] pub fn flag(input: NomSpan) -> IResult { - trace_step(input, "flag", move |input| { - let start = input.offset; - let (input, _) = tag("--")(input)?; - let (input, bare) = bare(input)?; - let end = input.offset; + let start = input.offset; + let (input, _) = tag("--")(input)?; + let (input, bare) = bare(input)?; + let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_flag(bare.tag(), (start, end, input.extra)), - )) - }) + Ok(( + input, + TokenTreeBuilder::tagged_flag(bare.tag(), (start, end, input.extra)), + )) } +#[tracable_parser] pub fn shorthand(input: NomSpan) -> IResult { - trace_step(input, "shorthand", move |input| { - let start = input.offset; - let (input, _) = tag("-")(input)?; - let (input, bare) = bare(input)?; - let end = input.offset; + let start = input.offset; + let (input, _) = tag("-")(input)?; + let (input, bare) = bare(input)?; + let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_shorthand(bare.tag(), (start, end, input.extra)), - )) - }) -} - -pub fn raw_unit(input: NomSpan) -> IResult> { - trace_step(input, "raw_unit", move |input| { - let start = input.offset; - let (input, unit) = alt(( - tag("B"), - tag("b"), - tag("KB"), - tag("kb"), - tag("Kb"), - tag("K"), - tag("k"), - tag("MB"), - tag("mb"), - tag("Mb"), - tag("GB"), - tag("gb"), - tag("Gb"), - tag("TB"), - tag("tb"), - tag("Tb"), - tag("PB"), - tag("pb"), - tag("Pb"), - ))(input)?; - let end = input.offset; - - Ok(( - input, - Unit::from(unit.fragment).tagged((start, end, input.extra)), - )) - }) -} - -pub fn size(input: NomSpan) -> IResult { - trace_step(input, "size", move |input| { - let mut is_size = false; - let start = input.offset; - let (input, number) = raw_number(input)?; - if let Ok((input, Some(size))) = opt(raw_unit)(input) { - let end = input.offset; - - // Check to make sure there is no trailing parseable characters - if let Ok((input, Some(extra))) = opt(bare)(input) { - return Err(nom::Err::Error((input, nom::error::ErrorKind::Char))); - } - - Ok(( - input, - TokenTreeBuilder::tagged_size((number.item, *size), (start, end, input.extra)), - )) - } else { - let end = input.offset; - - // Check to make sure there is no trailing parseable characters - if let Ok((input, Some(extra))) = opt(bare)(input) { - return Err(nom::Err::Error((input, nom::error::ErrorKind::Char))); - } - - Ok(( - input, - TokenTreeBuilder::tagged_number(number.item, number.tag), - )) - } - }) + Ok(( + input, + TokenTreeBuilder::tagged_shorthand(bare.tag(), (start, end, input.extra)), + )) } +#[tracable_parser] pub fn leaf(input: NomSpan) -> IResult { - trace_step(input, "leaf", move |input| { - let (input, node) = alt(( - size, - string, - operator, - flag, - shorthand, - var, - external, - bare, - pattern, - external_word, - ))(input)?; + let (input, node) = alt((number, string, operator, flag, shorthand, var, external))(input)?; - Ok((input, node)) - }) + Ok((input, node)) } -pub fn token_list(input: NomSpan) -> IResult> { - trace_step(input, "token_list", move |input| { - let (input, first) = node(input)?; - let (input, list) = many0(pair(space1, node))(input)?; +#[tracable_parser] +pub fn token_list(input: NomSpan) -> IResult>> { + let start = input.offset; + let (input, first) = node(input)?; - Ok((input, make_token_list(None, first, list, None))) - }) + let (input, mut list) = many0(pair(alt((whitespace, dot)), node))(input)?; + + let end = input.offset; + + Ok(( + input, + make_token_list(first, list, None).tagged((start, end, input.extra.origin)), + )) } -pub fn spaced_token_list(input: NomSpan) -> IResult> { - trace_step(input, "spaced_token_list", move |input| { - let (input, sp_left) = opt(space1)(input)?; - let (input, first) = node(input)?; - let (input, list) = many0(pair(space1, node))(input)?; - let (input, sp_right) = opt(space1)(input)?; +#[tracable_parser] +pub fn spaced_token_list(input: NomSpan) -> IResult>> { + let start = input.offset; + let (input, pre_ws) = opt(whitespace)(input)?; + let (input, items) = token_list(input)?; + let (input, post_ws) = opt(whitespace)(input)?; + let end = input.offset; - Ok((input, make_token_list(sp_left, first, list, sp_right))) - }) + let mut out = vec![]; + + out.extend(pre_ws); + out.extend(items.item); + out.extend(post_ws); + + Ok((input, out.tagged((start, end, input.extra.origin)))) } fn make_token_list( - sp_left: Option, - first: TokenNode, - list: Vec<(NomSpan, TokenNode)>, - sp_right: Option, + first: Vec, + list: Vec<(TokenNode, Vec)>, + sp_right: Option, ) -> Vec { let mut nodes = vec![]; - if let Some(sp_left) = sp_left { - nodes.push(TokenNode::Whitespace(Tag::from(sp_left))); - } + nodes.extend(first); - nodes.push(first); - - for (ws, token) in list { - nodes.push(TokenNode::Whitespace(Tag::from(ws))); - nodes.push(token); + for (left, right) in list { + nodes.push(left); + nodes.extend(right); } if let Some(sp_right) = sp_right { - nodes.push(TokenNode::Whitespace(Tag::from(sp_right))); + nodes.push(sp_right); } nodes } +#[tracable_parser] pub fn whitespace(input: NomSpan) -> IResult { - trace_step(input, "whitespace", move |input| { - let left = input.offset; - let (input, ws1) = space1(input)?; - let right = input.offset; + let left = input.offset; + let (input, ws1) = space1(input)?; + let right = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_ws((left, right, input.extra)), - )) - }) -} - -pub fn delimited_paren(input: NomSpan) -> IResult { - trace_step(input, "delimited_paren", move |input| { - let left = input.offset; - let (input, _) = char('(')(input)?; - let (input, ws1) = opt(whitespace)(input)?; - let (input, inner_items) = opt(token_list)(input)?; - let (input, ws2) = opt(whitespace)(input)?; - let (input, _) = char(')')(input)?; - let right = input.offset; - - let mut items = vec![]; - - if let Some(space) = ws1 { - items.push(space); - } - - if let Some(inner_items) = inner_items { - items.extend(inner_items); - } - - if let Some(space) = ws2 { - items.push(space); - } - - Ok(( - input, - TokenTreeBuilder::tagged_parens(items, (left, right, input.extra)), - )) - }) -} - -pub fn delimited_square(input: NomSpan) -> IResult { - trace_step(input, "delimited_paren", move |input| { - let left = input.offset; - let (input, _) = char('[')(input)?; - let (input, ws1) = opt(whitespace)(input)?; - let (input, inner_items) = opt(token_list)(input)?; - let (input, ws2) = opt(whitespace)(input)?; - let (input, _) = char(']')(input)?; - let right = input.offset; - - let mut items = vec![]; - - if let Some(space) = ws1 { - items.push(space); - } - - if let Some(inner_items) = inner_items { - items.extend(inner_items); - } - - if let Some(space) = ws2 { - items.push(space); - } - - Ok(( - input, - TokenTreeBuilder::tagged_square(items, (left, right, input.extra)), - )) - }) -} - -pub fn delimited_brace(input: NomSpan) -> IResult { - trace_step(input, "delimited_brace", move |input| { - let left = input.offset; - let (input, _) = char('{')(input)?; - let (input, _) = opt(space1)(input)?; - let (input, items) = opt(token_list)(input)?; - let (input, _) = opt(space1)(input)?; - let (input, _) = char('}')(input)?; - let right = input.offset; - - Ok(( - input, - TokenTreeBuilder::tagged_brace( - items.unwrap_or_else(|| vec![]), - (left, right, input.extra), - ), - )) - }) -} - -pub fn raw_call(input: NomSpan) -> IResult> { - trace_step(input, "raw_call", move |input| { - let left = input.offset; - let (input, items) = token_list(input)?; - let right = input.offset; - - Ok(( - input, - TokenTreeBuilder::tagged_call(items, (left, right, input.extra)), - )) - }) -} - -pub fn path(input: NomSpan) -> IResult { - trace_step(input, "path", move |input| { - let left = input.offset; - let (input, head) = node1(input)?; - let (input, _) = tag(".")(input)?; - let (input, tail) = separated_list(tag("."), alt((member, string)))(input)?; - let right = input.offset; - - Ok(( - input, - TokenTreeBuilder::tagged_path((head, tail), (left, right, input.extra)), - )) - }) -} - -pub fn node1(input: NomSpan) -> IResult { - trace_step(input, "node1", alt((leaf, delimited_paren))) -} - -pub fn node(input: NomSpan) -> IResult { - trace_step( + Ok(( input, - "node", - alt(( - path, - leaf, - delimited_paren, - delimited_brace, - delimited_square, - )), - ) + TokenTreeBuilder::tagged_ws((left, right, input.extra)), + )) } +pub fn delimited(input: NomSpan, delimiter: Delimiter) -> IResult>> { + let left = input.offset; + let (input, _) = char(delimiter.open())(input)?; + let (input, inner_items) = opt(spaced_token_list)(input)?; + let (input, _) = char(delimiter.close())(input)?; + let right = input.offset; + + let mut items = vec![]; + + if let Some(inner_items) = inner_items { + items.extend(inner_items.item); + } + + Ok((input, items.tagged((left, right, input.extra.origin)))) +} + +#[tracable_parser] +pub fn delimited_paren(input: NomSpan) -> IResult { + let (input, tokens) = delimited(input, Delimiter::Paren)?; + + Ok(( + input, + TokenTreeBuilder::tagged_parens(tokens.item, tokens.tag), + )) +} + +#[tracable_parser] +pub fn delimited_square(input: NomSpan) -> IResult { + let (input, tokens) = delimited(input, Delimiter::Square)?; + + Ok(( + input, + TokenTreeBuilder::tagged_square(tokens.item, tokens.tag), + )) +} + +#[tracable_parser] +pub fn delimited_brace(input: NomSpan) -> IResult { + let (input, tokens) = delimited(input, Delimiter::Brace)?; + + Ok(( + input, + TokenTreeBuilder::tagged_brace(tokens.item, tokens.tag), + )) +} + +#[tracable_parser] +pub fn raw_call(input: NomSpan) -> IResult> { + let left = input.offset; + let (input, items) = token_list(input)?; + let right = input.offset; + + Ok(( + input, + TokenTreeBuilder::tagged_call(items.item, (left, right, input.extra)), + )) +} + +#[tracable_parser] +pub fn bare_path(input: NomSpan) -> IResult> { + let (input, head) = alt((bare, dot))(input)?; + + let (input, tail) = many0(alt((bare, dot, string)))(input)?; + + let next_char = &input.fragment.chars().nth(0); + + if is_boundary(*next_char) { + let mut result = vec![head]; + result.extend(tail); + + Ok((input, result)) + } else { + Err(nom::Err::Error(nom::error::make_error( + input, + nom::error::ErrorKind::Many0, + ))) + } +} + +#[tracable_parser] +pub fn pattern_path(input: NomSpan) -> IResult> { + let (input, head) = alt((pattern, dot))(input)?; + + let (input, tail) = many0(alt((pattern, dot, string)))(input)?; + + let next_char = &input.fragment.chars().nth(0); + + if is_boundary(*next_char) { + let mut result = vec![head]; + result.extend(tail); + + Ok((input, result)) + } else { + Err(nom::Err::Error(nom::error::make_error( + input, + nom::error::ErrorKind::Many0, + ))) + } +} + +#[tracable_parser] +pub fn node1(input: NomSpan) -> IResult { + alt((leaf, bare, pattern, external_word, delimited_paren))(input) +} + +#[tracable_parser] +pub fn node(input: NomSpan) -> IResult> { + alt(( + to_list(leaf), + bare_path, + pattern_path, + to_list(external_word), + to_list(delimited_paren), + to_list(delimited_brace), + to_list(delimited_square), + ))(input) +} + +fn to_list( + parser: impl Fn(NomSpan) -> IResult, +) -> impl Fn(NomSpan) -> IResult> { + move |input| { + let (input, next) = parser(input)?; + + Ok((input, vec![next])) + } +} + +#[tracable_parser] +pub fn nodes(input: NomSpan) -> IResult { + let (input, tokens) = token_list(input)?; + + Ok(( + input, + TokenTreeBuilder::tagged_token_list(tokens.item, tokens.tag), + )) +} + +#[tracable_parser] pub fn pipeline(input: NomSpan) -> IResult { - trace_step(input, "pipeline", |input| { - let start = input.offset; - let (input, head) = opt(tuple((opt(space1), raw_call, opt(space1))))(input)?; - let (input, items) = trace_step( + let start = input.offset; + let (input, head) = spaced_token_list(input)?; + let (input, items) = many0(tuple((tag("|"), spaced_token_list)))(input)?; + + if input.input_len() != 0 { + return Err(Err::Error(error_position!( input, - "many0", - many0(tuple((tag("|"), opt(space1), raw_call, opt(space1)))), - )?; - - let (input, tail) = opt(space1)(input)?; - let (input, newline) = opt(multispace1)(input)?; - - if input.input_len() != 0 { - return Err(Err::Error(error_position!( - input, - nom::error::ErrorKind::Eof - ))); - } - - let end = input.offset; - - Ok(( - input, - TokenTreeBuilder::tagged_pipeline( - (make_call_list(head, items), tail.map(Tag::from)), - (start, end, input.extra), - ), - )) - }) -} - -fn make_call_list( - head: Option<(Option, Tagged, Option)>, - items: Vec<(NomSpan, Option, Tagged, Option)>, -) -> Vec { - let mut out = vec![]; - - if let Some(head) = head { - let el = PipelineElement::new(None, head.0.map(Tag::from), head.1, head.2.map(Tag::from)); - - out.push(el); + nom::error::ErrorKind::Eof + ))); } - for (pipe, ws1, call, ws2) in items { - let el = PipelineElement::new( - Some(pipe).map(Tag::from), - ws1.map(Tag::from), - call, - ws2.map(Tag::from), - ); + let end = input.offset; - out.push(el); - } + let head_tag = head.tag(); + let mut all_items: Vec> = + vec![PipelineElement::new(None, head).tagged(head_tag)]; - out + all_items.extend(items.into_iter().map(|(pipe, items)| { + let items_tag = items.tag(); + PipelineElement::new(Some(Tag::from(pipe)), items).tagged(Tag::from(pipe).until(items_tag)) + })); + + Ok(( + input, + TokenTreeBuilder::tagged_pipeline(all_items, (start, end, input.extra)), + )) } fn int(frag: &str, neg: Option) -> i64 { @@ -693,9 +653,19 @@ fn int(frag: &str, neg: Option) -> i64 { } } +fn is_boundary(c: Option) -> bool { + match c { + None => true, + Some(')') | Some(']') | Some('}') => true, + Some(c) if c.is_whitespace() => true, + _ => false, + } +} + fn is_external_word_char(c: char) -> bool { match c { - ';' | '|' | '#' | '-' | '"' | '\'' | '$' | '(' | ')' | '[' | ']' | '{' | '}' | '`' => false, + ';' | '|' | '#' | '-' | '"' | '\'' | '$' | '(' | ')' | '[' | ']' | '{' | '}' | '`' + | '.' => false, other if other.is_whitespace() => false, _ => true, } @@ -717,8 +687,7 @@ fn is_glob_char(c: char) -> bool { fn is_start_bare_char(c: char) -> bool { match c { '+' => false, - _ if c.is_alphabetic() => true, - '.' => true, + _ if c.is_alphanumeric() => true, '\\' => true, '/' => true, '_' => true, @@ -732,7 +701,6 @@ fn is_bare_char(c: char) -> bool { match c { '+' => false, _ if c.is_alphanumeric() => true, - '.' => true, '\\' => true, '/' => true, '_' => true, @@ -759,6 +727,16 @@ fn is_id_continue(c: char) -> bool { } } +fn is_member_start(c: char) -> bool { + match c { + '"' | '\'' => true, + '1'..='9' => true, + + other if is_id_start(other) => true, + _ => false, + } +} + #[cfg(test)] mod tests { use super::*; @@ -768,41 +746,6 @@ mod tests { pub type CurriedNode = Box T + 'static>; - macro_rules! assert_leaf { - (parsers [ $($name:tt)* ] $input:tt -> $left:tt .. $right:tt { $kind:tt $parens:tt } ) => { - $( - assert_eq!( - apply($name, stringify!($name), $input), - token(RawToken::$kind $parens, $left, $right) - ); - )* - - assert_eq!( - apply(leaf, "leaf", $input), - token(RawToken::$kind $parens, $left, $right) - ); - - assert_eq!( - apply(leaf, "leaf", $input), - token(RawToken::$kind $parens, $left, $right) - ); - - assert_eq!( - apply(node, "node", $input), - token(RawToken::$kind $parens, $left, $right) - ); - }; - - (parsers [ $($name:tt)* ] $input:tt -> $left:tt .. $right:tt { $kind:tt } ) => { - $( - assert_eq!( - apply($name, stringify!($name), $input), - token(RawToken::$kind, $left, $right) - ); - )* - } - } - macro_rules! equal_tokens { ($source:tt -> $tokens:expr) => { let result = apply(pipeline, "pipeline", $source); @@ -823,53 +766,50 @@ mod tests { assert_eq!(debug_result, debug_expected) } } - - // apply(pipeline, "pipeline", r#"cargo +nightly run"#), - // build_token(b::pipeline(vec![( - // None, - // b::call( - // b::bare("cargo"), - // vec![ - // b::sp(), - // b::external_word("+nightly"), - // b::sp(), - // b::bare("run") - // ] - // ), - // None - // )])) }; + + (<$parser:tt> $source:tt -> $tokens:expr) => { + let result = apply($parser, stringify!($parser), $source); + let (expected_tree, expected_source) = TokenTreeBuilder::build(uuid::Uuid::nil(), $tokens); + + if result != expected_tree { + let debug_result = format!("{}", result.debug($source)); + let debug_expected = format!("{}", expected_tree.debug(&expected_source)); + + if debug_result == debug_expected { + assert_eq!( + result, expected_tree, + "NOTE: actual and expected had equivalent debug serializations, source={:?}, debug_expected={:?}", + $source, + debug_expected + ) + } else { + assert_eq!(debug_result, debug_expected) + } + } + }; + } #[test] fn test_integer() { - assert_leaf! { - parsers [ size ] - "123" -> 0..3 { Number(RawNumber::int((0, 3, test_uuid())).item) } + equal_tokens! { + + "123" -> b::token_list(vec![b::int(123)]) } - assert_leaf! { - parsers [ size ] - "-123" -> 0..4 { Number(RawNumber::int((0, 4, test_uuid())).item) } - } - } - - #[test] - fn test_size() { - assert_leaf! { - parsers [ size ] - "123MB" -> 0..5 { Size(RawNumber::int((0, 3, test_uuid())).item, Unit::MB) } - } - - assert_leaf! { - parsers [ size ] - "10GB" -> 0..4 { Size(RawNumber::int((0, 2, test_uuid())).item, Unit::GB) } + equal_tokens! { + + "-123" -> b::token_list(vec![b::int(-123)]) } } #[test] fn test_operator() { - assert_eq!(apply(node, "node", ">"), build_token(b::op(">"))); + equal_tokens! { + + ">" -> b::token_list(vec![b::op(">")]) + } // assert_leaf! { // parsers [ operator ] @@ -899,37 +839,50 @@ mod tests { #[test] fn test_string() { - assert_leaf! { - parsers [ string dq_string ] - r#""hello world""# -> 0..13 { String(tag(1, 12)) } + equal_tokens! { + + r#""hello world""# -> b::token_list(vec![b::string("hello world")]) } - assert_leaf! { - parsers [ string sq_string ] - r"'hello world'" -> 0..13 { String(tag(1, 12)) } + equal_tokens! { + + r#"'hello world'"# -> b::token_list(vec![b::string("hello world")]) } } #[test] fn test_bare() { - assert_leaf! { - parsers [ bare ] - "hello" -> 0..5 { Bare } + equal_tokens! { + + "hello" -> b::token_list(vec![b::bare("hello")]) + } + } + + #[test] + fn test_simple_path() { + equal_tokens! { + + "450MB" -> b::token_list(vec![b::bare("450MB")]) } - assert_leaf! { - parsers [ bare ] - "chrome.exe" -> 0..10 { Bare } + equal_tokens! { + + "chrome.exe" -> b::token_list(vec![b::bare("chrome"), b::op(Operator::Dot), b::bare("exe")]) } - assert_leaf! { - parsers [ bare ] - r"C:\windows\system.dll" -> 0..21 { Bare } + equal_tokens! { + + ".azure" -> b::token_list(vec![b::op(Operator::Dot), b::bare("azure")]) } - assert_leaf! { - parsers [ bare ] - r"C:\Code\-testing\my_tests.js" -> 0..28 { Bare } + equal_tokens! { + + r"C:\windows\system.dll" -> b::token_list(vec![b::bare(r"C:\windows\system"), b::op(Operator::Dot), b::bare("dll")]) + } + + equal_tokens! { + + r"C:\Code\-testing\my_tests.js" -> b::token_list(vec![b::bare(r"C:\Code\-testing\my_tests"), b::op(Operator::Dot), b::bare("js")]) } } @@ -956,223 +909,170 @@ mod tests { #[test] fn test_variable() { - assert_leaf! { - parsers [ var ] - "$it" -> 0..3 { Variable(tag(1, 3)) } + equal_tokens! { + + "$it" -> b::token_list(vec![b::var("it")]) } - assert_leaf! { - parsers [ var ] - "$name" -> 0..5 { Variable(tag(1, 5)) } + equal_tokens! { + + "$name" -> b::token_list(vec![b::var("name")]) } } #[test] fn test_external() { - assert_leaf! { - parsers [ external ] - "^ls" -> 0..3 { ExternalCommand(tag(1, 3)) } + equal_tokens! { + + "^ls" -> b::token_list(vec![b::external_command("ls")]) + } + } + + #[test] + fn test_dot_prefixed_name() { + equal_tokens! { + + ".azure" -> b::token_list(vec![b::op("."), b::bare("azure")]) } } #[test] fn test_delimited_paren() { - assert_eq!( - apply(node, "node", "(abc)"), - build_token(b::parens(vec![b::bare("abc")])) - ); + equal_tokens! { + + "(abc)" -> b::token_list(vec![b::parens(vec![b::bare("abc")])]) + } - assert_eq!( - apply(node, "node", "( abc )"), - build_token(b::parens(vec![b::ws(" "), b::bare("abc"), b::ws(" ")])) - ); + equal_tokens! { + + "( abc )" -> b::token_list(vec![b::parens(vec![b::ws(" "), b::bare("abc"), b::ws(" ")])]) + } - assert_eq!( - apply(node, "node", "( abc def )"), - build_token(b::parens(vec![ - b::ws(" "), - b::bare("abc"), - b::sp(), - b::bare("def"), - b::sp() - ])) - ); + equal_tokens! { + + "( abc def )" -> b::token_list(vec![b::parens(vec![b::ws(" "), b::bare("abc"), b::ws(" "), b::bare("def"), b::ws(" ")])]) + } - assert_eq!( - apply(node, "node", "( abc def 123 456GB )"), - build_token(b::parens(vec![ - b::ws(" "), - b::bare("abc"), - b::sp(), - b::bare("def"), - b::sp(), - b::int(123), - b::sp(), - b::size(456, "GB"), - b::sp() - ])) - ); + equal_tokens! { + + "( abc def 123 456GB )" -> b::token_list(vec![b::parens(vec![ + b::ws(" "), b::bare("abc"), b::ws(" "), b::bare("def"), b::ws(" "), b::int(123), b::ws(" "), b::bare("456GB"), b::ws(" ") + ])]) + } } #[test] fn test_delimited_square() { - assert_eq!( - apply(node, "node", "[abc]"), - build_token(b::square(vec![b::bare("abc")])) - ); + equal_tokens! { + + "[abc]" -> b::token_list(vec![b::square(vec![b::bare("abc")])]) + } - assert_eq!( - apply(node, "node", "[ abc ]"), - build_token(b::square(vec![b::ws(" "), b::bare("abc"), b::ws(" ")])) - ); + equal_tokens! { + + "[ abc ]" -> b::token_list(vec![b::square(vec![b::ws(" "), b::bare("abc"), b::ws(" ")])]) + } - assert_eq!( - apply(node, "node", "[ abc def ]"), - build_token(b::square(vec![ - b::ws(" "), - b::bare("abc"), - b::sp(), - b::bare("def"), - b::sp() - ])) - ); + equal_tokens! { + + "[ abc def ]" -> b::token_list(vec![b::square(vec![b::ws(" "), b::bare("abc"), b::ws(" "), b::bare("def"), b::ws(" ")])]) + } - assert_eq!( - apply(node, "node", "[ abc def 123 456GB ]"), - build_token(b::square(vec![ - b::ws(" "), - b::bare("abc"), - b::sp(), - b::bare("def"), - b::sp(), - b::int(123), - b::sp(), - b::size(456, "GB"), - b::sp() - ])) - ); + equal_tokens! { + + "[ abc def 123 456GB ]" -> b::token_list(vec![b::square(vec![ + b::ws(" "), b::bare("abc"), b::ws(" "), b::bare("def"), b::ws(" "), b::int(123), b::ws(" "), b::bare("456GB"), b::ws(" ") + ])]) + } } #[test] fn test_path() { let _ = pretty_env_logger::try_init(); - assert_eq!( - apply(node, "node", "$it.print"), - build_token(b::path(b::var("it"), vec![b::member("print")])) - ); - assert_eq!( - apply(node, "node", "$head.part1.part2"), - build_token(b::path( - b::var("head"), - vec![b::member("part1"), b::member("part2")] - )) - ); + equal_tokens! { + + "$it.print" -> b::token_list(vec![b::var("it"), b::op("."), b::bare("print")]) + } - assert_eq!( - apply(node, "node", "( hello ).world"), - build_token(b::path( - b::parens(vec![b::sp(), b::bare("hello"), b::sp()]), - vec![b::member("world")] - )) - ); + equal_tokens! { + + "$head.part1.part2" -> b::token_list(vec![b::var("head"), b::op("."), b::bare("part1"), b::op("."), b::bare("part2")]) + } - assert_eq!( - apply(node, "node", "( hello ).\"world\""), - build_token(b::path( - b::parens(vec![b::sp(), b::bare("hello"), b::sp()],), - vec![b::string("world")] - )) - ); + equal_tokens! { + + "( hello ).world" -> b::token_list(vec![b::parens(vec![b::sp(), b::bare("hello"), b::sp()]), b::op("."), b::bare("world")]) + } + + equal_tokens! { + + r#"( hello )."world""# -> b::token_list(vec![b::parens(vec![b::sp(), b::bare("hello"), b::sp()]), b::op("."), b::string("world")]) + } } #[test] fn test_nested_path() { - assert_eq!( - apply( - node, - "node", - "( $it.is.\"great news\".right yep $yep ).\"world\"" - ), - build_token(b::path( - b::parens(vec![ - b::sp(), - b::path( + equal_tokens! { + + r#"( $it.is."great news".right yep $yep )."world""# -> b::token_list( + vec![ + b::parens(vec![ + b::sp(), b::var("it"), - vec![b::member("is"), b::string("great news"), b::member("right")] - ), - b::sp(), - b::bare("yep"), - b::sp(), - b::var("yep"), - b::sp() - ]), - vec![b::string("world")] - )) - ) + b::op("."), + b::bare("is"), + b::op("."), + b::string("great news"), + b::op("."), + b::bare("right"), + b::sp(), + b::bare("yep"), + b::sp(), + b::var("yep"), + b::sp() + ]), + b::op("."), b::string("world")] + ) + } } #[test] fn test_smoke_single_command() { - assert_eq!( - apply(raw_call, "raw_call", "git add ."), - build(b::call( - b::bare("git"), - vec![b::sp(), b::bare("add"), b::sp(), b::bare(".")] - )) - ); + equal_tokens! { + + "git add ." -> b::token_list(vec![b::bare("git"), b::sp(), b::bare("add"), b::sp(), b::op(".")]) + } - assert_eq!( - apply(raw_call, "raw_call", "open Cargo.toml"), - build(b::call( - b::bare("open"), - vec![b::sp(), b::bare("Cargo.toml")] - )) - ); + equal_tokens! { + + "open Cargo.toml" -> b::token_list(vec![b::bare("open"), b::sp(), b::bare("Cargo"), b::op("."), b::bare("toml")]) + } - assert_eq!( - apply(raw_call, "raw_call", "select package.version"), - build(b::call( - b::bare("select"), - vec![b::sp(), b::bare("package.version")] - )) - ); + equal_tokens! { + + "select package.version" -> b::token_list(vec![b::bare("select"), b::sp(), b::bare("package"), b::op("."), b::bare("version")]) + } - assert_eq!( - apply(raw_call, "raw_call", "echo $it"), - build(b::call(b::bare("echo"), vec![b::sp(), b::var("it")])) - ); + equal_tokens! { + + "echo $it" -> b::token_list(vec![b::bare("echo"), b::sp(), b::var("it")]) + } - assert_eq!( - apply(raw_call, "raw_call", "open Cargo.toml --raw"), - build(b::call( - b::bare("open"), - vec![b::sp(), b::bare("Cargo.toml"), b::sp(), b::flag("raw")] - )) - ); + equal_tokens! { + + "open Cargo.toml --raw" -> b::token_list(vec![b::bare("open"), b::sp(), b::bare("Cargo"), b::op("."), b::bare("toml"), b::sp(), b::flag("raw")]) + } - assert_eq!( - apply(raw_call, "raw_call", "open Cargo.toml -r"), - build(b::call( - b::bare("open"), - vec![b::sp(), b::bare("Cargo.toml"), b::sp(), b::shorthand("r")] - )) - ); + equal_tokens! { + + "open Cargo.toml -r" -> b::token_list(vec![b::bare("open"), b::sp(), b::bare("Cargo"), b::op("."), b::bare("toml"), b::sp(), b::shorthand("r")]) + } - assert_eq!( - apply(raw_call, "raw_call", "config --set tabs 2"), - build(b::call( - b::bare("config"), - vec![ - b::sp(), - b::flag("set"), - b::sp(), - b::bare("tabs"), - b::sp(), - b::int(2) - ] - )) - ); + equal_tokens! { + + "config --set tabs 2" -> b::token_list(vec![b::bare("config"), b::sp(), b::flag("set"), b::sp(), b::bare("tabs"), b::sp(), b::int(2)]) + } } #[test] @@ -1181,120 +1081,159 @@ mod tests { equal_tokens!( "cargo +nightly run" -> - b::pipeline(vec![( - None, - b::call( - b::bare("cargo"), - vec![ - b::sp(), - b::external_word("+nightly"), - b::sp(), - b::bare("run") - ] - ), - None - )]) + b::pipeline(vec![vec![ + b::bare("cargo"), + b::sp(), + b::external_word("+nightly"), + b::sp(), + b::bare("run") + ]]) ); equal_tokens!( "rm foo%bar" -> - b::pipeline(vec![( - None, - b::call(b::bare("rm"), vec![b::sp(), b::external_word("foo%bar"),]), - None - )]) + b::pipeline(vec![vec![ + b::bare("rm"), b::sp(), b::external_word("foo%bar") + ]]) ); equal_tokens!( "rm foo%bar" -> - b::pipeline(vec![( - None, - b::call(b::bare("rm"), vec![b::sp(), b::external_word("foo%bar"),]), - None - )]) + b::pipeline(vec![vec![ + b::bare("rm"), b::sp(), b::external_word("foo%bar"), + ]]) ); } #[test] - fn test_smoke_pipeline() { + fn test_pipeline() { let _ = pretty_env_logger::try_init(); - assert_eq!( - apply( - pipeline, - "pipeline", - r#"git branch --merged | split-row "`n" | where $it != "* master""# - ), - build_token(b::pipeline(vec![ - ( - None, - b::call( - b::bare("git"), - vec![b::sp(), b::bare("branch"), b::sp(), b::flag("merged")] - ), - Some(" ") - ), - ( - Some(" "), - b::call(b::bare("split-row"), vec![b::sp(), b::string("`n")]), - Some(" ") - ), - ( - Some(" "), - b::call( - b::bare("where"), - vec![ - b::sp(), - b::var("it"), - b::sp(), - b::op("!="), - b::sp(), - b::string("* master") - ] - ), - None - ) - ])) - ); - - assert_eq!( - apply(pipeline, "pipeline", "ls | where { $it.size > 100 }"), - build_token(b::pipeline(vec![ - (None, b::call(b::bare("ls"), vec![]), Some(" ")), - ( - Some(" "), - b::call( - b::bare("where"), - vec![ - b::sp(), - b::braced(vec![ - b::path(b::var("it"), vec![b::member("size")]), - b::sp(), - b::op(">"), - b::sp(), - b::int(100) - ]) - ] - ), - None - ) - ])) - ) + equal_tokens! { + "sys | echo" -> b::pipeline(vec![ + vec![ + b::bare("sys"), b::sp() + ], + vec![ + b::sp(), b::bare("echo") + ] + ]) + } } - fn apply( - f: impl Fn(NomSpan) -> Result<(NomSpan, T), nom::Err<(NomSpan, nom::error::ErrorKind)>>, + #[test] + fn test_patterns() { + equal_tokens! { + + "cp ../formats/*" -> b::pipeline(vec![vec![b::bare("cp"), b::ws(" "), b::op("."), b::op("."), b::pattern("/formats/*")]]) + } + + equal_tokens! { + + "cp * /dev/null" -> b::pipeline(vec![vec![b::bare("cp"), b::ws(" "), b::pattern("*"), b::ws(" "), b::bare("/dev/null")]]) + } + } + + // #[test] + // fn test_pseudo_paths() { + // let _ = pretty_env_logger::try_init(); + + // equal_tokens!( + // r#"sys | where cpu."max ghz" > 1"# -> + // b::pipeline(vec![ + // (None, b::call(b::bare("sys"), vec![]), Some(" ")), + // ( + // Some(" "), + // b::call( + // b::bare("where"), + // vec![ + // b::sp(), + // b::path(b::bare("cpu"), vec![b::string("max ghz")]), + // b::sp(), + // b::op(">"), + // b::sp(), + // b::int(1) + // ] + // ), + // None + // ) + // ]) + // ); + // } + + // #[test] + // fn test_smoke_pipeline() { + // let _ = pretty_env_logger::try_init(); + + // assert_eq!( + // apply( + // pipeline, + // "pipeline", + // r#"git branch --merged | split-row "`n" | where $it != "* master""# + // ), + // build_token(b::pipeline(vec![ + // ( + // None, + // b::call( + // b::bare("git"), + // vec![b::sp(), b::bare("branch"), b::sp(), b::flag("merged")] + // ), + // Some(" ") + // ), + // ( + // Some(" "), + // b::call(b::bare("split-row"), vec![b::sp(), b::string("`n")]), + // Some(" ") + // ), + // ( + // Some(" "), + // b::call( + // b::bare("where"), + // vec![ + // b::sp(), + // b::var("it"), + // b::sp(), + // b::op("!="), + // b::sp(), + // b::string("* master") + // ] + // ), + // None + // ) + // ])) + // ); + + // assert_eq!( + // apply(pipeline, "pipeline", "ls | where { $it.size > 100 }"), + // build_token(b::pipeline(vec![ + // (None, b::call(b::bare("ls"), vec![]), Some(" ")), + // ( + // Some(" "), + // b::call( + // b::bare("where"), + // vec![ + // b::sp(), + // b::braced(vec![ + // b::path(b::var("it"), vec![b::member("size")]), + // b::sp(), + // b::op(">"), + // b::sp(), + // b::int(100) + // ]) + // ] + // ), + // None + // ) + // ])) + // ) + // } + + fn apply( + f: impl Fn(NomSpan) -> Result<(NomSpan, TokenNode), nom::Err<(NomSpan, nom::error::ErrorKind)>>, desc: &str, string: &str, - ) -> T { - match f(NomSpan::new_extra(string, uuid::Uuid::nil())) { - Ok(v) => v.1, - Err(other) => { - println!("{:?}", other); - println!("for {} @ {}", string, desc); - panic!("No dice"); - } - } + ) -> TokenNode { + f(nom_input(string, uuid::Uuid::nil())).unwrap().1 } fn tag(left: usize, right: usize) -> Tag { @@ -1312,17 +1251,6 @@ mod tests { TokenNode::Delimited(spanned) } - fn path(head: TokenNode, tail: Vec, left: usize, right: usize) -> TokenNode { - let tag = head.tag(); - - let node = PathNode::new( - Box::new(head), - tail.into_iter().map(TokenNode::Token).collect(), - ); - let spanned = node.tagged((left, right, tag.anchor)); - TokenNode::Path(spanned) - } - fn token(token: RawToken, left: usize, right: usize) -> TokenNode { TokenNode::Token(token.tagged((left, right, uuid::Uuid::nil()))) } diff --git a/src/parser/parse/pipeline.rs b/src/parser/parse/pipeline.rs index 42bbe23a18..36813e39c4 100644 --- a/src/parser/parse/pipeline.rs +++ b/src/parser/parse/pipeline.rs @@ -1,4 +1,4 @@ -use crate::parser::CallNode; +use crate::parser::TokenNode; use crate::traits::ToDebug; use crate::{Tag, Tagged}; use derive_new::new; @@ -7,20 +7,16 @@ use std::fmt; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)] pub struct Pipeline { - pub(crate) parts: Vec, - pub(crate) post_ws: Option, + pub(crate) parts: Vec>, + // pub(crate) post_ws: Option, } impl ToDebug for Pipeline { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { - for part in &self.parts { + for part in self.parts.iter() { write!(f, "{}", part.debug(source))?; } - if let Some(post_ws) = self.post_ws { - write!(f, "{}", post_ws.slice(source))? - } - Ok(()) } } @@ -28,10 +24,7 @@ impl ToDebug for Pipeline { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] pub struct PipelineElement { pub pipe: Option, - pub pre_ws: Option, - #[get = "pub(crate)"] - call: Tagged, - pub post_ws: Option, + pub tokens: Tagged>, } impl ToDebug for PipelineElement { @@ -40,14 +33,8 @@ impl ToDebug for PipelineElement { write!(f, "{}", pipe.slice(source))?; } - if let Some(pre_ws) = self.pre_ws { - write!(f, "{}", pre_ws.slice(source))?; - } - - write!(f, "{}", self.call.debug(source))?; - - if let Some(post_ws) = self.post_ws { - write!(f, "{}", post_ws.slice(source))?; + for token in &self.tokens.item { + write!(f, "{}", token.debug(source))?; } Ok(()) diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index e0072360e8..8cbb28264b 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -1,5 +1,6 @@ use crate::errors::ShellError; -use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*}; +use crate::parser::parse::{call_node::*, flag::*, pipeline::*, tokens::*}; +use crate::prelude::*; use crate::traits::ToDebug; use crate::{Tag, Tagged, Text}; use derive_new::new; @@ -12,15 +13,14 @@ pub enum TokenNode { Token(Token), Call(Tagged), + Nodes(Tagged>), Delimited(Tagged), Pipeline(Tagged), - Operator(Tagged), Flag(Tagged), Member(Tag), Whitespace(Tag), Error(Tagged>), - Path(Tagged), } impl ToDebug for TokenNode { @@ -94,32 +94,33 @@ impl TokenNode { pub fn tag(&self) -> Tag { match self { TokenNode::Token(t) => t.tag(), + TokenNode::Nodes(t) => t.tag(), TokenNode::Call(s) => s.tag(), TokenNode::Delimited(s) => s.tag(), TokenNode::Pipeline(s) => s.tag(), - TokenNode::Operator(s) => s.tag(), TokenNode::Flag(s) => s.tag(), TokenNode::Member(s) => *s, TokenNode::Whitespace(s) => *s, TokenNode::Error(s) => s.tag(), - TokenNode::Path(s) => s.tag(), } } - pub fn type_name(&self) -> String { + pub fn type_name(&self) -> &'static str { match self { TokenNode::Token(t) => t.type_name(), + TokenNode::Nodes(_) => "nodes", TokenNode::Call(_) => "command", TokenNode::Delimited(d) => d.type_name(), TokenNode::Pipeline(_) => "pipeline", - TokenNode::Operator(_) => "operator", TokenNode::Flag(_) => "flag", TokenNode::Member(_) => "member", TokenNode::Whitespace(_) => "whitespace", TokenNode::Error(_) => "error", - TokenNode::Path(_) => "path", } - .to_string() + } + + pub fn tagged_type_name(&self) -> Tagged<&'static str> { + self.type_name().tagged(self.tag()) } pub fn old_debug<'a>(&'a self, source: &'a Text) -> DebugTokenNode<'a> { @@ -134,6 +135,16 @@ impl TokenNode { self.tag().slice(source) } + pub fn get_variable(&self) -> Result<(Tag, Tag), ShellError> { + match self { + TokenNode::Token(Tagged { + item: RawToken::Variable(inner_tag), + tag: outer_tag, + }) => Ok((*outer_tag, *inner_tag)), + _ => Err(ShellError::type_error("variable", self.tagged_type_name())), + } + } + pub fn is_bare(&self) -> bool { match self { TokenNode::Token(Tagged { @@ -144,6 +155,20 @@ impl TokenNode { } } + pub fn as_block(&self) -> Option> { + match self { + TokenNode::Delimited(Tagged { + item: + DelimitedNode { + delimiter, + children, + }, + tag, + }) if *delimiter == Delimiter::Brace => Some((&children[..]).tagged(tag)), + _ => None, + } + } + pub fn is_external(&self) -> bool { match self { TokenNode::Token(Tagged { @@ -181,13 +206,60 @@ impl TokenNode { _ => Err(ShellError::string("unimplemented")), } } + + pub fn is_whitespace(&self) -> bool { + match self { + TokenNode::Whitespace(_) => true, + _ => false, + } + } + + pub fn expect_string(&self) -> (Tag, Tag) { + match self { + TokenNode::Token(Tagged { + item: RawToken::String(inner_tag), + tag: outer_tag, + }) => (*outer_tag, *inner_tag), + other => panic!("Expected string, found {:?}", other), + } + } +} + +#[cfg(test)] +impl TokenNode { + pub fn expect_list(&self) -> Tagged<&[TokenNode]> { + match self { + TokenNode::Nodes(Tagged { item, tag }) => (&item[..]).tagged(tag), + other => panic!("Expected list, found {:?}", other), + } + } + + pub fn expect_var(&self) -> (Tag, Tag) { + match self { + TokenNode::Token(Tagged { + item: RawToken::Variable(inner_tag), + tag: outer_tag, + }) => (*outer_tag, *inner_tag), + other => panic!("Expected var, found {:?}", other), + } + } + + pub fn expect_bare(&self) -> Tag { + match self { + TokenNode::Token(Tagged { + item: RawToken::Bare, + tag, + }) => *tag, + other => panic!("Expected var, found {:?}", other), + } + } } #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] #[get = "pub(crate)"] pub struct DelimitedNode { - delimiter: Delimiter, - children: Vec, + pub(crate) delimiter: Delimiter, + pub(crate) children: Vec, } impl DelimitedNode { @@ -207,6 +279,24 @@ pub enum Delimiter { Square, } +impl Delimiter { + pub(crate) fn open(&self) -> char { + match self { + Delimiter::Paren => '(', + Delimiter::Brace => '{', + Delimiter::Square => '[', + } + } + + pub(crate) fn close(&self) -> char { + match self { + Delimiter::Paren => ')', + Delimiter::Brace => '}', + Delimiter::Square => ']', + } + } +} + #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] #[get = "pub(crate)"] pub struct PathNode { diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index 9a2e6ab721..67298987a4 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -3,7 +3,7 @@ use crate::prelude::*; use crate::parser::parse::flag::{Flag, FlagKind}; use crate::parser::parse::operator::Operator; use crate::parser::parse::pipeline::{Pipeline, PipelineElement}; -use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, PathNode, TokenNode}; +use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, TokenNode}; use crate::parser::parse::tokens::{RawNumber, RawToken}; use crate::parser::parse::unit::Unit; use crate::parser::CallNode; @@ -31,60 +31,68 @@ impl TokenTreeBuilder { (node, builder.output) } - pub fn pipeline(input: Vec<(Option<&str>, CurriedCall, Option<&str>)>) -> CurriedToken { - let input: Vec<(Option, CurriedCall, Option)> = input - .into_iter() - .map(|(pre, call, post)| { - ( - pre.map(|s| s.to_string()), - call, - post.map(|s| s.to_string()), - ) - }) - .collect(); + fn build_tagged(&mut self, callback: impl FnOnce(&mut TokenTreeBuilder) -> T) -> Tagged { + let start = self.pos; + let ret = callback(self); + let end = self.pos; + ret.tagged((start, end, self.anchor)) + } + + pub fn pipeline(input: Vec>) -> CurriedToken { Box::new(move |b| { let start = b.pos; - let mut out: Vec = vec![]; + let mut out: Vec> = vec![]; let mut input = input.into_iter().peekable(); - let (pre, call, post) = input + let head = input .next() .expect("A pipeline must contain at least one element"); let pipe = None; - let pre_tag = pre.map(|pre| b.consume_tag(&pre)); - let call = call(b); - let post_tag = post.map(|post| b.consume_tag(&post)); + let head = b.build_tagged(|b| head.into_iter().map(|node| node(b)).collect()); - out.push(PipelineElement::new(pipe, pre_tag, call, post_tag)); + let head_tag: Tag = head.tag; + out.push(PipelineElement::new(pipe, head).tagged(head_tag)); loop { match input.next() { None => break, - Some((pre, call, post)) => { + Some(node) => { + let start = b.pos; let pipe = Some(b.consume_tag("|")); - let pre_span = pre.map(|pre| b.consume_tag(&pre)); - let call = call(b); - let post_span = post.map(|post| b.consume_tag(&post)); + let node = + b.build_tagged(|b| node.into_iter().map(|node| node(b)).collect()); + let end = b.pos; - out.push(PipelineElement::new(pipe, pre_span, call, post_span)); + out.push(PipelineElement::new(pipe, node).tagged((start, end, b.anchor))); } } } let end = b.pos; - TokenTreeBuilder::tagged_pipeline((out, None), (start, end, b.anchor)) + TokenTreeBuilder::tagged_pipeline(out, (start, end, b.anchor)) }) } - pub fn tagged_pipeline( - input: (Vec, Option), - tag: impl Into, - ) -> TokenNode { - TokenNode::Pipeline(Pipeline::new(input.0, input.1.into()).tagged(tag.into())) + pub fn tagged_pipeline(input: Vec>, tag: impl Into) -> TokenNode { + TokenNode::Pipeline(Pipeline::new(input).tagged(tag.into())) + } + + pub fn token_list(input: Vec) -> CurriedToken { + Box::new(move |b| { + let start = b.pos; + let tokens = input.into_iter().map(|i| i(b)).collect(); + let end = b.pos; + + TokenTreeBuilder::tagged_token_list(tokens, (start, end, b.anchor)) + }) + } + + pub fn tagged_token_list(input: Vec, tag: impl Into) -> TokenNode { + TokenNode::Nodes(input.tagged(tag)) } pub fn op(input: impl Into) -> CurriedToken { @@ -100,7 +108,7 @@ impl TokenTreeBuilder { } pub fn tagged_op(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Operator(input.into().tagged(tag.into())) + TokenNode::Token(RawToken::Operator(input.into()).tagged(tag.into())) } pub fn string(input: impl Into) -> CurriedToken { @@ -168,8 +176,23 @@ impl TokenTreeBuilder { TokenNode::Token(RawToken::ExternalWord.tagged(input.into())) } - pub fn tagged_external(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::ExternalCommand(input.into()).tagged(tag.into())) + pub fn external_command(input: impl Into) -> CurriedToken { + let input = input.into(); + + Box::new(move |b| { + let (outer_start, _) = b.consume("^"); + let (inner_start, end) = b.consume(&input); + b.pos = end; + + TokenTreeBuilder::tagged_external_command( + (inner_start, end, b.anchor), + (outer_start, end, b.anchor), + ) + }) + } + + pub fn tagged_external_command(inner: impl Into, outer: impl Into) -> TokenNode { + TokenNode::Token(RawToken::ExternalCommand(inner.into()).tagged(outer.into())) } pub fn int(input: impl Into) -> CurriedToken { @@ -229,29 +252,6 @@ impl TokenTreeBuilder { TokenNode::Token(RawToken::Size(int, unit).tagged(tag.into())) } - pub fn path(head: CurriedToken, tail: Vec) -> CurriedToken { - Box::new(move |b| { - let start = b.pos; - let head = head(b); - - let mut output = vec![]; - - for item in tail { - b.consume("."); - - output.push(item(b)); - } - - let end = b.pos; - - TokenTreeBuilder::tagged_path((head, output), (start, end, b.anchor)) - }) - } - - pub fn tagged_path(input: (TokenNode, Vec), tag: impl Into) -> TokenNode { - TokenNode::Path(PathNode::new(Box::new(input.0), input.1).tagged(tag.into())) - } - pub fn var(input: impl Into) -> CurriedToken { let input = input.into(); diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index d796a8fcb7..77a856af3f 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -1,4 +1,5 @@ use crate::parser::parse::unit::*; +use crate::parser::Operator; use crate::prelude::*; use crate::{Tagged, Text}; use std::fmt; @@ -7,6 +8,7 @@ use std::str::FromStr; #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum RawToken { Number(RawNumber), + Operator(Operator), Size(RawNumber, Unit), String(Tag), Variable(Tag), @@ -49,12 +51,13 @@ impl RawToken { pub fn type_name(&self) -> &'static str { match self { RawToken::Number(_) => "Number", + RawToken::Operator(..) => "operator", RawToken::Size(..) => "Size", RawToken::String(_) => "String", - RawToken::Variable(_) => "Variable", - RawToken::ExternalCommand(_) => "ExternalCommand", - RawToken::ExternalWord => "ExternalWord", - RawToken::GlobPattern => "GlobPattern", + RawToken::Variable(_) => "variable", + RawToken::ExternalCommand(_) => "external command", + RawToken::ExternalWord => "external word", + RawToken::GlobPattern => "glob pattern", RawToken::Bare => "String", } } diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index 36ba82f8e5..d383689fd9 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -1,92 +1,35 @@ -use crate::context::Context; use crate::errors::{ArgumentError, ShellError}; +use crate::parser::hir::syntax_shape::{expand_expr, spaced}; use crate::parser::registry::{NamedType, PositionalType, Signature}; -use crate::parser::{baseline_parse_tokens, CallNode}; +use crate::parser::TokensIterator; use crate::parser::{ - hir::{self, NamedArguments}, - Flag, RawToken, TokenNode, + hir::{self, ExpandContext, NamedArguments}, + Flag, }; use crate::traits::ToDebug; -use crate::{Tag, Tagged, TaggedItem, Text}; +use crate::{Tag, Tagged, Text}; use log::trace; -pub fn parse_command( +pub fn parse_command_tail( config: &Signature, - context: &Context, - call: &Tagged, - source: &Text, -) -> Result { - let Tagged { item: raw_call, .. } = call; - - trace!("Processing {:?}", config); - - let head = parse_command_head(call.head())?; - - let children: Option> = raw_call.children().as_ref().map(|nodes| { - nodes - .iter() - .cloned() - .filter(|node| match node { - TokenNode::Whitespace(_) => false, - _ => true, - }) - .collect() - }); - - match parse_command_tail(&config, context, children, source, call.tag())? { - None => Ok(hir::Call::new(Box::new(head), None, None)), - Some((positional, named)) => Ok(hir::Call::new(Box::new(head), positional, named)), - } -} - -fn parse_command_head(head: &TokenNode) -> Result { - match head { - TokenNode::Token( - spanned @ Tagged { - item: RawToken::Bare, - .. - }, - ) => Ok(spanned.map(|_| hir::RawExpression::Literal(hir::Literal::Bare))), - - TokenNode::Token(Tagged { - item: RawToken::String(inner_tag), - tag, - }) => Ok(hir::RawExpression::Literal(hir::Literal::String(*inner_tag)).tagged(*tag)), - - other => Err(ShellError::unexpected(&format!( - "command head -> {:?}", - other - ))), - } -} - -fn parse_command_tail( - config: &Signature, - context: &Context, - tail: Option>, - source: &Text, + context: &ExpandContext, + tail: &mut TokensIterator, command_tag: Tag, ) -> Result>, Option)>, ShellError> { - let tail = &mut match &tail { - None => hir::TokensIterator::new(&[]), - Some(tail) => hir::TokensIterator::new(tail), - }; - let mut named = NamedArguments::new(); - - trace_remaining("nodes", tail.clone(), source); + trace_remaining("nodes", tail.clone(), context.source()); for (name, kind) in &config.named { trace!(target: "nu::parse", "looking for {} : {:?}", name, kind); match kind { NamedType::Switch => { - let flag = extract_switch(name, tail, source); + let flag = extract_switch(name, tail, context.source()); named.insert_switch(name, flag); } NamedType::Mandatory(syntax_type) => { - match extract_mandatory(config, name, tail, source, command_tag) { + match extract_mandatory(config, name, tail, context.source(), command_tag) { Err(err) => return Err(err), // produce a correct diagnostic Ok((pos, flag)) => { tail.move_to(pos); @@ -99,42 +42,47 @@ fn parse_command_tail( )); } - let expr = - hir::baseline_parse_next_expr(tail, context, source, *syntax_type)?; + let expr = expand_expr(&spaced(*syntax_type), tail, context)?; tail.restart(); named.insert_mandatory(name, expr); } } } - NamedType::Optional(syntax_type) => match extract_optional(name, tail, source) { - Err(err) => return Err(err), // produce a correct diagnostic - Ok(Some((pos, flag))) => { - tail.move_to(pos); + NamedType::Optional(syntax_type) => { + match extract_optional(name, tail, context.source()) { + Err(err) => return Err(err), // produce a correct diagnostic + Ok(Some((pos, flag))) => { + tail.move_to(pos); - if tail.at_end() { - return Err(ShellError::argument_error( - config.name.clone(), - ArgumentError::MissingValueForName(name.to_string()), - flag.tag(), - )); + if tail.at_end() { + return Err(ShellError::argument_error( + config.name.clone(), + ArgumentError::MissingValueForName(name.to_string()), + flag.tag(), + )); + } + + let expr = expand_expr(&spaced(*syntax_type), tail, context); + + match expr { + Err(_) => named.insert_optional(name, None), + Ok(expr) => named.insert_optional(name, Some(expr)), + } + + tail.restart(); } - let expr = hir::baseline_parse_next_expr(tail, context, source, *syntax_type)?; - - tail.restart(); - named.insert_optional(name, Some(expr)); + Ok(None) => { + tail.restart(); + named.insert_optional(name, None); + } } - - Ok(None) => { - tail.restart(); - named.insert_optional(name, None); - } - }, + } }; } - trace_remaining("after named", tail.clone(), source); + trace_remaining("after named", tail.clone(), context.source()); let mut positional = vec![]; @@ -143,7 +91,7 @@ fn parse_command_tail( match arg { PositionalType::Mandatory(..) => { - if tail.len() == 0 { + if tail.at_end() { return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryPositional(arg.name().to_string()), @@ -153,25 +101,36 @@ fn parse_command_tail( } PositionalType::Optional(..) => { - if tail.len() == 0 { + if tail.at_end() { break; } } } - let result = hir::baseline_parse_next_expr(tail, context, source, arg.syntax_type())?; + let result = expand_expr(&spaced(arg.syntax_type()), tail, context)?; positional.push(result); } - trace_remaining("after positional", tail.clone(), source); + trace_remaining("after positional", tail.clone(), context.source()); if let Some(syntax_type) = config.rest_positional { - let remainder = baseline_parse_tokens(tail, context, source, syntax_type)?; - positional.extend(remainder); + let mut out = vec![]; + + loop { + if tail.at_end_possible_ws() { + break; + } + + let next = expand_expr(&spaced(syntax_type), tail, context)?; + + out.push(next); + } + + positional.extend(out); } - trace_remaining("after rest", tail.clone(), source); + trace_remaining("after rest", tail.clone(), context.source()); trace!("Constructed positional={:?} named={:?}", positional, named); diff --git a/src/parser/registry.rs b/src/parser/registry.rs index 955a1a04c9..888e5ae1e9 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -1,11 +1,11 @@ // TODO: Temporary redirect pub(crate) use crate::context::CommandRegistry; use crate::evaluate::{evaluate_baseline_expr, Scope}; -use crate::parser::{hir, hir::SyntaxShape, parse_command, CallNode}; +use crate::parser::{hir, hir::SyntaxShape}; use crate::prelude::*; use derive_new::new; use indexmap::IndexMap; -use log::trace; + use serde::{Deserialize, Serialize}; use std::fmt; @@ -271,21 +271,6 @@ impl<'a> Iterator for PositionalIter<'a> { } } -impl Signature { - pub(crate) fn parse_args( - &self, - call: &Tagged, - context: &Context, - source: &Text, - ) -> Result { - let args = parse_command(self, context, call, source)?; - - trace!("parsed args: {:?}", args); - - Ok(args) - } -} - pub(crate) fn evaluate_args( call: &hir::Call, registry: &CommandRegistry, diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 03e1d42828..997400d67f 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -1,10 +1,13 @@ +use itertools::Itertools; use nu::{ - serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tagged, Value, + serve_plugin, CallInfo, Plugin, ReturnSuccess, ReturnValue, ShellError, Signature, SyntaxShape, + Tagged, Value, }; +pub type ColumnPath = Vec>; + struct Add { - field: Option, + field: Option, value: Option, } impl Add { @@ -19,12 +22,13 @@ impl Add { let value_tag = value.tag(); match (value.item, self.value.clone()) { (obj @ Value::Row(_), Some(v)) => match &self.field { - Some(f) => match obj.insert_data_at_path(value_tag, &f, v) { + Some(f) => match obj.insert_data_at_column_path(value_tag, &f, v) { Some(v) => return Ok(v), None => { return Err(ShellError::string(format!( "add could not find place to insert field {:?} {}", - obj, f + obj, + f.iter().map(|i| &i.item).join(".") ))) } }, @@ -44,7 +48,7 @@ impl Plugin for Add { fn config(&mut self) -> Result { Ok(Signature::build("add") .desc("Add a new field to the table.") - .required("Field", SyntaxShape::String) + .required("Field", SyntaxShape::ColumnPath) .required("Value", SyntaxShape::String) .rest(SyntaxShape::String) .filter()) @@ -53,12 +57,13 @@ impl Plugin for Add { fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { if let Some(args) = call_info.args.positional { match &args[0] { - Tagged { - item: Value::Primitive(Primitive::String(s)), + table @ Tagged { + item: Value::Table(_), .. } => { - self.field = Some(s.clone()); + self.field = Some(table.as_column_path()?.item); } + _ => { return Err(ShellError::string(format!( "Unrecognized type in params: {:?}", diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index db116fedf5..6d35530ef5 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -1,10 +1,12 @@ use nu::{ - serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tagged, Value, + serve_plugin, CallInfo, Plugin, ReturnSuccess, ReturnValue, ShellError, Signature, SyntaxShape, + Tagged, Value, }; +pub type ColumnPath = Vec>; + struct Edit { - field: Option, + field: Option, value: Option, } impl Edit { @@ -19,7 +21,7 @@ impl Edit { let value_tag = value.tag(); match (value.item, self.value.clone()) { (obj @ Value::Row(_), Some(v)) => match &self.field { - Some(f) => match obj.replace_data_at_path(value_tag, &f, v) { + Some(f) => match obj.replace_data_at_column_path(value_tag, &f, v) { Some(v) => return Ok(v), None => { return Err(ShellError::string( @@ -43,7 +45,7 @@ impl Plugin for Edit { fn config(&mut self) -> Result { Ok(Signature::build("edit") .desc("Edit an existing column to have a new value.") - .required("Field", SyntaxShape::String) + .required("Field", SyntaxShape::ColumnPath) .required("Value", SyntaxShape::String) .filter()) } @@ -51,11 +53,11 @@ impl Plugin for Edit { fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { if let Some(args) = call_info.args.positional { match &args[0] { - Tagged { - item: Value::Primitive(Primitive::String(s)), + table @ Tagged { + item: Value::Table(_), .. } => { - self.field = Some(s.clone()); + self.field = Some(table.as_column_path()?.item); } _ => { return Err(ShellError::string(format!( diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index ecab03dc97..4e6f6f0f64 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -14,8 +14,10 @@ pub enum SemVerAction { Patch, } +pub type ColumnPath = Vec>; + struct Inc { - field: Option, + field: Option, error: Option, action: Option, } @@ -85,16 +87,17 @@ impl Inc { } Value::Row(_) => match self.field { Some(ref f) => { - let replacement = match value.item.get_data_by_path(value.tag(), f) { + let replacement = match value.item.get_data_by_column_path(value.tag(), f) { Some(result) => self.inc(result.map(|x| x.clone()))?, None => { return Err(ShellError::string("inc could not find field to replace")) } }; - match value - .item - .replace_data_at_path(value.tag(), f, replacement.item.clone()) - { + match value.item.replace_data_at_column_path( + value.tag(), + f, + replacement.item.clone(), + ) { Some(v) => return Ok(v), None => { return Err(ShellError::string("inc could not find field to replace")) @@ -120,7 +123,7 @@ impl Plugin for Inc { .switch("major") .switch("minor") .switch("patch") - .rest(SyntaxShape::String) + .rest(SyntaxShape::ColumnPath) .filter()) } @@ -138,11 +141,11 @@ impl Plugin for Inc { if let Some(args) = call_info.args.positional { for arg in args { match arg { - Tagged { - item: Value::Primitive(Primitive::String(s)), + table @ Tagged { + item: Value::Table(_), .. } => { - self.field = Some(s); + self.field = Some(table.as_column_path()?.item); } _ => { return Err(ShellError::string(format!( @@ -209,8 +212,13 @@ mod tests { } fn with_parameter(&mut self, name: &str) -> &mut Self { + let fields: Vec> = name + .split(".") + .map(|s| Value::string(s.to_string()).tagged(Tag::unknown_span(self.anchor))) + .collect(); + self.positionals - .push(Value::string(name.to_string()).tagged(Tag::unknown_span(self.anchor))); + .push(Value::Table(fields).tagged(Tag::unknown_span(self.anchor))); self } @@ -297,7 +305,12 @@ mod tests { ) .is_ok()); - assert_eq!(plugin.field, Some("package.version".to_string())); + assert_eq!( + plugin + .field + .map(|f| f.into_iter().map(|f| f.item).collect()), + Some(vec!["package".to_string(), "version".to_string()]) + ); } #[test] diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 4b74914f09..7bd35733da 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -1,6 +1,6 @@ use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tagged, Value, + SyntaxShape, Tagged, TaggedItem, Value, }; #[derive(Debug, Eq, PartialEq)] @@ -10,8 +10,10 @@ enum Action { ToInteger, } +pub type ColumnPath = Vec>; + struct Str { - field: Option, + field: Option, params: Option>, error: Option, action: Option, @@ -43,8 +45,8 @@ impl Str { Ok(applied) } - fn for_field(&mut self, field: &str) { - self.field = Some(String::from(field)); + fn for_field(&mut self, column_path: ColumnPath) { + self.field = Some(column_path); } fn permit(&mut self) -> bool { @@ -92,14 +94,15 @@ impl Str { } Value::Row(_) => match self.field { Some(ref f) => { - let replacement = match value.item.get_data_by_path(value.tag(), f) { + let replacement = match value.item.get_data_by_column_path(value.tag(), f) { Some(result) => self.strutils(result.map(|x| x.clone()))?, None => return Ok(Tagged::from_item(Value::nothing(), value.tag)), }; - match value - .item - .replace_data_at_path(value.tag(), f, replacement.item.clone()) - { + match value.item.replace_data_at_column_path( + value.tag(), + f, + replacement.item.clone(), + ) { Some(v) => return Ok(v), None => { return Err(ShellError::string("str could not find field to replace")) @@ -127,7 +130,7 @@ impl Plugin for Str { .switch("downcase") .switch("upcase") .switch("to-int") - .rest(SyntaxShape::Member) + .rest(SyntaxShape::ColumnPath) .filter()) } @@ -148,15 +151,21 @@ impl Plugin for Str { match possible_field { Tagged { item: Value::Primitive(Primitive::String(s)), - .. + tag, } => match self.action { Some(Action::Downcase) | Some(Action::Upcase) | Some(Action::ToInteger) | None => { - self.for_field(&s); + self.for_field(vec![s.clone().tagged(tag)]); } }, + table @ Tagged { + item: Value::Table(_), + .. + } => { + self.field = Some(table.as_column_path()?.item); + } _ => { return Err(ShellError::string(format!( "Unrecognized type in params: {:?}", @@ -227,8 +236,13 @@ mod tests { } fn with_parameter(&mut self, name: &str) -> &mut Self { + let fields: Vec> = name + .split(".") + .map(|s| Value::string(s.to_string()).tagged(Tag::unknown_span(self.anchor))) + .collect(); + self.positionals - .push(Value::string(name.to_string()).tagged(Tag::unknown())); + .push(Value::Table(fields).tagged(Tag::unknown_span(self.anchor))); self } @@ -303,7 +317,12 @@ mod tests { ) .is_ok()); - assert_eq!(plugin.field, Some("package.description".to_string())); + assert_eq!( + plugin + .field + .map(|f| f.into_iter().map(|f| f.item).collect()), + Some(vec!["package".to_string(), "description".to_string()]) + ) } #[test] diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 6fb4544352..85591cf047 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -1,3 +1,4 @@ +use crate::parser::hir::TokensIterator; use crate::parser::nom_input; use crate::parser::parse::token_tree::TokenNode; use crate::parser::parse::tokens::RawToken; @@ -77,16 +78,12 @@ impl Highlighter for Helper { Ok(v) => v, }; - let Pipeline { parts, post_ws } = pipeline; + let Pipeline { parts } = pipeline; let mut iter = parts.into_iter(); loop { match iter.next() { None => { - if let Some(ws) = post_ws { - out.push_str(ws.slice(line)); - } - return Cow::Owned(out); } Some(token) => { @@ -107,13 +104,12 @@ impl Highlighter for Helper { fn paint_token_node(token_node: &TokenNode, line: &str) -> String { let styled = match token_node { TokenNode::Call(..) => Color::Cyan.bold().paint(token_node.tag().slice(line)), + TokenNode::Nodes(..) => Color::Green.bold().paint(token_node.tag().slice(line)), TokenNode::Whitespace(..) => Color::White.normal().paint(token_node.tag().slice(line)), TokenNode::Flag(..) => Color::Black.bold().paint(token_node.tag().slice(line)), TokenNode::Member(..) => Color::Yellow.bold().paint(token_node.tag().slice(line)), - TokenNode::Path(..) => Color::Green.bold().paint(token_node.tag().slice(line)), TokenNode::Error(..) => Color::Red.bold().paint(token_node.tag().slice(line)), TokenNode::Delimited(..) => Color::White.paint(token_node.tag().slice(line)), - TokenNode::Operator(..) => Color::White.normal().paint(token_node.tag().slice(line)), TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.tag().slice(line)), TokenNode::Token(Tagged { item: RawToken::Number(..), @@ -147,6 +143,10 @@ fn paint_token_node(token_node: &TokenNode, line: &str) -> String { item: RawToken::ExternalWord, .. }) => Color::Black.bold().paint(token_node.tag().slice(line)), + TokenNode::Token(Tagged { + item: RawToken::Operator(..), + .. + }) => Color::Black.bold().paint(token_node.tag().slice(line)), }; styled.to_string() @@ -159,25 +159,19 @@ fn paint_pipeline_element(pipeline_element: &PipelineElement, line: &str) -> Str styled.push_str(&Color::Purple.paint("|")); } - if let Some(ws) = pipeline_element.pre_ws { - styled.push_str(&Color::White.normal().paint(ws.slice(line))); - } + let mut tokens = + TokensIterator::new(&pipeline_element.tokens, pipeline_element.tokens.tag, false); + let head = tokens.next(); - styled.push_str( - &Color::Cyan - .bold() - .paint(pipeline_element.call().head().tag().slice(line)) - .to_string(), - ); - - if let Some(children) = pipeline_element.call().children() { - for child in children { - styled.push_str(&paint_token_node(child, line)); + match head { + None => return styled, + Some(head) => { + styled.push_str(&Color::Cyan.bold().paint(head.tag().slice(line)).to_string()) } } - if let Some(ws) = pipeline_element.post_ws { - styled.push_str(&Color::White.normal().paint(ws.slice(line))); + for token in tokens { + styled.push_str(&paint_token_node(token, line)); } styled.to_string() diff --git a/tests/command_open_tests.rs b/tests/command_open_tests.rs index 54dc7ad54d..e9047883cf 100644 --- a/tests/command_open_tests.rs +++ b/tests/command_open_tests.rs @@ -212,7 +212,7 @@ fn open_can_parse_ini() { fn open_can_parse_utf16_ini() { let actual = nu!( cwd: "tests/fixtures/formats", - "open utf16.ini | get .ShellClassInfo | get IconIndex | echo $it" + "open utf16.ini | get '.ShellClassInfo' | get IconIndex | echo $it" ); assert_eq!(actual, "-236") diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 04fd889925..199038b531 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -93,6 +93,7 @@ macro_rules! nu { .write_all(commands.as_bytes()) .expect("couldn't write to stdin"); + let output = process .wait_with_output() .expect("couldn't read from stdout"); From d2eb6f6646195b6c95b09c9ed141a811709b5502 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 10 Oct 2019 21:23:12 +0200 Subject: [PATCH 245/342] Adds .envrc and shell.nix --- .envrc | 1 + shell.nix | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 .envrc create mode 100644 shell.nix diff --git a/.envrc b/.envrc new file mode 100644 index 0000000000..65326bb6dd --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix \ No newline at end of file diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000000..e6062b2cb0 --- /dev/null +++ b/shell.nix @@ -0,0 +1,10 @@ +let + moz_overlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz); + nixpkgs = import { overlays = [ moz_overlay ]; }; + nightly = ((nixpkgs.rustChannelOf { date = "2019-09-01"; channel = "nightly"; }).rust.override { extensions = [ "rust-src" "rls-preview" "clippy-preview" "rust-analysis" "rustfmt-preview" ];}); +in +with nixpkgs; +stdenv.mkDerivation { + name = "nushell-rust"; + buildInputs = [ nightly openssl_1_1 pkg-config ]; +} From c2c10e2bc0254497c25968728641c4f1b9b7f135 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sun, 6 Oct 2019 13:22:50 -0700 Subject: [PATCH 246/342] Overhaul the coloring system This commit replaces the previous naive coloring system with a coloring system that is more aligned with the parser. The main benefit of this change is that it allows us to use parsing rules to decide how to color tokens. For example, consider the following syntax: ``` $ ps | where cpu > 10 ``` Ideally, we could color `cpu` like a column name and not a string, because `cpu > 10` is a shorthand block syntax that expands to `{ $it.cpu > 10 }`. The way that we know that it's a shorthand block is that the `where` command declares that its first parameter is a `SyntaxShape::Block`, which allows the shorthand block form. In order to accomplish this, we need to color the tokens in a way that corresponds to their expanded semantics, which means that high-fidelity coloring requires expansion. This commit adds a `ColorSyntax` trait that corresponds to the `ExpandExpression` trait. The semantics are fairly similar, with a few differences. First `ExpandExpression` consumes N tokens and returns a single `hir::Expression`. `ColorSyntax` consumes N tokens and writes M `FlatShape` tokens to the output. Concretely, for syntax like `[1 2 3]` - `ExpandExpression` takes a single token node and produces a single `hir::Expression` - `ColorSyntax` takes the same token node and emits 7 `FlatShape`s (open delimiter, int, whitespace, int, whitespace, int, close delimiter) Second, `ColorSyntax` is more willing to plow through failures than `ExpandExpression`. In particular, consider syntax like ``` $ ps | where cpu > ``` In this case - `ExpandExpression` will see that the `where` command is expecting a block, see that it's not a literal block and try to parse it as a shorthand block. It will successfully find a member followed by an infix operator, but not a following expression. That means that the entire pipeline part fails to parse and is a syntax error. - `ColorSyntax` will also try to parse it as a shorthand block and ultimately fail, but it will fall back to "backoff coloring mode", which parsing any unidentified tokens in an unfallible, simple way. In this case, `cpu` will color as a string and `>` will color as an operator. Finally, it's very important that coloring a pipeline infallibly colors the entire string, doesn't fail, and doesn't get stuck in an infinite loop. In order to accomplish this, this PR separates `ColorSyntax`, which is infallible from `FallibleColorSyntax`, which might fail. This allows the type system to let us know if our coloring rules bottom out at at an infallible rule. It's not perfect: it's still possible for the coloring process to get stuck or consume tokens non-atomically. I intend to reduce the opportunity for those problems in a future commit. In the meantime, the current system catches a number of mistakes (like trying to use a fallible coloring rule in a loop without thinking about the possibility that it will never terminate). --- Cargo.toml | 2 +- src/cli.rs | 95 +-- src/commands/autoview.rs | 32 +- src/commands/classified.rs | 29 +- src/commands/config.rs | 13 +- src/commands/fetch.rs | 12 +- src/commands/open.rs | 12 +- src/commands/plugin.rs | 12 +- src/commands/post.rs | 16 +- src/commands/save.rs | 5 +- src/commands/to_csv.rs | 57 +- src/commands/to_tsv.rs | 70 +- src/context.rs | 10 +- src/data/base.rs | 48 +- src/data/config.rs | 22 +- src/data/meta.rs | 29 + src/errors.rs | 108 +-- src/parser.rs | 6 +- src/parser/hir/expand_external_tokens.rs | 88 ++- src/parser/hir/syntax_shape.rs | 655 +++++++++++++++++- src/parser/hir/syntax_shape/block.rs | 172 ++++- src/parser/hir/syntax_shape/expression.rs | 255 +++++-- .../hir/syntax_shape/expression/atom.rs | 541 +++++++++++++++ .../hir/syntax_shape/expression/delimited.rs | 67 +- .../hir/syntax_shape/expression/file_path.rs | 94 +-- .../hir/syntax_shape/expression/list.rs | 141 +++- .../hir/syntax_shape/expression/number.rs | 108 +-- .../hir/syntax_shape/expression/pattern.rs | 34 +- .../hir/syntax_shape/expression/string.rs | 36 +- .../hir/syntax_shape/expression/unit.rs | 31 +- .../syntax_shape/expression/variable_path.rs | 346 ++++++++- src/parser/hir/syntax_shape/flat_shape.rs | 95 +++ src/parser/hir/tokens_iterator.rs | 142 +++- src/parser/parse/flag.rs | 16 +- src/parser/parse/parser.rs | 53 +- src/parser/parse/token_tree.rs | 55 +- src/parser/parse/token_tree_builder.rs | 120 ++-- src/parser/parse/tokens.rs | 103 ++- src/parser/parse_command.rs | 232 ++++++- src/plugin.rs | 8 +- src/plugins/add.rs | 35 +- src/plugins/edit.rs | 26 +- src/plugins/embed.rs | 11 +- src/plugins/inc.rs | 41 +- src/plugins/match.rs | 44 +- src/plugins/str.rs | 31 +- src/plugins/sum.rs | 25 +- src/prelude.rs | 10 + src/shell/filesystem_shell.rs | 2 +- src/shell/helper.rs | 177 +++-- 50 files changed, 3527 insertions(+), 845 deletions(-) create mode 100644 src/parser/hir/syntax_shape/expression/atom.rs create mode 100644 src/parser/hir/syntax_shape/flat_shape.rs diff --git a/Cargo.toml b/Cargo.toml index 66bd695c08..80a077dd88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,7 +96,7 @@ textview = ["syntect", "onig_sys", "crossterm"] binaryview = ["image", "crossterm"] sys = ["heim", "battery"] ps = ["heim"] -trace = ["nom-tracable/trace"] +# trace = ["nom-tracable/trace"] all = ["raw-key", "textview", "binaryview", "sys", "ps", "clipboard", "ptree"] [dependencies.rusqlite] diff --git a/src/cli.rs b/src/cli.rs index 6a35608d91..6c1ba5ef93 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -14,9 +14,9 @@ use crate::git::current_branch; use crate::parser::registry::Signature; use crate::parser::{ hir, - hir::syntax_shape::{CommandHeadShape, CommandSignature, ExpandSyntax}, + hir::syntax_shape::{expand_syntax, PipelineShape}, hir::{expand_external_tokens::expand_external_tokens, tokens_iterator::TokensIterator}, - parse_command_tail, Pipeline, PipelineElement, TokenNode, + TokenNode, }; use crate::prelude::*; @@ -99,11 +99,17 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel }, Err(e) => { trace!("incompatible plugin {:?}", input); - Err(ShellError::string(format!("Error: {:?}", e))) + Err(ShellError::untagged_runtime_error(format!( + "Error: {:?}", + e + ))) } } } - Err(e) => Err(ShellError::string(format!("Error: {:?}", e))), + Err(e) => Err(ShellError::untagged_runtime_error(format!( + "Error: {:?}", + e + ))), }; let _ = child.wait(); @@ -319,6 +325,7 @@ pub async fn cli() -> Result<(), Box> { )]); } } + let _ = load_plugins(&mut context); let config = Config::builder().color_mode(ColorMode::Forced).build(); @@ -347,9 +354,7 @@ pub async fn cli() -> Result<(), Box> { let cwd = context.shell_manager.path(); - rl.set_helper(Some(crate::shell::Helper::new( - context.shell_manager.clone(), - ))); + rl.set_helper(Some(crate::shell::Helper::new(context.clone()))); let edit_mode = config::config(Tag::unknown())? .get("edit_mode") @@ -476,7 +481,7 @@ async fn process_line(readline: Result, ctx: &mut Context Ok(line) => { let line = chomp_newline(line); - let result = match crate::parser::parse(&line, uuid::Uuid::new_v4()) { + let result = match crate::parser::parse(&line, uuid::Uuid::nil()) { Err(err) => { return LineResult::Error(line.to_string(), err); } @@ -614,74 +619,14 @@ fn classify_pipeline( context: &Context, source: &Text, ) -> Result { - let pipeline = pipeline.as_pipeline()?; + let mut pipeline_list = vec![pipeline.clone()]; + let mut iterator = TokensIterator::all(&mut pipeline_list, pipeline.tag()); - let Pipeline { parts, .. } = pipeline; - - let commands: Result, ShellError> = parts - .iter() - .map(|item| classify_command(&item, context, &source)) - .collect(); - - Ok(ClassifiedPipeline { - commands: commands?, - }) -} - -fn classify_command( - command: &Tagged, - context: &Context, - source: &Text, -) -> Result { - let mut iterator = TokensIterator::new(&command.tokens.item, command.tag, true); - - let head = CommandHeadShape - .expand_syntax(&mut iterator, &context.expand_context(source, command.tag))?; - - match &head { - CommandSignature::Expression(_) => Err(ShellError::syntax_error( - "Unexpected expression in command position".tagged(command.tag), - )), - - // If the command starts with `^`, treat it as an external command no matter what - CommandSignature::External(name) => { - let name_str = name.slice(source); - - external_command(&mut iterator, source, name_str.tagged(name)) - } - - CommandSignature::LiteralExternal { outer, inner } => { - let name_str = inner.slice(source); - - external_command(&mut iterator, source, name_str.tagged(outer)) - } - - CommandSignature::Internal(command) => { - let tail = parse_command_tail( - &command.signature(), - &context.expand_context(source, command.tag), - &mut iterator, - command.tag, - )?; - - let (positional, named) = match tail { - None => (None, None), - Some((positional, named)) => (positional, named), - }; - - let call = hir::Call { - head: Box::new(head.to_expression()), - positional, - named, - }; - - Ok(ClassifiedCommand::Internal(InternalCommand::new( - command.name().to_string(), - command.tag, - call, - ))) - } - } + expand_syntax( + &PipelineShape, + &mut iterator, + &context.expand_context(source, pipeline.tag()), + ) } // Classify this command as an external command, which doesn't give special meaning diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 57ab6269b3..29e7d18121 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -58,21 +58,21 @@ pub fn autoview( } } }; - // } else if is_single_origined_text_value(&input) { - // let text = context.get_command("textview"); - // if let Some(text) = text { - // let result = text.run(raw.with_input(input), &context.commands); - // result.collect::>().await; - // } else { - // for i in input { - // match i.item { - // Value::Primitive(Primitive::String(s)) => { - // println!("{}", s); - // } - // _ => {} - // } - // } - // } + } else if is_single_anchored_text_value(&input) { + let text = context.get_command("textview"); + if let Some(text) = text { + let result = text.run(raw.with_input(input), &context.commands, false); + result.collect::>().await; + } else { + for i in input { + match i.item { + Value::Primitive(Primitive::String(s)) => { + println!("{}", s); + } + _ => {} + } + } + } } else if is_single_text_value(&input) { for i in input { match i.item { @@ -112,7 +112,7 @@ fn is_single_text_value(input: &Vec>) -> bool { } #[allow(unused)] -fn is_single_origined_text_value(input: &Vec>) -> bool { +fn is_single_anchored_text_value(input: &Vec>) -> bool { if input.len() != 1 { return false; } diff --git a/src/commands/classified.rs b/src/commands/classified.rs index d30025b944..c73a56fee4 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -72,6 +72,7 @@ impl ClassifiedInputStream { } } +#[derive(Debug)] pub(crate) struct ClassifiedPipeline { pub(crate) commands: Vec, } @@ -117,15 +118,19 @@ impl InternalCommand { let command = context.expect_command(&self.name); - let result = context.run_command( - command, - self.name_tag.clone(), - context.source_map.clone(), - self.args, - &source, - objects, - is_first_command, - ); + let result = { + let source_map = context.source_map.lock().unwrap().clone(); + + context.run_command( + command, + self.name_tag.clone(), + source_map, + self.args, + &source, + objects, + is_first_command, + ) + }; let result = trace_out_stream!(target: "nu::trace_stream::internal", source: &source, "output" = result); let mut result = result.values; @@ -253,7 +258,11 @@ impl ExternalCommand { tag, )); } else { - return Err(ShellError::string("Error: $it needs string data")); + return Err(ShellError::labeled_error( + "Error: $it needs string data", + "given something else", + name_tag, + )); } } if !first { diff --git a/src/commands/config.rs b/src/commands/config.rs index 3b36c88fad..337e3437f9 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -70,9 +70,9 @@ pub fn config( if let Some(v) = get { let key = v.to_string(); - let value = result - .get(&key) - .ok_or_else(|| ShellError::string(&format!("Missing key {} in config", key)))?; + let value = result.get(&key).ok_or_else(|| { + ShellError::labeled_error(&format!("Missing key in config"), "key", v.tag()) + })?; let mut results = VecDeque::new(); @@ -120,10 +120,11 @@ pub fn config( result.swap_remove(&key); config::write(&result, &configuration)?; } else { - return Err(ShellError::string(&format!( + return Err(ShellError::labeled_error( "{} does not exist in config", - key - ))); + "key", + v.tag(), + )); } let obj = VecDeque::from_iter(vec![Value::Row(result.into()).tagged(v.tag())]); diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index 21ef7fbfd9..e7966a61bf 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -44,11 +44,13 @@ fn run( registry: &CommandRegistry, raw_args: &RawCommandArgs, ) -> Result { - let path = match call_info - .args - .nth(0) - .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? - { + let path = match call_info.args.nth(0).ok_or_else(|| { + ShellError::labeled_error( + "No file or directory specified", + "for command", + call_info.name_tag, + ) + })? { file => file, }; let path_buf = path.as_path()?; diff --git a/src/commands/open.rs b/src/commands/open.rs index 97b0df2744..6ea752e9da 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -45,11 +45,13 @@ fn run( let cwd = PathBuf::from(shell_manager.path()); let full_path = PathBuf::from(cwd); - let path = match call_info - .args - .nth(0) - .ok_or_else(|| ShellError::string(&format!("No file or directory specified")))? - { + let path = match call_info.args.nth(0).ok_or_else(|| { + ShellError::labeled_error( + "No file or directory specified", + "for command", + call_info.name_tag, + ) + })? { file => file, }; let path_buf = path.as_path()?; diff --git a/src/commands/plugin.rs b/src/commands/plugin.rs index e769a7b5c7..5dfbe6be5b 100644 --- a/src/commands/plugin.rs +++ b/src/commands/plugin.rs @@ -128,7 +128,7 @@ pub fn filter_plugin( }, Err(e) => { let mut result = VecDeque::new(); - result.push_back(Err(ShellError::string(format!( + result.push_back(Err(ShellError::untagged_runtime_error(format!( "Error while processing begin_filter response: {:?} {}", e, input )))); @@ -138,7 +138,7 @@ pub fn filter_plugin( } Err(e) => { let mut result = VecDeque::new(); - result.push_back(Err(ShellError::string(format!( + result.push_back(Err(ShellError::untagged_runtime_error(format!( "Error while reading begin_filter response: {:?}", e )))); @@ -189,7 +189,7 @@ pub fn filter_plugin( }, Err(e) => { let mut result = VecDeque::new(); - result.push_back(Err(ShellError::string(format!( + result.push_back(Err(ShellError::untagged_runtime_error(format!( "Error while processing end_filter response: {:?} {}", e, input )))); @@ -199,7 +199,7 @@ pub fn filter_plugin( } Err(e) => { let mut result = VecDeque::new(); - result.push_back(Err(ShellError::string(format!( + result.push_back(Err(ShellError::untagged_runtime_error(format!( "Error while reading end_filter: {:?}", e )))); @@ -236,7 +236,7 @@ pub fn filter_plugin( }, Err(e) => { let mut result = VecDeque::new(); - result.push_back(Err(ShellError::string(format!( + result.push_back(Err(ShellError::untagged_runtime_error(format!( "Error while processing filter response: {:?} {}", e, input )))); @@ -246,7 +246,7 @@ pub fn filter_plugin( } Err(e) => { let mut result = VecDeque::new(); - result.push_back(Err(ShellError::string(format!( + result.push_back(Err(ShellError::untagged_runtime_error(format!( "Error while reading filter response: {:?}", e )))); diff --git a/src/commands/post.rs b/src/commands/post.rs index 5a77afd14b..a82f1b42b1 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -55,18 +55,14 @@ fn run( raw_args: &RawCommandArgs, ) -> Result { let call_info = call_info.clone(); - let path = match call_info - .args - .nth(0) - .ok_or_else(|| ShellError::string(&format!("No url specified")))? - { + let path = match call_info.args.nth(0).ok_or_else(|| { + ShellError::labeled_error("No url specified", "for command", call_info.name_tag) + })? { file => file.clone(), }; - let body = match call_info - .args - .nth(1) - .ok_or_else(|| ShellError::string(&format!("No body specified")))? - { + let body = match call_info.args.nth(1).ok_or_else(|| { + ShellError::labeled_error("No body specified", "for command", call_info.name_tag) + })? { file => file.clone(), }; let path_str = path.as_string()?; diff --git a/src/commands/save.rs b/src/commands/save.rs index 44e07da5ed..0156fc3557 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -150,7 +150,6 @@ fn save( } }, None => { - eprintln!("{:?} {:?}", anchor, source_map); yield Err(ShellError::labeled_error( "Save requires a filepath (2)", "needs path", @@ -213,9 +212,9 @@ fn save( match content { Ok(save_data) => match std::fs::write(full_path, save_data) { Ok(o) => o, - Err(e) => yield Err(ShellError::string(e.to_string())), + Err(e) => yield Err(ShellError::labeled_error(e.to_string(), "for command", name)), }, - Err(e) => yield Err(ShellError::string(e.to_string())), + Err(e) => yield Err(ShellError::labeled_error(e.to_string(), "for command", name)), } }; diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 1897fb86b7..66121df53e 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -32,8 +32,8 @@ impl WholeStreamCommand for ToCSV { } } -pub fn value_to_csv_value(v: &Value) -> Value { - match v { +pub fn value_to_csv_value(v: &Tagged) -> Tagged { + match &v.item { Value::Primitive(Primitive::String(s)) => Value::Primitive(Primitive::String(s.clone())), Value::Primitive(Primitive::Nothing) => Value::Primitive(Primitive::Nothing), Value::Primitive(Primitive::Boolean(b)) => Value::Primitive(Primitive::Boolean(b.clone())), @@ -47,10 +47,11 @@ pub fn value_to_csv_value(v: &Value) -> Value { Value::Block(_) => Value::Primitive(Primitive::Nothing), _ => Value::Primitive(Primitive::Nothing), } + .tagged(v.tag) } -fn to_string_helper(v: &Value) -> Result { - match v { +fn to_string_helper(v: &Tagged) -> Result { + match &v.item { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), Value::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?), @@ -60,7 +61,7 @@ fn to_string_helper(v: &Value) -> Result { Value::Table(_) => return Ok(String::from("[Table]")), Value::Row(_) => return Ok(String::from("[Row]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), - _ => return Err(ShellError::string("Unexpected value")), + _ => return Err(ShellError::labeled_error("Unexpected value", "", v.tag)), } } @@ -76,7 +77,9 @@ fn merge_descriptors(values: &[Tagged]) -> Vec { ret } -pub fn to_string(v: &Value) -> Result { +pub fn to_string(tagged_value: &Tagged) -> Result { + let v = &tagged_value.item; + match v { Value::Row(o) => { let mut wtr = WriterBuilder::new().from_writer(vec![]); @@ -92,11 +95,20 @@ pub fn to_string(v: &Value) -> Result { wtr.write_record(fields).expect("can not write."); wtr.write_record(values).expect("can not write."); - return Ok(String::from_utf8( - wtr.into_inner() - .map_err(|_| ShellError::string("Could not convert record"))?, - ) - .map_err(|_| ShellError::string("Could not convert record"))?); + return Ok(String::from_utf8(wtr.into_inner().map_err(|_| { + ShellError::labeled_error( + "Could not convert record", + "original value", + tagged_value.tag, + ) + })?) + .map_err(|_| { + ShellError::labeled_error( + "Could not convert record", + "original value", + tagged_value.tag, + ) + })?); } Value::Table(list) => { let mut wtr = WriterBuilder::new().from_writer(vec![]); @@ -120,13 +132,22 @@ pub fn to_string(v: &Value) -> Result { wtr.write_record(&row).expect("can not write"); } - return Ok(String::from_utf8( - wtr.into_inner() - .map_err(|_| ShellError::string("Could not convert record"))?, - ) - .map_err(|_| ShellError::string("Could not convert record"))?); + return Ok(String::from_utf8(wtr.into_inner().map_err(|_| { + ShellError::labeled_error( + "Could not convert record", + "original value", + tagged_value.tag, + ) + })?) + .map_err(|_| { + ShellError::labeled_error( + "Could not convert record", + "original value", + tagged_value.tag, + ) + })?); } - _ => return to_string_helper(&v), + _ => return to_string_helper(tagged_value), } } @@ -148,7 +169,7 @@ fn to_csv( }; for value in to_process_input { - match to_string(&value_to_csv_value(&value.item)) { + match to_string(&value_to_csv_value(&value)) { Ok(x) => { let converted = if headerless { x.lines().skip(1).collect() diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index 4edc26face..7127a3195b 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -32,7 +32,9 @@ impl WholeStreamCommand for ToTSV { } } -pub fn value_to_tsv_value(v: &Value) -> Value { +pub fn value_to_tsv_value(tagged_value: &Tagged) -> Tagged { + let v = &tagged_value.item; + match v { Value::Primitive(Primitive::String(s)) => Value::Primitive(Primitive::String(s.clone())), Value::Primitive(Primitive::Nothing) => Value::Primitive(Primitive::Nothing), @@ -47,20 +49,28 @@ pub fn value_to_tsv_value(v: &Value) -> Value { Value::Block(_) => Value::Primitive(Primitive::Nothing), _ => Value::Primitive(Primitive::Nothing), } + .tagged(tagged_value.tag) } -fn to_string_helper(v: &Value) -> Result { +fn to_string_helper(tagged_value: &Tagged) -> Result { + let v = &tagged_value.item; match v { Value::Primitive(Primitive::Date(d)) => Ok(d.to_string()), Value::Primitive(Primitive::Bytes(b)) => Ok(format!("{}", b)), - Value::Primitive(Primitive::Boolean(_)) => Ok(v.as_string()?), - Value::Primitive(Primitive::Decimal(_)) => Ok(v.as_string()?), - Value::Primitive(Primitive::Int(_)) => Ok(v.as_string()?), - Value::Primitive(Primitive::Path(_)) => Ok(v.as_string()?), + Value::Primitive(Primitive::Boolean(_)) => Ok(tagged_value.as_string()?), + Value::Primitive(Primitive::Decimal(_)) => Ok(tagged_value.as_string()?), + Value::Primitive(Primitive::Int(_)) => Ok(tagged_value.as_string()?), + Value::Primitive(Primitive::Path(_)) => Ok(tagged_value.as_string()?), Value::Table(_) => return Ok(String::from("[table]")), Value::Row(_) => return Ok(String::from("[row]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), - _ => return Err(ShellError::string("Unexpected value")), + _ => { + return Err(ShellError::labeled_error( + "Unexpected value", + "original value", + tagged_value.tag, + )) + } } } @@ -76,7 +86,9 @@ fn merge_descriptors(values: &[Tagged]) -> Vec { ret } -pub fn to_string(v: &Value) -> Result { +pub fn to_string(tagged_value: &Tagged) -> Result { + let v = &tagged_value.item; + match v { Value::Row(o) => { let mut wtr = WriterBuilder::new().delimiter(b'\t').from_writer(vec![]); @@ -91,11 +103,20 @@ pub fn to_string(v: &Value) -> Result { wtr.write_record(fields).expect("can not write."); wtr.write_record(values).expect("can not write."); - return Ok(String::from_utf8( - wtr.into_inner() - .map_err(|_| ShellError::string("Could not convert record"))?, - ) - .map_err(|_| ShellError::string("Could not convert record"))?); + return Ok(String::from_utf8(wtr.into_inner().map_err(|_| { + ShellError::labeled_error( + "Could not convert record", + "original value", + tagged_value.tag, + ) + })?) + .map_err(|_| { + ShellError::labeled_error( + "Could not convert record", + "original value", + tagged_value.tag, + ) + })?); } Value::Table(list) => { let mut wtr = WriterBuilder::new().delimiter(b'\t').from_writer(vec![]); @@ -119,13 +140,22 @@ pub fn to_string(v: &Value) -> Result { wtr.write_record(&row).expect("can not write"); } - return Ok(String::from_utf8( - wtr.into_inner() - .map_err(|_| ShellError::string("Could not convert record"))?, - ) - .map_err(|_| ShellError::string("Could not convert record"))?); + return Ok(String::from_utf8(wtr.into_inner().map_err(|_| { + ShellError::labeled_error( + "Could not convert record", + "original value", + tagged_value.tag, + ) + })?) + .map_err(|_| { + ShellError::labeled_error( + "Could not convert record", + "original value", + tagged_value.tag, + ) + })?); } - _ => return to_string_helper(&v), + _ => return to_string_helper(tagged_value), } } @@ -147,7 +177,7 @@ fn to_tsv( }; for value in to_process_input { - match to_string(&value_to_tsv_value(&value.item)) { + match to_string(&value_to_tsv_value(&value)) { Ok(x) => { let converted = if headerless { x.lines().skip(1).collect() diff --git a/src/context.rs b/src/context.rs index 6c55aff5c4..a090898328 100644 --- a/src/context.rs +++ b/src/context.rs @@ -7,7 +7,7 @@ use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::error::Error; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; use uuid::Uuid; #[derive(Clone, Debug, Serialize, Deserialize)] @@ -77,7 +77,7 @@ impl CommandRegistry { #[derive(Clone)] pub struct Context { registry: CommandRegistry, - pub(crate) source_map: SourceMap, + pub(crate) source_map: Arc>, host: Arc>, pub(crate) shell_manager: ShellManager, } @@ -99,7 +99,7 @@ impl Context { let registry = CommandRegistry::new(); Ok(Context { registry: registry.clone(), - source_map: SourceMap::new(), + source_map: Arc::new(Mutex::new(SourceMap::new())), host: Arc::new(Mutex::new(crate::env::host::BasicHost)), shell_manager: ShellManager::basic(registry)?, }) @@ -118,7 +118,9 @@ impl Context { } pub fn add_anchor_location(&mut self, uuid: Uuid, anchor_location: AnchorLocation) { - self.source_map.insert(uuid, anchor_location); + let mut source_map = self.source_map.lock().unwrap(); + + source_map.insert(uuid, anchor_location); } pub(crate) fn get_command(&self, name: &str) -> Option> { diff --git a/src/data/base.rs b/src/data/base.rs index 176560137f..735196c97f 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -298,7 +298,7 @@ impl fmt::Debug for ValueDebug<'_> { } impl Tagged { - pub(crate) fn tagged_type_name(&self) -> Tagged { + pub fn tagged_type_name(&self) -> Tagged { let name = self.type_name(); Tagged::from_item(name, self.tag()) } @@ -424,10 +424,27 @@ impl Tagged { Ok(out.tagged(self.tag)) } + + pub(crate) fn as_string(&self) -> Result { + match &self.item { + Value::Primitive(Primitive::String(s)) => Ok(s.clone()), + Value::Primitive(Primitive::Boolean(x)) => Ok(format!("{}", x)), + Value::Primitive(Primitive::Decimal(x)) => Ok(format!("{}", x)), + Value::Primitive(Primitive::Int(x)) => Ok(format!("{}", x)), + Value::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)), + Value::Primitive(Primitive::Path(x)) => Ok(format!("{}", x.display())), + // TODO: this should definitely be more general with better errors + other => Err(ShellError::labeled_error( + "Expected string", + other.type_name(), + self.tag, + )), + } + } } impl Value { - pub(crate) fn type_name(&self) -> String { + pub fn type_name(&self) -> String { match self { Value::Primitive(p) => p.type_name(), Value::Row(_) => format!("row"), @@ -738,22 +755,6 @@ impl Value { } } - pub(crate) fn as_string(&self) -> Result { - match self { - Value::Primitive(Primitive::String(s)) => Ok(s.clone()), - Value::Primitive(Primitive::Boolean(x)) => Ok(format!("{}", x)), - Value::Primitive(Primitive::Decimal(x)) => Ok(format!("{}", x)), - Value::Primitive(Primitive::Int(x)) => Ok(format!("{}", x)), - Value::Primitive(Primitive::Bytes(x)) => Ok(format!("{}", x)), - Value::Primitive(Primitive::Path(x)) => Ok(format!("{}", x.display())), - // TODO: this should definitely be more general with better errors - other => Err(ShellError::string(format!( - "Expected string, got {:?}", - other - ))), - } - } - pub(crate) fn is_true(&self) -> bool { match self { Value::Primitive(Primitive::Boolean(true)) => true, @@ -806,9 +807,14 @@ impl Value { Value::Primitive(Primitive::Date(s.into())) } - pub fn date_from_str(s: &str) -> Result { - let date = DateTime::parse_from_rfc3339(s) - .map_err(|err| ShellError::string(&format!("Date parse error: {}", err)))?; + pub fn date_from_str(s: Tagged<&str>) -> Result { + let date = DateTime::parse_from_rfc3339(s.item).map_err(|err| { + ShellError::labeled_error( + &format!("Date parse error: {}", err), + "original value", + s.tag, + ) + })?; let date = date.with_timezone(&chrono::offset::Utc); diff --git a/src/data/config.rs b/src/data/config.rs index 1cb4533d8e..657287d2f2 100644 --- a/src/data/config.rs +++ b/src/data/config.rs @@ -51,8 +51,9 @@ pub fn user_data() -> Result { } pub fn app_path(app_data_type: AppDataType, display: &str) -> Result { - let path = app_root(app_data_type, &APP_INFO) - .map_err(|err| ShellError::string(&format!("Couldn't open {} path:\n{}", display, err)))?; + let path = app_root(app_data_type, &APP_INFO).map_err(|err| { + ShellError::untagged_runtime_error(&format!("Couldn't open {} path:\n{}", display, err)) + })?; Ok(path) } @@ -75,10 +76,21 @@ pub fn read( let tag = tag.into(); let contents = fs::read_to_string(filename) .map(|v| v.tagged(tag)) - .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?; + .map_err(|err| { + ShellError::labeled_error( + &format!("Couldn't read config file:\n{}", err), + "file name", + tag, + ) + })?; - let parsed: toml::Value = toml::from_str(&contents) - .map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?; + let parsed: toml::Value = toml::from_str(&contents).map_err(|err| { + ShellError::labeled_error( + &format!("Couldn't parse config file:\n{}", err), + "file name", + tag, + ) + })?; let value = convert_toml_value_to_nu_value(&parsed, tag); let tag = value.tag(); diff --git a/src/data/meta.rs b/src/data/meta.rs index b66b009cc2..08125359e4 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -240,6 +240,16 @@ impl Tag { } } + pub fn for_char(pos: usize, anchor: Uuid) -> Tag { + Tag { + anchor, + span: Span { + start: pos, + end: pos + 1, + }, + } + } + pub fn unknown_span(anchor: Uuid) -> Tag { Tag { anchor, @@ -267,6 +277,24 @@ impl Tag { } } + pub fn until_option(&self, other: Option>) -> Tag { + match other { + Some(other) => { + let other = other.into(); + debug_assert!( + self.anchor == other.anchor, + "Can only merge two tags with the same anchor" + ); + + Tag { + span: Span::new(self.span.start, other.span.end), + anchor: self.anchor, + } + } + None => *self, + } + } + pub fn slice<'a>(&self, source: &'a str) -> &'a str { self.span.slice(source) } @@ -284,6 +312,7 @@ impl Tag { } } +#[allow(unused)] pub fn tag_for_tagged_list(mut iter: impl Iterator) -> Tag { let first = iter.next(); diff --git a/src/errors.rs b/src/errors.rs index a070f6f54e..2d42552250 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -20,6 +20,14 @@ impl Description { Description::Synthetic(s) => Err(s), } } + + #[allow(unused)] + fn tag(&self) -> Tag { + match self { + Description::Source(tagged) => tagged.tag, + Description::Synthetic(_) => Tag::unknown(), + } + } } #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] @@ -36,6 +44,13 @@ pub struct ShellError { cause: Option>, } +impl ShellError { + #[allow(unused)] + pub(crate) fn tag(&self) -> Option { + self.error.tag() + } +} + impl ToDebug for ShellError { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { self.error.fmt_debug(f, source) @@ -47,12 +62,12 @@ impl serde::de::Error for ShellError { where T: std::fmt::Display, { - ShellError::string(msg.to_string()) + ShellError::untagged_runtime_error(msg.to_string()) } } impl ShellError { - pub(crate) fn type_error( + pub fn type_error( expected: impl Into, actual: Tagged>, ) -> ShellError { @@ -63,6 +78,13 @@ impl ShellError { .start() } + pub fn untagged_runtime_error(error: impl Into) -> ShellError { + ProximateShellError::UntaggedRuntimeError { + reason: error.into(), + } + .start() + } + pub(crate) fn unexpected_eof(expected: impl Into, tag: Tag) -> ShellError { ProximateShellError::UnexpectedEof { expected: expected.into(), @@ -174,9 +196,6 @@ impl ShellError { pub(crate) fn to_diagnostic(self) -> Diagnostic { match self.error { - ProximateShellError::String(StringError { title, .. }) => { - Diagnostic::new(Severity::Error, title) - } ProximateShellError::InvalidCommand { command } => { Diagnostic::new(Severity::Error, "Invalid command") .with_label(Label::new_primary(command)) @@ -286,7 +305,7 @@ impl ShellError { } => Diagnostic::new(Severity::Error, "Syntax Error") .with_label(Label::new_primary(tag).with_message(item)), - ProximateShellError::MissingProperty { subpath, expr } => { + ProximateShellError::MissingProperty { subpath, expr, .. } => { let subpath = subpath.into_label(); let expr = expr.into_label(); @@ -310,6 +329,8 @@ impl ShellError { .with_label(Label::new_primary(left.tag()).with_message(left.item)) .with_label(Label::new_secondary(right.tag()).with_message(right.item)) } + + ProximateShellError::UntaggedRuntimeError { reason } => Diagnostic::new(Severity::Error, format!("Error: {}", reason)) } } @@ -343,20 +364,16 @@ impl ShellError { ) } - pub fn string(title: impl Into) -> ShellError { - ProximateShellError::String(StringError::new(title.into(), Value::nothing())).start() - } - pub(crate) fn unimplemented(title: impl Into) -> ShellError { - ShellError::string(&format!("Unimplemented: {}", title.into())) + ShellError::untagged_runtime_error(&format!("Unimplemented: {}", title.into())) } pub(crate) fn unexpected(title: impl Into) -> ShellError { - ShellError::string(&format!("Unexpected: {}", title.into())) + ShellError::untagged_runtime_error(&format!("Unexpected: {}", title.into())) } pub(crate) fn unreachable(title: impl Into) -> ShellError { - ShellError::string(&format!("BUG: Unreachable: {}", title.into())) + ShellError::untagged_runtime_error(&format!("BUG: Unreachable: {}", title.into())) } } @@ -401,7 +418,6 @@ impl ExpectedRange { #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] pub enum ProximateShellError { - String(StringError), SyntaxError { problem: Tagged, }, @@ -419,6 +435,7 @@ pub enum ProximateShellError { MissingProperty { subpath: Description, expr: Description, + tag: Tag, }, MissingValue { tag: Option, @@ -439,6 +456,9 @@ pub enum ProximateShellError { left: Tagged, right: Tagged, }, + UntaggedRuntimeError { + reason: String, + }, } impl ProximateShellError { @@ -448,6 +468,22 @@ impl ProximateShellError { error: self, } } + + pub(crate) fn tag(&self) -> Option { + Some(match self { + ProximateShellError::SyntaxError { problem } => problem.tag(), + ProximateShellError::UnexpectedEof { tag, .. } => *tag, + ProximateShellError::InvalidCommand { command } => *command, + ProximateShellError::TypeError { actual, .. } => actual.tag, + ProximateShellError::MissingProperty { tag, .. } => *tag, + ProximateShellError::MissingValue { tag, .. } => return *tag, + ProximateShellError::ArgumentError { tag, .. } => *tag, + ProximateShellError::RangeError { actual_kind, .. } => actual_kind.tag, + ProximateShellError::Diagnostic(..) => return None, + ProximateShellError::UntaggedRuntimeError { .. } => return None, + ProximateShellError::CoerceError { left, right } => left.tag.until(right.tag), + }) + } } impl ToDebug for ProximateShellError { @@ -491,7 +527,6 @@ pub struct StringError { impl std::fmt::Display for ShellError { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { match &self.error { - ProximateShellError::String(s) => write!(f, "{}", &s.title), ProximateShellError::MissingValue { .. } => write!(f, "MissingValue"), ProximateShellError::InvalidCommand { .. } => write!(f, "InvalidCommand"), ProximateShellError::TypeError { .. } => write!(f, "TypeError"), @@ -502,6 +537,7 @@ impl std::fmt::Display for ShellError { ProximateShellError::ArgumentError { .. } => write!(f, "ArgumentError"), ProximateShellError::Diagnostic(_) => write!(f, ""), ProximateShellError::CoerceError { .. } => write!(f, "CoerceError"), + ProximateShellError::UntaggedRuntimeError { .. } => write!(f, "UntaggedRuntimeError"), } } } @@ -510,71 +546,43 @@ impl std::error::Error for ShellError {} impl std::convert::From> for ShellError { fn from(input: Box) -> ShellError { - ProximateShellError::String(StringError { - title: format!("{}", input), - error: Value::nothing(), - }) - .start() + ShellError::untagged_runtime_error(format!("{}", input)) } } impl std::convert::From for ShellError { fn from(input: std::io::Error) -> ShellError { - ProximateShellError::String(StringError { - title: format!("{}", input), - error: Value::nothing(), - }) - .start() + ShellError::untagged_runtime_error(format!("{}", input)) } } impl std::convert::From for ShellError { fn from(input: subprocess::PopenError) -> ShellError { - ProximateShellError::String(StringError { - title: format!("{}", input), - error: Value::nothing(), - }) - .start() + ShellError::untagged_runtime_error(format!("{}", input)) } } impl std::convert::From for ShellError { fn from(input: serde_yaml::Error) -> ShellError { - ProximateShellError::String(StringError { - title: format!("{:?}", input), - error: Value::nothing(), - }) - .start() + ShellError::untagged_runtime_error(format!("{:?}", input)) } } impl std::convert::From for ShellError { fn from(input: toml::ser::Error) -> ShellError { - ProximateShellError::String(StringError { - title: format!("{:?}", input), - error: Value::nothing(), - }) - .start() + ShellError::untagged_runtime_error(format!("{:?}", input)) } } impl std::convert::From for ShellError { fn from(input: serde_json::Error) -> ShellError { - ProximateShellError::String(StringError { - title: format!("{:?}", input), - error: Value::nothing(), - }) - .start() + ShellError::untagged_runtime_error(format!("{:?}", input)) } } impl std::convert::From> for ShellError { fn from(input: Box) -> ShellError { - ProximateShellError::String(StringError { - title: format!("{:?}", input), - error: Value::nothing(), - }) - .start() + ShellError::untagged_runtime_error(format!("{:?}", input)) } } diff --git a/src/parser.rs b/src/parser.rs index 5fcfaaa27e..3fd853c85c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -7,18 +7,18 @@ pub(crate) mod registry; use crate::errors::ShellError; pub(crate) use deserializer::ConfigDeserializer; +pub(crate) use hir::syntax_shape::flat_shape::FlatShape; pub(crate) use hir::TokensIterator; pub(crate) use parse::call_node::CallNode; pub(crate) use parse::files::Files; -pub(crate) use parse::flag::Flag; +pub(crate) use parse::flag::{Flag, FlagKind}; pub(crate) use parse::operator::Operator; pub(crate) use parse::parser::{nom_input, pipeline}; pub(crate) use parse::pipeline::{Pipeline, PipelineElement}; pub(crate) use parse::text::Text; pub(crate) use parse::token_tree::{DelimitedNode, Delimiter, TokenNode}; -pub(crate) use parse::tokens::{RawToken, Token}; +pub(crate) use parse::tokens::{RawNumber, RawToken}; pub(crate) use parse::unit::Unit; -pub(crate) use parse_command::parse_command_tail; pub(crate) use registry::CommandRegistry; pub fn parse(input: &str, anchor: uuid::Uuid) -> Result { diff --git a/src/parser/hir/expand_external_tokens.rs b/src/parser/hir/expand_external_tokens.rs index 30a2a90aaf..238cb4b01b 100644 --- a/src/parser/hir/expand_external_tokens.rs +++ b/src/parser/hir/expand_external_tokens.rs @@ -1,5 +1,11 @@ use crate::errors::ShellError; -use crate::parser::{TokenNode, TokensIterator}; +use crate::parser::{ + hir::syntax_shape::{ + color_syntax, expand_atom, AtomicToken, ColorSyntax, ExpandContext, ExpansionRule, + MaybeSpaceShape, + }, + FlatShape, TokenNode, TokensIterator, +}; use crate::{Tag, Tagged, Text}; pub fn expand_external_tokens( @@ -19,6 +25,34 @@ pub fn expand_external_tokens( Ok(out) } +#[derive(Debug, Copy, Clone)] +pub struct ExternalTokensShape; + +impl ColorSyntax for ExternalTokensShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Self::Info { + loop { + // Allow a space + color_syntax(&MaybeSpaceShape, token_nodes, context, shapes); + + // Process an external expression. External expressions are mostly words, with a + // few exceptions (like $variables and path expansion rules) + match color_syntax(&ExternalExpression, token_nodes, context, shapes).1 { + ExternalExpressionResult::Eof => break, + ExternalExpressionResult::Processed => continue, + } + } + } +} + pub fn expand_next_expression( token_nodes: &mut TokensIterator<'_>, ) -> Result, ShellError> { @@ -48,16 +82,15 @@ pub fn expand_next_expression( fn triage_external_head(node: &TokenNode) -> Result { Ok(match node { TokenNode::Token(token) => token.tag(), - TokenNode::Call(_call) => unimplemented!(), - TokenNode::Nodes(_nodes) => unimplemented!(), - TokenNode::Delimited(_delimited) => unimplemented!(), - TokenNode::Pipeline(_pipeline) => unimplemented!(), + TokenNode::Call(_call) => unimplemented!("TODO: OMG"), + TokenNode::Nodes(_nodes) => unimplemented!("TODO: OMG"), + TokenNode::Delimited(_delimited) => unimplemented!("TODO: OMG"), + TokenNode::Pipeline(_pipeline) => unimplemented!("TODO: OMG"), TokenNode::Flag(flag) => flag.tag(), - TokenNode::Member(member) => *member, TokenNode::Whitespace(_whitespace) => { unreachable!("This function should be called after next_non_ws()") } - TokenNode::Error(_error) => unimplemented!(), + TokenNode::Error(_error) => unimplemented!("TODO: OMG"), }) } @@ -73,7 +106,7 @@ fn triage_continuation<'a, 'b>( match &node { node if node.is_whitespace() => return Ok(None), - TokenNode::Token(..) | TokenNode::Flag(..) | TokenNode::Member(..) => {} + TokenNode::Token(..) | TokenNode::Flag(..) => {} TokenNode::Call(..) => unimplemented!("call"), TokenNode::Nodes(..) => unimplemented!("nodes"), TokenNode::Delimited(..) => unimplemented!("delimited"), @@ -85,3 +118,42 @@ fn triage_continuation<'a, 'b>( peeked.commit(); Ok(Some(node.tag())) } + +#[must_use] +enum ExternalExpressionResult { + Eof, + Processed, +} + +#[derive(Debug, Copy, Clone)] +struct ExternalExpression; + +impl ColorSyntax for ExternalExpression { + type Info = ExternalExpressionResult; + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> ExternalExpressionResult { + let atom = match expand_atom( + token_nodes, + "external word", + context, + ExpansionRule::permissive(), + ) { + Err(_) => unreachable!("TODO: separate infallible expand_atom"), + Ok(Tagged { + item: AtomicToken::Eof { .. }, + .. + }) => return ExternalExpressionResult::Eof, + Ok(atom) => atom, + }; + + atom.color_tokens(shapes); + return ExternalExpressionResult::Processed; + } +} diff --git a/src/parser/hir/syntax_shape.rs b/src/parser/hir/syntax_shape.rs index 5dcbd0fb76..1a140d86bd 100644 --- a/src/parser/hir/syntax_shape.rs +++ b/src/parser/hir/syntax_shape.rs @@ -1,34 +1,45 @@ mod block; mod expression; +pub(crate) mod flat_shape; use crate::cli::external_command; -use crate::commands::{classified::InternalCommand, ClassifiedCommand, Command}; +use crate::commands::{ + classified::{ClassifiedPipeline, InternalCommand}, + ClassifiedCommand, Command, +}; +use crate::parser::hir::expand_external_tokens::ExternalTokensShape; use crate::parser::hir::syntax_shape::block::AnyBlockShape; use crate::parser::hir::tokens_iterator::Peeked; -use crate::parser::parse_command::parse_command_tail; +use crate::parser::parse_command::{parse_command_tail, CommandTailShape}; +use crate::parser::PipelineElement; use crate::parser::{ hir, hir::{debug_tokens, TokensIterator}, - Operator, RawToken, TokenNode, + Operator, Pipeline, RawToken, TokenNode, }; use crate::prelude::*; use derive_new::new; use getset::Getters; -use log::trace; +use log::{self, log_enabled, trace}; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; +pub(crate) use self::expression::atom::{expand_atom, AtomicToken, ExpansionRule}; +pub(crate) use self::expression::delimited::{ + color_delimited_square, expand_delimited_square, DelimitedShape, +}; pub(crate) use self::expression::file_path::FilePathShape; -pub(crate) use self::expression::list::ExpressionListShape; +pub(crate) use self::expression::list::{BackoffColoringMode, ExpressionListShape}; pub(crate) use self::expression::number::{IntShape, NumberShape}; -pub(crate) use self::expression::pattern::PatternShape; +pub(crate) use self::expression::pattern::{BarePatternShape, PatternShape}; pub(crate) use self::expression::string::StringShape; pub(crate) use self::expression::unit::UnitShape; pub(crate) use self::expression::variable_path::{ - ColumnPathShape, DotShape, ExpressionContinuation, ExpressionContinuationShape, MemberShape, - PathTailShape, VariablePathShape, + ColorableDotShape, ColumnPathShape, DotShape, ExpressionContinuation, + ExpressionContinuationShape, MemberShape, PathTailShape, VariablePathShape, }; pub(crate) use self::expression::{continue_expression, AnyExpressionShape}; +pub(crate) use self::flat_shape::FlatShape; #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum SyntaxShape { @@ -41,9 +52,56 @@ pub enum SyntaxShape { Int, Path, Pattern, - Binary, Block, - Boolean, +} + +impl FallibleColorSyntax for SyntaxShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + match self { + SyntaxShape::Any => { + color_fallible_syntax(&AnyExpressionShape, token_nodes, context, shapes) + } + SyntaxShape::List => { + color_syntax(&ExpressionListShape, token_nodes, context, shapes); + Ok(()) + } + SyntaxShape::Int => color_fallible_syntax(&IntShape, token_nodes, context, shapes), + SyntaxShape::String => color_fallible_syntax_with( + &StringShape, + &FlatShape::String, + token_nodes, + context, + shapes, + ), + SyntaxShape::Member => { + color_fallible_syntax(&MemberShape, token_nodes, context, shapes) + } + SyntaxShape::ColumnPath => { + color_fallible_syntax(&ColumnPathShape, token_nodes, context, shapes) + } + SyntaxShape::Number => { + color_fallible_syntax(&NumberShape, token_nodes, context, shapes) + } + SyntaxShape::Path => { + color_fallible_syntax(&FilePathShape, token_nodes, context, shapes) + } + SyntaxShape::Pattern => { + color_fallible_syntax(&PatternShape, token_nodes, context, shapes) + } + SyntaxShape::Block => { + color_fallible_syntax(&AnyBlockShape, token_nodes, context, shapes) + } + } + } } impl ExpandExpression for SyntaxShape { @@ -73,9 +131,7 @@ impl ExpandExpression for SyntaxShape { SyntaxShape::Number => expand_expr(&NumberShape, token_nodes, context), SyntaxShape::Path => expand_expr(&FilePathShape, token_nodes, context), SyntaxShape::Pattern => expand_expr(&PatternShape, token_nodes, context), - SyntaxShape::Binary => Err(ShellError::unimplemented("SyntaxShape:Binary")), SyntaxShape::Block => expand_expr(&AnyBlockShape, token_nodes, context), - SyntaxShape::Boolean => Err(ShellError::unimplemented("SyntaxShape:Boolean")), } } } @@ -92,9 +148,7 @@ impl std::fmt::Display for SyntaxShape { SyntaxShape::Number => write!(f, "Number"), SyntaxShape::Path => write!(f, "Path"), SyntaxShape::Pattern => write!(f, "Pattern"), - SyntaxShape::Binary => write!(f, "Binary"), SyntaxShape::Block => write!(f, "Block"), - SyntaxShape::Boolean => write!(f, "Boolean"), } } } @@ -148,6 +202,50 @@ pub trait ExpandExpression: std::fmt::Debug + Copy { ) -> Result; } +pub trait FallibleColorSyntax: std::fmt::Debug + Copy { + type Info; + type Input; + + fn color_syntax<'a, 'b>( + &self, + input: &Self::Input, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result; +} + +pub trait ColorSyntax: std::fmt::Debug + Copy { + type Info; + type Input; + + fn color_syntax<'a, 'b>( + &self, + input: &Self::Input, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Self::Info; +} + +// impl ColorSyntax for T +// where +// T: FallibleColorSyntax, +// { +// type Info = Result; +// type Input = T::Input; + +// fn color_syntax<'a, 'b>( +// &self, +// input: &Self::Input, +// token_nodes: &'b mut TokensIterator<'a>, +// context: &ExpandContext, +// shapes: &mut Vec>, +// ) -> Result { +// FallibleColorSyntax::color_syntax(self, input, token_nodes, context, shapes) +// } +// } + pub(crate) trait ExpandSyntax: std::fmt::Debug + Copy { type Output: std::fmt::Debug; @@ -180,6 +278,130 @@ pub(crate) fn expand_syntax<'a, 'b, T: ExpandSyntax>( } } +pub fn color_syntax<'a, 'b, T: ColorSyntax, U>( + shape: &T, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, +) -> ((), U) { + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + let len = shapes.len(); + let result = shape.color_syntax(&(), token_nodes, context, shapes); + + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + + if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { + trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); + + if len < shapes.len() { + for i in len..(shapes.len()) { + trace!(target: "nu::color_syntax", "new shape :: {:?}", shapes[i]); + } + } else { + trace!(target: "nu::color_syntax", "no new shapes"); + } + } + + ((), result) +} + +pub fn color_fallible_syntax<'a, 'b, T: FallibleColorSyntax, U>( + shape: &T, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, +) -> Result { + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + if token_nodes.at_end() { + trace!(target: "nu::color_syntax", "at eof"); + return Err(ShellError::unexpected_eof("coloring", Tag::unknown())); + } + + let len = shapes.len(); + let result = shape.color_syntax(&(), token_nodes, context, shapes); + + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + + if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { + trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); + + if len < shapes.len() { + for i in len..(shapes.len()) { + trace!(target: "nu::color_syntax", "new shape :: {:?}", shapes[i]); + } + } else { + trace!(target: "nu::color_syntax", "no new shapes"); + } + } + + result +} + +pub fn color_syntax_with<'a, 'b, T: ColorSyntax, U, I>( + shape: &T, + input: &I, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, +) -> ((), U) { + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + let len = shapes.len(); + let result = shape.color_syntax(input, token_nodes, context, shapes); + + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + + if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { + trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); + + if len < shapes.len() { + for i in len..(shapes.len()) { + trace!(target: "nu::color_syntax", "new shape :: {:?}", shapes[i]); + } + } else { + trace!(target: "nu::color_syntax", "no new shapes"); + } + } + + ((), result) +} + +pub fn color_fallible_syntax_with<'a, 'b, T: FallibleColorSyntax, U, I>( + shape: &T, + input: &I, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, +) -> Result { + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + if token_nodes.at_end() { + trace!(target: "nu::color_syntax", "at eof"); + return Err(ShellError::unexpected_eof("coloring", Tag::unknown())); + } + + let len = shapes.len(); + let result = shape.color_syntax(input, token_nodes, context, shapes); + + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + + if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { + trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); + + if len < shapes.len() { + for i in len..(shapes.len()) { + trace!(target: "nu::color_syntax", "new shape :: {:?}", shapes[i]); + } + } else { + trace!(target: "nu::color_syntax", "no new shapes"); + } + } + + result +} + pub(crate) fn expand_expr<'a, 'b, T: ExpandExpression>( shape: &T, token_nodes: &'b mut TokensIterator<'a>, @@ -314,6 +536,33 @@ impl ExpandSyntax for BarePathShape { #[derive(Debug, Copy, Clone)] pub struct BareShape; +impl FallibleColorSyntax for BareShape { + type Info = (); + type Input = FlatShape; + + fn color_syntax<'a, 'b>( + &self, + input: &FlatShape, + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + token_nodes.peek_any_token(|token| match token { + // If it's a bare token, color it + TokenNode::Token(Tagged { + item: RawToken::Bare, + tag, + }) => { + shapes.push((*input).tagged(tag)); + Ok(()) + } + + // otherwise, fail + other => Err(ShellError::type_error("word", other.tagged_type_name())), + }) + } +} + impl ExpandSyntax for BareShape { type Output = Tagged; @@ -383,9 +632,129 @@ impl CommandSignature { } } +#[derive(Debug, Copy, Clone)] +pub struct PipelineShape; + +// The failure mode is if the head of the token stream is not a pipeline +impl FallibleColorSyntax for PipelineShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + // Make sure we're looking at a pipeline + let Pipeline { parts, .. } = token_nodes.peek_any_token(|node| node.as_pipeline())?; + + // Enumerate the pipeline parts + for part in parts { + // If the pipeline part has a prefix `|`, emit a pipe to color + if let Some(pipe) = part.pipe { + shapes.push(FlatShape::Pipe.tagged(pipe)); + } + + // Create a new iterator containing the tokens in the pipeline part to color + let mut token_nodes = TokensIterator::new(&part.tokens.item, part.tag, false); + + color_syntax(&MaybeSpaceShape, &mut token_nodes, context, shapes); + color_syntax(&CommandShape, &mut token_nodes, context, shapes); + } + + Ok(()) + } +} + +impl ExpandSyntax for PipelineShape { + type Output = ClassifiedPipeline; + fn expand_syntax<'a, 'b>( + &self, + iterator: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + let source = context.source; + + let peeked = iterator.peek_any().not_eof("pipeline")?; + let pipeline = peeked.node.as_pipeline()?; + peeked.commit(); + + let Pipeline { parts, .. } = pipeline; + + let commands: Result, ShellError> = parts + .iter() + .map(|item| classify_command(&item, context, &source)) + .collect(); + + Ok(ClassifiedPipeline { + commands: commands?, + }) + } +} + +pub enum CommandHeadKind { + External, + Internal(Signature), +} + #[derive(Debug, Copy, Clone)] pub struct CommandHeadShape; +impl FallibleColorSyntax for CommandHeadShape { + type Info = CommandHeadKind; + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result { + // If we don't ultimately find a token, roll back + token_nodes.atomic(|token_nodes| { + // First, take a look at the next token + let atom = expand_atom( + token_nodes, + "command head", + context, + ExpansionRule::permissive(), + )?; + + match atom.item { + // If the head is an explicit external command (^cmd), color it as an external command + AtomicToken::ExternalCommand { command } => { + shapes.push(FlatShape::ExternalCommand.tagged(command)); + Ok(CommandHeadKind::External) + } + + // If the head is a word, it depends on whether it matches a registered internal command + AtomicToken::Word { text } => { + let name = text.slice(context.source); + + if context.registry.has(name) { + // If the registry has the command, color it as an internal command + shapes.push(FlatShape::InternalCommand.tagged(text)); + let command = context.registry.expect_command(name); + Ok(CommandHeadKind::Internal(command.signature())) + } else { + // Otherwise, color it as an external command + shapes.push(FlatShape::ExternalCommand.tagged(text)); + Ok(CommandHeadKind::External) + } + } + + // Otherwise, we're not actually looking at a command + _ => Err(ShellError::syntax_error( + "No command at the head".tagged(atom.tag), + )), + } + }) + } +} + impl ExpandSyntax for CommandHeadShape { type Output = CommandSignature; @@ -395,7 +764,7 @@ impl ExpandSyntax for CommandHeadShape { context: &ExpandContext, ) -> Result { let node = - parse_single_node_skipping_ws(token_nodes, "command head1", |token, token_tag| { + parse_single_node_skipping_ws(token_nodes, "command head1", |token, token_tag, _| { Ok(match token { RawToken::ExternalCommand(tag) => CommandSignature::LiteralExternal { outer: token_tag, @@ -488,6 +857,44 @@ impl ExpandSyntax for ClassifiedCommandShape { #[derive(Debug, Copy, Clone)] pub struct InternalCommandHeadShape; +impl FallibleColorSyntax for InternalCommandHeadShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let peeked_head = token_nodes.peek_non_ws().not_eof("command head4"); + + let peeked_head = match peeked_head { + Err(_) => return Ok(()), + Ok(peeked_head) => peeked_head, + }; + + let _expr = match peeked_head.node { + TokenNode::Token(Tagged { + item: RawToken::Bare, + tag, + }) => shapes.push(FlatShape::Word.tagged(tag)), + + TokenNode::Token(Tagged { + item: RawToken::String(_inner_tag), + tag, + }) => shapes.push(FlatShape::String.tagged(tag)), + + _node => shapes.push(FlatShape::Error.tagged(peeked_head.node.tag())), + }; + + peeked_head.commit(); + + Ok(()) + } +} + impl ExpandExpression for InternalCommandHeadShape { fn expand_expr( &self, @@ -523,33 +930,52 @@ impl ExpandExpression for InternalCommandHeadShape { } } +pub(crate) struct SingleError<'token> { + expected: &'static str, + node: &'token Tagged, +} + +impl<'token> SingleError<'token> { + pub(crate) fn error(&self) -> ShellError { + ShellError::type_error(self.expected, self.node.type_name().tagged(self.node.tag)) + } +} + fn parse_single_node<'a, 'b, T>( token_nodes: &'b mut TokensIterator<'a>, expected: &'static str, - callback: impl FnOnce(RawToken, Tag) -> Result, + callback: impl FnOnce(RawToken, Tag, SingleError) -> Result, ) -> Result { - let peeked = token_nodes.peek_any().not_eof(expected)?; + token_nodes.peek_any_token(|node| match node { + TokenNode::Token(token) => callback( + token.item, + token.tag(), + SingleError { + expected, + node: token, + }, + ), - let expr = match peeked.node { - TokenNode::Token(token) => callback(token.item, token.tag())?, - - other => return Err(ShellError::type_error(expected, other.tagged_type_name())), - }; - - peeked.commit(); - - Ok(expr) + other => Err(ShellError::type_error(expected, other.tagged_type_name())), + }) } fn parse_single_node_skipping_ws<'a, 'b, T>( token_nodes: &'b mut TokensIterator<'a>, expected: &'static str, - callback: impl FnOnce(RawToken, Tag) -> Result, + callback: impl FnOnce(RawToken, Tag, SingleError) -> Result, ) -> Result { let peeked = token_nodes.peek_non_ws().not_eof(expected)?; let expr = match peeked.node { - TokenNode::Token(token) => callback(token.item, token.tag())?, + TokenNode::Token(token) => callback( + token.item, + token.tag(), + SingleError { + expected, + node: token, + }, + )?, other => return Err(ShellError::type_error(expected, other.tagged_type_name())), }; @@ -562,6 +988,36 @@ fn parse_single_node_skipping_ws<'a, 'b, T>( #[derive(Debug, Copy, Clone)] pub struct WhitespaceShape; +impl FallibleColorSyntax for WhitespaceShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let peeked = token_nodes.peek_any().not_eof("whitespace"); + + let peeked = match peeked { + Err(_) => return Ok(()), + Ok(peeked) => peeked, + }; + + let _tag = match peeked.node { + TokenNode::Whitespace(tag) => shapes.push(FlatShape::Whitespace.tagged(tag)), + + _other => return Ok(()), + }; + + peeked.commit(); + + Ok(()) + } +} + impl ExpandSyntax for WhitespaceShape { type Output = Tag; @@ -626,6 +1082,65 @@ pub struct MaybeSpacedExpression { inner: T, } +#[derive(Debug, Copy, Clone)] +pub struct MaybeSpaceShape; + +impl ColorSyntax for MaybeSpaceShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + shapes: &mut Vec>, + ) -> Self::Info { + let peeked = token_nodes.peek_any().not_eof("whitespace"); + + let peeked = match peeked { + Err(_) => return, + Ok(peeked) => peeked, + }; + + if let TokenNode::Whitespace(tag) = peeked.node { + peeked.commit(); + shapes.push(FlatShape::Whitespace.tagged(tag)); + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct SpaceShape; + +impl FallibleColorSyntax for SpaceShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let peeked = token_nodes.peek_any().not_eof("whitespace")?; + + match peeked.node { + TokenNode::Whitespace(tag) => { + peeked.commit(); + shapes.push(FlatShape::Whitespace.tagged(tag)); + Ok(()) + } + + other => Err(ShellError::type_error( + "whitespace", + other.tagged_type_name(), + )), + } + } +} + impl ExpandExpression for MaybeSpacedExpression { fn expand_expr<'a, 'b>( &self, @@ -660,3 +1175,87 @@ fn expand_variable(tag: Tag, token_tag: Tag, source: &Text) -> hir::Expression { hir::Expression::variable(tag, token_tag) } } + +fn classify_command( + command: &Tagged, + context: &ExpandContext, + source: &Text, +) -> Result { + let mut iterator = TokensIterator::new(&command.tokens.item, command.tag, true); + + let head = CommandHeadShape.expand_syntax(&mut iterator, &context)?; + + match &head { + CommandSignature::Expression(_) => Err(ShellError::syntax_error( + "Unexpected expression in command position".tagged(command.tag), + )), + + // If the command starts with `^`, treat it as an external command no matter what + CommandSignature::External(name) => { + let name_str = name.slice(source); + + external_command(&mut iterator, source, name_str.tagged(name)) + } + + CommandSignature::LiteralExternal { outer, inner } => { + let name_str = inner.slice(source); + + external_command(&mut iterator, source, name_str.tagged(outer)) + } + + CommandSignature::Internal(command) => { + let tail = + parse_command_tail(&command.signature(), &context, &mut iterator, command.tag)?; + + let (positional, named) = match tail { + None => (None, None), + Some((positional, named)) => (positional, named), + }; + + let call = hir::Call { + head: Box::new(head.to_expression()), + positional, + named, + }; + + Ok(ClassifiedCommand::Internal(InternalCommand::new( + command.name().to_string(), + command.tag, + call, + ))) + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct CommandShape; + +impl ColorSyntax for CommandShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) { + let kind = color_fallible_syntax(&CommandHeadShape, token_nodes, context, shapes); + + match kind { + Err(_) => { + // We didn't find a command, so we'll have to fall back to parsing this pipeline part + // as a blob of undifferentiated expressions + color_syntax(&ExpressionListShape, token_nodes, context, shapes); + } + + Ok(CommandHeadKind::External) => { + color_syntax(&ExternalTokensShape, token_nodes, context, shapes); + } + Ok(CommandHeadKind::Internal(signature)) => { + color_syntax_with(&CommandTailShape, &signature, token_nodes, context, shapes); + } + }; + } +} diff --git a/src/parser/hir/syntax_shape/block.rs b/src/parser/hir/syntax_shape/block.rs index a78292b34e..806681691e 100644 --- a/src/parser/hir/syntax_shape/block.rs +++ b/src/parser/hir/syntax_shape/block.rs @@ -2,10 +2,13 @@ use crate::errors::ShellError; use crate::parser::{ hir, hir::syntax_shape::{ - continue_expression, expand_expr, expand_syntax, ExpandContext, ExpandExpression, - ExpressionListShape, PathTailShape, VariablePathShape, + color_fallible_syntax, color_syntax_with, continue_expression, expand_expr, expand_syntax, + DelimitedShape, ExpandContext, ExpandExpression, ExpressionContinuationShape, + ExpressionListShape, FallibleColorSyntax, FlatShape, MemberShape, PathTailShape, + VariablePathShape, }, hir::tokens_iterator::TokensIterator, + parse::token_tree::Delimiter, RawToken, TokenNode, }; use crate::{Tag, Tagged, TaggedItem}; @@ -13,6 +16,49 @@ use crate::{Tag, Tagged, TaggedItem}; #[derive(Debug, Copy, Clone)] pub struct AnyBlockShape; +impl FallibleColorSyntax for AnyBlockShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let block = token_nodes.peek_non_ws().not_eof("block"); + + let block = match block { + Err(_) => return Ok(()), + Ok(block) => block, + }; + + // is it just a block? + let block = block.node.as_block(); + + match block { + // If so, color it as a block + Some((children, tags)) => { + let mut token_nodes = TokensIterator::new(children.item, context.tag, false); + color_syntax_with( + &DelimitedShape, + &(Delimiter::Brace, tags.0, tags.1), + &mut token_nodes, + context, + shapes, + ); + + return Ok(()); + } + _ => {} + } + + // Otherwise, look for a shorthand block. If none found, fail + color_fallible_syntax(&ShorthandBlock, token_nodes, context, shapes) + } +} + impl ExpandExpression for AnyBlockShape { fn expand_expr<'a, 'b>( &self, @@ -25,7 +71,7 @@ impl ExpandExpression for AnyBlockShape { let block = block.node.as_block(); match block { - Some(block) => { + Some((block, _tags)) => { let mut iterator = TokensIterator::new(&block.item, context.tag, false); let exprs = expand_syntax(&ExpressionListShape, &mut iterator, context)?; @@ -42,6 +88,37 @@ impl ExpandExpression for AnyBlockShape { #[derive(Debug, Copy, Clone)] pub struct ShorthandBlock; +impl FallibleColorSyntax for ShorthandBlock { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + // Try to find a shorthand head. If none found, fail + color_fallible_syntax(&ShorthandPath, token_nodes, context, shapes)?; + + loop { + // Check to see whether there's any continuation after the head expression + let result = + color_fallible_syntax(&ExpressionContinuationShape, token_nodes, context, shapes); + + match result { + // if no continuation was found, we're done + Err(_) => break, + // if a continuation was found, look for another one + Ok(_) => continue, + } + } + + Ok(()) + } +} + impl ExpandExpression for ShorthandBlock { fn expand_expr<'a, 'b>( &self, @@ -62,6 +139,50 @@ impl ExpandExpression for ShorthandBlock { #[derive(Debug, Copy, Clone)] pub struct ShorthandPath; +impl FallibleColorSyntax for ShorthandPath { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + token_nodes.atomic(|token_nodes| { + let variable = color_fallible_syntax(&VariablePathShape, token_nodes, context, shapes); + + match variable { + Ok(_) => { + // if it's a variable path, that's the head part + return Ok(()); + } + + Err(_) => { + // otherwise, we'll try to find a member path + } + } + + // look for a member (`` -> `$it.`) + color_fallible_syntax(&MemberShape, token_nodes, context, shapes)?; + + // Now that we've synthesized the head, of the path, proceed to expand the tail of the path + // like any other path. + let tail = color_fallible_syntax(&PathTailShape, token_nodes, context, shapes); + + match tail { + Ok(_) => {} + Err(_) => { + // It's ok if there's no path tail; a single member is sufficient + } + } + + Ok(()) + }) + } +} + impl ExpandExpression for ShorthandPath { fn expand_expr<'a, 'b>( &self, @@ -92,8 +213,6 @@ impl ExpandExpression for ShorthandPath { head = hir::Expression::dot_member(head, member); } - println!("{:?}", head); - Ok(head) } } @@ -104,6 +223,49 @@ impl ExpandExpression for ShorthandPath { #[derive(Debug, Copy, Clone)] pub struct ShorthandHeadShape; +impl FallibleColorSyntax for ShorthandHeadShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + // A shorthand path must not be at EOF + let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?; + + match peeked.node { + // If the head of a shorthand path is a bare token, it expands to `$it.bare` + TokenNode::Token(Tagged { + item: RawToken::Bare, + tag, + }) => { + peeked.commit(); + shapes.push(FlatShape::BareMember.tagged(tag)); + Ok(()) + } + + // If the head of a shorthand path is a string, it expands to `$it."some string"` + TokenNode::Token(Tagged { + item: RawToken::String(_), + tag: outer, + }) => { + peeked.commit(); + shapes.push(FlatShape::StringMember.tagged(outer)); + Ok(()) + } + + other => Err(ShellError::type_error( + "shorthand head", + other.tagged_type_name(), + )), + } + } +} + impl ExpandExpression for ShorthandHeadShape { fn expand_expr<'a, 'b>( &self, diff --git a/src/parser/hir/syntax_shape/expression.rs b/src/parser/hir/syntax_shape/expression.rs index 58cfa4a1a5..fc99c38dc3 100644 --- a/src/parser/hir/syntax_shape/expression.rs +++ b/src/parser/hir/syntax_shape/expression.rs @@ -1,3 +1,4 @@ +pub(crate) mod atom; pub(crate) mod delimited; pub(crate) mod file_path; pub(crate) mod list; @@ -8,14 +9,14 @@ pub(crate) mod unit; pub(crate) mod variable_path; use crate::parser::hir::syntax_shape::{ - expand_expr, expand_syntax, expand_variable, expression::delimited::expand_delimited_expr, - BareShape, DotShape, ExpandContext, ExpandExpression, ExpandSyntax, ExpressionContinuation, - ExpressionContinuationShape, UnitShape, + color_delimited_square, color_fallible_syntax, color_fallible_syntax_with, expand_atom, + expand_delimited_square, expand_expr, expand_syntax, AtomicToken, BareShape, ColorableDotShape, + DotShape, ExpandContext, ExpandExpression, ExpandSyntax, ExpansionRule, ExpressionContinuation, + ExpressionContinuationShape, FallibleColorSyntax, FlatShape, }; use crate::parser::{ hir, - hir::{Expression, Operator, TokensIterator}, - RawToken, Token, TokenNode, + hir::{Expression, TokensIterator}, }; use crate::prelude::*; use std::path::PathBuf; @@ -36,6 +37,32 @@ impl ExpandExpression for AnyExpressionShape { } } +impl FallibleColorSyntax for AnyExpressionShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + // Look for an expression at the cursor + color_fallible_syntax(&AnyExpressionStartShape, token_nodes, context, shapes)?; + + match continue_coloring_expression(token_nodes, context, shapes) { + Err(_) => { + // it's fine for there to be no continuation + } + + Ok(()) => {} + } + + Ok(()) + } +} + pub(crate) fn continue_expression( mut head: hir::Expression, token_nodes: &mut TokensIterator<'_>, @@ -64,6 +91,30 @@ pub(crate) fn continue_expression( } } +pub(crate) fn continue_coloring_expression( + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, + shapes: &mut Vec>, +) -> Result<(), ShellError> { + // if there's not even one expression continuation, fail + color_fallible_syntax(&ExpressionContinuationShape, token_nodes, context, shapes)?; + + loop { + // Check to see whether there's any continuation after the head expression + let result = + color_fallible_syntax(&ExpressionContinuationShape, token_nodes, context, shapes); + + match result { + Err(_) => { + // We already saw one continuation, so just return + return Ok(()); + } + + Ok(_) => {} + } + } +} + #[derive(Debug, Copy, Clone)] pub struct AnyExpressionStartShape; @@ -73,59 +124,148 @@ impl ExpandExpression for AnyExpressionStartShape { token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - let size = expand_expr(&UnitShape, token_nodes, context); + let atom = expand_atom(token_nodes, "expression", context, ExpansionRule::new())?; - match size { - Ok(expr) => return Ok(expr), - Err(_) => {} - } - - let peek_next = token_nodes.peek_any().not_eof("expression")?; - - let head = match peek_next.node { - TokenNode::Token(token) => match token.item { - RawToken::Bare | RawToken::Operator(Operator::Dot) => { - let start = token.tag; - peek_next.commit(); - - let end = expand_syntax(&BareTailShape, token_nodes, context)?; - - match end { - Some(end) => return Ok(hir::Expression::bare(start.until(end))), - None => return Ok(hir::Expression::bare(start)), - } - } - _ => { - peek_next.commit(); - expand_one_context_free_token(*token, context) - } - }, - node @ TokenNode::Call(_) - | node @ TokenNode::Nodes(_) - | node @ TokenNode::Pipeline(_) - | node @ TokenNode::Flag(_) - | node @ TokenNode::Member(_) - | node @ TokenNode::Whitespace(_) => { - return Err(ShellError::type_error( - "expression", - node.tagged_type_name(), + match atom.item { + AtomicToken::Size { number, unit } => { + return Ok(hir::Expression::size( + number.to_number(context.source), + unit.item, + atom.tag, )) } - TokenNode::Delimited(delimited) => { - peek_next.commit(); - expand_delimited_expr(delimited, context) + + AtomicToken::SquareDelimited { nodes, .. } => { + expand_delimited_square(&nodes, atom.tag, context) } - TokenNode::Error(error) => return Err(*error.item.clone()), - }?; + AtomicToken::Word { .. } | AtomicToken::Dot { .. } => { + let end = expand_syntax(&BareTailShape, token_nodes, context)?; + Ok(hir::Expression::bare(atom.tag.until_option(end))) + } - Ok(head) + other => return other.tagged(atom.tag).into_hir(context, "expression"), + } + } +} + +impl FallibleColorSyntax for AnyExpressionStartShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let atom = token_nodes.spanned(|token_nodes| { + expand_atom( + token_nodes, + "expression", + context, + ExpansionRule::permissive(), + ) + }); + + let atom = match atom { + Tagged { + item: Err(_err), + tag, + } => { + shapes.push(FlatShape::Error.tagged(tag)); + return Ok(()); + } + + Tagged { + item: Ok(value), .. + } => value, + }; + + match atom.item { + AtomicToken::Size { number, unit } => shapes.push( + FlatShape::Size { + number: number.tag, + unit: unit.tag, + } + .tagged(atom.tag), + ), + + AtomicToken::SquareDelimited { nodes, tags } => { + color_delimited_square(tags, &nodes, atom.tag, context, shapes) + } + + AtomicToken::Word { .. } | AtomicToken::Dot { .. } => { + shapes.push(FlatShape::Word.tagged(atom.tag)); + } + + _ => atom.color_tokens(shapes), + } + + Ok(()) } } #[derive(Debug, Copy, Clone)] pub struct BareTailShape; +impl FallibleColorSyntax for BareTailShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let len = shapes.len(); + + loop { + let word = color_fallible_syntax_with( + &BareShape, + &FlatShape::Word, + token_nodes, + context, + shapes, + ); + + match word { + // if a word was found, continue + Ok(_) => continue, + // if a word wasn't found, try to find a dot + Err(_) => {} + } + + // try to find a dot + let dot = color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Word, + token_nodes, + context, + shapes, + ); + + match dot { + // if a dot was found, try to find another word + Ok(_) => continue, + // otherwise, we're done + Err(_) => break, + } + } + + if shapes.len() > len { + Ok(()) + } else { + Err(ShellError::syntax_error( + "No tokens matched BareTailShape".tagged_unknown(), + )) + } + } +} + impl ExpandSyntax for BareTailShape { type Output = Option; @@ -158,29 +298,6 @@ impl ExpandSyntax for BareTailShape { } } -fn expand_one_context_free_token<'a, 'b>( - token: Token, - context: &ExpandContext, -) -> Result { - Ok(match token.item { - RawToken::Number(number) => { - hir::Expression::number(number.to_number(context.source), token.tag) - } - RawToken::Operator(..) => { - return Err(ShellError::syntax_error( - "unexpected operator, expected an expression".tagged(token.tag), - )) - } - RawToken::Size(..) => unimplemented!("size"), - RawToken::String(tag) => hir::Expression::string(tag, token.tag), - RawToken::Variable(tag) => expand_variable(tag, token.tag, &context.source), - RawToken::ExternalCommand(_) => unimplemented!(), - RawToken::ExternalWord => unimplemented!(), - RawToken::GlobPattern => hir::Expression::pattern(token.tag), - RawToken::Bare => hir::Expression::string(token.tag, token.tag), - }) -} - pub fn expand_file_path(string: &str, context: &ExpandContext) -> PathBuf { let expanded = shellexpand::tilde_with_context(string, || context.homedir()); diff --git a/src/parser/hir/syntax_shape/expression/atom.rs b/src/parser/hir/syntax_shape/expression/atom.rs new file mode 100644 index 0000000000..83306da741 --- /dev/null +++ b/src/parser/hir/syntax_shape/expression/atom.rs @@ -0,0 +1,541 @@ +use crate::parser::hir::syntax_shape::{ + expand_syntax, expression::expand_file_path, parse_single_node, BarePathShape, + BarePatternShape, ExpandContext, UnitShape, +}; +use crate::parser::{ + hir, + hir::{Expression, RawNumber, TokensIterator}, + parse::flag::{Flag, FlagKind}, + DelimitedNode, Delimiter, FlatShape, RawToken, TokenNode, Unit, +}; +use crate::prelude::*; + +#[derive(Debug)] +pub enum AtomicToken<'tokens> { + Eof { + tag: Tag, + }, + Error { + error: Tagged, + }, + Number { + number: RawNumber, + }, + Size { + number: Tagged, + unit: Tagged, + }, + String { + body: Tag, + }, + ItVariable { + name: Tag, + }, + Variable { + name: Tag, + }, + ExternalCommand { + command: Tag, + }, + ExternalWord { + text: Tag, + }, + GlobPattern { + pattern: Tag, + }, + FilePath { + path: Tag, + }, + Word { + text: Tag, + }, + SquareDelimited { + tags: (Tag, Tag), + nodes: &'tokens Vec, + }, + ParenDelimited { + tags: (Tag, Tag), + nodes: &'tokens Vec, + }, + BraceDelimited { + tags: (Tag, Tag), + nodes: &'tokens Vec, + }, + Pipeline { + pipe: Option, + elements: Tagged<&'tokens Vec>, + }, + ShorthandFlag { + name: Tag, + }, + LonghandFlag { + name: Tag, + }, + Dot { + text: Tag, + }, + Operator { + text: Tag, + }, + Whitespace { + text: Tag, + }, +} + +pub type TaggedAtomicToken<'tokens> = Tagged>; + +impl<'tokens> TaggedAtomicToken<'tokens> { + pub fn into_hir( + &self, + context: &ExpandContext, + expected: &'static str, + ) -> Result { + Ok(match &self.item { + AtomicToken::Eof { .. } => { + return Err(ShellError::type_error( + expected, + "eof atomic token".tagged(self.tag), + )) + } + AtomicToken::Error { .. } => { + return Err(ShellError::type_error( + expected, + "eof atomic token".tagged(self.tag), + )) + } + AtomicToken::Operator { .. } => { + return Err(ShellError::type_error( + expected, + "operator".tagged(self.tag), + )) + } + AtomicToken::ShorthandFlag { .. } => { + return Err(ShellError::type_error( + expected, + "shorthand flag".tagged(self.tag), + )) + } + AtomicToken::LonghandFlag { .. } => { + return Err(ShellError::type_error(expected, "flag".tagged(self.tag))) + } + AtomicToken::Whitespace { .. } => { + return Err(ShellError::unimplemented("whitespace in AtomicToken")) + } + AtomicToken::Dot { .. } => { + return Err(ShellError::type_error(expected, "dot".tagged(self.tag))) + } + AtomicToken::Number { number } => { + Expression::number(number.to_number(context.source), self.tag) + } + AtomicToken::FilePath { path } => Expression::file_path( + expand_file_path(path.slice(context.source), context), + self.tag, + ), + AtomicToken::Size { number, unit } => { + Expression::size(number.to_number(context.source), **unit, self.tag) + } + AtomicToken::String { body } => Expression::string(body, self.tag), + AtomicToken::ItVariable { name } => Expression::it_variable(name, self.tag), + AtomicToken::Variable { name } => Expression::variable(name, self.tag), + AtomicToken::ExternalCommand { command } => { + Expression::external_command(command, self.tag) + } + AtomicToken::ExternalWord { text } => Expression::string(text, self.tag), + AtomicToken::GlobPattern { pattern } => Expression::pattern(pattern), + AtomicToken::Word { text } => Expression::string(text, text), + AtomicToken::SquareDelimited { .. } => unimplemented!("into_hir"), + AtomicToken::ParenDelimited { .. } => unimplemented!("into_hir"), + AtomicToken::BraceDelimited { .. } => unimplemented!("into_hir"), + AtomicToken::Pipeline { .. } => unimplemented!("into_hir"), + }) + } + + pub fn tagged_type_name(&self) -> Tagged<&'static str> { + match &self.item { + AtomicToken::Eof { .. } => "eof", + AtomicToken::Error { .. } => "error", + AtomicToken::Operator { .. } => "operator", + AtomicToken::ShorthandFlag { .. } => "shorthand flag", + AtomicToken::LonghandFlag { .. } => "flag", + AtomicToken::Whitespace { .. } => "whitespace", + AtomicToken::Dot { .. } => "dot", + AtomicToken::Number { .. } => "number", + AtomicToken::FilePath { .. } => "file path", + AtomicToken::Size { .. } => "size", + AtomicToken::String { .. } => "string", + AtomicToken::ItVariable { .. } => "$it", + AtomicToken::Variable { .. } => "variable", + AtomicToken::ExternalCommand { .. } => "external command", + AtomicToken::ExternalWord { .. } => "external word", + AtomicToken::GlobPattern { .. } => "file pattern", + AtomicToken::Word { .. } => "word", + AtomicToken::SquareDelimited { .. } => "array literal", + AtomicToken::ParenDelimited { .. } => "parenthesized expression", + AtomicToken::BraceDelimited { .. } => "block", + AtomicToken::Pipeline { .. } => "pipeline", + } + .tagged(self.tag) + } + + pub(crate) fn color_tokens(&self, shapes: &mut Vec>) { + match &self.item { + AtomicToken::Eof { .. } => {} + AtomicToken::Error { .. } => return shapes.push(FlatShape::Error.tagged(self.tag)), + AtomicToken::Operator { .. } => { + return shapes.push(FlatShape::Operator.tagged(self.tag)); + } + AtomicToken::ShorthandFlag { .. } => { + return shapes.push(FlatShape::ShorthandFlag.tagged(self.tag)); + } + AtomicToken::LonghandFlag { .. } => { + return shapes.push(FlatShape::Flag.tagged(self.tag)); + } + AtomicToken::Whitespace { .. } => { + return shapes.push(FlatShape::Whitespace.tagged(self.tag)); + } + AtomicToken::FilePath { .. } => return shapes.push(FlatShape::Path.tagged(self.tag)), + AtomicToken::Dot { .. } => return shapes.push(FlatShape::Dot.tagged(self.tag)), + AtomicToken::Number { + number: RawNumber::Decimal(_), + } => { + return shapes.push(FlatShape::Decimal.tagged(self.tag)); + } + AtomicToken::Number { + number: RawNumber::Int(_), + } => { + return shapes.push(FlatShape::Int.tagged(self.tag)); + } + AtomicToken::Size { number, unit } => { + return shapes.push( + FlatShape::Size { + number: number.tag, + unit: unit.tag, + } + .tagged(self.tag), + ); + } + AtomicToken::String { .. } => return shapes.push(FlatShape::String.tagged(self.tag)), + AtomicToken::ItVariable { .. } => { + return shapes.push(FlatShape::ItVariable.tagged(self.tag)) + } + AtomicToken::Variable { .. } => { + return shapes.push(FlatShape::Variable.tagged(self.tag)) + } + AtomicToken::ExternalCommand { .. } => { + return shapes.push(FlatShape::ExternalCommand.tagged(self.tag)); + } + AtomicToken::ExternalWord { .. } => { + return shapes.push(FlatShape::ExternalWord.tagged(self.tag)) + } + AtomicToken::GlobPattern { .. } => { + return shapes.push(FlatShape::GlobPattern.tagged(self.tag)) + } + AtomicToken::Word { .. } => return shapes.push(FlatShape::Word.tagged(self.tag)), + _ => return shapes.push(FlatShape::Error.tagged(self.tag)), + } + } +} + +#[derive(Debug)] +pub enum WhitespaceHandling { + #[allow(unused)] + AllowWhitespace, + RejectWhitespace, +} + +#[derive(Debug)] +pub struct ExpansionRule { + pub(crate) allow_external_command: bool, + pub(crate) allow_external_word: bool, + pub(crate) allow_operator: bool, + pub(crate) allow_eof: bool, + pub(crate) treat_size_as_word: bool, + pub(crate) commit_errors: bool, + pub(crate) whitespace: WhitespaceHandling, +} + +impl ExpansionRule { + pub fn new() -> ExpansionRule { + ExpansionRule { + allow_external_command: false, + allow_external_word: false, + allow_operator: false, + allow_eof: false, + treat_size_as_word: false, + commit_errors: false, + whitespace: WhitespaceHandling::RejectWhitespace, + } + } + + /// The intent of permissive mode is to return an atomic token for every possible + /// input token. This is important for error-correcting parsing, such as the + /// syntax highlighter. + pub fn permissive() -> ExpansionRule { + ExpansionRule { + allow_external_command: true, + allow_external_word: true, + allow_operator: true, + allow_eof: true, + treat_size_as_word: false, + commit_errors: true, + whitespace: WhitespaceHandling::AllowWhitespace, + } + } + + #[allow(unused)] + pub fn allow_external_command(mut self) -> ExpansionRule { + self.allow_external_command = true; + self + } + + #[allow(unused)] + pub fn allow_operator(mut self) -> ExpansionRule { + self.allow_operator = true; + self + } + + #[allow(unused)] + pub fn no_operator(mut self) -> ExpansionRule { + self.allow_operator = false; + self + } + + #[allow(unused)] + pub fn no_external_command(mut self) -> ExpansionRule { + self.allow_external_command = false; + self + } + + #[allow(unused)] + pub fn allow_external_word(mut self) -> ExpansionRule { + self.allow_external_word = true; + self + } + + #[allow(unused)] + pub fn no_external_word(mut self) -> ExpansionRule { + self.allow_external_word = false; + self + } + + #[allow(unused)] + pub fn treat_size_as_word(mut self) -> ExpansionRule { + self.treat_size_as_word = true; + self + } + + #[allow(unused)] + pub fn commit_errors(mut self) -> ExpansionRule { + self.commit_errors = true; + self + } + + #[allow(unused)] + pub fn allow_whitespace(mut self) -> ExpansionRule { + self.whitespace = WhitespaceHandling::AllowWhitespace; + self + } + + #[allow(unused)] + pub fn reject_whitespace(mut self) -> ExpansionRule { + self.whitespace = WhitespaceHandling::RejectWhitespace; + self + } +} + +/// If the caller of expand_atom throws away the returned atomic token returned, it +/// must use a checkpoint to roll it back. +pub fn expand_atom<'me, 'content>( + token_nodes: &'me mut TokensIterator<'content>, + expected: &'static str, + context: &ExpandContext, + rule: ExpansionRule, +) -> Result, ShellError> { + if token_nodes.at_end() { + match rule.allow_eof { + true => { + return Ok(AtomicToken::Eof { + tag: Tag::unknown(), + } + .tagged_unknown()) + } + false => return Err(ShellError::unexpected_eof("anything", Tag::unknown())), + } + } + + // First, we'll need to handle the situation where more than one token corresponds + // to a single atomic token + + // If treat_size_as_word, don't try to parse the head of the token stream + // as a size. + match rule.treat_size_as_word { + true => {} + false => match expand_syntax(&UnitShape, token_nodes, context) { + // If the head of the stream isn't a valid unit, we'll try to parse + // it again next as a word + Err(_) => {} + + // But if it was a valid unit, we're done here + Ok(Tagged { + item: (number, unit), + tag, + }) => return Ok(AtomicToken::Size { number, unit }.tagged(tag)), + }, + } + + // Try to parse the head of the stream as a bare path. A bare path includes + // words as well as `.`s, connected together without whitespace. + match expand_syntax(&BarePathShape, token_nodes, context) { + // If we didn't find a bare path + Err(_) => {} + Ok(tag) => { + let next = token_nodes.peek_any(); + + match next.node { + Some(token) if token.is_pattern() => { + // if the very next token is a pattern, we're looking at a glob, not a + // word, and we should try to parse it as a glob next + } + + _ => return Ok(AtomicToken::Word { text: tag }.tagged(tag)), + } + } + } + + // Try to parse the head of the stream as a pattern. A pattern includes + // words, words with `*` as well as `.`s, connected together without whitespace. + match expand_syntax(&BarePatternShape, token_nodes, context) { + // If we didn't find a bare path + Err(_) => {} + Ok(tag) => return Ok(AtomicToken::GlobPattern { pattern: tag }.tagged(tag)), + } + + // The next token corresponds to at most one atomic token + + // We need to `peek` because `parse_single_node` doesn't cover all of the + // cases that `expand_atom` covers. We should probably collapse the two + // if possible. + let peeked = token_nodes.peek_any().not_eof(expected)?; + + match peeked.node { + TokenNode::Token(_) => { + // handle this next + } + + TokenNode::Error(error) => { + peeked.commit(); + return Ok(AtomicToken::Error { + error: error.clone(), + } + .tagged(error.tag)); + } + + // [ ... ] + TokenNode::Delimited(Tagged { + item: + DelimitedNode { + delimiter: Delimiter::Square, + tags, + children, + }, + tag, + }) => { + peeked.commit(); + return Ok(AtomicToken::SquareDelimited { + nodes: children, + tags: *tags, + } + .tagged(tag)); + } + + TokenNode::Flag(Tagged { + item: + Flag { + kind: FlagKind::Shorthand, + name, + }, + tag, + }) => { + peeked.commit(); + return Ok(AtomicToken::ShorthandFlag { name: *name }.tagged(tag)); + } + + TokenNode::Flag(Tagged { + item: + Flag { + kind: FlagKind::Longhand, + name, + }, + tag, + }) => { + peeked.commit(); + return Ok(AtomicToken::ShorthandFlag { name: *name }.tagged(tag)); + } + + // If we see whitespace, process the whitespace according to the whitespace + // handling rules + TokenNode::Whitespace(tag) => match rule.whitespace { + // if whitespace is allowed, return a whitespace token + WhitespaceHandling::AllowWhitespace => { + peeked.commit(); + return Ok(AtomicToken::Whitespace { text: *tag }.tagged(tag)); + } + + // if whitespace is disallowed, return an error + WhitespaceHandling::RejectWhitespace => { + return Err(ShellError::syntax_error( + "Unexpected whitespace".tagged(tag), + )) + } + }, + + other => { + let tag = peeked.node.tag(); + + peeked.commit(); + return Ok(AtomicToken::Error { + error: ShellError::type_error("token", other.tagged_type_name()).tagged(tag), + } + .tagged(tag)); + } + } + + parse_single_node(token_nodes, expected, |token, token_tag, err| { + Ok(match token { + // First, the error cases. Each error case corresponds to a expansion rule + // flag that can be used to allow the case + + // rule.allow_operator + RawToken::Operator(_) if !rule.allow_operator => return Err(err.error()), + // rule.allow_external_command + RawToken::ExternalCommand(_) if !rule.allow_external_command => { + return Err(ShellError::type_error( + expected, + token.type_name().tagged(token_tag), + )) + } + // rule.allow_external_word + RawToken::ExternalWord if !rule.allow_external_word => { + return Err(ShellError::invalid_external_word(token_tag)) + } + + RawToken::Number(number) => AtomicToken::Number { number }.tagged(token_tag), + RawToken::Operator(_) => AtomicToken::Operator { text: token_tag }.tagged(token_tag), + RawToken::String(body) => AtomicToken::String { body }.tagged(token_tag), + RawToken::Variable(name) if name.slice(context.source) == "it" => { + AtomicToken::ItVariable { name }.tagged(token_tag) + } + RawToken::Variable(name) => AtomicToken::Variable { name }.tagged(token_tag), + RawToken::ExternalCommand(command) => { + AtomicToken::ExternalCommand { command }.tagged(token_tag) + } + RawToken::ExternalWord => { + AtomicToken::ExternalWord { text: token_tag }.tagged(token_tag) + } + RawToken::GlobPattern => { + AtomicToken::GlobPattern { pattern: token_tag }.tagged(token_tag) + } + RawToken::Bare => AtomicToken::Word { text: token_tag }.tagged(token_tag), + }) + }) +} diff --git a/src/parser/hir/syntax_shape/expression/delimited.rs b/src/parser/hir/syntax_shape/expression/delimited.rs index 0a01b0fc26..001e3812f4 100644 --- a/src/parser/hir/syntax_shape/expression/delimited.rs +++ b/src/parser/hir/syntax_shape/expression/delimited.rs @@ -1,38 +1,49 @@ -use crate::parser::hir::syntax_shape::{expand_syntax, ExpandContext, ExpressionListShape}; -use crate::parser::{hir, hir::TokensIterator}; -use crate::parser::{DelimitedNode, Delimiter}; +use crate::parser::hir::syntax_shape::{ + color_syntax, expand_syntax, ColorSyntax, ExpandContext, ExpressionListShape, TokenNode, +}; +use crate::parser::{hir, hir::TokensIterator, Delimiter, FlatShape}; use crate::prelude::*; -pub fn expand_delimited_expr( - delimited: &Tagged, +pub fn expand_delimited_square( + children: &Vec, + tag: Tag, context: &ExpandContext, ) -> Result { - match &delimited.item { - DelimitedNode { - delimiter: Delimiter::Square, - children, - } => { - let mut tokens = TokensIterator::new(&children, delimited.tag, false); + let mut tokens = TokensIterator::new(&children, tag, false); - let list = expand_syntax(&ExpressionListShape, &mut tokens, context); + let list = expand_syntax(&ExpressionListShape, &mut tokens, context); - Ok(hir::Expression::list(list?, delimited.tag)) - } + Ok(hir::Expression::list(list?, tag)) +} - DelimitedNode { - delimiter: Delimiter::Paren, - .. - } => Err(ShellError::type_error( - "expression", - "unimplemented call expression".tagged(delimited.tag), - )), +pub fn color_delimited_square( + (open, close): (Tag, Tag), + children: &Vec, + tag: Tag, + context: &ExpandContext, + shapes: &mut Vec>, +) { + shapes.push(FlatShape::OpenDelimiter(Delimiter::Square).tagged(open)); + let mut tokens = TokensIterator::new(&children, tag, false); + let _list = color_syntax(&ExpressionListShape, &mut tokens, context, shapes); + shapes.push(FlatShape::CloseDelimiter(Delimiter::Square).tagged(close)); +} - DelimitedNode { - delimiter: Delimiter::Brace, - .. - } => Err(ShellError::type_error( - "expression", - "unimplemented block expression".tagged(delimited.tag), - )), +#[derive(Debug, Copy, Clone)] +pub struct DelimitedShape; + +impl ColorSyntax for DelimitedShape { + type Info = (); + type Input = (Delimiter, Tag, Tag); + fn color_syntax<'a, 'b>( + &self, + (delimiter, open, close): &(Delimiter, Tag, Tag), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Self::Info { + shapes.push(FlatShape::OpenDelimiter(*delimiter).tagged(open)); + color_syntax(&ExpressionListShape, token_nodes, context, shapes); + shapes.push(FlatShape::CloseDelimiter(*delimiter).tagged(close)); } } diff --git a/src/parser/hir/syntax_shape/expression/file_path.rs b/src/parser/hir/syntax_shape/expression/file_path.rs index c0e5c7c2ab..e73dc8d647 100644 --- a/src/parser/hir/syntax_shape/expression/file_path.rs +++ b/src/parser/hir/syntax_shape/expression/file_path.rs @@ -1,59 +1,71 @@ +use crate::parser::hir::syntax_shape::expression::atom::{expand_atom, AtomicToken, ExpansionRule}; use crate::parser::hir::syntax_shape::{ - expand_syntax, expression::expand_file_path, parse_single_node, BarePathShape, ExpandContext, - ExpandExpression, + expression::expand_file_path, ExpandContext, ExpandExpression, FallibleColorSyntax, FlatShape, }; -use crate::parser::{hir, hir::TokensIterator, RawToken}; +use crate::parser::{hir, hir::TokensIterator}; use crate::prelude::*; #[derive(Debug, Copy, Clone)] pub struct FilePathShape; +impl FallibleColorSyntax for FilePathShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let atom = expand_atom( + token_nodes, + "file path", + context, + ExpansionRule::permissive(), + ); + + let atom = match atom { + Err(_) => return Ok(()), + Ok(atom) => atom, + }; + + match atom.item { + AtomicToken::Word { .. } + | AtomicToken::String { .. } + | AtomicToken::Number { .. } + | AtomicToken::Size { .. } => { + shapes.push(FlatShape::Path.tagged(atom.tag)); + } + + _ => atom.color_tokens(shapes), + } + + Ok(()) + } +} + impl ExpandExpression for FilePathShape { fn expand_expr<'a, 'b>( &self, token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - let bare = expand_syntax(&BarePathShape, token_nodes, context); + let atom = expand_atom(token_nodes, "file path", context, ExpansionRule::new())?; - match bare { - Ok(tag) => { - let string = tag.slice(context.source); - let path = expand_file_path(string, context); - return Ok(hir::Expression::file_path(path, tag)); + match atom.item { + AtomicToken::Word { text: body } | AtomicToken::String { body } => { + let path = expand_file_path(body.slice(context.source), context); + return Ok(hir::Expression::file_path(path, atom.tag)); } - Err(_) => {} + + AtomicToken::Number { .. } | AtomicToken::Size { .. } => { + let path = atom.tag.slice(context.source); + return Ok(hir::Expression::file_path(path, atom.tag)); + } + + _ => return atom.into_hir(context, "file path"), } - - parse_single_node(token_nodes, "Path", |token, token_tag| { - Ok(match token { - RawToken::GlobPattern => { - return Err(ShellError::type_error( - "Path", - "glob pattern".tagged(token_tag), - )) - } - RawToken::Operator(..) => { - return Err(ShellError::type_error("Path", "operator".tagged(token_tag))) - } - RawToken::Variable(tag) if tag.slice(context.source) == "it" => { - hir::Expression::it_variable(tag, token_tag) - } - RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), - RawToken::Number(_) => hir::Expression::bare(token_tag), - RawToken::Size(_, _) => hir::Expression::bare(token_tag), - RawToken::Bare => hir::Expression::file_path( - expand_file_path(token_tag.slice(context.source), context), - token_tag, - ), - - RawToken::String(tag) => hir::Expression::file_path( - expand_file_path(tag.slice(context.source), context), - token_tag, - ), - }) - }) } } diff --git a/src/parser/hir/syntax_shape/expression/list.rs b/src/parser/hir/syntax_shape/expression/list.rs index 9d28f44141..4109108a37 100644 --- a/src/parser/hir/syntax_shape/expression/list.rs +++ b/src/parser/hir/syntax_shape/expression/list.rs @@ -2,10 +2,14 @@ use crate::errors::ShellError; use crate::parser::{ hir, hir::syntax_shape::{ - expand_expr, maybe_spaced, spaced, AnyExpressionShape, ExpandContext, ExpandSyntax, + color_fallible_syntax, color_syntax, expand_atom, expand_expr, maybe_spaced, spaced, + AnyExpressionShape, ColorSyntax, ExpandContext, ExpandSyntax, ExpansionRule, + MaybeSpaceShape, SpaceShape, }, - hir::{debug_tokens, TokensIterator}, + hir::TokensIterator, + FlatShape, }; +use crate::Tagged; #[derive(Debug, Copy, Clone)] pub struct ExpressionListShape; @@ -28,8 +32,6 @@ impl ExpandSyntax for ExpressionListShape { exprs.push(expr); - println!("{:?}", debug_tokens(token_nodes, context.source)); - loop { if token_nodes.at_end_possible_ws() { return Ok(exprs); @@ -41,3 +43,134 @@ impl ExpandSyntax for ExpressionListShape { } } } + +impl ColorSyntax for ExpressionListShape { + type Info = (); + type Input = (); + + /// The intent of this method is to fully color an expression list shape infallibly. + /// This means that if we can't expand a token into an expression, we fall back to + /// a simpler coloring strategy. + /// + /// This would apply to something like `where x >`, which includes an incomplete + /// binary operator. Since we will fail to process it as a binary operator, we'll + /// fall back to a simpler coloring and move on. + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) { + // We encountered a parsing error and will continue with simpler coloring ("backoff + // coloring mode") + let mut backoff = false; + + // Consume any leading whitespace + color_syntax(&MaybeSpaceShape, token_nodes, context, shapes); + + loop { + // If we reached the very end of the token stream, we're done + if token_nodes.at_end() { + return; + } + + if backoff { + let len = shapes.len(); + + // If we previously encountered a parsing error, use backoff coloring mode + color_syntax(&SimplestExpression, token_nodes, context, shapes); + + if len == shapes.len() && !token_nodes.at_end() { + // This should never happen, but if it does, a panic is better than an infinite loop + panic!("Unexpected tokens left that couldn't be colored even with SimplestExpression") + } + } else { + // Try to color the head of the stream as an expression + match color_fallible_syntax(&AnyExpressionShape, token_nodes, context, shapes) { + // If no expression was found, switch to backoff coloring mode + Err(_) => { + backoff = true; + continue; + } + Ok(_) => {} + } + + // If an expression was found, consume a space + match color_fallible_syntax(&SpaceShape, token_nodes, context, shapes) { + Err(_) => { + // If no space was found, we're either at the end or there's an error. + // Either way, switch to backoff coloring mode. If we're at the end + // it won't have any consequences. + backoff = true; + } + Ok(_) => { + // Otherwise, move on to the next expression + } + } + } + } + } +} + +/// BackoffColoringMode consumes all of the remaining tokens in an infallible way +#[derive(Debug, Copy, Clone)] +pub struct BackoffColoringMode; + +impl ColorSyntax for BackoffColoringMode { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &Self::Input, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Self::Info { + loop { + if token_nodes.at_end() { + break; + } + + let len = shapes.len(); + color_syntax(&SimplestExpression, token_nodes, context, shapes); + + if len == shapes.len() && !token_nodes.at_end() { + // This shouldn't happen, but if it does, a panic is better than an infinite loop + panic!("SimplestExpression failed to consume any tokens, but it's not at the end. This is unexpected\n== token nodes==\n{:#?}\n\n== shapes ==\n{:#?}", token_nodes, shapes); + } + } + } +} + +/// The point of `SimplestExpression` is to serve as an infallible base case for coloring. +/// As a last ditch effort, if we can't find any way to parse the head of the stream as an +/// expression, fall back to simple coloring. +#[derive(Debug, Copy, Clone)] +pub struct SimplestExpression; + +impl ColorSyntax for SimplestExpression { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) { + let atom = expand_atom( + token_nodes, + "any token", + context, + ExpansionRule::permissive(), + ); + + match atom { + Err(_) => {} + Ok(atom) => atom.color_tokens(shapes), + } + } +} diff --git a/src/parser/hir/syntax_shape/expression/number.rs b/src/parser/hir/syntax_shape/expression/number.rs index 5b77044a2d..8d3cb048c6 100644 --- a/src/parser/hir/syntax_shape/expression/number.rs +++ b/src/parser/hir/syntax_shape/expression/number.rs @@ -1,4 +1,7 @@ -use crate::parser::hir::syntax_shape::{parse_single_node, ExpandContext, ExpandExpression}; +use crate::parser::hir::syntax_shape::{ + expand_atom, parse_single_node, ExpandContext, ExpandExpression, ExpansionRule, + FallibleColorSyntax, FlatShape, +}; use crate::parser::{ hir, hir::{RawNumber, TokensIterator}, @@ -15,20 +18,9 @@ impl ExpandExpression for NumberShape { token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - parse_single_node(token_nodes, "Number", |token, token_tag| { + parse_single_node(token_nodes, "Number", |token, token_tag, err| { Ok(match token { - RawToken::GlobPattern => { - return Err(ShellError::type_error( - "Number", - "glob pattern".to_string().tagged(token_tag), - )) - } - RawToken::Operator(..) => { - return Err(ShellError::type_error( - "Number", - "operator".to_string().tagged(token_tag), - )) - } + RawToken::GlobPattern | RawToken::Operator(..) => return Err(err.error()), RawToken::Variable(tag) if tag.slice(context.source) == "it" => { hir::Expression::it_variable(tag, token_tag) } @@ -38,9 +30,6 @@ impl ExpandExpression for NumberShape { RawToken::Number(number) => { hir::Expression::number(number.to_number(context.source), token_tag) } - RawToken::Size(number, unit) => { - hir::Expression::size(number.to_number(context.source), unit, token_tag) - } RawToken::Bare => hir::Expression::bare(token_tag), RawToken::String(tag) => hir::Expression::string(tag, token_tag), }) @@ -48,6 +37,35 @@ impl ExpandExpression for NumberShape { } } +impl FallibleColorSyntax for NumberShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let atom = token_nodes.spanned(|token_nodes| { + expand_atom(token_nodes, "number", context, ExpansionRule::permissive()) + }); + + let atom = match atom { + Tagged { item: Err(_), tag } => { + shapes.push(FlatShape::Error.tagged(tag)); + return Ok(()); + } + Tagged { item: Ok(atom), .. } => atom, + }; + + atom.color_tokens(shapes); + + Ok(()) + } +} + #[derive(Debug, Copy, Clone)] pub struct IntShape; @@ -57,41 +75,51 @@ impl ExpandExpression for IntShape { token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - parse_single_node(token_nodes, "Integer", |token, token_tag| { + parse_single_node(token_nodes, "Integer", |token, token_tag, err| { Ok(match token { - RawToken::GlobPattern => { - return Err(ShellError::type_error( - "Integer", - "glob pattern".to_string().tagged(token_tag), - )) - } - RawToken::Operator(..) => { - return Err(ShellError::type_error( - "Integer", - "operator".to_string().tagged(token_tag), - )) - } + RawToken::GlobPattern | RawToken::Operator(..) => return Err(err.error()), + RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), RawToken::Variable(tag) if tag.slice(context.source) == "it" => { hir::Expression::it_variable(tag, token_tag) } RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), RawToken::Number(number @ RawNumber::Int(_)) => { hir::Expression::number(number.to_number(context.source), token_tag) } - token @ RawToken::Number(_) => { - return Err(ShellError::type_error( - "Integer", - token.type_name().tagged(token_tag), - )); - } - RawToken::Size(number, unit) => { - hir::Expression::size(number.to_number(context.source), unit, token_tag) - } + RawToken::Number(_) => return Err(err.error()), RawToken::Bare => hir::Expression::bare(token_tag), RawToken::String(tag) => hir::Expression::string(tag, token_tag), }) }) } } + +impl FallibleColorSyntax for IntShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let atom = token_nodes.spanned(|token_nodes| { + expand_atom(token_nodes, "integer", context, ExpansionRule::permissive()) + }); + + let atom = match atom { + Tagged { item: Err(_), tag } => { + shapes.push(FlatShape::Error.tagged(tag)); + return Ok(()); + } + Tagged { item: Ok(atom), .. } => atom, + }; + + atom.color_tokens(shapes); + + Ok(()) + } +} diff --git a/src/parser/hir/syntax_shape/expression/pattern.rs b/src/parser/hir/syntax_shape/expression/pattern.rs index 4105b79b4f..5c863de728 100644 --- a/src/parser/hir/syntax_shape/expression/pattern.rs +++ b/src/parser/hir/syntax_shape/expression/pattern.rs @@ -1,6 +1,7 @@ use crate::parser::hir::syntax_shape::{ - expand_bare, expand_syntax, expression::expand_file_path, parse_single_node, ExpandContext, - ExpandExpression, ExpandSyntax, + expand_atom, expand_bare, expand_syntax, expression::expand_file_path, parse_single_node, + AtomicToken, ExpandContext, ExpandExpression, ExpandSyntax, ExpansionRule, FallibleColorSyntax, + FlatShape, }; use crate::parser::{hir, hir::TokensIterator, Operator, RawToken, TokenNode}; use crate::prelude::*; @@ -8,6 +9,32 @@ use crate::prelude::*; #[derive(Debug, Copy, Clone)] pub struct PatternShape; +impl FallibleColorSyntax for PatternShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + token_nodes.atomic(|token_nodes| { + let atom = expand_atom(token_nodes, "pattern", context, ExpansionRule::permissive())?; + + match &atom.item { + AtomicToken::GlobPattern { .. } | AtomicToken::Word { .. } => { + shapes.push(FlatShape::GlobPattern.tagged(atom.tag)); + Ok(()) + } + + _ => Err(ShellError::type_error("pattern", atom.tagged_type_name())), + } + }) + } +} + impl ExpandExpression for PatternShape { fn expand_expr<'a, 'b>( &self, @@ -23,7 +50,7 @@ impl ExpandExpression for PatternShape { Err(_) => {} } - parse_single_node(token_nodes, "Pattern", |token, token_tag| { + parse_single_node(token_nodes, "Pattern", |token, token_tag, _| { Ok(match token { RawToken::GlobPattern => { return Err(ShellError::unreachable( @@ -44,7 +71,6 @@ impl ExpandExpression for PatternShape { RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), RawToken::Number(_) => hir::Expression::bare(token_tag), - RawToken::Size(_, _) => hir::Expression::bare(token_tag), RawToken::String(tag) => hir::Expression::file_path( expand_file_path(tag.slice(context.source), context), diff --git a/src/parser/hir/syntax_shape/expression/string.rs b/src/parser/hir/syntax_shape/expression/string.rs index 6a4973febe..6f33ae5eb1 100644 --- a/src/parser/hir/syntax_shape/expression/string.rs +++ b/src/parser/hir/syntax_shape/expression/string.rs @@ -1,5 +1,6 @@ use crate::parser::hir::syntax_shape::{ - expand_variable, parse_single_node, ExpandContext, ExpandExpression, TestSyntax, + expand_atom, expand_variable, parse_single_node, AtomicToken, ExpandContext, ExpandExpression, + ExpansionRule, FallibleColorSyntax, FlatShape, TestSyntax, }; use crate::parser::hir::tokens_iterator::Peeked; use crate::parser::{hir, hir::TokensIterator, RawToken, TokenNode}; @@ -8,13 +9,43 @@ use crate::prelude::*; #[derive(Debug, Copy, Clone)] pub struct StringShape; +impl FallibleColorSyntax for StringShape { + type Info = (); + type Input = FlatShape; + + fn color_syntax<'a, 'b>( + &self, + input: &FlatShape, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let atom = expand_atom(token_nodes, "string", context, ExpansionRule::permissive()); + + let atom = match atom { + Err(_) => return Ok(()), + Ok(atom) => atom, + }; + + match atom { + Tagged { + item: AtomicToken::String { .. }, + tag, + } => shapes.push((*input).tagged(tag)), + other => other.color_tokens(shapes), + } + + Ok(()) + } +} + impl ExpandExpression for StringShape { fn expand_expr<'a, 'b>( &self, token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - parse_single_node(token_nodes, "String", |token, token_tag| { + parse_single_node(token_nodes, "String", |token, token_tag, _| { Ok(match token { RawToken::GlobPattern => { return Err(ShellError::type_error( @@ -32,7 +63,6 @@ impl ExpandExpression for StringShape { RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), RawToken::Number(_) => hir::Expression::bare(token_tag), - RawToken::Size(_, _) => hir::Expression::bare(token_tag), RawToken::Bare => hir::Expression::bare(token_tag), RawToken::String(tag) => hir::Expression::string(tag, token_tag), }) diff --git a/src/parser/hir/syntax_shape/expression/unit.rs b/src/parser/hir/syntax_shape/expression/unit.rs index cc3642bda5..65fca1a468 100644 --- a/src/parser/hir/syntax_shape/expression/unit.rs +++ b/src/parser/hir/syntax_shape/expression/unit.rs @@ -1,7 +1,8 @@ -use crate::parser::hir::syntax_shape::{ExpandContext, ExpandExpression}; +use crate::data::meta::Span; +use crate::parser::hir::syntax_shape::{ExpandContext, ExpandSyntax}; use crate::parser::parse::tokens::RawNumber; use crate::parser::parse::unit::Unit; -use crate::parser::{hir, hir::TokensIterator, RawToken, TokenNode}; +use crate::parser::{hir::TokensIterator, RawToken, TokenNode}; use crate::prelude::*; use nom::branch::alt; use nom::bytes::complete::tag; @@ -12,12 +13,14 @@ use nom::IResult; #[derive(Debug, Copy, Clone)] pub struct UnitShape; -impl ExpandExpression for UnitShape { - fn expand_expr<'a, 'b>( +impl ExpandSyntax for UnitShape { + type Output = Tagged<(Tagged, Tagged)>; + + fn expand_syntax<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - ) -> Result { + ) -> Result, Tagged)>, ShellError> { let peeked = token_nodes.peek_any().not_eof("unit")?; let tag = match peeked.node { @@ -40,15 +43,12 @@ impl ExpandExpression for UnitShape { Ok((number, unit)) => (number, unit), }; - Ok(hir::Expression::size( - number.to_number(context.source), - unit, - tag, - )) + peeked.commit(); + Ok((number, unit).tagged(tag)) } } -fn unit_size(input: &str, bare_tag: Tag) -> IResult<&str, (Tagged, Unit)> { +fn unit_size(input: &str, bare_tag: Tag) -> IResult<&str, (Tagged, Tagged)> { let (input, digits) = digit1(input)?; let (input, dot) = opt(tag("."))(input)?; @@ -85,5 +85,12 @@ fn unit_size(input: &str, bare_tag: Tag) -> IResult<&str, (Tagged, Un value(Unit::MB, alt((tag("PB"), tag("pb"), tag("Pb")))), )))(input)?; - Ok((input, (number, unit))) + let start_span = number.tag.span.end(); + + let unit_tag = Tag::new( + bare_tag.anchor, + Span::from((start_span, bare_tag.span.end())), + ); + + Ok((input, (number, unit.tagged(unit_tag)))) } diff --git a/src/parser/hir/syntax_shape/expression/variable_path.rs b/src/parser/hir/syntax_shape/expression/variable_path.rs index afea1b1499..a7f17a5971 100644 --- a/src/parser/hir/syntax_shape/expression/variable_path.rs +++ b/src/parser/hir/syntax_shape/expression/variable_path.rs @@ -1,6 +1,8 @@ use crate::parser::hir::syntax_shape::{ - expand_expr, expand_syntax, parse_single_node, AnyExpressionShape, BareShape, ExpandContext, - ExpandExpression, ExpandSyntax, Peeked, SkipSyntax, StringShape, TestSyntax, WhitespaceShape, + color_fallible_syntax, color_fallible_syntax_with, expand_atom, expand_expr, expand_syntax, + parse_single_node, AnyExpressionShape, AtomicToken, BareShape, ExpandContext, ExpandExpression, + ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, Peeked, SkipSyntax, StringShape, + TestSyntax, WhitespaceShape, }; use crate::parser::{hir, hir::Expression, hir::TokensIterator, Operator, RawToken}; use crate::prelude::*; @@ -42,9 +44,81 @@ impl ExpandExpression for VariablePathShape { } } +impl FallibleColorSyntax for VariablePathShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + token_nodes.atomic(|token_nodes| { + // If the head of the token stream is not a variable, fail + color_fallible_syntax(&VariableShape, token_nodes, context, shapes)?; + + loop { + // look for a dot at the head of a stream + let dot = color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Dot, + token_nodes, + context, + shapes, + ); + + // if there's no dot, we're done + match dot { + Err(_) => break, + Ok(_) => {} + } + + // otherwise, look for a member, and if you don't find one, fail + color_fallible_syntax(&MemberShape, token_nodes, context, shapes)?; + } + + Ok(()) + }) + } +} + #[derive(Debug, Copy, Clone)] pub struct PathTailShape; +/// The failure mode of `PathTailShape` is a dot followed by a non-member +impl FallibleColorSyntax for PathTailShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + token_nodes.atomic(|token_nodes| loop { + let result = color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Dot, + token_nodes, + context, + shapes, + ); + + match result { + Err(_) => return Ok(()), + Ok(_) => {} + } + + // If we've seen a dot but not a member, fail + color_fallible_syntax(&MemberShape, token_nodes, context, shapes)?; + }) + } +} + impl ExpandSyntax for PathTailShape { type Output = (Vec>, Tag); fn expand_syntax<'a, 'b>( @@ -121,6 +195,63 @@ impl ExpandSyntax for ExpressionContinuationShape { } } +pub enum ContinuationInfo { + Dot, + Infix, +} + +impl FallibleColorSyntax for ExpressionContinuationShape { + type Info = ContinuationInfo; + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result { + token_nodes.atomic(|token_nodes| { + // Try to expand a `.` + let dot = color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Dot, + token_nodes, + context, + shapes, + ); + + match dot { + Ok(_) => { + // we found a dot, so let's keep looking for a member; if no member was found, fail + color_fallible_syntax(&MemberShape, token_nodes, context, shapes)?; + + Ok(ContinuationInfo::Dot) + } + Err(_) => { + let mut new_shapes = vec![]; + let result = token_nodes.atomic(|token_nodes| { + // we didn't find a dot, so let's see if we're looking at an infix. If not found, fail + color_fallible_syntax(&InfixShape, token_nodes, context, &mut new_shapes)?; + + // now that we've seen an infix shape, look for any expression. If not found, fail + color_fallible_syntax( + &AnyExpressionShape, + token_nodes, + context, + &mut new_shapes, + )?; + + Ok(ContinuationInfo::Infix) + })?; + shapes.extend(new_shapes); + Ok(result) + } + } + }) + } +} + #[derive(Debug, Copy, Clone)] pub struct VariableShape; @@ -130,7 +261,7 @@ impl ExpandExpression for VariableShape { token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - parse_single_node(token_nodes, "variable", |token, token_tag| { + parse_single_node(token_nodes, "variable", |token, token_tag, _| { Ok(match token { RawToken::Variable(tag) => { if tag.slice(context.source) == "it" { @@ -150,6 +281,43 @@ impl ExpandExpression for VariableShape { } } +impl FallibleColorSyntax for VariableShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let atom = expand_atom( + token_nodes, + "variable", + context, + ExpansionRule::permissive(), + ); + + let atom = match atom { + Err(err) => return Err(err), + Ok(atom) => atom, + }; + + match &atom.item { + AtomicToken::Variable { .. } => { + shapes.push(FlatShape::Variable.tagged(atom.tag)); + Ok(()) + } + AtomicToken::ItVariable { .. } => { + shapes.push(FlatShape::ItVariable.tagged(atom.tag)); + Ok(()) + } + _ => Err(ShellError::type_error("variable", atom.tagged_type_name())), + } + } +} + #[derive(Debug, Clone, Copy)] pub enum Member { String(/* outer */ Tag, /* inner */ Tag), @@ -272,6 +440,55 @@ pub fn expand_column_path<'a, 'b>( #[derive(Debug, Copy, Clone)] pub struct ColumnPathShape; +impl FallibleColorSyntax for ColumnPathShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + // If there's not even one member shape, fail + color_fallible_syntax(&MemberShape, token_nodes, context, shapes)?; + + loop { + let checkpoint = token_nodes.checkpoint(); + + match color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Dot, + checkpoint.iterator, + context, + shapes, + ) { + Err(_) => { + // we already saw at least one member shape, so return successfully + return Ok(()); + } + + Ok(_) => { + match color_fallible_syntax(&MemberShape, checkpoint.iterator, context, shapes) + { + Err(_) => { + // we saw a dot but not a member (but we saw at least one member), + // so don't commit the dot but return successfully + return Ok(()); + } + + Ok(_) => { + // we saw a dot and a member, so commit it and continue on + checkpoint.commit(); + } + } + } + } + } + } +} + impl ExpandSyntax for ColumnPathShape { type Output = Tagged>; @@ -287,6 +504,43 @@ impl ExpandSyntax for ColumnPathShape { #[derive(Debug, Copy, Clone)] pub struct MemberShape; +impl FallibleColorSyntax for MemberShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let bare = color_fallible_syntax_with( + &BareShape, + &FlatShape::BareMember, + token_nodes, + context, + shapes, + ); + + match bare { + Ok(_) => return Ok(()), + Err(_) => { + // If we don't have a bare word, we'll look for a string + } + } + + // Look for a string token. If we don't find one, fail + color_fallible_syntax_with( + &StringShape, + &FlatShape::StringMember, + token_nodes, + context, + shapes, + ) + } +} + impl ExpandSyntax for MemberShape { type Output = Member; @@ -317,6 +571,34 @@ impl ExpandSyntax for MemberShape { #[derive(Debug, Copy, Clone)] pub struct DotShape; +#[derive(Debug, Copy, Clone)] +pub struct ColorableDotShape; + +impl FallibleColorSyntax for ColorableDotShape { + type Info = (); + type Input = FlatShape; + + fn color_syntax<'a, 'b>( + &self, + input: &FlatShape, + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let peeked = token_nodes.peek_any().not_eof("dot")?; + + match peeked.node { + node if node.is_dot() => { + peeked.commit(); + shapes.push((*input).tagged(node.tag())); + Ok(()) + } + + other => Err(ShellError::type_error("dot", other.tagged_type_name())), + } + } +} + impl SkipSyntax for DotShape { fn skip<'a, 'b>( &self, @@ -337,7 +619,7 @@ impl ExpandSyntax for DotShape { token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, ) -> Result { - parse_single_node(token_nodes, "dot", |token, token_tag| { + parse_single_node(token_nodes, "dot", |token, token_tag, _| { Ok(match token { RawToken::Operator(Operator::Dot) => token_tag, _ => { @@ -354,6 +636,53 @@ impl ExpandSyntax for DotShape { #[derive(Debug, Copy, Clone)] pub struct InfixShape; +impl FallibleColorSyntax for InfixShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + outer_shapes: &mut Vec>, + ) -> Result<(), ShellError> { + let checkpoint = token_nodes.checkpoint(); + let mut shapes = vec![]; + + // An infix operator must be prefixed by whitespace. If no whitespace was found, fail + color_fallible_syntax(&WhitespaceShape, checkpoint.iterator, context, &mut shapes)?; + + // Parse the next TokenNode after the whitespace + parse_single_node( + checkpoint.iterator, + "infix operator", + |token, token_tag, _| { + match token { + // If it's an operator (and not `.`), it's a match + RawToken::Operator(operator) if operator != Operator::Dot => { + shapes.push(FlatShape::Operator.tagged(token_tag)); + Ok(()) + } + + // Otherwise, it's not a match + _ => Err(ShellError::type_error( + "infix operator", + token.type_name().tagged(token_tag), + )), + } + }, + )?; + + // An infix operator must be followed by whitespace. If no whitespace was found, fail + color_fallible_syntax(&WhitespaceShape, checkpoint.iterator, context, &mut shapes)?; + + outer_shapes.extend(shapes); + checkpoint.commit(); + Ok(()) + } +} + impl ExpandSyntax for InfixShape { type Output = (Tag, Tagged, Tag); @@ -368,8 +697,10 @@ impl ExpandSyntax for InfixShape { let start = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?; // Parse the next TokenNode after the whitespace - let operator = - parse_single_node(checkpoint.iterator, "infix operator", |token, token_tag| { + let operator = parse_single_node( + checkpoint.iterator, + "infix operator", + |token, token_tag, _| { Ok(match token { // If it's an operator (and not `.`), it's a match RawToken::Operator(operator) if operator != Operator::Dot => { @@ -384,7 +715,8 @@ impl ExpandSyntax for InfixShape { )) } }) - })?; + }, + )?; // An infix operator must be followed by whitespace let end = expand_syntax(&WhitespaceShape, checkpoint.iterator, context)?; diff --git a/src/parser/hir/syntax_shape/flat_shape.rs b/src/parser/hir/syntax_shape/flat_shape.rs new file mode 100644 index 0000000000..48e867199e --- /dev/null +++ b/src/parser/hir/syntax_shape/flat_shape.rs @@ -0,0 +1,95 @@ +use crate::parser::{Delimiter, Flag, FlagKind, Operator, RawNumber, RawToken, TokenNode}; +use crate::{Tag, Tagged, TaggedItem, Text}; + +#[derive(Debug, Copy, Clone)] +pub enum FlatShape { + OpenDelimiter(Delimiter), + CloseDelimiter(Delimiter), + ItVariable, + Variable, + Operator, + Dot, + InternalCommand, + ExternalCommand, + ExternalWord, + BareMember, + StringMember, + String, + Path, + Word, + Pipe, + GlobPattern, + Flag, + ShorthandFlag, + Int, + Decimal, + Whitespace, + Error, + Size { number: Tag, unit: Tag }, +} + +impl FlatShape { + pub fn from(token: &TokenNode, source: &Text, shapes: &mut Vec>) -> () { + match token { + TokenNode::Token(token) => match token.item { + RawToken::Number(RawNumber::Int(_)) => { + shapes.push(FlatShape::Int.tagged(token.tag)) + } + RawToken::Number(RawNumber::Decimal(_)) => { + shapes.push(FlatShape::Decimal.tagged(token.tag)) + } + RawToken::Operator(Operator::Dot) => shapes.push(FlatShape::Dot.tagged(token.tag)), + RawToken::Operator(_) => shapes.push(FlatShape::Operator.tagged(token.tag)), + RawToken::String(_) => shapes.push(FlatShape::String.tagged(token.tag)), + RawToken::Variable(v) if v.slice(source) == "it" => { + shapes.push(FlatShape::ItVariable.tagged(token.tag)) + } + RawToken::Variable(_) => shapes.push(FlatShape::Variable.tagged(token.tag)), + RawToken::ExternalCommand(_) => { + shapes.push(FlatShape::ExternalCommand.tagged(token.tag)) + } + RawToken::ExternalWord => shapes.push(FlatShape::ExternalWord.tagged(token.tag)), + RawToken::GlobPattern => shapes.push(FlatShape::GlobPattern.tagged(token.tag)), + RawToken::Bare => shapes.push(FlatShape::Word.tagged(token.tag)), + }, + TokenNode::Call(_) => unimplemented!(), + TokenNode::Nodes(nodes) => { + for node in &nodes.item { + FlatShape::from(node, source, shapes); + } + } + TokenNode::Delimited(v) => { + shapes.push(FlatShape::OpenDelimiter(v.item.delimiter).tagged(v.item.tags.0)); + for token in &v.item.children { + FlatShape::from(token, source, shapes); + } + shapes.push(FlatShape::CloseDelimiter(v.item.delimiter).tagged(v.item.tags.1)); + } + TokenNode::Pipeline(pipeline) => { + for part in &pipeline.parts { + if let Some(_) = part.pipe { + shapes.push(FlatShape::Pipe.tagged(part.tag)); + } + } + } + TokenNode::Flag(Tagged { + item: + Flag { + kind: FlagKind::Longhand, + .. + }, + tag, + }) => shapes.push(FlatShape::Flag.tagged(tag)), + TokenNode::Flag(Tagged { + item: + Flag { + kind: FlagKind::Shorthand, + .. + }, + tag, + }) => shapes.push(FlatShape::ShorthandFlag.tagged(tag)), + TokenNode::Whitespace(_) => shapes.push(FlatShape::Whitespace.tagged(token.tag())), + TokenNode::Error(v) => shapes.push(FlatShape::Error.tagged(v.tag)), + } + } +} diff --git a/src/parser/hir/tokens_iterator.rs b/src/parser/hir/tokens_iterator.rs index c0dd9c50fd..f597c850bd 100644 --- a/src/parser/hir/tokens_iterator.rs +++ b/src/parser/hir/tokens_iterator.rs @@ -3,16 +3,13 @@ pub(crate) mod debug; use crate::errors::ShellError; use crate::parser::TokenNode; use crate::{Tag, Tagged, TaggedItem}; -use derive_new::new; -#[derive(Debug, new)] -pub struct TokensIterator<'a> { - tokens: &'a [TokenNode], +#[derive(Debug)] +pub struct TokensIterator<'content> { + tokens: &'content [TokenNode], tag: Tag, skip_ws: bool, - #[new(default)] index: usize, - #[new(default)] seen: indexmap::IndexSet, } @@ -124,11 +121,41 @@ pub fn peek_error( } impl<'content> TokensIterator<'content> { - #[cfg(test)] + pub fn new(items: &'content [TokenNode], tag: Tag, skip_ws: bool) -> TokensIterator<'content> { + TokensIterator { + tokens: items, + tag, + skip_ws, + index: 0, + seen: indexmap::IndexSet::new(), + } + } + + pub fn anchor(&self) -> uuid::Uuid { + self.tag.anchor + } + pub fn all(tokens: &'content [TokenNode], tag: Tag) -> TokensIterator<'content> { TokensIterator::new(tokens, tag, false) } + pub fn len(&self) -> usize { + self.tokens.len() + } + + pub fn spanned( + &mut self, + block: impl FnOnce(&mut TokensIterator<'content>) -> T, + ) -> Tagged { + let start = self.tag_at_cursor(); + + let result = block(self); + + let end = self.tag_at_cursor(); + + result.tagged(start.until(end)) + } + /// Use a checkpoint when you need to peek more than one token ahead, but can't be sure /// that you'll succeed. pub fn checkpoint<'me>(&'me mut self) -> Checkpoint<'content, 'me> { @@ -143,8 +170,26 @@ impl<'content> TokensIterator<'content> { } } - pub fn anchor(&self) -> uuid::Uuid { - self.tag.anchor + /// Use a checkpoint when you need to peek more than one token ahead, but can't be sure + /// that you'll succeed. + pub fn atomic<'me, T>( + &'me mut self, + block: impl FnOnce(&mut TokensIterator<'content>) -> Result, + ) -> Result { + let index = self.index; + let seen = self.seen.clone(); + + let checkpoint = Checkpoint { + iterator: self, + index, + seen, + committed: false, + }; + + let value = block(checkpoint.iterator)?; + + checkpoint.commit(); + return Ok(value); } fn eof_tag(&self) -> Tag { @@ -160,6 +205,15 @@ impl<'content> TokensIterator<'content> { } } + pub fn tag_at_cursor(&mut self) -> Tag { + let next = self.peek_any(); + + match next.node { + None => self.eof_tag(), + Some(node) => node.tag(), + } + } + pub fn remove(&mut self, position: usize) { self.seen.insert(position); } @@ -231,6 +285,26 @@ impl<'content> TokensIterator<'content> { start_next(self, false) } + // Peek the next token, including whitespace, but not EOF + pub fn peek_any_token<'me, T>( + &'me mut self, + block: impl FnOnce(&'content TokenNode) -> Result, + ) -> Result { + let peeked = start_next(self, false); + let peeked = peeked.not_eof("invariant"); + + match peeked { + Err(err) => return Err(err), + Ok(peeked) => match block(peeked.node) { + Err(err) => return Err(err), + Ok(val) => { + peeked.commit(); + return Ok(val); + } + }, + } + } + fn commit(&mut self, from: usize, to: usize) { for index in from..to { self.seen.insert(index); @@ -239,6 +313,10 @@ impl<'content> TokensIterator<'content> { self.index = to; } + pub fn pos(&self, skip_ws: bool) -> Option { + peek_pos(self, skip_ws) + } + pub fn debug_remaining(&self) -> Vec { let mut tokens = self.clone(); tokens.restart(); @@ -246,18 +324,18 @@ impl<'content> TokensIterator<'content> { } } -impl<'a> Iterator for TokensIterator<'a> { - type Item = &'a TokenNode; +impl<'content> Iterator for TokensIterator<'content> { + type Item = &'content TokenNode; - fn next(&mut self) -> Option<&'a TokenNode> { + fn next(&mut self) -> Option<&'content TokenNode> { next(self, self.skip_ws) } } fn peek<'content, 'me>( - iterator: &TokensIterator<'content>, + iterator: &'me TokensIterator<'content>, skip_ws: bool, -) -> Option<&'content TokenNode> { +) -> Option<&'me TokenNode> { let mut to = iterator.index; loop { @@ -287,6 +365,37 @@ fn peek<'content, 'me>( } } +fn peek_pos<'content, 'me>( + iterator: &'me TokensIterator<'content>, + skip_ws: bool, +) -> Option { + let mut to = iterator.index; + + loop { + if to >= iterator.tokens.len() { + return None; + } + + if iterator.seen.contains(&to) { + to += 1; + continue; + } + + if to >= iterator.tokens.len() { + return None; + } + + let node = &iterator.tokens[to]; + + match node { + TokenNode::Whitespace(_) if skip_ws => { + to += 1; + } + _ => return Some(to), + } + } +} + fn start_next<'content, 'me>( iterator: &'me mut TokensIterator<'content>, skip_ws: bool, @@ -337,7 +446,10 @@ fn start_next<'content, 'me>( } } -fn next<'a>(iterator: &mut TokensIterator<'a>, skip_ws: bool) -> Option<&'a TokenNode> { +fn next<'me, 'content>( + iterator: &'me mut TokensIterator<'content>, + skip_ws: bool, +) -> Option<&'content TokenNode> { loop { if iterator.index >= iterator.tokens.len() { return None; diff --git a/src/parser/parse/flag.rs b/src/parser/parse/flag.rs index 09d1e86337..b8995305d2 100644 --- a/src/parser/parse/flag.rs +++ b/src/parser/parse/flag.rs @@ -1,4 +1,5 @@ -use crate::Tag; +use crate::parser::hir::syntax_shape::flat_shape::FlatShape; +use crate::{Tag, Tagged, TaggedItem}; use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; @@ -12,6 +13,15 @@ pub enum FlagKind { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Getters, new)] #[get = "pub(crate)"] pub struct Flag { - kind: FlagKind, - name: Tag, + pub(crate) kind: FlagKind, + pub(crate) name: Tag, +} + +impl Tagged { + pub fn color(&self) -> Tagged { + match self.item.kind { + FlagKind::Longhand => FlatShape::Flag.tagged(self.tag), + FlagKind::Shorthand => FlatShape::ShorthandFlag.tagged(self.tag), + } + } } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 93ba043ba1..73833f7be5 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -189,7 +189,7 @@ pub fn raw_number(input: NomSpan) -> IResult> { match input.fragment.chars().next() { None => return Ok((input, RawNumber::int((start, input.offset, input.extra)))), Some('.') => (), - Some(other) if other.is_whitespace() => { + other if is_boundary(other) => { return Ok((input, RawNumber::int((start, input.offset, input.extra)))) } _ => { @@ -215,16 +215,14 @@ pub fn raw_number(input: NomSpan) -> IResult> { let next = input.fragment.chars().next(); - if let Some(next) = next { - if !next.is_whitespace() { - return Err(nom::Err::Error(nom::error::make_error( - input, - nom::error::ErrorKind::Tag, - ))); - } + if is_boundary(next) { + Ok((input, RawNumber::decimal((start, end, input.extra)))) + } else { + Err(nom::Err::Error(nom::error::make_error( + input, + nom::error::ErrorKind::Tag, + ))) } - - Ok((input, RawNumber::decimal((start, end, input.extra)))) } #[tracable_parser] @@ -476,11 +474,14 @@ pub fn whitespace(input: NomSpan) -> IResult { )) } -pub fn delimited(input: NomSpan, delimiter: Delimiter) -> IResult>> { +pub fn delimited( + input: NomSpan, + delimiter: Delimiter, +) -> IResult>)> { let left = input.offset; - let (input, _) = char(delimiter.open())(input)?; + let (input, open_tag) = tag(delimiter.open())(input)?; let (input, inner_items) = opt(spaced_token_list)(input)?; - let (input, _) = char(delimiter.close())(input)?; + let (input, close_tag) = tag(delimiter.close())(input)?; let right = input.offset; let mut items = vec![]; @@ -489,36 +490,43 @@ pub fn delimited(input: NomSpan, delimiter: Delimiter) -> IResult IResult { - let (input, tokens) = delimited(input, Delimiter::Paren)?; + let (input, (left, right, tokens)) = delimited(input, Delimiter::Paren)?; Ok(( input, - TokenTreeBuilder::tagged_parens(tokens.item, tokens.tag), + TokenTreeBuilder::tagged_parens(tokens.item, (left, right), tokens.tag), )) } #[tracable_parser] pub fn delimited_square(input: NomSpan) -> IResult { - let (input, tokens) = delimited(input, Delimiter::Square)?; + let (input, (left, right, tokens)) = delimited(input, Delimiter::Square)?; Ok(( input, - TokenTreeBuilder::tagged_square(tokens.item, tokens.tag), + TokenTreeBuilder::tagged_square(tokens.item, (left, right), tokens.tag), )) } #[tracable_parser] pub fn delimited_brace(input: NomSpan) -> IResult { - let (input, tokens) = delimited(input, Delimiter::Brace)?; + let (input, (left, right, tokens)) = delimited(input, Delimiter::Brace)?; Ok(( input, - TokenTreeBuilder::tagged_brace(tokens.item, tokens.tag), + TokenTreeBuilder::tagged_square(tokens.item, (left, right), tokens.tag), )) } @@ -1246,7 +1254,10 @@ mod tests { left: usize, right: usize, ) -> TokenNode { - let node = DelimitedNode::new(*delimiter, children); + let start = Tag::for_char(left, delimiter.tag.anchor); + let end = Tag::for_char(right, delimiter.tag.anchor); + + let node = DelimitedNode::new(delimiter.item, (start, end), children); let spanned = node.tagged((left, right, delimiter.tag.anchor)); TokenNode::Delimited(spanned) } diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index 8cbb28264b..85961d1dab 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -1,5 +1,5 @@ use crate::errors::ShellError; -use crate::parser::parse::{call_node::*, flag::*, pipeline::*, tokens::*}; +use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*}; use crate::prelude::*; use crate::traits::ToDebug; use crate::{Tag, Tagged, Text}; @@ -17,10 +17,9 @@ pub enum TokenNode { Delimited(Tagged), Pipeline(Tagged), Flag(Tagged), - Member(Tag), Whitespace(Tag), - Error(Tagged>), + Error(Tagged), } impl ToDebug for TokenNode { @@ -78,7 +77,7 @@ impl fmt::Debug for DebugTokenNode<'_> { ) } TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)), - TokenNode::Error(s) => write!(f, " for {:?}", s.tag().slice(self.source)), + TokenNode::Error(_) => write!(f, ""), rest => write!(f, "{}", rest.tag().slice(self.source)), } } @@ -99,9 +98,8 @@ impl TokenNode { TokenNode::Delimited(s) => s.tag(), TokenNode::Pipeline(s) => s.tag(), TokenNode::Flag(s) => s.tag(), - TokenNode::Member(s) => *s, TokenNode::Whitespace(s) => *s, - TokenNode::Error(s) => s.tag(), + TokenNode::Error(s) => return s.tag, } } @@ -113,7 +111,6 @@ impl TokenNode { TokenNode::Delimited(d) => d.type_name(), TokenNode::Pipeline(_) => "pipeline", TokenNode::Flag(_) => "flag", - TokenNode::Member(_) => "member", TokenNode::Whitespace(_) => "whitespace", TokenNode::Error(_) => "error", } @@ -155,16 +152,37 @@ impl TokenNode { } } - pub fn as_block(&self) -> Option> { + pub fn is_pattern(&self) -> bool { + match self { + TokenNode::Token(Tagged { + item: RawToken::GlobPattern, + .. + }) => true, + _ => false, + } + } + + pub fn is_dot(&self) -> bool { + match self { + TokenNode::Token(Tagged { + item: RawToken::Operator(Operator::Dot), + .. + }) => true, + _ => false, + } + } + + pub fn as_block(&self) -> Option<(Tagged<&[TokenNode]>, (Tag, Tag))> { match self { TokenNode::Delimited(Tagged { item: DelimitedNode { delimiter, children, + tags, }, tag, - }) if *delimiter == Delimiter::Brace => Some((&children[..]).tagged(tag)), + }) if *delimiter == Delimiter::Brace => Some(((&children[..]).tagged(tag), *tags)), _ => None, } } @@ -203,7 +221,7 @@ impl TokenNode { pub fn as_pipeline(&self) -> Result { match self { TokenNode::Pipeline(Tagged { item, .. }) => Ok(item.clone()), - _ => Err(ShellError::string("unimplemented")), + _ => Err(ShellError::unimplemented("unimplemented")), } } @@ -259,6 +277,7 @@ impl TokenNode { #[get = "pub(crate)"] pub struct DelimitedNode { pub(crate) delimiter: Delimiter, + pub(crate) tags: (Tag, Tag), pub(crate) children: Vec, } @@ -280,19 +299,19 @@ pub enum Delimiter { } impl Delimiter { - pub(crate) fn open(&self) -> char { + pub(crate) fn open(&self) -> &'static str { match self { - Delimiter::Paren => '(', - Delimiter::Brace => '{', - Delimiter::Square => '[', + Delimiter::Paren => "(", + Delimiter::Brace => "{", + Delimiter::Square => "[", } } - pub(crate) fn close(&self) -> char { + pub(crate) fn close(&self) -> &'static str { match self { - Delimiter::Paren => ')', - Delimiter::Brace => '}', - Delimiter::Square => ']', + Delimiter::Paren => ")", + Delimiter::Brace => "}", + Delimiter::Square => "]", } } } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index 67298987a4..549462a979 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -5,7 +5,6 @@ use crate::parser::parse::operator::Operator; use crate::parser::parse::pipeline::{Pipeline, PipelineElement}; use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, TokenNode}; use crate::parser::parse::tokens::{RawNumber, RawToken}; -use crate::parser::parse::unit::Unit; use crate::parser::CallNode; use derive_new::new; use uuid::Uuid; @@ -227,31 +226,6 @@ impl TokenTreeBuilder { TokenNode::Token(RawToken::Number(input.into()).tagged(tag.into())) } - pub fn size(int: impl Into, unit: impl Into) -> CurriedToken { - let int = int.into(); - let unit = unit.into(); - - Box::new(move |b| { - let (start_int, end_int) = b.consume(&int.to_string()); - let (_, end_unit) = b.consume(unit.as_str()); - b.pos = end_unit; - - TokenTreeBuilder::tagged_size( - (RawNumber::Int((start_int, end_int, b.anchor).into()), unit), - (start_int, end_unit, b.anchor), - ) - }) - } - - pub fn tagged_size( - input: (impl Into, impl Into), - tag: impl Into, - ) -> TokenNode { - let (int, unit) = (input.0.into(), input.1.into()); - - TokenNode::Token(RawToken::Size(int, unit).tagged(tag.into())) - } - pub fn var(input: impl Into) -> CurriedToken { let input = input.into(); @@ -297,19 +271,6 @@ impl TokenTreeBuilder { TokenNode::Flag(Flag::new(FlagKind::Shorthand, input.into()).tagged(tag.into())) } - pub fn member(input: impl Into) -> CurriedToken { - let input = input.into(); - - Box::new(move |b| { - let (start, end) = b.consume(&input); - TokenTreeBuilder::tagged_member((start, end, b.anchor)) - }) - } - - pub fn tagged_member(tag: impl Into) -> TokenNode { - TokenNode::Member(tag.into()) - } - pub fn call(head: CurriedToken, input: Vec) -> CurriedCall { Box::new(move |b| { let start = b.pos; @@ -340,58 +301,79 @@ impl TokenTreeBuilder { CallNode::new(Box::new(head), tail).tagged(tag.into()) } + fn consume_delimiter( + &mut self, + input: Vec, + _open: &str, + _close: &str, + ) -> (Tag, Tag, Tag, Vec) { + let (start_open_paren, end_open_paren) = self.consume("("); + let mut output = vec![]; + for item in input { + output.push(item(self)); + } + + let (start_close_paren, end_close_paren) = self.consume(")"); + + let open = Tag::from((start_open_paren, end_open_paren, self.anchor)); + let close = Tag::from((start_close_paren, end_close_paren, self.anchor)); + let whole = Tag::from((start_open_paren, end_close_paren, self.anchor)); + + (open, close, whole, output) + } + pub fn parens(input: Vec) -> CurriedToken { Box::new(move |b| { - let (start, _) = b.consume("("); - let mut output = vec![]; - for item in input { - output.push(item(b)); - } + let (open, close, whole, output) = b.consume_delimiter(input, "(", ")"); - let (_, end) = b.consume(")"); - - TokenTreeBuilder::tagged_parens(output, (start, end, b.anchor)) + TokenTreeBuilder::tagged_parens(output, (open, close), whole) }) } - pub fn tagged_parens(input: impl Into>, tag: impl Into) -> TokenNode { - TokenNode::Delimited(DelimitedNode::new(Delimiter::Paren, input.into()).tagged(tag.into())) + pub fn tagged_parens( + input: impl Into>, + tags: (Tag, Tag), + tag: impl Into, + ) -> TokenNode { + TokenNode::Delimited( + DelimitedNode::new(Delimiter::Paren, tags, input.into()).tagged(tag.into()), + ) } pub fn square(input: Vec) -> CurriedToken { Box::new(move |b| { - let (start, _) = b.consume("["); - let mut output = vec![]; - for item in input { - output.push(item(b)); - } + let (open, close, whole, tokens) = b.consume_delimiter(input, "[", "]"); - let (_, end) = b.consume("]"); - - TokenTreeBuilder::tagged_square(output, (start, end, b.anchor)) + TokenTreeBuilder::tagged_square(tokens, (open, close), whole) }) } - pub fn tagged_square(input: impl Into>, tag: impl Into) -> TokenNode { - TokenNode::Delimited(DelimitedNode::new(Delimiter::Square, input.into()).tagged(tag.into())) + pub fn tagged_square( + input: impl Into>, + tags: (Tag, Tag), + tag: impl Into, + ) -> TokenNode { + TokenNode::Delimited( + DelimitedNode::new(Delimiter::Square, tags, input.into()).tagged(tag.into()), + ) } pub fn braced(input: Vec) -> CurriedToken { Box::new(move |b| { - let (start, _) = b.consume("{ "); - let mut output = vec![]; - for item in input { - output.push(item(b)); - } + let (open, close, whole, tokens) = b.consume_delimiter(input, "{", "}"); - let (_, end) = b.consume(" }"); - - TokenTreeBuilder::tagged_brace(output, (start, end, b.anchor)) + TokenTreeBuilder::tagged_brace(tokens, (open, close), whole) }) } - pub fn tagged_brace(input: impl Into>, tag: impl Into) -> TokenNode { - TokenNode::Delimited(DelimitedNode::new(Delimiter::Brace, input.into()).tagged(tag.into())) + pub fn tagged_brace( + input: impl Into>, + tags: (Tag, Tag), + tag: impl Into, + ) -> TokenNode { + TokenNode::Delimited( + DelimitedNode::new(Delimiter::Brace, tags, input.into()).tagged(tag.into()), + ) } pub fn sp() -> CurriedToken { diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index 77a856af3f..41bdfcebd6 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -1,4 +1,3 @@ -use crate::parser::parse::unit::*; use crate::parser::Operator; use crate::prelude::*; use crate::{Tagged, Text}; @@ -9,7 +8,6 @@ use std::str::FromStr; pub enum RawToken { Number(RawNumber), Operator(Operator), - Size(RawNumber, Unit), String(Tag), Variable(Tag), ExternalCommand(Tag), @@ -18,6 +16,21 @@ pub enum RawToken { Bare, } +impl RawToken { + pub fn type_name(&self) -> &'static str { + match self { + RawToken::Number(_) => "Number", + RawToken::Operator(..) => "operator", + RawToken::String(_) => "String", + RawToken::Variable(_) => "variable", + RawToken::ExternalCommand(_) => "external command", + RawToken::ExternalWord => "external word", + RawToken::GlobPattern => "glob pattern", + RawToken::Bare => "String", + } + } +} + #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum RawNumber { Int(Tag), @@ -47,22 +60,6 @@ impl RawNumber { } } -impl RawToken { - pub fn type_name(&self) -> &'static str { - match self { - RawToken::Number(_) => "Number", - RawToken::Operator(..) => "operator", - RawToken::Size(..) => "Size", - RawToken::String(_) => "String", - RawToken::Variable(_) => "variable", - RawToken::ExternalCommand(_) => "external command", - RawToken::ExternalWord => "external word", - RawToken::GlobPattern => "glob pattern", - RawToken::Bare => "String", - } - } -} - pub type Token = Tagged; impl Token { @@ -72,6 +69,76 @@ impl Token { source, } } + + pub fn extract_number(&self) -> Option> { + match self.item { + RawToken::Number(number) => Some((number).tagged(self.tag)), + _ => None, + } + } + + pub fn extract_int(&self) -> Option<(Tag, Tag)> { + match self.item { + RawToken::Number(RawNumber::Int(int)) => Some((int, self.tag)), + _ => None, + } + } + + pub fn extract_decimal(&self) -> Option<(Tag, Tag)> { + match self.item { + RawToken::Number(RawNumber::Decimal(decimal)) => Some((decimal, self.tag)), + _ => None, + } + } + + pub fn extract_operator(&self) -> Option> { + match self.item { + RawToken::Operator(operator) => Some(operator.tagged(self.tag)), + _ => None, + } + } + + pub fn extract_string(&self) -> Option<(Tag, Tag)> { + match self.item { + RawToken::String(tag) => Some((tag, self.tag)), + _ => None, + } + } + + pub fn extract_variable(&self) -> Option<(Tag, Tag)> { + match self.item { + RawToken::Variable(tag) => Some((tag, self.tag)), + _ => None, + } + } + + pub fn extract_external_command(&self) -> Option<(Tag, Tag)> { + match self.item { + RawToken::ExternalCommand(tag) => Some((tag, self.tag)), + _ => None, + } + } + + pub fn extract_external_word(&self) -> Option { + match self.item { + RawToken::ExternalWord => Some(self.tag), + _ => None, + } + } + + pub fn extract_glob_pattern(&self) -> Option { + match self.item { + RawToken::GlobPattern => Some(self.tag), + _ => None, + } + } + + pub fn extract_bare(&self) -> Option { + match self.item { + RawToken::Bare => Some(self.tag), + _ => None, + } + } } pub struct DebugToken<'a> { diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index d383689fd9..603ff2956d 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -1,5 +1,8 @@ use crate::errors::{ArgumentError, ShellError}; -use crate::parser::hir::syntax_shape::{expand_expr, spaced}; +use crate::parser::hir::syntax_shape::{ + color_fallible_syntax, color_syntax, expand_expr, flat_shape::FlatShape, spaced, + BackoffColoringMode, ColorSyntax, MaybeSpaceShape, +}; use crate::parser::registry::{NamedType, PositionalType, Signature}; use crate::parser::TokensIterator; use crate::parser::{ @@ -153,6 +156,232 @@ pub fn parse_command_tail( Ok(Some((positional, named))) } +#[derive(Debug)] +struct ColoringArgs { + vec: Vec>>>, +} + +impl ColoringArgs { + fn new(len: usize) -> ColoringArgs { + let vec = vec![None; len]; + ColoringArgs { vec } + } + + fn insert(&mut self, pos: usize, shapes: Vec>) { + self.vec[pos] = Some(shapes); + } + + fn spread_shapes(self, shapes: &mut Vec>) { + for item in self.vec { + match item { + None => {} + Some(vec) => { + shapes.extend(vec); + } + } + } + } +} + +#[derive(Debug, Copy, Clone)] +pub struct CommandTailShape; + +impl ColorSyntax for CommandTailShape { + type Info = (); + type Input = Signature; + + fn color_syntax<'a, 'b>( + &self, + signature: &Signature, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + shapes: &mut Vec>, + ) -> Self::Info { + let mut args = ColoringArgs::new(token_nodes.len()); + trace_remaining("nodes", token_nodes.clone(), context.source()); + + for (name, kind) in &signature.named { + trace!(target: "nu::color_syntax", "looking for {} : {:?}", name, kind); + + match kind { + NamedType::Switch => { + match token_nodes.extract(|t| t.as_flag(name, context.source())) { + Some((pos, flag)) => args.insert(pos, vec![flag.color()]), + None => {} + } + } + NamedType::Mandatory(syntax_type) => { + match extract_mandatory( + signature, + name, + token_nodes, + context.source(), + Tag::unknown(), + ) { + Err(_) => { + // The mandatory flag didn't exist at all, so there's nothing to color + } + Ok((pos, flag)) => { + let mut shapes = vec![flag.color()]; + token_nodes.move_to(pos); + + if token_nodes.at_end() { + args.insert(pos, shapes); + token_nodes.restart(); + continue; + } + + // We can live with unmatched syntax after a mandatory flag + let _ = token_nodes.atomic(|token_nodes| { + color_syntax(&MaybeSpaceShape, token_nodes, context, &mut shapes); + + // If the part after a mandatory flag isn't present, that's ok, but we + // should roll back any whitespace we chomped + color_fallible_syntax( + syntax_type, + token_nodes, + context, + &mut shapes, + ) + }); + + args.insert(pos, shapes); + token_nodes.restart(); + } + } + } + NamedType::Optional(syntax_type) => { + match extract_optional(name, token_nodes, context.source()) { + Err(_) => { + // The optional flag didn't exist at all, so there's nothing to color + } + Ok(Some((pos, flag))) => { + let mut shapes = vec![flag.color()]; + token_nodes.move_to(pos); + + if token_nodes.at_end() { + args.insert(pos, shapes); + token_nodes.restart(); + continue; + } + + // We can live with unmatched syntax after an optional flag + let _ = token_nodes.atomic(|token_nodes| { + color_syntax(&MaybeSpaceShape, token_nodes, context, &mut shapes); + + // If the part after a mandatory flag isn't present, that's ok, but we + // should roll back any whitespace we chomped + color_fallible_syntax( + syntax_type, + token_nodes, + context, + &mut shapes, + ) + }); + + args.insert(pos, shapes); + token_nodes.restart(); + } + + Ok(None) => { + token_nodes.restart(); + } + } + } + }; + } + + trace_remaining("after named", token_nodes.clone(), context.source()); + + for arg in &signature.positional { + trace!("Processing positional {:?}", arg); + + match arg { + PositionalType::Mandatory(..) => { + if token_nodes.at_end() { + break; + } + } + + PositionalType::Optional(..) => { + if token_nodes.at_end() { + break; + } + } + } + + let mut shapes = vec![]; + let pos = token_nodes.pos(false); + + match pos { + None => break, + Some(pos) => { + // We can live with an unmatched positional argument. Hopefully it will be + // matched by a future token + let _ = token_nodes.atomic(|token_nodes| { + color_syntax(&MaybeSpaceShape, token_nodes, context, &mut shapes); + + // If no match, we should roll back any whitespace we chomped + color_fallible_syntax( + &arg.syntax_type(), + token_nodes, + context, + &mut shapes, + )?; + + args.insert(pos, shapes); + + Ok(()) + }); + } + } + } + + trace_remaining("after positional", token_nodes.clone(), context.source()); + + if let Some(syntax_type) = signature.rest_positional { + loop { + if token_nodes.at_end_possible_ws() { + break; + } + + let pos = token_nodes.pos(false); + + match pos { + None => break, + Some(pos) => { + let mut shapes = vec![]; + + // If any arguments don't match, we'll fall back to backoff coloring mode + let result = token_nodes.atomic(|token_nodes| { + color_syntax(&MaybeSpaceShape, token_nodes, context, &mut shapes); + + // If no match, we should roll back any whitespace we chomped + color_fallible_syntax(&syntax_type, token_nodes, context, &mut shapes)?; + + args.insert(pos, shapes); + + Ok(()) + }); + + match result { + Err(_) => break, + Ok(_) => continue, + } + } + } + } + } + + args.spread_shapes(shapes); + + // Consume any remaining tokens with backoff coloring mode + color_syntax(&BackoffColoringMode, token_nodes, context, shapes); + + shapes.sort_by(|a, b| a.tag.span.start().cmp(&b.tag.span.start())); + } +} + fn extract_switch(name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Text) -> Option { tokens .extract(|t| t.as_flag(name, source)) @@ -200,6 +429,7 @@ fn extract_optional( pub fn trace_remaining(desc: &'static str, tail: hir::TokensIterator<'_>, source: &Text) { trace!( + target: "nu::expand_args", "{} = {:?}", desc, itertools::join( diff --git a/src/plugin.rs b/src/plugin.rs index afd9871108..004e937fe8 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -32,7 +32,7 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) { let input = match input { Some(arg) => std::fs::read_to_string(arg), None => { - send_response(ShellError::string(format!("No input given."))); + send_response(ShellError::untagged_runtime_error("No input given.")); return; } }; @@ -64,7 +64,7 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) { return; } e => { - send_response(ShellError::string(format!( + send_response(ShellError::untagged_runtime_error(format!( "Could not handle plugin message: {} {:?}", input, e ))); @@ -102,7 +102,7 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) { break; } e => { - send_response(ShellError::string(format!( + send_response(ShellError::untagged_runtime_error(format!( "Could not handle plugin message: {} {:?}", input, e ))); @@ -111,7 +111,7 @@ pub fn serve_plugin(plugin: &mut dyn Plugin) { } } e => { - send_response(ShellError::string(format!( + send_response(ShellError::untagged_runtime_error(format!( "Could not handle plugin message: {:?}", e, ))); diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 997400d67f..6fc034226c 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -1,7 +1,7 @@ use itertools::Itertools; use nu::{ serve_plugin, CallInfo, Plugin, ReturnSuccess, ReturnValue, ShellError, Signature, SyntaxShape, - Tagged, Value, + Tagged, TaggedItem, Value, }; pub type ColumnPath = Vec>; @@ -25,21 +25,27 @@ impl Add { Some(f) => match obj.insert_data_at_column_path(value_tag, &f, v) { Some(v) => return Ok(v), None => { - return Err(ShellError::string(format!( - "add could not find place to insert field {:?} {}", - obj, - f.iter().map(|i| &i.item).join(".") - ))) + return Err(ShellError::labeled_error( + format!( + "add could not find place to insert field {:?} {}", + obj, + f.iter().map(|i| &i.item).join(".") + ), + "column name", + value_tag, + )) } }, - None => Err(ShellError::string( + None => Err(ShellError::labeled_error( "add needs a column name when adding a value to a table", + "column name", + value_tag, )), }, - x => Err(ShellError::string(format!( - "Unrecognized type in stream: {:?}", - x - ))), + (value, _) => Err(ShellError::type_error( + "row", + value.type_name().tagged(value_tag), + )), } } } @@ -64,12 +70,7 @@ impl Plugin for Add { self.field = Some(table.as_column_path()?.item); } - _ => { - return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", - args[0] - ))) - } + value => return Err(ShellError::type_error("table", value.tagged_type_name())), } match &args[1] { Tagged { item: v, .. } => { diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index 6d35530ef5..c0f6dfbedd 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -3,7 +3,7 @@ use nu::{ Tagged, Value, }; -pub type ColumnPath = Vec>; +pub type ColumnPath = Tagged>>; struct Edit { field: Option, @@ -24,19 +24,22 @@ impl Edit { Some(f) => match obj.replace_data_at_column_path(value_tag, &f, v) { Some(v) => return Ok(v), None => { - return Err(ShellError::string( + return Err(ShellError::labeled_error( "edit could not find place to insert column", + "column name", + f.tag, )) } }, - None => Err(ShellError::string( + None => Err(ShellError::untagged_runtime_error( "edit needs a column when changing a value in a table", )), }, - x => Err(ShellError::string(format!( - "Unrecognized type in stream: {:?}", - x - ))), + _ => Err(ShellError::labeled_error( + "Unrecognized type in stream", + "original value", + value_tag, + )), } } } @@ -57,14 +60,9 @@ impl Plugin for Edit { item: Value::Table(_), .. } => { - self.field = Some(table.as_column_path()?.item); - } - _ => { - return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", - args[0] - ))) + self.field = Some(table.as_column_path()?); } + value => return Err(ShellError::type_error("table", value.tagged_type_name())), } match &args[1] { Tagged { item: v, .. } => { diff --git a/src/plugins/embed.rs b/src/plugins/embed.rs index 646db80918..4e3545d055 100644 --- a/src/plugins/embed.rs +++ b/src/plugins/embed.rs @@ -25,8 +25,10 @@ impl Embed { }); Ok(()) } - None => Err(ShellError::string( + None => Err(ShellError::labeled_error( "embed needs a field when embedding a value", + "original value", + value.tag, )), }, } @@ -52,12 +54,7 @@ impl Plugin for Embed { self.field = Some(s.clone()); self.values = Vec::new(); } - _ => { - return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", - args[0] - ))) - } + value => return Err(ShellError::type_error("string", value.tagged_type_name())), } } diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 4e6f6f0f64..c58ca89369 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -14,7 +14,7 @@ pub enum SemVerAction { Patch, } -pub type ColumnPath = Vec>; +pub type ColumnPath = Tagged>>; struct Inc { field: Option, @@ -90,7 +90,11 @@ impl Inc { let replacement = match value.item.get_data_by_column_path(value.tag(), f) { Some(result) => self.inc(result.map(|x| x.clone()))?, None => { - return Err(ShellError::string("inc could not find field to replace")) + return Err(ShellError::labeled_error( + "inc could not find field to replace", + "column name", + f.tag, + )) } }; match value.item.replace_data_at_column_path( @@ -100,18 +104,22 @@ impl Inc { ) { Some(v) => return Ok(v), None => { - return Err(ShellError::string("inc could not find field to replace")) + return Err(ShellError::labeled_error( + "inc could not find field to replace", + "column name", + f.tag, + )) } } } - None => Err(ShellError::string( + None => Err(ShellError::untagged_runtime_error( "inc needs a field when incrementing a column in a table", )), }, - x => Err(ShellError::string(format!( - "Unrecognized type in stream: {:?}", - x - ))), + _ => Err(ShellError::type_error( + "incrementable value", + value.tagged_type_name(), + )), } } } @@ -145,14 +153,9 @@ impl Plugin for Inc { item: Value::Table(_), .. } => { - self.field = Some(table.as_column_path()?.item); - } - _ => { - return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", - arg - ))) + self.field = Some(table.as_column_path()?); } + value => return Err(ShellError::type_error("table", value.tagged_type_name())), } } } @@ -163,7 +166,11 @@ impl Plugin for Inc { match &self.error { Some(reason) => { - return Err(ShellError::string(format!("{}: {}", reason, Inc::usage()))) + return Err(ShellError::untagged_runtime_error(format!( + "{}: {}", + reason, + Inc::usage() + ))) } None => Ok(vec![]), } @@ -308,7 +315,7 @@ mod tests { assert_eq!( plugin .field - .map(|f| f.into_iter().map(|f| f.item).collect()), + .map(|f| f.iter().map(|f| f.item.clone()).collect()), Some(vec!["package".to_string(), "version".to_string()]) ); } diff --git a/src/plugins/match.rs b/src/plugins/match.rs index 1f2aad83fc..7133524050 100644 --- a/src/plugins/match.rs +++ b/src/plugins/match.rs @@ -35,11 +35,12 @@ impl Plugin for Match { } => { self.column = s.clone(); } - _ => { - return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", - args[0] - ))); + Tagged { tag, .. } => { + return Err(ShellError::labeled_error( + "Unrecognized type in params", + "value", + tag, + )); } } match &args[1] { @@ -49,11 +50,12 @@ impl Plugin for Match { } => { self.regex = Regex::new(s).unwrap(); } - _ => { - return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", - args[1] - ))); + Tagged { tag, .. } => { + return Err(ShellError::labeled_error( + "Unrecognized type in params", + "value", + tag, + )); } } } @@ -65,7 +67,7 @@ impl Plugin for Match { match &input { Tagged { item: Value::Row(dict), - .. + tag, } => { if let Some(val) = dict.entries.get(&self.column) { match val { @@ -75,22 +77,20 @@ impl Plugin for Match { } => { flag = self.regex.is_match(s); } - _ => { - return Err(ShellError::string(format!( - "value is not a string! {:?}", - &val - ))); + Tagged { tag, .. } => { + return Err(ShellError::labeled_error("expected string", "value", tag)); } } } else { - return Err(ShellError::string(format!( - "column not in row! {:?} {:?}", - &self.column, dict - ))); + return Err(ShellError::labeled_error( + format!("column not in row! {:?} {:?}", &self.column, dict), + "row", + tag, + )); } } - _ => { - return Err(ShellError::string(format!("Not a row! {:?}", &input))); + Tagged { tag, .. } => { + return Err(ShellError::labeled_error("Expected row", "value", tag)); } } if flag { diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 7bd35733da..4635d60c35 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -105,20 +105,24 @@ impl Str { ) { Some(v) => return Ok(v), None => { - return Err(ShellError::string("str could not find field to replace")) + return Err(ShellError::type_error( + "column name", + value.tagged_type_name(), + )) } } } - None => Err(ShellError::string(format!( + None => Err(ShellError::untagged_runtime_error(format!( "{}: {}", "str needs a column when applied to a value in a row", Str::usage() ))), }, - x => Err(ShellError::string(format!( - "Unrecognized type in stream: {:?}", - x - ))), + _ => Err(ShellError::labeled_error( + "Unrecognized type in stream", + value.type_name(), + value.tag, + )), } } } @@ -167,10 +171,11 @@ impl Plugin for Str { self.field = Some(table.as_column_path()?.item); } _ => { - return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", - possible_field - ))) + return Err(ShellError::labeled_error( + "Unrecognized type in params", + possible_field.type_name(), + possible_field.tag, + )) } } } @@ -187,7 +192,11 @@ impl Plugin for Str { match &self.error { Some(reason) => { - return Err(ShellError::string(format!("{}: {}", reason, Str::usage()))) + return Err(ShellError::untagged_runtime_error(format!( + "{}: {}", + reason, + Str::usage() + ))) } None => Ok(vec![]), } diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs index ffb39cb90b..2bb89b74e1 100644 --- a/src/plugins/sum.rs +++ b/src/plugins/sum.rs @@ -28,9 +28,11 @@ impl Sum { self.total = Some(value.clone()); Ok(()) } - _ => Err(ShellError::string(format!( - "Could not sum non-integer or unrelated types" - ))), + _ => Err(ShellError::labeled_error( + "Could not sum non-integer or unrelated types", + "source", + value.tag, + )), } } Value::Primitive(Primitive::Bytes(b)) => { @@ -47,15 +49,18 @@ impl Sum { self.total = Some(value); Ok(()) } - _ => Err(ShellError::string(format!( - "Could not sum non-integer or unrelated types" - ))), + _ => Err(ShellError::labeled_error( + "Could not sum non-integer or unrelated types", + "source", + value.tag, + )), } } - x => Err(ShellError::string(format!( - "Unrecognized type in stream: {:?}", - x - ))), + x => Err(ShellError::labeled_error( + format!("Unrecognized type in stream: {:?}", x), + "source", + value.tag, + )), } } } diff --git a/src/prelude.rs b/src/prelude.rs index eabd778717..1f80126a4f 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -1,3 +1,13 @@ +#[macro_export] +macro_rules! return_err { + ($expr:expr) => { + match $expr { + Err(_) => return, + Ok(expr) => expr, + }; + }; +} + #[macro_export] macro_rules! stream { ($($expr:expr),*) => {{ diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 3c1ae79ea3..aec736ec0f 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -145,7 +145,7 @@ impl Shell for FilesystemShell { source.tag(), )); } else { - return Err(ShellError::string("Invalid pattern.")); + return Err(ShellError::untagged_runtime_error("Invalid pattern.")); } } }; diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 85591cf047..b590d82826 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -1,11 +1,11 @@ +use crate::context::Context; +use crate::parser::hir::syntax_shape::{color_fallible_syntax, FlatShape, PipelineShape}; use crate::parser::hir::TokensIterator; use crate::parser::nom_input; use crate::parser::parse::token_tree::TokenNode; -use crate::parser::parse::tokens::RawToken; -use crate::parser::{Pipeline, PipelineElement}; -use crate::shell::shell_manager::ShellManager; -use crate::Tagged; +use crate::{Tag, Tagged, TaggedItem, Text}; use ansi_term::Color; +use log::trace; use rustyline::completion::Completer; use rustyline::error::ReadlineError; use rustyline::highlight::Highlighter; @@ -13,12 +13,12 @@ use rustyline::hint::Hinter; use std::borrow::Cow::{self, Owned}; pub(crate) struct Helper { - helper: ShellManager, + context: Context, } impl Helper { - pub(crate) fn new(helper: ShellManager) -> Helper { - Helper { helper } + pub(crate) fn new(context: Context) -> Helper { + Helper { context } } } @@ -30,7 +30,7 @@ impl Completer for Helper { pos: usize, ctx: &rustyline::Context<'_>, ) -> Result<(usize, Vec), ReadlineError> { - self.helper.complete(line, pos, ctx) + self.context.shell_manager.complete(line, pos, ctx) } } @@ -53,7 +53,7 @@ impl Completer for Helper { impl Hinter for Helper { fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { - self.helper.hint(line, pos, ctx) + self.context.shell_manager.hint(line, pos, ctx) } } @@ -78,20 +78,42 @@ impl Highlighter for Helper { Ok(v) => v, }; - let Pipeline { parts } = pipeline; - let mut iter = parts.into_iter(); + let tokens = vec![TokenNode::Pipeline(pipeline.clone().tagged(v.tag()))]; + let mut tokens = TokensIterator::all(&tokens[..], v.tag()); - loop { - match iter.next() { - None => { - return Cow::Owned(out); - } - Some(token) => { - let styled = paint_pipeline_element(&token, line); - out.push_str(&styled.to_string()); - } - } + let text = Text::from(line); + let expand_context = self + .context + .expand_context(&text, Tag::from((0, line.len() - 1, uuid::Uuid::nil()))); + let mut shapes = vec![]; + + // We just constructed a token list that only contains a pipeline, so it can't fail + color_fallible_syntax(&PipelineShape, &mut tokens, &expand_context, &mut shapes) + .unwrap(); + + trace!(target: "nu::shapes", + "SHAPES :: {:?}", + shapes.iter().map(|shape| shape.item).collect::>() + ); + + for shape in shapes { + let styled = paint_flat_shape(shape, line); + out.push_str(&styled); } + + Cow::Owned(out) + + // loop { + // match iter.next() { + // None => { + // return Cow::Owned(out); + // } + // Some(token) => { + // let styled = paint_pipeline_element(&token, line); + // out.push_str(&styled.to_string()); + // } + // } + // } } } } @@ -101,80 +123,55 @@ impl Highlighter for Helper { } } -fn paint_token_node(token_node: &TokenNode, line: &str) -> String { - let styled = match token_node { - TokenNode::Call(..) => Color::Cyan.bold().paint(token_node.tag().slice(line)), - TokenNode::Nodes(..) => Color::Green.bold().paint(token_node.tag().slice(line)), - TokenNode::Whitespace(..) => Color::White.normal().paint(token_node.tag().slice(line)), - TokenNode::Flag(..) => Color::Black.bold().paint(token_node.tag().slice(line)), - TokenNode::Member(..) => Color::Yellow.bold().paint(token_node.tag().slice(line)), - TokenNode::Error(..) => Color::Red.bold().paint(token_node.tag().slice(line)), - TokenNode::Delimited(..) => Color::White.paint(token_node.tag().slice(line)), - TokenNode::Pipeline(..) => Color::Blue.normal().paint(token_node.tag().slice(line)), - TokenNode::Token(Tagged { - item: RawToken::Number(..), - .. - }) => Color::Purple.bold().paint(token_node.tag().slice(line)), - TokenNode::Token(Tagged { - item: RawToken::Size(..), - .. - }) => Color::Purple.bold().paint(token_node.tag().slice(line)), - TokenNode::Token(Tagged { - item: RawToken::GlobPattern, - .. - }) => Color::Cyan.normal().paint(token_node.tag().slice(line)), - TokenNode::Token(Tagged { - item: RawToken::String(..), - .. - }) => Color::Green.normal().paint(token_node.tag().slice(line)), - TokenNode::Token(Tagged { - item: RawToken::Variable(..), - .. - }) => Color::Yellow.bold().paint(token_node.tag().slice(line)), - TokenNode::Token(Tagged { - item: RawToken::Bare, - .. - }) => Color::Green.normal().paint(token_node.tag().slice(line)), - TokenNode::Token(Tagged { - item: RawToken::ExternalCommand(..), - .. - }) => Color::Cyan.bold().paint(token_node.tag().slice(line)), - TokenNode::Token(Tagged { - item: RawToken::ExternalWord, - .. - }) => Color::Black.bold().paint(token_node.tag().slice(line)), - TokenNode::Token(Tagged { - item: RawToken::Operator(..), - .. - }) => Color::Black.bold().paint(token_node.tag().slice(line)), - }; +#[allow(unused)] +fn vec_tag(input: Vec>) -> Option { + let mut iter = input.iter(); + let first = iter.next()?.tag; + let last = iter.last(); - styled.to_string() + Some(match last { + None => first, + Some(last) => first.until(last.tag), + }) } -fn paint_pipeline_element(pipeline_element: &PipelineElement, line: &str) -> String { - let mut styled = String::new(); - - if let Some(_) = pipeline_element.pipe { - styled.push_str(&Color::Purple.paint("|")); - } - - let mut tokens = - TokensIterator::new(&pipeline_element.tokens, pipeline_element.tokens.tag, false); - let head = tokens.next(); - - match head { - None => return styled, - Some(head) => { - styled.push_str(&Color::Cyan.bold().paint(head.tag().slice(line)).to_string()) +fn paint_flat_shape(flat_shape: Tagged, line: &str) -> String { + let style = match &flat_shape.item { + FlatShape::OpenDelimiter(_) => Color::White.normal(), + FlatShape::CloseDelimiter(_) => Color::White.normal(), + FlatShape::ItVariable => Color::Purple.bold(), + FlatShape::Variable => Color::Purple.normal(), + FlatShape::Operator => Color::Yellow.normal(), + FlatShape::Dot => Color::White.normal(), + FlatShape::InternalCommand => Color::Cyan.bold(), + FlatShape::ExternalCommand => Color::Cyan.normal(), + FlatShape::ExternalWord => Color::Black.bold(), + FlatShape::BareMember => Color::Yellow.bold(), + FlatShape::StringMember => Color::Yellow.bold(), + FlatShape::String => Color::Green.normal(), + FlatShape::Path => Color::Cyan.normal(), + FlatShape::GlobPattern => Color::Cyan.bold(), + FlatShape::Word => Color::Green.normal(), + FlatShape::Pipe => Color::Purple.bold(), + FlatShape::Flag => Color::Black.bold(), + FlatShape::ShorthandFlag => Color::Black.bold(), + FlatShape::Int => Color::Purple.bold(), + FlatShape::Decimal => Color::Purple.bold(), + FlatShape::Whitespace => Color::White.normal(), + FlatShape::Error => Color::Red.bold(), + FlatShape::Size { number, unit } => { + let number = number.slice(line); + let unit = unit.slice(line); + return format!( + "{}{}", + Color::Purple.bold().paint(number), + Color::Cyan.bold().paint(unit) + ); } - } + }; - for token in tokens { - styled.push_str(&paint_token_node(token, line)); - } - - styled.to_string() + let body = flat_shape.tag.slice(line); + style.paint(body).to_string() } impl rustyline::Helper for Helper {} From f0ca0312f3ef0061033c888b56de2a64fa530365 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 11 Oct 2019 19:06:24 +0200 Subject: [PATCH 247/342] Adds racer, formats shell.nix --- shell.nix | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/shell.nix b/shell.nix index e6062b2cb0..2b2bbca078 100644 --- a/shell.nix +++ b/shell.nix @@ -1,10 +1,25 @@ let - moz_overlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz); + moz_overlay = import (builtins.fetchTarball + "https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz"); + nixpkgs = import { overlays = [ moz_overlay ]; }; - nightly = ((nixpkgs.rustChannelOf { date = "2019-09-01"; channel = "nightly"; }).rust.override { extensions = [ "rust-src" "rls-preview" "clippy-preview" "rust-analysis" "rustfmt-preview" ];}); -in -with nixpkgs; + + nightly = ((nixpkgs.rustChannelOf { + date = "2019-09-01"; + channel = "nightly"; + }).rust.override { + extensions = [ + "rust-src" + "rls-preview" + "clippy-preview" + "rust-analysis" + "rustfmt-preview" + ]; + }); + +in with nixpkgs; stdenv.mkDerivation { name = "nushell-rust"; - buildInputs = [ nightly openssl_1_1 pkg-config ]; + buildInputs = [ nightly openssl_1_1 pkg-config rustracer ]; + RUST_SRC_PATH = "${nightly}/lib/rustlib/src/rust/src"; } From af2ec609804d4890bd2c507941c7d273b92474dc Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 11 Oct 2019 21:13:00 +0200 Subject: [PATCH 248/342] Shell.nix cleanup. --- shell.nix | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/shell.nix b/shell.nix index 2b2bbca078..9e7cbe9721 100644 --- a/shell.nix +++ b/shell.nix @@ -1,25 +1,32 @@ +{ pkgs ? import { + overlays = [ + (import (builtins.fetchTarball + "https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz")) + ]; +} }: +with pkgs; let - moz_overlay = import (builtins.fetchTarball - "https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz"); - nixpkgs = import { overlays = [ moz_overlay ]; }; - - nightly = ((nixpkgs.rustChannelOf { + nightly = ((pkgs.rustChannelOf { date = "2019-09-01"; channel = "nightly"; }).rust.override { extensions = [ - "rust-src" - "rls-preview" "clippy-preview" + "rls-preview" "rust-analysis" + "rust-src" "rustfmt-preview" ]; }); -in with nixpkgs; -stdenv.mkDerivation { + nu-deps = [ openssl_1_1 pkg-config x11 python3 ]; + + rust = [ nightly rustracer cargo-watch ]; + +in stdenv.mkDerivation { name = "nushell-rust"; - buildInputs = [ nightly openssl_1_1 pkg-config rustracer ]; + buildInputs = nu-deps ++ rust; RUST_SRC_PATH = "${nightly}/lib/rustlib/src/rust/src"; + SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; } From 5ec6bac7d99e7e2e3c4aa503f17efbc9e95608cf Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 11 Oct 2019 21:39:11 +0200 Subject: [PATCH 249/342] Removes redundant parens. --- shell.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell.nix b/shell.nix index 9e7cbe9721..f5c61ac0a8 100644 --- a/shell.nix +++ b/shell.nix @@ -7,7 +7,7 @@ with pkgs; let - nightly = ((pkgs.rustChannelOf { + nightly = (pkgs.rustChannelOf { date = "2019-09-01"; channel = "nightly"; }).rust.override { @@ -18,7 +18,7 @@ let "rust-src" "rustfmt-preview" ]; - }); + }; nu-deps = [ openssl_1_1 pkg-config x11 python3 ]; From 439889dcefb25d92b517f0ceb050de8276759f7a Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Fri, 11 Oct 2019 11:39:05 -0700 Subject: [PATCH 250/342] Feature flagging infrastructure This commit adds the ability to work on features behind a feature flag that won't be included in normal builds of nu. These features are not exposed as Cargo features, as they reflect incomplete features that are not yet stable. To create a feature, add it to `features.toml`: ```toml [hintsv1] description = "Adding hints based on error states in the highlighter" enabled = false ``` Each feature in `features.toml` becomes a feature flag accessible to `cfg`: ```rs println!("hintsv1 is enabled"); ``` By default, features are enabled based on the value of the `enabled` field. You can also enable a feature from the command line via the `NUSHELL_ENABLE_FLAGS` environment variable: ```sh $ NUSHELL_ENABLE_FLAGS=hintsv1 cargo run ``` You can enable all flags via `NUSHELL_ENABLE_ALL_FLAGS`. This commit also updates the CI setup to run the build with all flags off and with all flags on. It also extracts the linting test into its own parallelizable test, which means it doesn't need to run together with every other test anymore. When working on a feature, you should also add tests behind the same flag. A commit is mergable if all tests pass with and without the flag, allowing incomplete commits to land on master as long as the incomplete code builds and passes tests. --- .azure/azure-pipelines.yml | 20 ++++++++++++++ Cargo.lock | 56 +++++++++++++++++++------------------- Cargo.toml | 4 +++ build.rs | 39 ++++++++++++++++++++++++++ features.toml | 4 +++ src/main.rs | 3 ++ 6 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 build.rs create mode 100644 features.toml diff --git a/.azure/azure-pipelines.yml b/.azure/azure-pipelines.yml index e1f9b93681..2ab7e05c46 100644 --- a/.azure/azure-pipelines.yml +++ b/.azure/azure-pipelines.yml @@ -5,10 +5,25 @@ strategy: matrix: linux-nightly: image: ubuntu-16.04 + style: 'unflagged' macos-nightly: image: macos-10.14 + style: 'unflagged' windows-nightly: image: vs2017-win2016 + style: 'unflagged' + linux-nightly-canary: + image: ubuntu-16.04 + style: 'canary' + macos-nightly-canary: + image: macos-10.14 + style: 'canary' + windows-nightly-canary: + image: vs2017-win2016 + style: 'canary' + fmt: + image: ubuntu-16.04 + style: 'fmt' pool: vmImage: $(image) @@ -27,6 +42,11 @@ steps: rustup component add rustfmt --toolchain `cat rust-toolchain` displayName: Install Rust - bash: RUSTFLAGS="-D warnings" cargo test --all-features + condition: eq(variables['style'], 'unflagged') + displayName: Run tests + - bash: NUSHELL_ENABLE_ALL_FLAGS=1 RUSTFLAGS="-D warnings" cargo test --all-features + condition: eq(variables['style'], 'canary') displayName: Run tests - bash: cargo fmt --all -- --check + condition: eq(variables['style'], 'fmt') displayName: Lint diff --git a/Cargo.lock b/Cargo.lock index af1d46aa0e..1457db7c51 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -138,7 +138,7 @@ dependencies = [ "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -148,7 +148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -184,7 +184,7 @@ dependencies = [ "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -197,7 +197,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -260,7 +260,7 @@ dependencies = [ "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -322,7 +322,7 @@ dependencies = [ "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -471,7 +471,7 @@ dependencies = [ "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -558,7 +558,7 @@ dependencies = [ "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "ord_subset 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1105,7 +1105,7 @@ name = "indexmap" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1220,7 +1220,7 @@ dependencies = [ "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "render-tree 0.1.1 (git+https://github.com/wycats/language-reporting)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1438,7 +1438,7 @@ dependencies = [ "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1586,7 +1586,7 @@ dependencies = [ "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1616,7 +1616,7 @@ dependencies = [ "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1808,7 +1808,7 @@ dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "line-wrap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1893,7 +1893,7 @@ dependencies = [ "directories 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde-value 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "tint 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2268,7 +2268,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.100" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2304,7 +2304,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2312,7 +2312,7 @@ name = "serde_bytes" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2340,7 +2340,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2352,7 +2352,7 @@ dependencies = [ "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2370,7 +2370,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2381,7 +2381,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2481,7 +2481,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2535,7 +2535,7 @@ dependencies = [ "onig 4.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "plist 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2641,7 +2641,7 @@ name = "toml" version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2649,7 +2649,7 @@ name = "toml" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2758,7 +2758,7 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3242,7 +3242,7 @@ dependencies = [ "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" -"checksum serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)" = "f4473e8506b213730ff2061073b48fa51dcc66349219e2e7c5608f0296a1d95a" +"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" "checksum serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0b833c5ad67d52ced5f5938b2980f32a9c1c5ef047f0b4fb3127e7a423c76153" "checksum serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" "checksum serde-value 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7a663f873dedc4eac1a559d4c6bc0d0b2c34dc5ac4702e105014b8281489e44f" diff --git a/Cargo.toml b/Cargo.toml index 80a077dd88..5a4ed6a027 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -106,6 +106,10 @@ features = ["bundled", "blob"] [dev-dependencies] pretty_assertions = "0.6.1" +[build-dependencies] +toml = "0.5.3" +serde = { version = "1.0.101", features = ["derive"] } + [lib] name = "nu" path = "src/lib.rs" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000..44a55f9573 --- /dev/null +++ b/build.rs @@ -0,0 +1,39 @@ +use serde::Deserialize; +use std::collections::HashMap; +use std::collections::HashSet; +use std::env; +use std::path::Path; + +#[derive(Deserialize)] +struct Feature { + #[allow(unused)] + description: String, + enabled: bool, +} + +fn main() -> Result<(), Box> { + let input = env::var("CARGO_MANIFEST_DIR").unwrap(); + let all_on = env::var("NUSHELL_ENABLE_ALL_FLAGS").is_ok(); + let flags: HashSet = env::var("NUSHELL_ENABLE_FLAGS") + .map(|s| s.split(",").map(|s| s.to_string()).collect()) + .unwrap_or_else(|_| HashSet::new()); + + if all_on && !flags.is_empty() { + println!( + "cargo:warning={}", + "Both NUSHELL_ENABLE_ALL_FLAGS and NUSHELL_ENABLE_FLAGS were set. You don't need both." + ); + } + + let path = Path::new(&input).join("features.toml"); + + let toml: HashMap = toml::from_str(&std::fs::read_to_string(path)?)?; + + for (key, value) in toml.iter() { + if value.enabled == true || all_on || flags.contains(key) { + println!("cargo:rustc-cfg={}", key); + } + } + + Ok(()) +} diff --git a/features.toml b/features.toml new file mode 100644 index 0000000000..290f673d26 --- /dev/null +++ b/features.toml @@ -0,0 +1,4 @@ +[hintsv1] + +description = "Adding hints based upon error states in the syntax highlighter" +enabled = false diff --git a/src/main.rs b/src/main.rs index 7f82808e74..4b10944a2b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,9 @@ use log::LevelFilter; use std::error::Error; fn main() -> Result<(), Box> { + #[cfg(feature1)] + println!("feature1 is enabled"); + let matches = App::new("nushell") .version(clap::crate_version!()) .arg( From 193b00764b0cf5ad0335446c55f6c8d3aeb388a9 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 13 Oct 2019 17:12:43 +1300 Subject: [PATCH 251/342] Stream support (#812) * Moves off of draining between filters. Instead, the sink will pull on the stream, and will drain element-wise. This moves the whole stream to being lazy. * Adds ctrl-c support and connects it into some of the key points where we pull on the stream. If a ctrl-c is detect, we immediately halt pulling on the stream and return to the prompt. * Moves away from having a SourceMap where anchor locations are stored. Now AnchorLocation is kept directly in the Tag. * To make this possible, split tag and span. Span is largely used in the parser and is copyable. Tag is now no longer copyable. --- Cargo.lock | 88 ------- Cargo.toml | 1 - src/cli.rs | 65 +++-- src/commands/autoview.rs | 198 ++++++++------ src/commands/classified.rs | 121 ++++----- src/commands/command.rs | 58 ++-- src/commands/config.rs | 2 +- src/commands/date.rs | 18 +- src/commands/echo.rs | 2 +- src/commands/enter.rs | 17 +- src/commands/env.rs | 16 +- src/commands/fetch.rs | 51 ++-- src/commands/from_bson.rs | 66 ++--- src/commands/from_csv.rs | 18 +- src/commands/from_ini.rs | 17 +- src/commands/from_json.rs | 32 +-- src/commands/from_sqlite.rs | 6 +- src/commands/from_toml.rs | 16 +- src/commands/from_tsv.rs | 18 +- src/commands/from_url.rs | 6 +- src/commands/from_xml.rs | 14 +- src/commands/from_yaml.rs | 18 +- src/commands/get.rs | 2 +- src/commands/help.rs | 2 +- src/commands/lines.rs | 2 +- src/commands/ls.rs | 2 +- src/commands/open.rs | 59 ++--- src/commands/pivot.rs | 2 +- src/commands/post.rs | 107 ++++---- src/commands/save.rs | 17 +- src/commands/shells.rs | 7 +- src/commands/size.rs | 2 +- src/commands/split_column.rs | 2 +- src/commands/split_row.rs | 2 +- src/commands/table.rs | 28 +- src/commands/tags.rs | 3 +- src/commands/to_bson.rs | 19 +- src/commands/to_csv.rs | 24 +- src/commands/to_json.rs | 11 +- src/commands/to_toml.rs | 13 +- src/commands/to_tsv.rs | 18 +- src/commands/to_url.rs | 8 +- src/commands/to_yaml.rs | 11 +- src/commands/version.rs | 6 +- src/commands/where_.rs | 2 +- src/commands/which_.rs | 4 +- src/context.rs | 53 +--- src/data/base.rs | 69 ++--- src/data/command.rs | 14 +- src/data/config.rs | 8 +- src/data/dict.rs | 4 +- src/data/meta.rs | 235 ++++++++++------- src/data/types.rs | 4 +- src/errors.rs | 83 +++--- src/evaluate/evaluator.rs | 39 +-- src/format/generic.rs | 4 +- src/format/table.rs | 4 +- src/lib.rs | 6 +- src/parser.rs | 4 +- src/parser/deserializer.rs | 2 +- src/parser/hir.rs | 102 ++++---- src/parser/hir/baseline_parse/tests.rs | 35 ++- src/parser/hir/binary.rs | 4 +- src/parser/hir/expand_external_tokens.rs | 28 +- src/parser/hir/external_command.rs | 2 +- src/parser/hir/named.rs | 10 +- src/parser/hir/path.rs | 7 +- src/parser/hir/syntax_shape.rs | 201 +++++++------- src/parser/hir/syntax_shape/block.rs | 62 ++--- src/parser/hir/syntax_shape/expression.rs | 47 ++-- .../hir/syntax_shape/expression/atom.rs | 247 ++++++++++-------- .../hir/syntax_shape/expression/delimited.rs | 28 +- .../hir/syntax_shape/expression/file_path.rs | 10 +- .../hir/syntax_shape/expression/list.rs | 8 +- .../hir/syntax_shape/expression/number.rs | 61 +++-- .../hir/syntax_shape/expression/pattern.rs | 14 +- .../hir/syntax_shape/expression/string.rs | 30 ++- .../hir/syntax_shape/expression/unit.rs | 44 ++-- .../syntax_shape/expression/variable_path.rs | 117 +++++---- src/parser/hir/syntax_shape/flat_shape.rs | 52 ++-- src/parser/hir/tokens_iterator.rs | 56 ++-- src/parser/parse/files.rs | 31 ++- src/parser/parse/flag.rs | 12 +- src/parser/parse/parser.rs | 118 ++++----- src/parser/parse/pipeline.rs | 8 +- src/parser/parse/token_tree.rs | 127 ++++----- src/parser/parse/token_tree_builder.rs | 171 ++++++------ src/parser/parse/tokens.rs | 68 ++--- src/parser/parse_command.rs | 35 +-- src/parser/registry.rs | 2 +- src/plugins/add.rs | 4 +- src/plugins/binaryview.rs | 3 +- src/plugins/edit.rs | 2 +- src/plugins/embed.rs | 2 +- src/plugins/inc.rs | 47 ++-- src/plugins/ps.rs | 2 +- src/plugins/str.rs | 23 +- src/plugins/sum.rs | 4 +- src/plugins/sys.rs | 31 ++- src/plugins/textview.rs | 17 +- src/prelude.rs | 18 +- src/shell/filesystem_shell.rs | 78 +++--- src/shell/help_shell.rs | 7 +- src/shell/helper.rs | 18 +- src/shell/shell.rs | 5 +- src/shell/shell_manager.rs | 74 +++--- src/shell/value_shell.rs | 19 +- src/stream.rs | 11 + tests/command_config_test.rs | 46 ++-- tests/command_open_tests.rs | 2 +- 110 files changed, 1988 insertions(+), 1892 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1457db7c51..765f42d637 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1604,7 +1604,6 @@ dependencies = [ "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1920,24 +1919,6 @@ dependencies = [ "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rand" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rand" version = "0.7.0" @@ -1950,15 +1931,6 @@ dependencies = [ "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rand_chacha" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rand_chacha" version = "0.2.1" @@ -1989,14 +1961,6 @@ dependencies = [ "getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rand_hc" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rand_hc" version = "0.2.0" @@ -2005,24 +1969,6 @@ dependencies = [ "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rand_isaac" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_jitter" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "rand_os" version = "0.1.3" @@ -2036,23 +1982,6 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "rand_pcg" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rand_xorshift" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "raw-cpuid" version = "7.0.3" @@ -2752,15 +2681,6 @@ name = "utf8parse" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "uuid" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "vcpkg" version = "0.2.7" @@ -3201,20 +3121,13 @@ dependencies = [ "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" "checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" -"checksum rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" "checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" -"checksum rand_hc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -"checksum rand_isaac 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" -"checksum rand_jitter 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" -"checksum rand_pcg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" -"checksum rand_xorshift 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" "checksum raw-cpuid 7.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf" "checksum rawkey 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "33ec17a493dcb820725c002bc253f6f3ba4e4dc635e72c238540691b05e43897" "checksum rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" @@ -3297,7 +3210,6 @@ dependencies = [ "checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" "checksum user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ef4711d107b21b410a3a974b1204d9accc8b10dad75d8324b5d755de1617d47" "checksum utf8parse 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" -"checksum uuid 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "90dbc611eb48397705a6b0f6e917da23ae517e4d127123d2cf7674206627d32a" "checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" diff --git a/Cargo.toml b/Cargo.toml index 5a4ed6a027..9ae1ada021 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -66,7 +66,6 @@ hex = "0.3.2" tempfile = "3.1.0" semver = "0.9.0" which = "2.0.1" -uuid = {version = "0.7.4", features = [ "v4", "serde" ]} textwrap = {version = "0.11.0", features = ["term_size"]} shellexpand = "1.0.0" futures-timer = "0.4.0" diff --git a/src/cli.rs b/src/cli.rs index 6c1ba5ef93..16dc983540 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -28,8 +28,7 @@ use std::error::Error; use std::io::{BufRead, BufReader, Write}; use std::iter::Iterator; use std::path::PathBuf; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Arc; +use std::sync::atomic::Ordering; #[derive(Debug)] pub enum MaybeOwned<'a, T> { @@ -339,16 +338,15 @@ pub async fn cli() -> Result<(), Box> { // we are ok if history does not exist let _ = rl.load_history(&History::path()); - let ctrl_c = Arc::new(AtomicBool::new(false)); - let cc = ctrl_c.clone(); + let cc = context.ctrl_c.clone(); ctrlc::set_handler(move || { cc.store(true, Ordering::SeqCst); }) .expect("Error setting Ctrl-C handler"); let mut ctrlcbreak = false; loop { - if ctrl_c.load(Ordering::SeqCst) { - ctrl_c.store(false, Ordering::SeqCst); + if context.ctrl_c.load(Ordering::SeqCst) { + context.ctrl_c.store(false, Ordering::SeqCst); continue; } @@ -481,7 +479,7 @@ async fn process_line(readline: Result, ctx: &mut Context Ok(line) => { let line = chomp_newline(line); - let result = match crate::parser::parse(&line, uuid::Uuid::nil()) { + let result = match crate::parser::parse(&line) { Err(err) => { return LineResult::Error(line.to_string(), err); } @@ -549,30 +547,45 @@ async fn process_line(readline: Result, ctx: &mut Context ( Some(ClassifiedCommand::Internal(left)), Some(ClassifiedCommand::External(_)), - ) => match left - .run(ctx, input, Text::from(line), is_first_command) - .await - { + ) => match left.run(ctx, input, Text::from(line), is_first_command) { Ok(val) => ClassifiedInputStream::from_input_stream(val), Err(err) => return LineResult::Error(line.to_string(), err), }, (Some(ClassifiedCommand::Internal(left)), Some(_)) => { - match left - .run(ctx, input, Text::from(line), is_first_command) - .await - { + match left.run(ctx, input, Text::from(line), is_first_command) { Ok(val) => ClassifiedInputStream::from_input_stream(val), Err(err) => return LineResult::Error(line.to_string(), err), } } (Some(ClassifiedCommand::Internal(left)), None) => { - match left - .run(ctx, input, Text::from(line), is_first_command) - .await - { - Ok(val) => ClassifiedInputStream::from_input_stream(val), + match left.run(ctx, input, Text::from(line), is_first_command) { + Ok(val) => { + use futures::stream::TryStreamExt; + + let mut output_stream: OutputStream = val.into(); + loop { + match output_stream.try_next().await { + Ok(Some(ReturnSuccess::Value(Tagged { + item: Value::Error(e), + .. + }))) => { + return LineResult::Error(line.to_string(), e); + } + Ok(Some(_item)) => { + if ctx.ctrl_c.load(Ordering::SeqCst) { + break; + } + } + _ => { + break; + } + } + } + + return LineResult::Success(line.to_string()); + } Err(err) => return LineResult::Error(line.to_string(), err), } } @@ -620,12 +633,12 @@ fn classify_pipeline( source: &Text, ) -> Result { let mut pipeline_list = vec![pipeline.clone()]; - let mut iterator = TokensIterator::all(&mut pipeline_list, pipeline.tag()); + let mut iterator = TokensIterator::all(&mut pipeline_list, pipeline.span()); expand_syntax( &PipelineShape, &mut iterator, - &context.expand_context(source, pipeline.tag()), + &context.expand_context(source, pipeline.span()), ) } @@ -642,7 +655,13 @@ pub(crate) fn external_command( Ok(ClassifiedCommand::External(ExternalCommand { name: name.to_string(), name_tag: name.tag(), - args: arg_list_strings, + args: arg_list_strings + .iter() + .map(|x| Tagged { + tag: x.span.into(), + item: x.item.clone(), + }) + .collect(), })) } diff --git a/src/commands/autoview.rs b/src/commands/autoview.rs index 29e7d18121..4f7d7172a2 100644 --- a/src/commands/autoview.rs +++ b/src/commands/autoview.rs @@ -1,9 +1,14 @@ use crate::commands::{RawCommandArgs, WholeStreamCommand}; use crate::errors::ShellError; +use crate::parser::hir::{Expression, NamedArguments}; use crate::prelude::*; +use futures::stream::TryStreamExt; +use std::sync::atomic::Ordering; pub struct Autoview; +const STREAM_PAGE_SIZE: u64 = 50; + #[derive(Deserialize)] pub struct AutoviewArgs {} @@ -31,61 +36,132 @@ impl WholeStreamCommand for Autoview { pub fn autoview( AutoviewArgs {}: AutoviewArgs, - mut context: RunnableContext, + context: RunnableContext, raw: RawCommandArgs, ) -> Result { - Ok(OutputStream::new(async_stream! { - let input = context.input.drain_vec().await; + let binary = context.get_command("binaryview"); + let text = context.get_command("textview"); + let table = context.get_command("table"); - if input.len() > 0 { - if let Tagged { - item: Value::Primitive(Primitive::Binary(_)), - .. - } = input[0usize] - { - let binary = context.get_command("binaryview"); - if let Some(binary) = binary { - let result = binary.run(raw.with_input(input), &context.commands, false); - result.collect::>().await; - } else { - for i in input { - match i.item { - Value::Primitive(Primitive::Binary(b)) => { - use pretty_hex::*; - println!("{:?}", b.hex_dump()); + Ok(OutputStream::new(async_stream! { + let mut output_stream: OutputStream = context.input.into(); + + match output_stream.try_next().await { + Ok(Some(x)) => { + match output_stream.try_next().await { + Ok(Some(y)) => { + let ctrl_c = context.ctrl_c.clone(); + let stream = async_stream! { + yield Ok(x); + yield Ok(y); + + loop { + match output_stream.try_next().await { + Ok(Some(z)) => { + if ctrl_c.load(Ordering::SeqCst) { + break; + } + yield Ok(z); + } + _ => break, + } + } + }; + if let Some(table) = table { + let mut new_output_stream: OutputStream = stream.to_output_stream(); + let mut finished = false; + let mut current_idx = 0; + loop { + let mut new_input = VecDeque::new(); + + for _ in 0..STREAM_PAGE_SIZE { + match new_output_stream.try_next().await { + + Ok(Some(a)) => { + if let ReturnSuccess::Value(v) = a { + new_input.push_back(v); + } + } + _ => { + finished = true; + break; + } + } + } + + let raw = raw.clone(); + + let mut command_args = raw.with_input(new_input.into()); + let mut named_args = NamedArguments::new(); + named_args.insert_optional("start_number", Some(Expression::number(current_idx, Tag::unknown()))); + command_args.call_info.args.named = Some(named_args); + + let result = table.run(command_args, &context.commands, false); + result.collect::>().await; + + if finished { + break; + } else { + current_idx += STREAM_PAGE_SIZE; + } } - _ => {} } } - }; - } else if is_single_anchored_text_value(&input) { - let text = context.get_command("textview"); - if let Some(text) = text { - let result = text.run(raw.with_input(input), &context.commands, false); - result.collect::>().await; - } else { - for i in input { - match i.item { - Value::Primitive(Primitive::String(s)) => { - println!("{}", s); + _ => { + if let ReturnSuccess::Value(x) = x { + match x { + Tagged { + item: Value::Primitive(Primitive::String(ref s)), + tag: Tag { anchor, span }, + } if anchor.is_some() => { + if let Some(text) = text { + let mut stream = VecDeque::new(); + stream.push_back(Value::string(s).tagged(Tag { anchor, span })); + let result = text.run(raw.with_input(stream.into()), &context.commands, false); + result.collect::>().await; + } else { + println!("{}", s); + } + } + Tagged { + item: Value::Primitive(Primitive::String(s)), + .. + } => { + println!("{}", s); + } + + Tagged { item: Value::Primitive(Primitive::Binary(ref b)), .. } => { + if let Some(binary) = binary { + let mut stream = VecDeque::new(); + stream.push_back(x.clone()); + let result = binary.run(raw.with_input(stream.into()), &context.commands, false); + result.collect::>().await; + } else { + use pretty_hex::*; + println!("{:?}", b.hex_dump()); + } + } + + Tagged { item: Value::Error(e), .. } => { + yield Err(e); + } + Tagged { item: ref item, .. } => { + if let Some(table) = table { + let mut stream = VecDeque::new(); + stream.push_back(x.clone()); + let result = table.run(raw.with_input(stream.into()), &context.commands, false); + result.collect::>().await; + } else { + println!("{:?}", item); + } + } } - _ => {} } } } - } else if is_single_text_value(&input) { - for i in input { - match i.item { - Value::Primitive(Primitive::String(s)) => { - println!("{}", s); - } - _ => {} - } - } - } else { - let table = context.expect_command("table"); - let result = table.run(raw.with_input(input), &context.commands, false); - result.collect::>().await; + } + _ => { + //println!(""); } } @@ -95,35 +171,3 @@ pub fn autoview( } })) } - -fn is_single_text_value(input: &Vec>) -> bool { - if input.len() != 1 { - return false; - } - if let Tagged { - item: Value::Primitive(Primitive::String(_)), - .. - } = input[0] - { - true - } else { - false - } -} - -#[allow(unused)] -fn is_single_anchored_text_value(input: &Vec>) -> bool { - if input.len() != 1 { - return false; - } - - if let Tagged { - item: Value::Primitive(Primitive::String(_)), - tag: Tag { anchor, .. }, - } = input[0] - { - anchor != uuid::Uuid::nil() - } else { - false - } -} diff --git a/src/commands/classified.rs b/src/commands/classified.rs index c73a56fee4..105daff771 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -100,7 +100,7 @@ pub(crate) struct DynamicCommand { } impl InternalCommand { - pub(crate) async fn run( + pub(crate) fn run( self, context: &mut Context, input: ClassifiedInputStream, @@ -119,12 +119,9 @@ impl InternalCommand { let command = context.expect_command(&self.name); let result = { - let source_map = context.source_map.lock().unwrap().clone(); - context.run_command( command, self.name_tag.clone(), - source_map, self.args, &source, objects, @@ -134,69 +131,73 @@ impl InternalCommand { let result = trace_out_stream!(target: "nu::trace_stream::internal", source: &source, "output" = result); let mut result = result.values; + let mut context = context.clone(); - let mut stream = VecDeque::new(); - while let Some(item) = result.next().await { - match item? { - ReturnSuccess::Action(action) => match action { - CommandAction::ChangePath(path) => { - context.shell_manager.set_path(path); - } - CommandAction::AddAnchorLocation(uuid, anchor_location) => { - context.add_anchor_location(uuid, anchor_location); - } - CommandAction::Exit => std::process::exit(0), // TODO: save history.txt - CommandAction::EnterHelpShell(value) => { - match value { - Tagged { - item: Value::Primitive(Primitive::String(cmd)), - tag, - } => { - context.shell_manager.insert_at_current(Box::new( - HelpShell::for_command( - Value::string(cmd).tagged(tag), - &context.registry(), - )?, - )); - } - _ => { - context.shell_manager.insert_at_current(Box::new( - HelpShell::index(&context.registry())?, - )); + let stream = async_stream! { + while let Some(item) = result.next().await { + match item { + Ok(ReturnSuccess::Action(action)) => match action { + CommandAction::ChangePath(path) => { + context.shell_manager.set_path(path); + } + CommandAction::Exit => std::process::exit(0), // TODO: save history.txt + CommandAction::EnterHelpShell(value) => { + match value { + Tagged { + item: Value::Primitive(Primitive::String(cmd)), + tag, + } => { + context.shell_manager.insert_at_current(Box::new( + HelpShell::for_command( + Value::string(cmd).tagged(tag), + &context.registry(), + ).unwrap(), + )); + } + _ => { + context.shell_manager.insert_at_current(Box::new( + HelpShell::index(&context.registry()).unwrap(), + )); + } } } - } - CommandAction::EnterValueShell(value) => { - context - .shell_manager - .insert_at_current(Box::new(ValueShell::new(value))); - } - CommandAction::EnterShell(location) => { - context.shell_manager.insert_at_current(Box::new( - FilesystemShell::with_location(location, context.registry().clone())?, - )); - } - CommandAction::PreviousShell => { - context.shell_manager.prev(); - } - CommandAction::NextShell => { - context.shell_manager.next(); - } - CommandAction::LeaveShell => { - context.shell_manager.remove_at_current(); - if context.shell_manager.is_empty() { - std::process::exit(0); // TODO: save history.txt + CommandAction::EnterValueShell(value) => { + context + .shell_manager + .insert_at_current(Box::new(ValueShell::new(value))); } - } - }, + CommandAction::EnterShell(location) => { + context.shell_manager.insert_at_current(Box::new( + FilesystemShell::with_location(location, context.registry().clone()).unwrap(), + )); + } + CommandAction::PreviousShell => { + context.shell_manager.prev(); + } + CommandAction::NextShell => { + context.shell_manager.next(); + } + CommandAction::LeaveShell => { + context.shell_manager.remove_at_current(); + if context.shell_manager.is_empty() { + std::process::exit(0); // TODO: save history.txt + } + } + }, - ReturnSuccess::Value(v) => { - stream.push_back(v); + Ok(ReturnSuccess::Value(v)) => { + yield Ok(v); + } + + Err(x) => { + yield Ok(Value::Error(x).tagged_unknown()); + break; + } } } - } + }; - Ok(stream.into()) + Ok(stream.to_input_stream()) } } @@ -346,7 +347,7 @@ impl ExternalCommand { let stdout = popen.stdout.take().unwrap(); let file = futures::io::AllowStdIo::new(stdout); let stream = Framed::new(file, LinesCodec {}); - let stream = stream.map(move |line| Value::string(line.unwrap()).tagged(name_tag)); + let stream = stream.map(move |line| Value::string(line.unwrap()).tagged(&name_tag)); Ok(ClassifiedInputStream::from_input_stream( stream.boxed() as BoxStream<'static, Tagged> )) diff --git a/src/commands/command.rs b/src/commands/command.rs index 7fb08bcefa..5f3f4809bd 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -1,4 +1,3 @@ -use crate::context::{AnchorLocation, SourceMap}; use crate::data::Value; use crate::errors::ShellError; use crate::evaluate::Scope; @@ -11,13 +10,12 @@ use serde::{Deserialize, Serialize}; use std::fmt; use std::ops::Deref; use std::path::PathBuf; -use uuid::Uuid; +use std::sync::atomic::AtomicBool; #[derive(Deserialize, Serialize, Debug, Clone)] pub struct UnevaluatedCallInfo { pub args: hir::Call, pub source: Text, - pub source_map: SourceMap, pub name_tag: Tag, } @@ -37,7 +35,6 @@ impl UnevaluatedCallInfo { Ok(CallInfo { args, - source_map: self.source_map, name_tag: self.name_tag, }) } @@ -46,7 +43,6 @@ impl UnevaluatedCallInfo { #[derive(Deserialize, Serialize, Debug, Clone)] pub struct CallInfo { pub args: registry::EvaluatedArgs, - pub source_map: SourceMap, pub name_tag: Tag, } @@ -62,7 +58,7 @@ impl CallInfo { args: T::deserialize(&mut deserializer)?, context: RunnablePerItemContext { shell_manager: shell_manager.clone(), - name: self.name_tag, + name: self.name_tag.clone(), }, callback, }) @@ -73,6 +69,7 @@ impl CallInfo { #[get = "pub(crate)"] pub struct CommandArgs { pub host: Arc>, + pub ctrl_c: Arc, pub shell_manager: ShellManager, pub call_info: UnevaluatedCallInfo, pub input: InputStream, @@ -82,6 +79,7 @@ pub struct CommandArgs { #[get = "pub(crate)"] pub struct RawCommandArgs { pub host: Arc>, + pub ctrl_c: Arc, pub shell_manager: ShellManager, pub call_info: UnevaluatedCallInfo, } @@ -90,6 +88,7 @@ impl RawCommandArgs { pub fn with_input(self, input: Vec>) -> CommandArgs { CommandArgs { host: self.host, + ctrl_c: self.ctrl_c, shell_manager: self.shell_manager, call_info: self.call_info, input: input.into(), @@ -109,12 +108,14 @@ impl CommandArgs { registry: ®istry::CommandRegistry, ) -> Result { let host = self.host.clone(); + let ctrl_c = self.ctrl_c.clone(); let shell_manager = self.shell_manager.clone(); let input = self.input; let call_info = self.call_info.evaluate(registry, &Scope::empty())?; Ok(EvaluatedWholeStreamCommandArgs::new( host, + ctrl_c, shell_manager, call_info, input, @@ -127,12 +128,13 @@ impl CommandArgs { callback: fn(T, RunnableContext) -> Result, ) -> Result, ShellError> { let shell_manager = self.shell_manager.clone(); - let source_map = self.call_info.source_map.clone(); let host = self.host.clone(); + let ctrl_c = self.ctrl_c.clone(); let args = self.evaluate_once(registry)?; + let call_info = args.call_info.clone(); let (input, args) = args.split(); let name_tag = args.call_info.name_tag; - let mut deserializer = ConfigDeserializer::from_call_info(args.call_info); + let mut deserializer = ConfigDeserializer::from_call_info(call_info); Ok(RunnableArgs { args: T::deserialize(&mut deserializer)?, @@ -141,8 +143,8 @@ impl CommandArgs { commands: registry.clone(), shell_manager, name: name_tag, - source_map, host, + ctrl_c, }, callback, }) @@ -155,17 +157,20 @@ impl CommandArgs { ) -> Result, ShellError> { let raw_args = RawCommandArgs { host: self.host.clone(), + ctrl_c: self.ctrl_c.clone(), shell_manager: self.shell_manager.clone(), call_info: self.call_info.clone(), }; let shell_manager = self.shell_manager.clone(); - let source_map = self.call_info.source_map.clone(); let host = self.host.clone(); + let ctrl_c = self.ctrl_c.clone(); let args = self.evaluate_once(registry)?; + let call_info = args.call_info.clone(); + let (input, args) = args.split(); let name_tag = args.call_info.name_tag; - let mut deserializer = ConfigDeserializer::from_call_info(args.call_info); + let mut deserializer = ConfigDeserializer::from_call_info(call_info.clone()); Ok(RunnableRawArgs { args: T::deserialize(&mut deserializer)?, @@ -174,8 +179,8 @@ impl CommandArgs { commands: registry.clone(), shell_manager, name: name_tag, - source_map, host, + ctrl_c, }, raw_args, callback, @@ -198,18 +203,12 @@ pub struct RunnableContext { pub input: InputStream, pub shell_manager: ShellManager, pub host: Arc>, + pub ctrl_c: Arc, pub commands: CommandRegistry, - pub source_map: SourceMap, pub name: Tag, } impl RunnableContext { - pub fn expect_command(&self, name: &str) -> Arc { - self.commands - .get_command(name) - .expect(&format!("Expected command {}", name)) - } - pub fn get_command(&self, name: &str) -> Option> { self.commands.get_command(name) } @@ -270,6 +269,7 @@ impl Deref for EvaluatedWholeStreamCommandArgs { impl EvaluatedWholeStreamCommandArgs { pub fn new( host: Arc>, + ctrl_c: Arc, shell_manager: ShellManager, call_info: CallInfo, input: impl Into, @@ -277,6 +277,7 @@ impl EvaluatedWholeStreamCommandArgs { EvaluatedWholeStreamCommandArgs { args: EvaluatedCommandArgs { host, + ctrl_c, shell_manager, call_info, }, @@ -285,7 +286,7 @@ impl EvaluatedWholeStreamCommandArgs { } pub fn name_tag(&self) -> Tag { - self.args.call_info.name_tag + self.args.call_info.name_tag.clone() } pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) { @@ -317,12 +318,14 @@ impl Deref for EvaluatedFilterCommandArgs { impl EvaluatedFilterCommandArgs { pub fn new( host: Arc>, + ctrl_c: Arc, shell_manager: ShellManager, call_info: CallInfo, ) -> EvaluatedFilterCommandArgs { EvaluatedFilterCommandArgs { args: EvaluatedCommandArgs { host, + ctrl_c, shell_manager, call_info, }, @@ -334,6 +337,7 @@ impl EvaluatedFilterCommandArgs { #[get = "pub(crate)"] pub struct EvaluatedCommandArgs { pub host: Arc>, + pub ctrl_c: Arc, pub shell_manager: ShellManager, pub call_info: CallInfo, } @@ -376,7 +380,6 @@ impl EvaluatedCommandArgs { #[derive(Debug, Serialize, Deserialize)] pub enum CommandAction { ChangePath(String), - AddAnchorLocation(Uuid, AnchorLocation), Exit, EnterShell(String), EnterValueShell(Tagged), @@ -390,9 +393,6 @@ 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::AddAnchorLocation(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) => { @@ -564,6 +564,7 @@ impl Command { ) -> OutputStream { let raw_args = RawCommandArgs { host: args.host, + ctrl_c: args.ctrl_c, shell_manager: args.shell_manager, call_info: args.call_info, }; @@ -633,6 +634,7 @@ impl WholeStreamCommand for FnFilterCommand { ) -> Result { let CommandArgs { host, + ctrl_c, shell_manager, call_info, input, @@ -650,8 +652,12 @@ impl WholeStreamCommand for FnFilterCommand { Ok(args) => args, }; - let args = - EvaluatedFilterCommandArgs::new(host.clone(), shell_manager.clone(), call_info); + let args = EvaluatedFilterCommandArgs::new( + host.clone(), + ctrl_c.clone(), + shell_manager.clone(), + call_info, + ); match func(args) { Err(err) => return OutputStream::from(vec![Err(err)]).values, diff --git a/src/commands/config.rs b/src/commands/config.rs index 337e3437f9..82fbbf1db6 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -58,7 +58,7 @@ pub fn config( }: ConfigArgs, RunnableContext { name, .. }: RunnableContext, ) -> Result { - let name_span = name; + let name_span = name.clone(); let configuration = if let Some(supplied) = load { Some(supplied.item().clone()) diff --git a/src/commands/date.rs b/src/commands/date.rs index 6df9e27209..bff6b550f7 100644 --- a/src/commands/date.rs +++ b/src/commands/date.rs @@ -39,27 +39,27 @@ where { let mut indexmap = IndexMap::new(); - indexmap.insert("year".to_string(), Value::int(dt.year()).tagged(tag)); - indexmap.insert("month".to_string(), Value::int(dt.month()).tagged(tag)); - indexmap.insert("day".to_string(), Value::int(dt.day()).tagged(tag)); - indexmap.insert("hour".to_string(), Value::int(dt.hour()).tagged(tag)); - indexmap.insert("minute".to_string(), Value::int(dt.minute()).tagged(tag)); - indexmap.insert("second".to_string(), Value::int(dt.second()).tagged(tag)); + indexmap.insert("year".to_string(), Value::int(dt.year()).tagged(&tag)); + indexmap.insert("month".to_string(), Value::int(dt.month()).tagged(&tag)); + indexmap.insert("day".to_string(), Value::int(dt.day()).tagged(&tag)); + indexmap.insert("hour".to_string(), Value::int(dt.hour()).tagged(&tag)); + indexmap.insert("minute".to_string(), Value::int(dt.minute()).tagged(&tag)); + indexmap.insert("second".to_string(), Value::int(dt.second()).tagged(&tag)); let tz = dt.offset(); indexmap.insert( "timezone".to_string(), - Value::string(format!("{}", tz)).tagged(tag), + Value::string(format!("{}", tz)).tagged(&tag), ); - Value::Row(Dictionary::from(indexmap)).tagged(tag) + Value::Row(Dictionary::from(indexmap)).tagged(&tag) } pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; let mut date_out = VecDeque::new(); - let tag = args.call_info.name_tag; + let tag = args.call_info.name_tag.clone(); let value = if args.has("utc") { let utc: DateTime = Utc::now(); diff --git a/src/commands/echo.rs b/src/commands/echo.rs index 5bfc12efb7..4483f91371 100644 --- a/src/commands/echo.rs +++ b/src/commands/echo.rs @@ -35,7 +35,7 @@ fn run( _registry: &CommandRegistry, _raw_args: &RawCommandArgs, ) -> Result { - let name = call_info.name_tag; + let name = call_info.name_tag.clone(); let mut output = String::new(); diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 94688acd56..4a400241e8 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -67,7 +67,7 @@ impl PerItemCommand for Enter { let full_path = std::path::PathBuf::from(cwd); - let (file_extension, contents, contents_tag, anchor_location) = + let (file_extension, contents, contents_tag) = crate::commands::open::fetch( &full_path, &location_clone, @@ -75,18 +75,9 @@ impl PerItemCommand for Enter { ) .await.unwrap(); - if contents_tag.anchor != uuid::Uuid::nil() { - // If we have loaded something, track its source - yield ReturnSuccess::action(CommandAction::AddAnchorLocation( - contents_tag.anchor, - anchor_location, - )); - } - - match contents { Value::Primitive(Primitive::String(_)) => { - let tagged_contents = contents.tagged(contents_tag); + let tagged_contents = contents.tagged(&contents_tag); if let Some(extension) = file_extension { let command_name = format!("from-{}", extension); @@ -95,6 +86,7 @@ impl PerItemCommand for Enter { { let new_args = RawCommandArgs { host: raw_args.host, + ctrl_c: raw_args.ctrl_c, shell_manager: raw_args.shell_manager, call_info: UnevaluatedCallInfo { args: crate::parser::hir::Call { @@ -103,7 +95,6 @@ impl PerItemCommand for Enter { named: None, }, source: raw_args.call_info.source, - source_map: raw_args.call_info.source_map, name_tag: raw_args.call_info.name_tag, }, }; @@ -123,7 +114,7 @@ impl PerItemCommand for Enter { yield Ok(ReturnSuccess::Action(CommandAction::EnterValueShell( Tagged { item, - tag: contents_tag, + tag: contents_tag.clone(), }))); } x => yield x, diff --git a/src/commands/env.rs b/src/commands/env.rs index c0af785557..0572b499c1 100644 --- a/src/commands/env.rs +++ b/src/commands/env.rs @@ -37,22 +37,22 @@ pub fn get_environment(tag: Tag) -> Result, Box Result, Box Result { let args = args.evaluate_once(registry)?; let mut env_out = VecDeque::new(); - let tag = args.call_info.name_tag; + let tag = args.call_info.name_tag.clone(); let value = get_environment(tag)?; env_out.push_back(value); diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index e7966a61bf..e66536729f 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -10,7 +10,6 @@ use mime::Mime; use std::path::PathBuf; use std::str::FromStr; use surf::mime; -use uuid::Uuid; pub struct Fetch; impl PerItemCommand for Fetch { @@ -48,7 +47,7 @@ fn run( ShellError::labeled_error( "No file or directory specified", "for command", - call_info.name_tag, + &call_info.name_tag, ) })? { file => file, @@ -68,7 +67,7 @@ fn run( yield Err(e); return; } - let (file_extension, contents, contents_tag, anchor_location) = result.unwrap(); + let (file_extension, contents, contents_tag) = result.unwrap(); let file_extension = if has_raw { None @@ -78,21 +77,14 @@ fn run( file_extension.or(path_str.split('.').last().map(String::from)) }; - if contents_tag.anchor != uuid::Uuid::nil() { - // If we have loaded something, track its source - yield ReturnSuccess::action(CommandAction::AddAnchorLocation( - contents_tag.anchor, - anchor_location, - )); - } - - let tagged_contents = contents.tagged(contents_tag); + let tagged_contents = contents.tagged(&contents_tag); if let Some(extension) = file_extension { let command_name = format!("from-{}", extension); if let Some(converter) = registry.get_command(&command_name) { let new_args = RawCommandArgs { host: raw_args.host, + ctrl_c: raw_args.ctrl_c, shell_manager: raw_args.shell_manager, call_info: UnevaluatedCallInfo { args: crate::parser::hir::Call { @@ -101,7 +93,6 @@ fn run( named: None }, source: raw_args.call_info.source, - source_map: raw_args.call_info.source_map, name_tag: raw_args.call_info.name_tag, } }; @@ -115,7 +106,7 @@ fn run( } } Ok(ReturnSuccess::Value(Tagged { item, .. })) => { - yield Ok(ReturnSuccess::Value(Tagged { item, tag: contents_tag })); + yield Ok(ReturnSuccess::Value(Tagged { item, tag: contents_tag.clone() })); } x => yield x, } @@ -131,10 +122,7 @@ fn run( Ok(stream.to_output_stream()) } -pub async fn fetch( - location: &str, - span: Span, -) -> Result<(Option, Value, Tag, AnchorLocation), ShellError> { +pub async fn fetch(location: &str, span: Span) -> Result<(Option, Value, Tag), ShellError> { if let Err(_) = url::Url::parse(location) { return Err(ShellError::labeled_error( "Incomplete or incorrect url", @@ -160,9 +148,8 @@ pub async fn fetch( })?), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::Url(location.to_string())), }, - AnchorLocation::Url(location.to_string()), )), (mime::APPLICATION, mime::JSON) => Ok(( Some("json".to_string()), @@ -175,9 +162,8 @@ pub async fn fetch( })?), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::Url(location.to_string())), }, - AnchorLocation::Url(location.to_string()), )), (mime::APPLICATION, mime::OCTET_STREAM) => { let buf: Vec = r.body_bytes().await.map_err(|_| { @@ -192,9 +178,8 @@ pub async fn fetch( Value::binary(buf), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::Url(location.to_string())), }, - AnchorLocation::Url(location.to_string()), )) } (mime::IMAGE, mime::SVG) => Ok(( @@ -208,9 +193,8 @@ pub async fn fetch( })?), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::Url(location.to_string())), }, - AnchorLocation::Url(location.to_string()), )), (mime::IMAGE, image_ty) => { let buf: Vec = r.body_bytes().await.map_err(|_| { @@ -225,9 +209,8 @@ pub async fn fetch( Value::binary(buf), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::Url(location.to_string())), }, - AnchorLocation::Url(location.to_string()), )) } (mime::TEXT, mime::HTML) => Ok(( @@ -241,9 +224,8 @@ pub async fn fetch( })?), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::Url(location.to_string())), }, - AnchorLocation::Url(location.to_string()), )), (mime::TEXT, mime::PLAIN) => { let path_extension = url::Url::parse(location) @@ -268,9 +250,8 @@ pub async fn fetch( })?), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::Url(location.to_string())), }, - AnchorLocation::Url(location.to_string()), )) } (ty, sub_ty) => Ok(( @@ -278,9 +259,8 @@ pub async fn fetch( Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::Url(location.to_string())), }, - AnchorLocation::Url(location.to_string()), )), } } @@ -289,9 +269,8 @@ pub async fn fetch( Value::string(format!("No content type found")), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::Url(location.to_string())), }, - AnchorLocation::Url(location.to_string()), )), }, Err(_) => { diff --git a/src/commands/from_bson.rs b/src/commands/from_bson.rs index 7dd00983fc..469e15f35e 100644 --- a/src/commands/from_bson.rs +++ b/src/commands/from_bson.rs @@ -33,7 +33,7 @@ fn bson_array(input: &Vec, tag: Tag) -> Result>, ShellEr let mut out = vec![]; for value in input { - out.push(convert_bson_value_to_nu_value(value, tag)?); + out.push(convert_bson_value_to_nu_value(value, &tag)?); } Ok(out) @@ -46,100 +46,100 @@ fn convert_bson_value_to_nu_value( let tag = tag.into(); Ok(match v { - Bson::FloatingPoint(n) => Value::Primitive(Primitive::from(*n)).tagged(tag), - Bson::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag), - Bson::Array(a) => Value::Table(bson_array(a, tag)?).tagged(tag), + Bson::FloatingPoint(n) => Value::Primitive(Primitive::from(*n)).tagged(&tag), + Bson::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(&tag), + Bson::Array(a) => Value::Table(bson_array(a, tag.clone())?).tagged(&tag), Bson::Document(doc) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(tag.clone()); for (k, v) in doc.iter() { - collected.insert_tagged(k.clone(), convert_bson_value_to_nu_value(v, tag)?); + collected.insert_tagged(k.clone(), convert_bson_value_to_nu_value(v, &tag)?); } collected.into_tagged_value() } - Bson::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(tag), - Bson::Null => Value::Primitive(Primitive::Nothing).tagged(tag), + Bson::Boolean(b) => Value::Primitive(Primitive::Boolean(*b)).tagged(&tag), + Bson::Null => Value::Primitive(Primitive::Nothing).tagged(&tag), Bson::RegExp(r, opts) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(tag.clone()); collected.insert_tagged( "$regex".to_string(), - Value::Primitive(Primitive::String(String::from(r))).tagged(tag), + Value::Primitive(Primitive::String(String::from(r))).tagged(&tag), ); collected.insert_tagged( "$options".to_string(), - Value::Primitive(Primitive::String(String::from(opts))).tagged(tag), + Value::Primitive(Primitive::String(String::from(opts))).tagged(&tag), ); collected.into_tagged_value() } - Bson::I32(n) => Value::number(n).tagged(tag), - Bson::I64(n) => Value::number(n).tagged(tag), + Bson::I32(n) => Value::number(n).tagged(&tag), + Bson::I64(n) => Value::number(n).tagged(&tag), Bson::Decimal128(n) => { // TODO: this really isn't great, and we should update this to do a higher // fidelity translation let decimal = BigDecimal::from_str(&format!("{}", n)).map_err(|_| { ShellError::range_error( ExpectedRange::BigDecimal, - &n.tagged(tag), + &n.tagged(&tag), format!("converting BSON Decimal128 to BigDecimal"), ) })?; - Value::Primitive(Primitive::Decimal(decimal)).tagged(tag) + Value::Primitive(Primitive::Decimal(decimal)).tagged(&tag) } Bson::JavaScriptCode(js) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(tag.clone()); collected.insert_tagged( "$javascript".to_string(), - Value::Primitive(Primitive::String(String::from(js))).tagged(tag), + Value::Primitive(Primitive::String(String::from(js))).tagged(&tag), ); collected.into_tagged_value() } Bson::JavaScriptCodeWithScope(js, doc) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(tag.clone()); collected.insert_tagged( "$javascript".to_string(), - Value::Primitive(Primitive::String(String::from(js))).tagged(tag), + Value::Primitive(Primitive::String(String::from(js))).tagged(&tag), ); collected.insert_tagged( "$scope".to_string(), - convert_bson_value_to_nu_value(&Bson::Document(doc.to_owned()), tag)?, + convert_bson_value_to_nu_value(&Bson::Document(doc.to_owned()), tag.clone())?, ); collected.into_tagged_value() } Bson::TimeStamp(ts) => { - let mut collected = TaggedDictBuilder::new(tag); - collected.insert_tagged("$timestamp".to_string(), Value::number(ts).tagged(tag)); + let mut collected = TaggedDictBuilder::new(tag.clone()); + collected.insert_tagged("$timestamp".to_string(), Value::number(ts).tagged(&tag)); collected.into_tagged_value() } Bson::Binary(bst, bytes) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(tag.clone()); collected.insert_tagged( "$binary_subtype".to_string(), match bst { BinarySubtype::UserDefined(u) => Value::number(u), _ => Value::Primitive(Primitive::String(binary_subtype_to_string(*bst))), } - .tagged(tag), + .tagged(&tag), ); collected.insert_tagged( "$binary".to_string(), - Value::Primitive(Primitive::Binary(bytes.to_owned())).tagged(tag), + Value::Primitive(Primitive::Binary(bytes.to_owned())).tagged(&tag), ); collected.into_tagged_value() } Bson::ObjectId(obj_id) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(tag.clone()); collected.insert_tagged( "$object_id".to_string(), - Value::Primitive(Primitive::String(obj_id.to_hex())).tagged(tag), + Value::Primitive(Primitive::String(obj_id.to_hex())).tagged(&tag), ); collected.into_tagged_value() } - Bson::UtcDatetime(dt) => Value::Primitive(Primitive::Date(*dt)).tagged(tag), + Bson::UtcDatetime(dt) => Value::Primitive(Primitive::Date(*dt)).tagged(&tag), Bson::Symbol(s) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(tag.clone()); collected.insert_tagged( "$symbol".to_string(), - Value::Primitive(Primitive::String(String::from(s))).tagged(tag), + Value::Primitive(Primitive::String(String::from(s))).tagged(&tag), ); collected.into_tagged_value() } @@ -208,13 +208,13 @@ fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result - match from_bson_bytes_to_value(vb, tag) { + match from_bson_bytes_to_value(vb, tag.clone()) { Ok(x) => yield ReturnSuccess::value(x), Err(_) => { yield Err(ShellError::labeled_error_with_secondary( "Could not parse as BSON", "input cannot be parsed as BSON", - tag, + tag.clone(), "value originates from here", value_tag, )) @@ -223,7 +223,7 @@ fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + tag.clone(), "value originates from here", value_tag, )), diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index ea90ab3de1..877c8dc166 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -62,12 +62,12 @@ pub fn from_csv_string_to_value( if let Some(row_values) = iter.next() { let row_values = row_values?; - let mut row = TaggedDictBuilder::new(tag); + let mut row = TaggedDictBuilder::new(tag.clone()); for (idx, entry) in row_values.iter().enumerate() { row.insert_tagged( fields.get(idx).unwrap(), - Value::Primitive(Primitive::String(String::from(entry))).tagged(tag), + Value::Primitive(Primitive::String(String::from(entry))).tagged(&tag), ); } @@ -77,7 +77,7 @@ pub fn from_csv_string_to_value( } } - Ok(Tagged::from_item(Value::Table(rows), tag)) + Ok(Value::Table(rows).tagged(&tag)) } fn from_csv( @@ -96,7 +96,7 @@ fn from_csv( for value in values { let value_tag = value.tag(); - latest_tag = Some(value_tag); + latest_tag = Some(value_tag.clone()); match value.item { Value::Primitive(Primitive::String(s)) => { concat_string.push_str(&s); @@ -105,15 +105,15 @@ fn from_csv( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_tag, + name_tag.clone(), "value originates from here", - value_tag, + value_tag.clone(), )), } } - match from_csv_string_to_value(concat_string, skip_headers, name_tag) { + match from_csv_string_to_value(concat_string, skip_headers, name_tag.clone()) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -126,9 +126,9 @@ fn from_csv( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as CSV", "input cannot be parsed as CSV", - name_tag, + name_tag.clone(), "value originates from here", - last_tag, + last_tag.clone(), )) } , } diff --git a/src/commands/from_ini.rs b/src/commands/from_ini.rs index d53ad67773..e55bbd45c4 100644 --- a/src/commands/from_ini.rs +++ b/src/commands/from_ini.rs @@ -45,10 +45,13 @@ fn convert_ini_top_to_nu_value( tag: impl Into, ) -> Tagged { let tag = tag.into(); - let mut top_level = TaggedDictBuilder::new(tag); + let mut top_level = TaggedDictBuilder::new(tag.clone()); for (key, value) in v.iter() { - top_level.insert_tagged(key.clone(), convert_ini_second_to_nu_value(value, tag)); + top_level.insert_tagged( + key.clone(), + convert_ini_second_to_nu_value(value, tag.clone()), + ); } top_level.into_tagged_value() @@ -75,7 +78,7 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result { concat_string.push_str(&s); @@ -84,15 +87,15 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + &tag, "value originates from here", - value_tag, + &value_tag, )), } } - match from_ini_string_to_value(concat_string, tag) { + match from_ini_string_to_value(concat_string, tag.clone()) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -105,7 +108,7 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result) - let tag = tag.into(); match v { - serde_hjson::Value::Null => Value::Primitive(Primitive::Nothing).tagged(tag), - serde_hjson::Value::Bool(b) => Value::boolean(*b).tagged(tag), - serde_hjson::Value::F64(n) => Value::number(n).tagged(tag), - serde_hjson::Value::U64(n) => Value::number(n).tagged(tag), - serde_hjson::Value::I64(n) => Value::number(n).tagged(tag), + serde_hjson::Value::Null => Value::Primitive(Primitive::Nothing).tagged(&tag), + serde_hjson::Value::Bool(b) => Value::boolean(*b).tagged(&tag), + serde_hjson::Value::F64(n) => Value::number(n).tagged(&tag), + serde_hjson::Value::U64(n) => Value::number(n).tagged(&tag), + serde_hjson::Value::I64(n) => Value::number(n).tagged(&tag), serde_hjson::Value::String(s) => { - Value::Primitive(Primitive::String(String::from(s))).tagged(tag) + Value::Primitive(Primitive::String(String::from(s))).tagged(&tag) } serde_hjson::Value::Array(a) => Value::Table( a.iter() - .map(|x| convert_json_value_to_nu_value(x, tag)) + .map(|x| convert_json_value_to_nu_value(x, &tag)) .collect(), ) .tagged(tag), serde_hjson::Value::Object(o) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(&tag); for (k, v) in o.iter() { - collected.insert_tagged(k.clone(), convert_json_value_to_nu_value(v, tag)); + collected.insert_tagged(k.clone(), convert_json_value_to_nu_value(v, &tag)); } collected.into_tagged_value() @@ -82,7 +82,7 @@ fn from_json( for value in values { let value_tag = value.tag(); - latest_tag = Some(value_tag); + latest_tag = Some(value_tag.clone()); match value.item { Value::Primitive(Primitive::String(s)) => { concat_string.push_str(&s); @@ -91,9 +91,9 @@ fn from_json( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_tag, + &name_tag, "value originates from here", - value_tag, + &value_tag, )), } @@ -106,15 +106,15 @@ fn from_json( continue; } - match from_json_string_to_value(json_str.to_string(), name_tag) { + match from_json_string_to_value(json_str.to_string(), &name_tag) { Ok(x) => yield ReturnSuccess::value(x), Err(_) => { - if let Some(last_tag) = latest_tag { + if let Some(ref last_tag) = latest_tag { yield Err(ShellError::labeled_error_with_secondary( "Could nnot parse as JSON", "input cannot be parsed as JSON", - name_tag, + &name_tag, "value originates from here", last_tag)) } @@ -122,7 +122,7 @@ fn from_json( } } } else { - match from_json_string_to_value(concat_string, name_tag) { + match from_json_string_to_value(concat_string, name_tag.clone()) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { diff --git a/src/commands/from_sqlite.rs b/src/commands/from_sqlite.rs index 20d087bd5c..7b93dc1633 100644 --- a/src/commands/from_sqlite.rs +++ b/src/commands/from_sqlite.rs @@ -138,7 +138,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result - match from_sqlite_bytes_to_value(vb, tag) { + match from_sqlite_bytes_to_value(vb, tag.clone()) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -151,7 +151,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + &tag, "value originates from here", value_tag, )), diff --git a/src/commands/from_toml.rs b/src/commands/from_toml.rs index c0098d9267..2cfd059165 100644 --- a/src/commands/from_toml.rs +++ b/src/commands/from_toml.rs @@ -36,7 +36,7 @@ pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into) -> T toml::Value::String(s) => Value::Primitive(Primitive::String(String::from(s))).tagged(tag), toml::Value::Array(a) => Value::Table( a.iter() - .map(|x| convert_toml_value_to_nu_value(x, tag)) + .map(|x| convert_toml_value_to_nu_value(x, &tag)) .collect(), ) .tagged(tag), @@ -44,10 +44,10 @@ pub fn convert_toml_value_to_nu_value(v: &toml::Value, tag: impl Into) -> T Value::Primitive(Primitive::String(dt.to_string())).tagged(tag) } toml::Value::Table(t) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(&tag); for (k, v) in t.iter() { - collected.insert_tagged(k.clone(), convert_toml_value_to_nu_value(v, tag)); + collected.insert_tagged(k.clone(), convert_toml_value_to_nu_value(v, &tag)); } collected.into_tagged_value() @@ -79,7 +79,7 @@ pub fn from_toml( for value in values { let value_tag = value.tag(); - latest_tag = Some(value_tag); + latest_tag = Some(value_tag.clone()); match value.item { Value::Primitive(Primitive::String(s)) => { concat_string.push_str(&s); @@ -88,15 +88,15 @@ pub fn from_toml( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + &tag, "value originates from here", - value_tag, + &value_tag, )), } } - match from_toml_string_to_value(concat_string, tag) { + match from_toml_string_to_value(concat_string, tag.clone()) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -109,7 +109,7 @@ pub fn from_toml( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as TOML", "input cannot be parsed as TOML", - tag, + &tag, "value originates from here", last_tag, )) diff --git a/src/commands/from_tsv.rs b/src/commands/from_tsv.rs index bba532d17b..80951b71aa 100644 --- a/src/commands/from_tsv.rs +++ b/src/commands/from_tsv.rs @@ -63,12 +63,12 @@ pub fn from_tsv_string_to_value( if let Some(row_values) = iter.next() { let row_values = row_values?; - let mut row = TaggedDictBuilder::new(tag); + let mut row = TaggedDictBuilder::new(&tag); for (idx, entry) in row_values.iter().enumerate() { row.insert_tagged( fields.get(idx).unwrap(), - Value::Primitive(Primitive::String(String::from(entry))).tagged(tag), + Value::Primitive(Primitive::String(String::from(entry))).tagged(&tag), ); } @@ -78,7 +78,7 @@ pub fn from_tsv_string_to_value( } } - Ok(Tagged::from_item(Value::Table(rows), tag)) + Ok(Value::Table(rows).tagged(&tag)) } fn from_tsv( @@ -97,7 +97,7 @@ fn from_tsv( for value in values { let value_tag = value.tag(); - latest_tag = Some(value_tag); + latest_tag = Some(value_tag.clone()); match value.item { Value::Primitive(Primitive::String(s)) => { concat_string.push_str(&s); @@ -106,15 +106,15 @@ fn from_tsv( _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name_tag, + &name_tag, "value originates from here", - value_tag, + &value_tag, )), } } - match from_tsv_string_to_value(concat_string, skip_headers, name_tag) { + match from_tsv_string_to_value(concat_string, skip_headers, name_tag.clone()) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -127,9 +127,9 @@ fn from_tsv( yield Err(ShellError::labeled_error_with_secondary( "Could not parse as TSV", "input cannot be parsed as TSV", - name_tag, + &name_tag, "value originates from here", - last_tag, + &last_tag, )) } , } diff --git a/src/commands/from_url.rs b/src/commands/from_url.rs index 662508deb6..ad23ea5b53 100644 --- a/src/commands/from_url.rs +++ b/src/commands/from_url.rs @@ -39,7 +39,7 @@ fn from_url(args: CommandArgs, registry: &CommandRegistry) -> Result { concat_string.push_str(&s); @@ -47,9 +47,9 @@ fn from_url(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + &tag, "value originates from here", - value_tag, + &value_tag, )), } diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index 5bba67b42a..0425eb408b 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -34,7 +34,7 @@ fn from_node_to_value<'a, 'd>(n: &roxmltree::Node<'a, 'd>, tag: impl Into) let mut children_values = vec![]; for c in n.children() { - children_values.push(from_node_to_value(&c, tag)); + children_values.push(from_node_to_value(&c, &tag)); } let children_values: Vec> = children_values @@ -94,7 +94,7 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result { concat_string.push_str(&s); @@ -103,15 +103,15 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + &tag, "value originates from here", - value_tag, + &value_tag, )), } } - match from_xml_string_to_value(concat_string, tag) { + match from_xml_string_to_value(concat_string, tag.clone()) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -124,9 +124,9 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result) -> serde_yaml::Value::String(s) => Value::string(s).tagged(tag), serde_yaml::Value::Sequence(a) => Value::Table( a.iter() - .map(|x| convert_yaml_value_to_nu_value(x, tag)) + .map(|x| convert_yaml_value_to_nu_value(x, &tag)) .collect(), ) .tagged(tag), serde_yaml::Value::Mapping(t) => { - let mut collected = TaggedDictBuilder::new(tag); + let mut collected = TaggedDictBuilder::new(&tag); for (k, v) in t.iter() { match k { serde_yaml::Value::String(k) => { - collected.insert_tagged(k.clone(), convert_yaml_value_to_nu_value(v, tag)); + collected.insert_tagged(k.clone(), convert_yaml_value_to_nu_value(v, &tag)); } _ => unimplemented!("Unknown key type"), } @@ -108,7 +108,7 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result { concat_string.push_str(&s); @@ -117,15 +117,15 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + &tag, "value originates from here", - value_tag, + &value_tag, )), } } - match from_yaml_string_to_value(concat_string, tag) { + match from_yaml_string_to_value(concat_string, tag.clone()) { Ok(x) => match x { Tagged { item: Value::Table(list), .. } => { for l in list { @@ -138,9 +138,9 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result Ok(obj.clone()), - _ => Ok(Value::nothing().tagged(obj.tag)), + _ => Ok(Value::nothing().tagged(&obj.tag)), }, } } diff --git a/src/commands/help.rs b/src/commands/help.rs index d780f13459..04e03fb10d 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -26,7 +26,7 @@ impl PerItemCommand for Help { _raw_args: &RawCommandArgs, _input: Tagged, ) -> Result { - let tag = call_info.name_tag; + let tag = &call_info.name_tag; match call_info.args.nth(0) { Some(Tagged { diff --git a/src/commands/lines.rs b/src/commands/lines.rs index d2a9cdffd1..8375098b70 100644 --- a/src/commands/lines.rs +++ b/src/commands/lines.rs @@ -58,7 +58,7 @@ fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result Result { - context.shell_manager.ls(path, context.name) + context.shell_manager.ls(path, &context) } diff --git a/src/commands/open.rs b/src/commands/open.rs index 6ea752e9da..2972144bcd 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -7,7 +7,6 @@ use crate::parser::hir::SyntaxShape; use crate::parser::registry::Signature; use crate::prelude::*; use std::path::{Path, PathBuf}; -use uuid::Uuid; pub struct Open; impl PerItemCommand for Open { @@ -49,7 +48,7 @@ fn run( ShellError::labeled_error( "No file or directory specified", "for command", - call_info.name_tag, + &call_info.name_tag, ) })? { file => file, @@ -69,7 +68,7 @@ fn run( yield Err(e); return; } - let (file_extension, contents, contents_tag, anchor_location) = result.unwrap(); + let (file_extension, contents, contents_tag) = result.unwrap(); let file_extension = if has_raw { None @@ -79,21 +78,14 @@ fn run( file_extension.or(path_str.split('.').last().map(String::from)) }; - if contents_tag.anchor != uuid::Uuid::nil() { - // If we have loaded something, track its source - yield ReturnSuccess::action(CommandAction::AddAnchorLocation( - contents_tag.anchor, - anchor_location, - )); - } - - let tagged_contents = contents.tagged(contents_tag); + let tagged_contents = contents.tagged(&contents_tag); if let Some(extension) = file_extension { let command_name = format!("from-{}", extension); if let Some(converter) = registry.get_command(&command_name) { let new_args = RawCommandArgs { host: raw_args.host, + ctrl_c: raw_args.ctrl_c, shell_manager: raw_args.shell_manager, call_info: UnevaluatedCallInfo { args: crate::parser::hir::Call { @@ -102,7 +94,6 @@ fn run( named: None }, source: raw_args.call_info.source, - source_map: raw_args.call_info.source_map, name_tag: raw_args.call_info.name_tag, } }; @@ -116,7 +107,7 @@ fn run( } } Ok(ReturnSuccess::Value(Tagged { item, .. })) => { - yield Ok(ReturnSuccess::Value(Tagged { item, tag: contents_tag })); + yield Ok(ReturnSuccess::Value(Tagged { item, tag: contents_tag.clone() })); } x => yield x, } @@ -136,7 +127,7 @@ pub async fn fetch( cwd: &PathBuf, location: &str, span: Span, -) -> Result<(Option, Value, Tag, AnchorLocation), ShellError> { +) -> Result<(Option, Value, Tag), ShellError> { let mut cwd = cwd.clone(); cwd.push(Path::new(location)); @@ -149,9 +140,8 @@ pub async fn fetch( Value::string(s), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::File(cwd.to_string_lossy().to_string())), }, - AnchorLocation::File(cwd.to_string_lossy().to_string()), )), Err(_) => { //Non utf8 data. @@ -168,18 +158,20 @@ pub async fn fetch( Value::string(s), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::File( + cwd.to_string_lossy().to_string(), + )), }, - AnchorLocation::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, Value::binary(bytes), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::File( + cwd.to_string_lossy().to_string(), + )), }, - AnchorLocation::File(cwd.to_string_lossy().to_string()), )), } } else { @@ -188,9 +180,10 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::File( + cwd.to_string_lossy().to_string(), + )), }, - AnchorLocation::File(cwd.to_string_lossy().to_string()), )) } } @@ -206,18 +199,20 @@ pub async fn fetch( Value::string(s), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::File( + cwd.to_string_lossy().to_string(), + )), }, - AnchorLocation::File(cwd.to_string_lossy().to_string()), )), Err(_) => Ok(( None, Value::binary(bytes), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::File( + cwd.to_string_lossy().to_string(), + )), }, - AnchorLocation::File(cwd.to_string_lossy().to_string()), )), } } else { @@ -226,9 +221,10 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::File( + cwd.to_string_lossy().to_string(), + )), }, - AnchorLocation::File(cwd.to_string_lossy().to_string()), )) } } @@ -237,9 +233,10 @@ pub async fn fetch( Value::binary(bytes), Tag { span, - anchor: Uuid::new_v4(), + anchor: Some(AnchorLocation::File( + cwd.to_string_lossy().to_string(), + )), }, - AnchorLocation::File(cwd.to_string_lossy().to_string()), )), } } diff --git a/src/commands/pivot.rs b/src/commands/pivot.rs index 1a6bb901fb..e52ab90924 100644 --- a/src/commands/pivot.rs +++ b/src/commands/pivot.rs @@ -104,7 +104,7 @@ pub fn pivot(args: PivotArgs, context: RunnableContext) -> Result Result { + let name_tag = call_info.name_tag.clone(); let call_info = call_info.clone(); - let path = match call_info.args.nth(0).ok_or_else(|| { - ShellError::labeled_error("No url specified", "for command", call_info.name_tag) - })? { - file => file.clone(), - }; - let body = match call_info.args.nth(1).ok_or_else(|| { - ShellError::labeled_error("No body specified", "for command", call_info.name_tag) - })? { - file => file.clone(), - }; + let path = + match call_info.args.nth(0).ok_or_else(|| { + ShellError::labeled_error("No url specified", "for command", &name_tag) + })? { + file => file.clone(), + }; + let body = + match call_info.args.nth(1).ok_or_else(|| { + ShellError::labeled_error("No body specified", "for command", &name_tag) + })? { + file => file.clone(), + }; let path_str = path.as_string()?; let path_span = path.tag(); let has_raw = call_info.args.has("raw"); @@ -79,7 +82,7 @@ fn run( let headers = get_headers(&call_info)?; let stream = async_stream! { - let (file_extension, contents, contents_tag, anchor_location) = + let (file_extension, contents, contents_tag) = post(&path_str, &body, user, password, &headers, path_span, ®istry, &raw_args).await.unwrap(); let file_extension = if has_raw { @@ -90,21 +93,14 @@ fn run( file_extension.or(path_str.split('.').last().map(String::from)) }; - if contents_tag.anchor != uuid::Uuid::nil() { - // If we have loaded something, track its source - yield ReturnSuccess::action(CommandAction::AddAnchorLocation( - contents_tag.anchor, - anchor_location, - )); - } - - let tagged_contents = contents.tagged(contents_tag); + let tagged_contents = contents.tagged(&contents_tag); if let Some(extension) = file_extension { let command_name = format!("from-{}", extension); if let Some(converter) = registry.get_command(&command_name) { let new_args = RawCommandArgs { host: raw_args.host, + ctrl_c: raw_args.ctrl_c, shell_manager: raw_args.shell_manager, call_info: UnevaluatedCallInfo { args: crate::parser::hir::Call { @@ -113,7 +109,6 @@ fn run( named: None }, source: raw_args.call_info.source, - source_map: raw_args.call_info.source_map, name_tag: raw_args.call_info.name_tag, } }; @@ -127,7 +122,7 @@ fn run( } } Ok(ReturnSuccess::Value(Tagged { item, .. })) => { - yield Ok(ReturnSuccess::Value(Tagged { item, tag: contents_tag })); + yield Ok(ReturnSuccess::Value(Tagged { item, tag: contents_tag.clone() })); } x => yield x, } @@ -207,7 +202,7 @@ pub async fn post( tag: Tag, registry: &CommandRegistry, raw_args: &RawCommandArgs, -) -> Result<(Option, Value, Tag, AnchorLocation), ShellError> { +) -> Result<(Option, Value, Tag), ShellError> { let registry = registry.clone(); let raw_args = raw_args.clone(); if location.starts_with("http:") || location.starts_with("https:") { @@ -248,6 +243,7 @@ pub async fn post( if let Some(converter) = registry.get_command("to-json") { let new_args = RawCommandArgs { host: raw_args.host, + ctrl_c: raw_args.ctrl_c, shell_manager: raw_args.shell_manager, call_info: UnevaluatedCallInfo { args: crate::parser::hir::Call { @@ -256,7 +252,6 @@ pub async fn post( named: None, }, source: raw_args.call_info.source, - source_map: raw_args.call_info.source_map, name_tag: raw_args.call_info.name_tag, }, }; @@ -280,7 +275,7 @@ pub async fn post( return Err(ShellError::labeled_error( "Save could not successfully save", "unexpected data during save", - *tag, + tag, )); } } @@ -296,7 +291,7 @@ pub async fn post( return Err(ShellError::labeled_error( "Could not automatically convert table", "needs manual conversion", - *tag, + tag, )); } } @@ -312,11 +307,13 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + &tag, ) })?), - tag, - AnchorLocation::Url(location.to_string()), + Tag { + anchor: Some(AnchorLocation::Url(location.to_string())), + span: tag.span, + }, )), (mime::APPLICATION, mime::JSON) => Ok(( Some("json".to_string()), @@ -324,25 +321,29 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + &tag, ) })?), - tag, - AnchorLocation::Url(location.to_string()), + Tag { + anchor: Some(AnchorLocation::Url(location.to_string())), + span: tag.span, + }, )), (mime::APPLICATION, mime::OCTET_STREAM) => { let buf: Vec = r.body_bytes().await.map_err(|_| { ShellError::labeled_error( "Could not load binary file", "could not load", - tag, + &tag, ) })?; Ok(( None, Value::binary(buf), - tag, - AnchorLocation::Url(location.to_string()), + Tag { + anchor: Some(AnchorLocation::Url(location.to_string())), + span: tag.span, + }, )) } (mime::IMAGE, image_ty) => { @@ -350,14 +351,16 @@ pub async fn post( ShellError::labeled_error( "Could not load image file", "could not load", - tag, + &tag, ) })?; Ok(( Some(image_ty.to_string()), Value::binary(buf), - tag, - AnchorLocation::Url(location.to_string()), + Tag { + anchor: Some(AnchorLocation::Url(location.to_string())), + span: tag.span, + }, )) } (mime::TEXT, mime::HTML) => Ok(( @@ -366,11 +369,13 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + &tag, ) })?), - tag, - AnchorLocation::Url(location.to_string()), + Tag { + anchor: Some(AnchorLocation::Url(location.to_string())), + span: tag.span, + }, )), (mime::TEXT, mime::PLAIN) => { let path_extension = url::Url::parse(location) @@ -390,11 +395,13 @@ pub async fn post( ShellError::labeled_error( "Could not load text from remote url", "could not load", - tag, + &tag, ) })?), - tag, - AnchorLocation::Url(location.to_string()), + Tag { + anchor: Some(AnchorLocation::Url(location.to_string())), + span: tag.span, + }, )) } (ty, sub_ty) => Ok(( @@ -403,16 +410,20 @@ pub async fn post( "Not yet supported MIME type: {} {}", ty, sub_ty )), - tag, - AnchorLocation::Url(location.to_string()), + Tag { + anchor: Some(AnchorLocation::Url(location.to_string())), + span: tag.span, + }, )), } } None => Ok(( None, Value::string(format!("No content type found")), - tag, - AnchorLocation::Url(location.to_string()), + Tag { + anchor: Some(AnchorLocation::Url(location.to_string())), + span: tag.span, + }, )), }, Err(_) => { diff --git a/src/commands/save.rs b/src/commands/save.rs index 0156fc3557..ac48fe280f 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -119,33 +119,32 @@ fn save( input, name, shell_manager, - source_map, host, + ctrl_c, commands: registry, .. }: RunnableContext, raw_args: RawCommandArgs, ) -> Result { let mut full_path = PathBuf::from(shell_manager.path()); - let name_tag = name; + let name_tag = name.clone(); - let source_map = source_map.clone(); let stream = async_stream! { let input: Vec> = input.values.collect().await; if path.is_none() { // If there is no filename, check the metadata for the anchor filename if input.len() > 0 { let anchor = input[0].anchor(); - match source_map.get(&anchor) { + match anchor { Some(path) => match path { AnchorLocation::File(file) => { - full_path.push(Path::new(file)); + full_path.push(Path::new(&file)); } _ => { yield Err(ShellError::labeled_error( "Save requires a filepath (1)", "needs path", - name_tag, + name_tag.clone(), )); } }, @@ -153,7 +152,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath (2)", "needs path", - name_tag, + name_tag.clone(), )); } } @@ -161,7 +160,7 @@ fn save( yield Err(ShellError::labeled_error( "Save requires a filepath (3)", "needs path", - name_tag, + name_tag.clone(), )); } } else { @@ -179,6 +178,7 @@ fn save( if let Some(converter) = registry.get_command(&command_name) { let new_args = RawCommandArgs { host, + ctrl_c, shell_manager, call_info: UnevaluatedCallInfo { args: crate::parser::hir::Call { @@ -187,7 +187,6 @@ fn save( named: None }, source: raw_args.call_info.source, - source_map: raw_args.call_info.source_map, name_tag: raw_args.call_info.name_tag, } }; diff --git a/src/commands/shells.rs b/src/commands/shells.rs index 2aee2c8564..6058a42032 100644 --- a/src/commands/shells.rs +++ b/src/commands/shells.rs @@ -2,6 +2,7 @@ use crate::commands::WholeStreamCommand; use crate::data::TaggedDictBuilder; use crate::errors::ShellError; use crate::prelude::*; +use std::sync::atomic::Ordering; pub struct Shells; @@ -32,14 +33,14 @@ fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result Result Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - tag, + &tag, "value originates from here", v.tag(), )), diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index 00e2609f26..d174283023 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -94,7 +94,7 @@ fn split_column( _ => Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name, + &name, "value originates from here", v.tag(), )), diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index e70e5cfa84..94f7564b40 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -60,7 +60,7 @@ fn split_row( result.push_back(Err(ShellError::labeled_error_with_secondary( "Expected a string from pipeline", "requires string input", - name, + &name, "value originates from here", v.tag(), ))); diff --git a/src/commands/table.rs b/src/commands/table.rs index e9fbe35f2e..8ad2c246db 100644 --- a/src/commands/table.rs +++ b/src/commands/table.rs @@ -5,16 +5,13 @@ use crate::prelude::*; pub struct Table; -#[derive(Deserialize)] -pub struct TableArgs {} - impl WholeStreamCommand for Table { fn name(&self) -> &str { "table" } fn signature(&self) -> Signature { - Signature::build("table") + Signature::build("table").named("start_number", SyntaxShape::Number) } fn usage(&self) -> &str { @@ -26,16 +23,29 @@ impl WholeStreamCommand for Table { args: CommandArgs, registry: &CommandRegistry, ) -> Result { - args.process(registry, table)?.run() + table(args, registry) } } -pub fn table(_args: TableArgs, context: RunnableContext) -> Result { +fn table(args: CommandArgs, registry: &CommandRegistry) -> Result { + let args = args.evaluate_once(registry)?; + let stream = async_stream! { - let input: Vec> = context.input.into_vec().await; + let host = args.host.clone(); + let start_number = match args.get("start_number") { + Some(Tagged { item: Value::Primitive(Primitive::Int(i)), .. }) => { + i.to_usize().unwrap() + } + _ => { + 0 + } + }; + + let input: Vec> = args.input.into_vec().await; if input.len() > 0 { - let mut host = context.host.lock().unwrap(); - let view = TableView::from_list(&input); + let mut host = host.lock().unwrap(); + let view = TableView::from_list(&input, start_number); + if let Some(view) = view { handle_unexpected(&mut *host, |host| crate::format::print_view(&view, host)); } diff --git a/src/commands/tags.rs b/src/commands/tags.rs index 2b710d1b61..221e8cc303 100644 --- a/src/commands/tags.rs +++ b/src/commands/tags.rs @@ -28,7 +28,6 @@ impl WholeStreamCommand for Tags { } fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result { - let source_map = args.call_info.source_map.clone(); Ok(args .input .values @@ -42,7 +41,7 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result { tags.insert("anchor", Value::string(source)); } diff --git a/src/commands/to_bson.rs b/src/commands/to_bson.rs index a36d99c077..eabf8381ec 100644 --- a/src/commands/to_bson.rs +++ b/src/commands/to_bson.rs @@ -46,7 +46,7 @@ pub fn value_to_bson_value(v: &Tagged) -> Result { Value::Primitive(Primitive::BeginningOfStream) => Bson::Null, Value::Primitive(Primitive::Decimal(d)) => Bson::FloatingPoint(d.to_f64().unwrap()), Value::Primitive(Primitive::Int(i)) => { - Bson::I64(i.tagged(v.tag).coerce_into("converting to BSON")?) + Bson::I64(i.tagged(&v.tag).coerce_into("converting to BSON")?) } Value::Primitive(Primitive::Nothing) => Bson::Null, Value::Primitive(Primitive::String(s)) => Bson::String(s.clone()), @@ -58,6 +58,7 @@ pub fn value_to_bson_value(v: &Tagged) -> Result { .collect::>()?, ), Value::Block(_) => Bson::Null, + Value::Error(e) => return Err(e.clone()), Value::Primitive(Primitive::Binary(b)) => Bson::Binary(BinarySubtype::Generic, b.clone()), Value::Row(o) => object_value_to_bson(o)?, }) @@ -170,7 +171,7 @@ fn get_binary_subtype<'a>(tagged_value: &'a Tagged) -> Result unreachable!(), }), Value::Primitive(Primitive::Int(i)) => Ok(BinarySubtype::UserDefined( - i.tagged(tagged_value.tag) + i.tagged(&tagged_value.tag) .coerce_into("converting to BSON binary subtype")?, )), _ => Err(ShellError::type_error( @@ -207,12 +208,12 @@ fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result, ShellError> { Bson::Array(a) => { for v in a.into_iter() { match v { - Bson::Document(d) => shell_encode_document(&mut out, d, tag)?, + Bson::Document(d) => shell_encode_document(&mut out, d, tag.clone())?, _ => { return Err(ShellError::labeled_error( format!("All top level values must be Documents, got {:?}", v), "requires BSON-compatible document", - tag, + &tag, )) } } @@ -237,7 +238,7 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result> = args.input.values.collect().await; let to_process_input = if input.len() > 1 { - let tag = input[0].tag; + let tag = input[0].tag.clone(); vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input @@ -248,14 +249,14 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result { - match bson_value_to_bytes(bson_value, name_tag) { + match bson_value_to_bytes(bson_value, name_tag.clone()) { Ok(x) => yield ReturnSuccess::value( - Value::binary(x).tagged(name_tag), + Value::binary(x).tagged(&name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a table with BSON-compatible structure.tag() from pipeline", "requires BSON-compatible input", - name_tag, + &name_tag, "originates from here".to_string(), value.tag(), )), @@ -264,7 +265,7 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error( "Expected a table with BSON-compatible structure from pipeline", "requires BSON-compatible input", - name_tag)) + &name_tag)) } } }; diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 66121df53e..90f4837453 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -47,7 +47,7 @@ pub fn value_to_csv_value(v: &Tagged) -> Tagged { Value::Block(_) => Value::Primitive(Primitive::Nothing), _ => Value::Primitive(Primitive::Nothing), } - .tagged(v.tag) + .tagged(v.tag.clone()) } fn to_string_helper(v: &Tagged) -> Result { @@ -61,7 +61,13 @@ fn to_string_helper(v: &Tagged) -> Result { Value::Table(_) => return Ok(String::from("[Table]")), Value::Row(_) => return Ok(String::from("[Row]")), Value::Primitive(Primitive::String(s)) => return Ok(s.to_string()), - _ => return Err(ShellError::labeled_error("Unexpected value", "", v.tag)), + _ => { + return Err(ShellError::labeled_error( + "Unexpected value", + "", + v.tag.clone(), + )) + } } } @@ -99,14 +105,14 @@ pub fn to_string(tagged_value: &Tagged) -> Result { ShellError::labeled_error( "Could not convert record", "original value", - tagged_value.tag, + &tagged_value.tag, ) })?) .map_err(|_| { ShellError::labeled_error( "Could not convert record", "original value", - tagged_value.tag, + &tagged_value.tag, ) })?); } @@ -136,14 +142,14 @@ pub fn to_string(tagged_value: &Tagged) -> Result { ShellError::labeled_error( "Could not convert record", "original value", - tagged_value.tag, + &tagged_value.tag, ) })?) .map_err(|_| { ShellError::labeled_error( "Could not convert record", "original value", - tagged_value.tag, + &tagged_value.tag, ) })?); } @@ -160,7 +166,7 @@ fn to_csv( let input: Vec> = input.values.collect().await; let to_process_input = if input.len() > 1 { - let tag = input[0].tag; + let tag = input[0].tag.clone(); vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input @@ -176,13 +182,13 @@ fn to_csv( } else { x }; - yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag)) + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(&name_tag)) } _ => { yield Err(ShellError::labeled_error_with_secondary( "Expected a table with CSV-compatible structure.tag() from pipeline", "requires CSV-compatible input", - name_tag, + &name_tag, "originates from here".to_string(), value.tag(), )) diff --git a/src/commands/to_json.rs b/src/commands/to_json.rs index 9c06299aad..40edc5aeb8 100644 --- a/src/commands/to_json.rs +++ b/src/commands/to_json.rs @@ -42,7 +42,7 @@ pub fn value_to_json_value(v: &Tagged) -> Result serde_json::Value::Number(serde_json::Number::from( - CoerceInto::::coerce_into(i.tagged(v.tag), "converting to JSON number")?, + CoerceInto::::coerce_into(i.tagged(&v.tag), "converting to JSON number")?, )), Value::Primitive(Primitive::Nothing) => serde_json::Value::Null, Value::Primitive(Primitive::Pattern(s)) => serde_json::Value::String(s.clone()), @@ -50,6 +50,7 @@ pub fn value_to_json_value(v: &Tagged) -> Result serde_json::Value::String(s.display().to_string()), Value::Table(l) => serde_json::Value::Array(json_list(l)?), + Value::Error(e) => return Err(e.clone()), Value::Block(_) => serde_json::Value::Null, Value::Primitive(Primitive::Binary(b)) => serde_json::Value::Array( b.iter() @@ -85,7 +86,7 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result> = args.input.values.collect().await; let to_process_input = if input.len() > 1 { - let tag = input[0].tag; + let tag = input[0].tag.clone(); vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input @@ -98,12 +99,12 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result { match serde_json::to_string(&json_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).tagged(name_tag), + Value::Primitive(Primitive::String(x)).tagged(&name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a table with JSON-compatible structure.tag() from pipeline", "requires JSON-compatible input", - name_tag, + &name_tag, "originates from here".to_string(), value.tag(), )), @@ -112,7 +113,7 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error( "Expected a table with JSON-compatible structure from pipeline", "requires JSON-compatible input", - name_tag)) + &name_tag)) } } }; diff --git a/src/commands/to_toml.rs b/src/commands/to_toml.rs index 6c8904e0c2..778fdd2561 100644 --- a/src/commands/to_toml.rs +++ b/src/commands/to_toml.rs @@ -38,10 +38,10 @@ pub fn value_to_toml_value(v: &Tagged) -> Result toml::Value::String("".to_string()) } Value::Primitive(Primitive::Decimal(f)) => { - toml::Value::Float(f.tagged(v.tag).coerce_into("converting to TOML float")?) + toml::Value::Float(f.tagged(&v.tag).coerce_into("converting to TOML float")?) } Value::Primitive(Primitive::Int(i)) => { - toml::Value::Integer(i.tagged(v.tag).coerce_into("converting to TOML integer")?) + toml::Value::Integer(i.tagged(&v.tag).coerce_into("converting to TOML integer")?) } Value::Primitive(Primitive::Nothing) => toml::Value::String("".to_string()), Value::Primitive(Primitive::Pattern(s)) => toml::Value::String(s.clone()), @@ -49,6 +49,7 @@ pub fn value_to_toml_value(v: &Tagged) -> Result Value::Primitive(Primitive::Path(s)) => toml::Value::String(s.display().to_string()), Value::Table(l) => toml::Value::Array(collect_values(l)?), + Value::Error(e) => return Err(e.clone()), Value::Block(_) => toml::Value::String("".to_string()), Value::Primitive(Primitive::Binary(b)) => { toml::Value::Array(b.iter().map(|x| toml::Value::Integer(*x as i64)).collect()) @@ -80,7 +81,7 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result> = args.input.values.collect().await; let to_process_input = if input.len() > 1 { - let tag = input[0].tag; + let tag = input[0].tag.clone(); vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input @@ -93,12 +94,12 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result { match toml::to_string(&toml_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).tagged(name_tag), + Value::Primitive(Primitive::String(x)).tagged(&name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a table with TOML-compatible structure.tag() from pipeline", "requires TOML-compatible input", - name_tag, + &name_tag, "originates from here".to_string(), value.tag(), )), @@ -107,7 +108,7 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error( "Expected a table with TOML-compatible structure from pipeline", "requires TOML-compatible input", - name_tag)) + &name_tag)) } } }; diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index 7127a3195b..83cb4a07f1 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -49,7 +49,7 @@ pub fn value_to_tsv_value(tagged_value: &Tagged) -> Tagged { Value::Block(_) => Value::Primitive(Primitive::Nothing), _ => Value::Primitive(Primitive::Nothing), } - .tagged(tagged_value.tag) + .tagged(&tagged_value.tag) } fn to_string_helper(tagged_value: &Tagged) -> Result { @@ -68,7 +68,7 @@ fn to_string_helper(tagged_value: &Tagged) -> Result return Err(ShellError::labeled_error( "Unexpected value", "original value", - tagged_value.tag, + &tagged_value.tag, )) } } @@ -107,14 +107,14 @@ pub fn to_string(tagged_value: &Tagged) -> Result { ShellError::labeled_error( "Could not convert record", "original value", - tagged_value.tag, + &tagged_value.tag, ) })?) .map_err(|_| { ShellError::labeled_error( "Could not convert record", "original value", - tagged_value.tag, + &tagged_value.tag, ) })?); } @@ -144,14 +144,14 @@ pub fn to_string(tagged_value: &Tagged) -> Result { ShellError::labeled_error( "Could not convert record", "original value", - tagged_value.tag, + &tagged_value.tag, ) })?) .map_err(|_| { ShellError::labeled_error( "Could not convert record", "original value", - tagged_value.tag, + &tagged_value.tag, ) })?); } @@ -168,7 +168,7 @@ fn to_tsv( let input: Vec> = input.values.collect().await; let to_process_input = if input.len() > 1 { - let tag = input[0].tag; + let tag = input[0].tag.clone(); vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input @@ -184,13 +184,13 @@ fn to_tsv( } else { x }; - yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag)) + yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(&name_tag)) } _ => { yield Err(ShellError::labeled_error_with_secondary( "Expected a table with TSV-compatible structure.tag() from pipeline", "requires TSV-compatible input", - name_tag, + &name_tag, "originates from here".to_string(), value.tag(), )) diff --git a/src/commands/to_url.rs b/src/commands/to_url.rs index dfba5faf4d..8dee0a87d5 100644 --- a/src/commands/to_url.rs +++ b/src/commands/to_url.rs @@ -47,7 +47,7 @@ fn to_url(args: CommandArgs, registry: &CommandRegistry) -> Result Result { - yield ReturnSuccess::value(Value::string(s).tagged(tag)); + yield ReturnSuccess::value(Value::string(s).tagged(&tag)); } _ => { yield Err(ShellError::labeled_error( "Failed to convert to url-encoded", "cannot url-encode", - tag, + &tag, )) } } @@ -72,7 +72,7 @@ fn to_url(args: CommandArgs, registry: &CommandRegistry) -> Result) -> Result serde_yaml::Value::Number(serde_yaml::Number::from( - CoerceInto::::coerce_into(i.tagged(v.tag), "converting to YAML number")?, + CoerceInto::::coerce_into(i.tagged(&v.tag), "converting to YAML number")?, )), Value::Primitive(Primitive::Nothing) => serde_yaml::Value::Null, Value::Primitive(Primitive::Pattern(s)) => serde_yaml::Value::String(s.clone()), @@ -55,6 +55,7 @@ pub fn value_to_yaml_value(v: &Tagged) -> Result return Err(e.clone()), Value::Block(_) => serde_yaml::Value::Null, Value::Primitive(Primitive::Binary(b)) => serde_yaml::Value::Sequence( b.iter() @@ -81,7 +82,7 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result> = args.input.values.collect().await; let to_process_input = if input.len() > 1 { - let tag = input[0].tag; + let tag = input[0].tag.clone(); vec![Tagged { item: Value::Table(input), tag } ] } else if input.len() == 1 { input @@ -94,12 +95,12 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result { match serde_yaml::to_string(&yaml_value) { Ok(x) => yield ReturnSuccess::value( - Value::Primitive(Primitive::String(x)).tagged(name_tag), + Value::Primitive(Primitive::String(x)).tagged(&name_tag), ), _ => yield Err(ShellError::labeled_error_with_secondary( "Expected a table with YAML-compatible structure.tag() from pipeline", "requires YAML-compatible input", - name_tag, + &name_tag, "originates from here".to_string(), value.tag(), )), @@ -108,7 +109,7 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result yield Err(ShellError::labeled_error( "Expected a table with YAML-compatible structure from pipeline", "requires YAML-compatible input", - name_tag)) + &name_tag)) } } }; diff --git a/src/commands/version.rs b/src/commands/version.rs index 01a134929e..11b243f08b 100644 --- a/src/commands/version.rs +++ b/src/commands/version.rs @@ -31,14 +31,14 @@ impl WholeStreamCommand for Version { pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result { let args = args.evaluate_once(registry)?; - let tag = args.call_info.name_tag; + let tag = args.call_info.name_tag.clone(); let mut indexmap = IndexMap::new(); indexmap.insert( "version".to_string(), - Value::string(clap::crate_version!()).tagged(tag), + Value::string(clap::crate_version!()).tagged(&tag), ); - let value = Value::Row(Dictionary::from(indexmap)).tagged(tag); + let value = Value::Row(Dictionary::from(indexmap)).tagged(&tag); Ok(OutputStream::one(value)) } diff --git a/src/commands/where_.rs b/src/commands/where_.rs index 673c6dda84..9e3c4d2c07 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -49,7 +49,7 @@ impl PerItemCommand for Where { return Err(ShellError::labeled_error( "Expected a condition", "where needs a condition", - *tag, + tag, )) } }; diff --git a/src/commands/which_.rs b/src/commands/which_.rs index 905515848c..e3b6d1c96c 100644 --- a/src/commands/which_.rs +++ b/src/commands/which_.rs @@ -33,7 +33,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result 0 { @@ -52,7 +52,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result); - -impl SourceMap { - pub fn insert(&mut self, uuid: Uuid, anchor_location: AnchorLocation) { - self.0.insert(uuid, anchor_location); - } - - pub fn get(&self, uuid: &Uuid) -> Option<&AnchorLocation> { - self.0.get(uuid) - } - - pub fn new() -> SourceMap { - SourceMap(HashMap::new()) - } -} - #[derive(Clone, new)] pub struct CommandRegistry { #[new(value = "Arc::new(Mutex::new(IndexMap::default()))")] @@ -77,8 +58,8 @@ impl CommandRegistry { #[derive(Clone)] pub struct Context { registry: CommandRegistry, - pub(crate) source_map: Arc>, host: Arc>, + pub ctrl_c: Arc, pub(crate) shell_manager: ShellManager, } @@ -90,17 +71,17 @@ impl Context { pub(crate) fn expand_context<'context>( &'context self, source: &'context Text, - tag: Tag, + span: Span, ) -> ExpandContext<'context> { - ExpandContext::new(&self.registry, tag, source, self.shell_manager.homedir()) + ExpandContext::new(&self.registry, span, source, self.shell_manager.homedir()) } pub(crate) fn basic() -> Result> { let registry = CommandRegistry::new(); Ok(Context { registry: registry.clone(), - source_map: Arc::new(Mutex::new(SourceMap::new())), host: Arc::new(Mutex::new(crate::env::host::BasicHost)), + ctrl_c: Arc::new(AtomicBool::new(false)), shell_manager: ShellManager::basic(registry)?, }) } @@ -117,12 +98,6 @@ impl Context { } } - pub fn add_anchor_location(&mut self, uuid: Uuid, anchor_location: AnchorLocation) { - let mut source_map = self.source_map.lock().unwrap(); - - source_map.insert(uuid, anchor_location); - } - pub(crate) fn get_command(&self, name: &str) -> Option> { self.registry.get_command(name) } @@ -135,27 +110,19 @@ impl Context { &mut self, command: Arc, name_tag: Tag, - source_map: SourceMap, args: hir::Call, source: &Text, input: InputStream, is_first_command: bool, ) -> OutputStream { - let command_args = self.command_args(args, input, source, source_map, name_tag); + let command_args = self.command_args(args, input, source, name_tag); command.run(command_args, self.registry(), is_first_command) } - fn call_info( - &self, - args: hir::Call, - source: &Text, - source_map: SourceMap, - name_tag: Tag, - ) -> UnevaluatedCallInfo { + fn call_info(&self, args: hir::Call, source: &Text, name_tag: Tag) -> UnevaluatedCallInfo { UnevaluatedCallInfo { args, source: source.clone(), - source_map, name_tag, } } @@ -165,13 +132,13 @@ impl Context { args: hir::Call, input: InputStream, source: &Text, - source_map: SourceMap, name_tag: Tag, ) -> CommandArgs { CommandArgs { host: self.host.clone(), + ctrl_c: self.ctrl_c.clone(), shell_manager: self.shell_manager.clone(), - call_info: self.call_info(args, source, source_map, name_tag), + call_info: self.call_info(args, source, name_tag), input, } } diff --git a/src/data/base.rs b/src/data/base.rs index 735196c97f..f7b875ef53 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -213,7 +213,7 @@ impl Block { let scope = Scope::new(value.clone()); if self.expressions.len() == 0 { - return Ok(Value::nothing().tagged(self.tag)); + return Ok(Value::nothing().tagged(&self.tag)); } let mut last = None; @@ -245,6 +245,9 @@ pub enum Value { Row(crate::data::Dictionary), Table(Vec>), + // Errors are a type of value too + Error(ShellError), + Block(Block), } @@ -293,6 +296,7 @@ impl fmt::Debug for ValueDebug<'_> { Value::Row(o) => o.debug(f), Value::Table(l) => debug_list(l).fmt(f), Value::Block(_) => write!(f, "[[block]]"), + Value::Error(_) => write!(f, "[[error]]"), } } } @@ -300,7 +304,7 @@ impl fmt::Debug for ValueDebug<'_> { impl Tagged { pub fn tagged_type_name(&self) -> Tagged { let name = self.type_name(); - Tagged::from_item(name, self.tag()) + name.tagged(self.tag()) } } @@ -312,7 +316,7 @@ impl std::convert::TryFrom<&Tagged> for Block { Value::Block(block) => Ok(block.clone()), v => Err(ShellError::type_error( "Block", - value.copy_tag(v.type_name()), + v.type_name().tagged(value.tag()), )), } } @@ -324,11 +328,11 @@ impl std::convert::TryFrom<&Tagged> for i64 { fn try_from(value: &Tagged) -> Result { match value.item() { Value::Primitive(Primitive::Int(int)) => { - int.tagged(value.tag).coerce_into("converting to i64") + int.tagged(&value.tag).coerce_into("converting to i64") } v => Err(ShellError::type_error( "Integer", - value.copy_tag(v.type_name()), + v.type_name().tagged(value.tag()), )), } } @@ -342,7 +346,7 @@ impl std::convert::TryFrom<&Tagged> for String { Value::Primitive(Primitive::String(s)) => Ok(s.clone()), v => Err(ShellError::type_error( "String", - value.copy_tag(v.type_name()), + v.type_name().tagged(value.tag()), )), } } @@ -356,7 +360,7 @@ impl std::convert::TryFrom<&Tagged> for Vec { Value::Primitive(Primitive::Binary(b)) => Ok(b.clone()), v => Err(ShellError::type_error( "Binary", - value.copy_tag(v.type_name()), + v.type_name().tagged(value.tag()), )), } } @@ -370,7 +374,7 @@ impl<'a> std::convert::TryFrom<&'a Tagged> for &'a crate::data::Dictionar Value::Row(d) => Ok(d), v => Err(ShellError::type_error( "Dictionary", - value.copy_tag(v.type_name()), + v.type_name().tagged(value.tag()), )), } } @@ -392,7 +396,7 @@ impl std::convert::TryFrom>> for Switch { Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present), v => Err(ShellError::type_error( "Boolean", - value.copy_tag(v.type_name()), + v.type_name().tagged(value.tag()), )), }, } @@ -410,19 +414,19 @@ impl Tagged { match &self.item { Value::Table(table) => { for item in table { - out.push(item.as_string()?.tagged(item.tag)); + out.push(item.as_string()?.tagged(&item.tag)); } } other => { return Err(ShellError::type_error( "column name", - other.type_name().tagged(self.tag), + other.type_name().tagged(&self.tag), )) } } - Ok(out.tagged(self.tag)) + Ok(out.tagged(&self.tag)) } pub(crate) fn as_string(&self) -> Result { @@ -437,7 +441,7 @@ impl Tagged { other => Err(ShellError::labeled_error( "Expected string", other.type_name(), - self.tag, + &self.tag, )), } } @@ -450,6 +454,7 @@ impl Value { Value::Row(_) => format!("row"), Value::Table(_) => format!("list"), Value::Block(_) => format!("block"), + Value::Error(_) => format!("error"), } } @@ -465,6 +470,7 @@ impl Value { .collect(), Value::Block(_) => vec![], Value::Table(_) => vec![], + Value::Error(_) => vec![], } } @@ -503,7 +509,7 @@ impl Value { } } - Some(Tagged::from_item(current, tag)) + Some(current.tagged(tag)) } pub fn get_data_by_path(&self, tag: Tag, path: &str) -> Option> { @@ -515,7 +521,7 @@ impl Value { } } - Some(Tagged::from_item(current, tag)) + Some(current.tagged(tag)) } pub fn insert_data_at_path( @@ -535,8 +541,8 @@ impl Value { // Special case for inserting at the top level current .entries - .insert(path.to_string(), Tagged::from_item(new_value, tag)); - return Some(Tagged::from_item(new_obj, tag)); + .insert(path.to_string(), new_value.tagged(&tag)); + return Some(new_obj.tagged(&tag)); } for idx in 0..split_path.len() { @@ -547,13 +553,13 @@ impl Value { Value::Row(o) => { o.entries.insert( split_path[idx + 1].to_string(), - Tagged::from_item(new_value, tag), + new_value.tagged(&tag), ); } _ => {} } - return Some(Tagged::from_item(new_obj, tag)); + return Some(new_obj.tagged(&tag)); } else { match next.item { Value::Row(ref mut o) => { @@ -584,11 +590,10 @@ impl Value { if split_path.len() == 1 { // Special case for inserting at the top level - current.entries.insert( - split_path[0].item.clone(), - Tagged::from_item(new_value, tag), - ); - return Some(Tagged::from_item(new_obj, tag)); + current + .entries + .insert(split_path[0].item.clone(), new_value.tagged(&tag)); + return Some(new_obj.tagged(&tag)); } for idx in 0..split_path.len() { @@ -599,13 +604,13 @@ impl Value { Value::Row(o) => { o.entries.insert( split_path[idx + 1].to_string(), - Tagged::from_item(new_value, tag), + new_value.tagged(&tag), ); } _ => {} } - return Some(Tagged::from_item(new_obj, tag)); + return Some(new_obj.tagged(&tag)); } else { match next.item { Value::Row(ref mut o) => { @@ -639,8 +644,8 @@ impl Value { match current.entries.get_mut(split_path[idx]) { Some(next) => { if idx == (split_path.len() - 1) { - *next = Tagged::from_item(replaced_value, tag); - return Some(Tagged::from_item(new_obj, tag)); + *next = replaced_value.tagged(&tag); + return Some(new_obj.tagged(&tag)); } else { match next.item { Value::Row(ref mut o) => { @@ -672,8 +677,8 @@ impl Value { match current.entries.get_mut(&split_path[idx].item) { Some(next) => { if idx == (split_path.len() - 1) { - *next = Tagged::from_item(replaced_value, tag); - return Some(Tagged::from_item(new_obj, tag)); + *next = replaced_value.tagged(&tag); + return Some(new_obj.tagged(&tag)); } else { match next.item { Value::Row(ref mut o) => { @@ -697,6 +702,7 @@ impl Value { Value::Row(o) => o.get_data(desc), Value::Block(_) => MaybeOwned::Owned(Value::nothing()), Value::Table(_) => MaybeOwned::Owned(Value::nothing()), + Value::Error(_) => MaybeOwned::Owned(Value::nothing()), } } @@ -706,7 +712,7 @@ impl Value { Value::Block(b) => itertools::join( b.expressions .iter() - .map(|e| e.source(&b.source).to_string()), + .map(|e| e.span.slice(&b.source).to_string()), "; ", ), Value::Row(_) => format!("[table: 1 row]"), @@ -715,6 +721,7 @@ impl Value { l.len(), if l.len() == 1 { "row" } else { "rows" } ), + Value::Error(_) => format!("[error]"), } } diff --git a/src/data/command.rs b/src/data/command.rs index a2046aa7aa..25301e6fa1 100644 --- a/src/data/command.rs +++ b/src/data/command.rs @@ -7,7 +7,7 @@ use std::ops::Deref; pub(crate) fn command_dict(command: Arc, tag: impl Into) -> Tagged { let tag = tag.into(); - let mut cmd_dict = TaggedDictBuilder::new(tag); + let mut cmd_dict = TaggedDictBuilder::new(&tag); cmd_dict.insert("name", Value::string(command.name())); @@ -42,7 +42,7 @@ fn for_spec(name: &str, ty: &str, required: bool, tag: impl Into) -> Tagged fn signature_dict(signature: Signature, tag: impl Into) -> Tagged { let tag = tag.into(); - let mut sig = TaggedListBuilder::new(tag); + let mut sig = TaggedListBuilder::new(&tag); for arg in signature.positional.iter() { let is_required = match arg { @@ -50,19 +50,19 @@ fn signature_dict(signature: Signature, tag: impl Into) -> Tagged { PositionalType::Optional(_, _) => false, }; - sig.insert_tagged(for_spec(arg.name(), "argument", is_required, tag)); + sig.insert_tagged(for_spec(arg.name(), "argument", is_required, &tag)); } if let Some(_) = signature.rest_positional { let is_required = false; - sig.insert_tagged(for_spec("rest", "argument", is_required, tag)); + sig.insert_tagged(for_spec("rest", "argument", is_required, &tag)); } for (name, ty) in signature.named.iter() { match ty { - NamedType::Mandatory(_) => sig.insert_tagged(for_spec(name, "flag", true, tag)), - NamedType::Optional(_) => sig.insert_tagged(for_spec(name, "flag", false, tag)), - NamedType::Switch => sig.insert_tagged(for_spec(name, "switch", false, tag)), + NamedType::Mandatory(_) => sig.insert_tagged(for_spec(name, "flag", true, &tag)), + NamedType::Optional(_) => sig.insert_tagged(for_spec(name, "flag", false, &tag)), + NamedType::Switch => sig.insert_tagged(for_spec(name, "switch", false, &tag)), } } diff --git a/src/data/config.rs b/src/data/config.rs index 657287d2f2..26e3e3c7d5 100644 --- a/src/data/config.rs +++ b/src/data/config.rs @@ -75,12 +75,12 @@ pub fn read( let tag = tag.into(); let contents = fs::read_to_string(filename) - .map(|v| v.tagged(tag)) + .map(|v| v.tagged(&tag)) .map_err(|err| { ShellError::labeled_error( &format!("Couldn't read config file:\n{}", err), "file name", - tag, + &tag, ) })?; @@ -88,7 +88,7 @@ pub fn read( ShellError::labeled_error( &format!("Couldn't parse config file:\n{}", err), "file name", - tag, + &tag, ) })?; @@ -98,7 +98,7 @@ pub fn read( Value::Row(Dictionary { entries }) => Ok(entries), other => Err(ShellError::type_error( "Dictionary", - other.type_name().tagged(tag), + other.type_name().tagged(&tag), )), } } diff --git a/src/data/dict.rs b/src/data/dict.rs index c14c86dd90..8f9bb556ba 100644 --- a/src/data/dict.rs +++ b/src/data/dict.rs @@ -115,7 +115,7 @@ impl TaggedListBuilder { } pub fn push(&mut self, value: impl Into) { - self.list.push(value.into().tagged(self.tag)); + self.list.push(value.into().tagged(&self.tag)); } pub fn insert_tagged(&mut self, value: impl Into>) { @@ -155,7 +155,7 @@ impl TaggedDictBuilder { } pub fn insert(&mut self, key: impl Into, value: impl Into) { - self.dict.insert(key.into(), value.into().tagged(self.tag)); + self.dict.insert(key.into(), value.into().tagged(&self.tag)); } pub fn insert_tagged(&mut self, key: impl Into, value: impl Into>) { diff --git a/src/data/meta.rs b/src/data/meta.rs index 08125359e4..2f3f0cc4c1 100644 --- a/src/data/meta.rs +++ b/src/data/meta.rs @@ -1,15 +1,52 @@ -use crate::context::{AnchorLocation, SourceMap}; +use crate::context::AnchorLocation; use crate::parser::parse::parser::TracableContext; use crate::prelude::*; -use crate::Text; use derive_new::new; use getset::Getters; use serde::Deserialize; use serde::Serialize; use std::path::{Path, PathBuf}; -use uuid::Uuid; #[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] +pub struct Spanned { + pub span: Span, + pub item: T, +} + +impl Spanned { + pub fn map(self, input: impl FnOnce(T) -> U) -> Spanned { + let span = self.span; + + let mapped = input(self.item); + mapped.spanned(span) + } +} + +pub trait SpannedItem: Sized { + fn spanned(self, span: impl Into) -> Spanned { + Spanned { + item: self, + span: span.into(), + } + } + + fn spanned_unknown(self) -> Spanned { + Spanned { + item: self, + span: Span::unknown(), + } + } +} +impl SpannedItem for T {} + +impl std::ops::Deref for Spanned { + type Target = T; + + fn deref(&self) -> &T { + &self.item + } +} +#[derive(new, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] pub struct Tagged { pub tag: Tag, pub item: T, @@ -17,7 +54,7 @@ pub struct Tagged { impl HasTag for Tagged { fn tag(&self) -> Tag { - self.tag + self.tag.clone() } } @@ -29,20 +66,23 @@ impl AsRef for Tagged { pub trait TaggedItem: Sized { fn tagged(self, tag: impl Into) -> Tagged { - Tagged::from_item(self, tag.into()) + Tagged { + item: self, + tag: tag.into(), + } } // For now, this is a temporary facility. In many cases, there are other useful spans that we // could be using, such as the original source spans of JSON or Toml files, but we don't yet // have the infrastructure to make that work. fn tagged_unknown(self) -> Tagged { - Tagged::from_item( - self, - Tag { + Tagged { + item: self, + tag: Tag { span: Span::unknown(), - anchor: uuid::Uuid::nil(), + anchor: None, }, - ) + } } } @@ -57,48 +97,29 @@ impl std::ops::Deref for Tagged { } impl Tagged { - pub fn with_tag(self, tag: impl Into) -> Tagged { - Tagged::from_item(self.item, tag) - } - - pub fn from_item(item: T, tag: impl Into) -> Tagged { - Tagged { - item, - tag: tag.into(), - } - } - pub fn map(self, input: impl FnOnce(T) -> U) -> Tagged { let tag = self.tag(); let mapped = input(self.item); - Tagged::from_item(mapped, tag) - } - - pub(crate) fn copy_tag(&self, output: U) -> Tagged { - Tagged::from_item(output, self.tag()) - } - - pub fn source(&self, source: &Text) -> Text { - Text::from(self.tag().slice(source)) + mapped.tagged(tag) } pub fn tag(&self) -> Tag { - self.tag + self.tag.clone() } pub fn span(&self) -> Span { self.tag.span } - pub fn anchor(&self) -> uuid::Uuid { - self.tag.anchor + pub fn anchor(&self) -> Option { + self.tag.anchor.clone() } - pub fn anchor_name(&self, source_map: &SourceMap) -> Option { - match source_map.get(&self.tag.anchor) { - Some(AnchorLocation::File(file)) => Some(file.clone()), - Some(AnchorLocation::Url(url)) => Some(url.clone()), + pub fn anchor_name(&self) -> Option { + match self.tag.anchor { + Some(AnchorLocation::File(ref file)) => Some(file.clone()), + Some(AnchorLocation::Url(ref url)) => Some(url.clone()), _ => None, } } @@ -114,26 +135,32 @@ impl Tagged { impl From<&Tag> for Tag { fn from(input: &Tag) -> Tag { - *input + input.clone() } } -impl From> for Span { - fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Span { +impl From> for Span { + fn from(input: nom_locate::LocatedSpanEx<&str, TracableContext>) -> Span { + Span::new(input.offset, input.offset + input.fragment.len()) + } +} + +impl From> for Span { + fn from(input: nom_locate::LocatedSpanEx<&str, u64>) -> Span { Span::new(input.offset, input.offset + input.fragment.len()) } } impl From<( - nom_locate::LocatedSpanEx, - nom_locate::LocatedSpanEx, + nom_locate::LocatedSpanEx, + nom_locate::LocatedSpanEx, )> for Span { fn from( input: ( - nom_locate::LocatedSpanEx, - nom_locate::LocatedSpanEx, + nom_locate::LocatedSpanEx, + nom_locate::LocatedSpanEx, ), ) -> Span { Span { @@ -159,42 +186,48 @@ impl From<&std::ops::Range> for Span { } #[derive( - Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters, new, + Debug, Clone, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters, new, )] pub struct Tag { - pub anchor: Uuid, + pub anchor: Option, pub span: Span, } impl From for Tag { fn from(span: Span) -> Self { - Tag { - anchor: uuid::Uuid::nil(), - span, - } + Tag { anchor: None, span } } } impl From<&Span> for Tag { fn from(span: &Span) -> Self { Tag { - anchor: uuid::Uuid::nil(), + anchor: None, span: *span, } } } impl From<(usize, usize, TracableContext)> for Tag { - fn from((start, end, context): (usize, usize, TracableContext)) -> Self { + fn from((start, end, _context): (usize, usize, TracableContext)) -> Self { Tag { - anchor: context.origin, + anchor: None, span: Span::new(start, end), } } } -impl From<(usize, usize, Uuid)> for Tag { - fn from((start, end, anchor): (usize, usize, Uuid)) -> Self { +impl From<(usize, usize, AnchorLocation)> for Tag { + fn from((start, end, anchor): (usize, usize, AnchorLocation)) -> Self { + Tag { + anchor: Some(anchor), + span: Span::new(start, end), + } + } +} + +impl From<(usize, usize, Option)> for Tag { + fn from((start, end, anchor): (usize, usize, Option)) -> Self { Tag { anchor, span: Span::new(start, end), @@ -202,19 +235,10 @@ impl From<(usize, usize, Uuid)> for Tag { } } -impl From<(usize, usize, Option)> for Tag { - fn from((start, end, anchor): (usize, usize, Option)) -> Self { - Tag { - anchor: anchor.unwrap_or(uuid::Uuid::nil()), - span: Span::new(start, end), - } - } -} - impl From> for Tag { fn from(input: nom_locate::LocatedSpanEx<&str, TracableContext>) -> Tag { Tag { - anchor: input.extra.origin, + anchor: None, span: Span::new(input.offset, input.offset + input.fragment.len()), } } @@ -234,15 +258,12 @@ impl From<&Tag> for Span { impl Tag { pub fn unknown_anchor(span: Span) -> Tag { - Tag { - anchor: uuid::Uuid::nil(), - span, - } + Tag { anchor: None, span } } - pub fn for_char(pos: usize, anchor: Uuid) -> Tag { + pub fn for_char(pos: usize, anchor: AnchorLocation) -> Tag { Tag { - anchor, + anchor: Some(anchor), span: Span { start: pos, end: pos + 1, @@ -250,16 +271,16 @@ impl Tag { } } - pub fn unknown_span(anchor: Uuid) -> Tag { + pub fn unknown_span(anchor: AnchorLocation) -> Tag { Tag { - anchor, + anchor: Some(anchor), span: Span::unknown(), } } pub fn unknown() -> Tag { Tag { - anchor: uuid::Uuid::nil(), + anchor: None, span: Span::unknown(), } } @@ -273,7 +294,7 @@ impl Tag { Tag { span: Span::new(self.span.start, other.span.end), - anchor: self.anchor, + anchor: self.anchor.clone(), } } @@ -288,10 +309,10 @@ impl Tag { Tag { span: Span::new(self.span.start, other.span.end), - anchor: self.anchor, + anchor: self.anchor.clone(), } } - None => *self, + None => self.clone(), } } @@ -360,6 +381,42 @@ impl Span { Span { start, end } } + pub fn for_char(pos: usize) -> Span { + Span { + start: pos, + end: pos + 1, + } + } + + pub fn until(&self, other: impl Into) -> Span { + let other = other.into(); + + Span::new(self.start, other.end) + } + + pub fn until_option(&self, other: Option>) -> Span { + match other { + Some(other) => { + let other = other.into(); + + Span::new(self.start, other.end) + } + None => *self, + } + } + + pub fn string<'a>(&self, source: &'a str) -> String { + self.slice(source).to_string() + } + + pub fn spanned_slice<'a>(&self, source: &'a str) -> Spanned<&'a str> { + self.slice(source).spanned(*self) + } + + pub fn spanned_string<'a>(&self, source: &'a str) -> Spanned { + self.slice(source).to_string().spanned(*self) + } + /* pub fn unknown_with_uuid(uuid: Uuid) -> Span { Span { @@ -404,27 +461,3 @@ impl language_reporting::ReportingSpan for Span { self.end } } - -impl language_reporting::ReportingSpan for Tag { - fn with_start(&self, start: usize) -> Self { - Tag { - span: Span::new(start, self.span.end), - anchor: self.anchor, - } - } - - fn with_end(&self, end: usize) -> Self { - Tag { - span: Span::new(self.span.start, end), - anchor: self.anchor, - } - } - - fn start(&self) -> usize { - self.span.start - } - - fn end(&self) -> usize { - self.span.end - } -} diff --git a/src/data/types.rs b/src/data/types.rs index 8dca43d878..b4ff545deb 100644 --- a/src/data/types.rs +++ b/src/data/types.rs @@ -54,7 +54,7 @@ impl ExtractType for i64 { &Tagged { item: Value::Primitive(Primitive::Int(int)), .. - } => Ok(int.tagged(value.tag).coerce_into("converting to i64")?), + } => Ok(int.tagged(&value.tag).coerce_into("converting to i64")?), other => Err(ShellError::type_error("Integer", other.tagged_type_name())), } } @@ -68,7 +68,7 @@ impl ExtractType for u64 { &Tagged { item: Value::Primitive(Primitive::Int(int)), .. - } => Ok(int.tagged(value.tag).coerce_into("converting to u64")?), + } => Ok(int.tagged(&value.tag).coerce_into("converting to u64")?), other => Err(ShellError::type_error("Integer", other.tagged_type_name())), } } diff --git a/src/errors.rs b/src/errors.rs index 2d42552250..11628dde4b 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -14,9 +14,9 @@ pub enum Description { } impl Description { - fn into_label(self) -> Result, String> { + fn into_label(self) -> Result, String> { match self { - Description::Source(s) => Ok(Label::new_primary(s.tag()).with_message(s.item)), + Description::Source(s) => Ok(Label::new_primary(s.span()).with_message(s.item)), Description::Synthetic(s) => Err(s), } } @@ -24,7 +24,7 @@ impl Description { #[allow(unused)] fn tag(&self) -> Tag { match self { - Description::Source(tagged) => tagged.tag, + Description::Source(tagged) => tagged.tag.clone(), Description::Synthetic(_) => Tag::unknown(), } } @@ -85,10 +85,10 @@ impl ShellError { .start() } - pub(crate) fn unexpected_eof(expected: impl Into, tag: Tag) -> ShellError { + pub(crate) fn unexpected_eof(expected: impl Into, tag: impl Into) -> ShellError { ProximateShellError::UnexpectedEof { expected: expected.into(), - tag, + tag: tag.into(), } .start() } @@ -100,7 +100,7 @@ impl ShellError { ) -> ShellError { ProximateShellError::RangeError { kind: expected.into(), - actual_kind: actual.copy_tag(format!("{:?}", actual.item)), + actual_kind: format!("{:?}", actual.item).tagged(actual.tag()), operation, } .start() @@ -143,22 +143,22 @@ impl ShellError { pub(crate) fn argument_error( command: impl Into, kind: ArgumentError, - tag: Tag, + tag: impl Into, ) -> ShellError { ProximateShellError::ArgumentError { command: command.into(), error: kind, - tag, + tag: tag.into(), } .start() } - pub(crate) fn invalid_external_word(tag: Tag) -> ShellError { + pub(crate) fn invalid_external_word(tag: impl Into) -> ShellError { ProximateShellError::ArgumentError { command: "Invalid argument to Nu command (did you mean to call an external command?)" .into(), error: ArgumentError::InvalidExternalWord, - tag, + tag: tag.into(), } .start() } @@ -183,22 +183,22 @@ impl ShellError { } nom::Err::Failure(span) | nom::Err::Error(span) => { let diagnostic = Diagnostic::new(Severity::Error, format!("Parse Error")) - .with_label(Label::new_primary(Tag::from(span.0))); + .with_label(Label::new_primary(Span::from(span.0))); ShellError::diagnostic(diagnostic) } } } - pub(crate) fn diagnostic(diagnostic: Diagnostic) -> ShellError { + pub(crate) fn diagnostic(diagnostic: Diagnostic) -> ShellError { ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start() } - pub(crate) fn to_diagnostic(self) -> Diagnostic { + pub(crate) fn to_diagnostic(self) -> Diagnostic { match self.error { ProximateShellError::InvalidCommand { command } => { Diagnostic::new(Severity::Error, "Invalid command") - .with_label(Label::new_primary(command)) + .with_label(Label::new_primary(command.span)) } ProximateShellError::MissingValue { tag, reason } => { let mut d = Diagnostic::new( @@ -207,7 +207,7 @@ impl ShellError { ); if let Some(tag) = tag { - d = d.with_label(Label::new_primary(tag)); + d = d.with_label(Label::new_primary(tag.span)); } d @@ -220,7 +220,7 @@ impl ShellError { ArgumentError::InvalidExternalWord => Diagnostic::new( Severity::Error, format!("Invalid bare word for Nu command (did you intend to invoke an external command?)")) - .with_label(Label::new_primary(tag)), + .with_label(Label::new_primary(tag.span)), ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new( Severity::Error, format!( @@ -230,7 +230,7 @@ impl ShellError { Color::Black.bold().paint(name) ), ) - .with_label(Label::new_primary(tag)), + .with_label(Label::new_primary(tag.span)), ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new( Severity::Error, format!( @@ -240,7 +240,7 @@ impl ShellError { ), ) .with_label( - Label::new_primary(tag).with_message(format!("requires {} parameter", name)), + Label::new_primary(tag.span).with_message(format!("requires {} parameter", name)), ), ArgumentError::MissingValueForName(name) => Diagnostic::new( Severity::Error, @@ -251,7 +251,7 @@ impl ShellError { Color::Black.bold().paint(name) ), ) - .with_label(Label::new_primary(tag)), + .with_label(Label::new_primary(tag.span)), }, ProximateShellError::TypeError { expected, @@ -261,7 +261,7 @@ impl ShellError { tag, }, } => Diagnostic::new(Severity::Error, "Type Error").with_label( - Label::new_primary(tag) + Label::new_primary(tag.span) .with_message(format!("Expected {}, found {}", expected, actual)), ), ProximateShellError::TypeError { @@ -272,12 +272,12 @@ impl ShellError { tag }, } => Diagnostic::new(Severity::Error, "Type Error") - .with_label(Label::new_primary(tag).with_message(expected)), + .with_label(Label::new_primary(tag.span).with_message(expected)), ProximateShellError::UnexpectedEof { expected, tag } => Diagnostic::new(Severity::Error, format!("Unexpected end of input")) - .with_label(Label::new_primary(tag).with_message(format!("Expected {}", expected))), + .with_label(Label::new_primary(tag.span).with_message(format!("Expected {}", expected))), ProximateShellError::RangeError { kind, @@ -288,7 +288,7 @@ impl ShellError { tag }, } => Diagnostic::new(Severity::Error, "Range Error").with_label( - Label::new_primary(tag).with_message(format!( + Label::new_primary(tag.span).with_message(format!( "Expected to convert {} to {} while {}, but it was out of range", item, kind.desc(), @@ -303,7 +303,7 @@ impl ShellError { item }, } => Diagnostic::new(Severity::Error, "Syntax Error") - .with_label(Label::new_primary(tag).with_message(item)), + .with_label(Label::new_primary(tag.span).with_message(item)), ProximateShellError::MissingProperty { subpath, expr, .. } => { let subpath = subpath.into_label(); @@ -326,8 +326,8 @@ impl ShellError { ProximateShellError::Diagnostic(diag) => diag.diagnostic, ProximateShellError::CoerceError { left, right } => { Diagnostic::new(Severity::Error, "Coercion error") - .with_label(Label::new_primary(left.tag()).with_message(left.item)) - .with_label(Label::new_secondary(right.tag()).with_message(right.item)) + .with_label(Label::new_primary(left.tag().span).with_message(left.item)) + .with_label(Label::new_secondary(right.tag().span).with_message(right.item)) } ProximateShellError::UntaggedRuntimeError { reason } => Diagnostic::new(Severity::Error, format!("Error: {}", reason)) @@ -341,7 +341,7 @@ impl ShellError { ) -> ShellError { ShellError::diagnostic( Diagnostic::new(Severity::Error, msg.into()) - .with_label(Label::new_primary(tag.into()).with_message(label.into())), + .with_label(Label::new_primary(tag.into().span).with_message(label.into())), ) } @@ -355,15 +355,19 @@ impl ShellError { ShellError::diagnostic( Diagnostic::new_error(msg.into()) .with_label( - Label::new_primary(primary_span.into()).with_message(primary_label.into()), + Label::new_primary(primary_span.into().span).with_message(primary_label.into()), ) .with_label( - Label::new_secondary(secondary_span.into()) + Label::new_secondary(secondary_span.into().span) .with_message(secondary_label.into()), ), ) } + // pub fn string(title: impl Into) -> ShellError { + // ProximateShellError::String(StringError::new(title.into(), String::new())).start() + // } + pub(crate) fn unimplemented(title: impl Into) -> ShellError { ShellError::untagged_runtime_error(&format!("Unimplemented: {}", title.into())) } @@ -472,16 +476,16 @@ impl ProximateShellError { pub(crate) fn tag(&self) -> Option { Some(match self { ProximateShellError::SyntaxError { problem } => problem.tag(), - ProximateShellError::UnexpectedEof { tag, .. } => *tag, - ProximateShellError::InvalidCommand { command } => *command, - ProximateShellError::TypeError { actual, .. } => actual.tag, - ProximateShellError::MissingProperty { tag, .. } => *tag, - ProximateShellError::MissingValue { tag, .. } => return *tag, - ProximateShellError::ArgumentError { tag, .. } => *tag, - ProximateShellError::RangeError { actual_kind, .. } => actual_kind.tag, + ProximateShellError::UnexpectedEof { tag, .. } => tag.clone(), + ProximateShellError::InvalidCommand { command } => command.clone(), + ProximateShellError::TypeError { actual, .. } => actual.tag.clone(), + ProximateShellError::MissingProperty { tag, .. } => tag.clone(), + ProximateShellError::MissingValue { tag, .. } => return tag.clone(), + ProximateShellError::ArgumentError { tag, .. } => tag.clone(), + ProximateShellError::RangeError { actual_kind, .. } => actual_kind.tag.clone(), ProximateShellError::Diagnostic(..) => return None, ProximateShellError::UntaggedRuntimeError { .. } => return None, - ProximateShellError::CoerceError { left, right } => left.tag.until(right.tag), + ProximateShellError::CoerceError { left, right } => left.tag.until(&right.tag), }) } } @@ -495,7 +499,7 @@ impl ToDebug for ProximateShellError { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ShellDiagnostic { - pub(crate) diagnostic: Diagnostic, + pub(crate) diagnostic: Diagnostic, } impl PartialEq for ShellDiagnostic { @@ -521,7 +525,7 @@ impl std::cmp::Ord for ShellDiagnostic { #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, new, Clone, Serialize, Deserialize)] pub struct StringError { title: String, - error: Value, + error: String, } impl std::fmt::Display for ShellError { @@ -598,7 +602,6 @@ impl ShellErrorUtils> for Option> { } } } - pub trait CoerceInto { fn coerce_into(self, operation: impl Into) -> Result; } diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 248d2a0816..1e19c31e78 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -48,19 +48,23 @@ pub(crate) fn evaluate_baseline_expr( scope: &Scope, source: &Text, ) -> Result, ShellError> { + let tag = Tag { + span: expr.span, + anchor: None, + }; match &expr.item { - RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_tag(literal), source)), + RawExpression::Literal(literal) => Ok(evaluate_literal(literal.tagged(tag), source)), RawExpression::ExternalWord => Err(ShellError::argument_error( "Invalid external word", ArgumentError::InvalidExternalWord, - expr.tag(), + tag, )), - RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.tag())), + RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(tag)), RawExpression::Synthetic(hir::Synthetic::String(s)) => { Ok(Value::string(s).tagged_unknown()) } - RawExpression::Variable(var) => evaluate_reference(var, scope, source, expr.tag()), - RawExpression::Command(_) => evaluate_command(expr.tag(), scope, source), + RawExpression::Variable(var) => evaluate_reference(var, scope, source, tag), + RawExpression::Command(_) => evaluate_command(tag, scope, source), RawExpression::ExternalCommand(external) => evaluate_external(external, scope, source), RawExpression::Binary(binary) => { let left = evaluate_baseline_expr(binary.left(), registry, scope, source)?; @@ -69,10 +73,16 @@ pub(crate) fn evaluate_baseline_expr( trace!("left={:?} right={:?}", left.item, right.item); match left.compare(binary.op(), &*right) { - Ok(result) => Ok(Value::boolean(result).tagged(expr.tag())), + Ok(result) => Ok(Value::boolean(result).tagged(tag)), Err((left_type, right_type)) => Err(ShellError::coerce_error( - binary.left().copy_tag(left_type), - binary.right().copy_tag(right_type), + left_type.tagged(Tag { + span: binary.left().span, + anchor: None, + }), + right_type.tagged(Tag { + span: binary.right().span, + anchor: None, + }), )), } } @@ -84,13 +94,10 @@ pub(crate) fn evaluate_baseline_expr( exprs.push(expr); } - Ok(Value::Table(exprs).tagged(expr.tag())) + Ok(Value::Table(exprs).tagged(tag)) } RawExpression::Block(block) => { - Ok( - Value::Block(Block::new(block.clone(), source.clone(), expr.tag())) - .tagged(expr.tag()), - ) + Ok(Value::Block(Block::new(block.clone(), source.clone(), tag.clone())).tagged(&tag)) } RawExpression::Path(path) => { let value = evaluate_baseline_expr(path.head(), registry, scope, source)?; @@ -113,16 +120,16 @@ pub(crate) fn evaluate_baseline_expr( return Err(ShellError::labeled_error( "Unknown column", format!("did you mean '{}'?", possible_matches[0].1), - expr.tag(), + &tag, )); } Some(next) => { - item = next.clone().item.tagged(expr.tag()); + item = next.clone().item.tagged(&tag); } }; } - Ok(item.item().clone().tagged(expr.tag())) + Ok(item.item().clone().tagged(tag)) } RawExpression::Boolean(_boolean) => unimplemented!(), } diff --git a/src/format/generic.rs b/src/format/generic.rs index b6f9e29f26..fd058f31fc 100644 --- a/src/format/generic.rs +++ b/src/format/generic.rs @@ -14,7 +14,7 @@ impl RenderView for GenericView<'_> { match self.value { Value::Primitive(p) => Ok(host.stdout(&p.format(None))), Value::Table(l) => { - let view = TableView::from_list(l); + let view = TableView::from_list(l, 0); if let Some(view) = view { view.render_view(host)?; @@ -35,6 +35,8 @@ impl RenderView for GenericView<'_> { view.render_view(host)?; Ok(()) } + + Value::Error(e) => Err(e.clone()), } } } diff --git a/src/format/table.rs b/src/format/table.rs index 286be222c3..b2680a6c96 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -34,7 +34,7 @@ impl TableView { ret } - pub fn from_list(values: &[Tagged]) -> Option { + pub fn from_list(values: &[Tagged], starting_idx: usize) -> Option { if values.len() == 0 { return None; } @@ -68,7 +68,7 @@ impl TableView { if values.len() > 1 { // Indices are black, bold, right-aligned: - row.insert(0, (format!("{}", idx.to_string()), "Fdbr")); + row.insert(0, (format!("{}", (starting_idx + idx).to_string()), "Fdbr")); } entries.push(row); diff --git a/src/lib.rs b/src/lib.rs index b955f426e9..bfcaa4510f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![recursion_limit = "512"] +#![recursion_limit = "1024"] #[macro_use] mod prelude; @@ -21,7 +21,7 @@ mod traits; mod utils; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; -pub use crate::context::{AnchorLocation, SourceMap}; +pub use crate::context::AnchorLocation; pub use crate::env::host::BasicHost; pub use crate::parser::hir::SyntaxShape; pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder; @@ -31,7 +31,7 @@ pub use cli::cli; pub use data::base::{Primitive, Value}; pub use data::config::{config_path, APP_INFO}; pub use data::dict::{Dictionary, TaggedDictBuilder}; -pub use data::meta::{Span, Tag, Tagged, TaggedItem}; +pub use data::meta::{Span, Spanned, SpannedItem, Tag, Tagged, TaggedItem}; pub use errors::{CoerceInto, ShellError}; pub use num_traits::cast::ToPrimitive; pub use parser::parse::text::Text; diff --git a/src/parser.rs b/src/parser.rs index 3fd853c85c..37c8c09c30 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -21,10 +21,10 @@ pub(crate) use parse::tokens::{RawNumber, RawToken}; pub(crate) use parse::unit::Unit; pub(crate) use registry::CommandRegistry; -pub fn parse(input: &str, anchor: uuid::Uuid) -> Result { +pub fn parse(input: &str) -> Result { let _ = pretty_env_logger::try_init(); - match pipeline(nom_input(input, anchor)) { + match pipeline(nom_input(input)) { Ok((_rest, val)) => Ok(val), Err(err) => Err(ShellError::parse_error(err)), } diff --git a/src/parser/deserializer.rs b/src/parser/deserializer.rs index 43409fc4df..4b8bf913d5 100644 --- a/src/parser/deserializer.rs +++ b/src/parser/deserializer.rs @@ -52,7 +52,7 @@ impl<'de> ConfigDeserializer<'de> { self.stack.push(DeserializerItem { key_struct_field: Some((name.to_string(), name)), - val: value.unwrap_or_else(|| Value::nothing().tagged(self.call.name_tag)), + val: value.unwrap_or_else(|| Value::nothing().tagged(&self.call.name_tag)), }); Ok(()) diff --git a/src/parser/hir.rs b/src/parser/hir.rs index 4fd0a71b3d..ac6423943d 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -86,7 +86,7 @@ pub enum RawExpression { FilePath(PathBuf), ExternalCommand(ExternalCommand), - Command(Tag), + Command(Span), Boolean(bool), } @@ -123,14 +123,14 @@ impl RawExpression { } } -pub type Expression = Tagged; +pub type Expression = Spanned; impl std::fmt::Display for Expression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let span = self.tag.span; + let span = self.span; match &self.item { - RawExpression::Literal(literal) => write!(f, "{}", literal.tagged(self.tag)), + RawExpression::Literal(literal) => write!(f, "{}", literal.tagged(self.span)), RawExpression::Synthetic(Synthetic::String(s)) => write!(f, "{}", s), RawExpression::Command(_) => write!(f, "Command{{ {}..{} }}", span.start(), span.end()), RawExpression::ExternalWord => { @@ -159,97 +159,97 @@ impl std::fmt::Display for Expression { } impl Expression { - pub(crate) fn number(i: impl Into, tag: impl Into) -> Expression { - RawExpression::Literal(Literal::Number(i.into())).tagged(tag.into()) + pub(crate) fn number(i: impl Into, span: impl Into) -> Expression { + RawExpression::Literal(Literal::Number(i.into())).spanned(span.into()) } pub(crate) fn size( i: impl Into, unit: impl Into, - tag: impl Into, + span: impl Into, ) -> Expression { - RawExpression::Literal(Literal::Size(i.into(), unit.into())).tagged(tag.into()) + RawExpression::Literal(Literal::Size(i.into(), unit.into())).spanned(span.into()) } pub(crate) fn synthetic_string(s: impl Into) -> Expression { - RawExpression::Synthetic(Synthetic::String(s.into())).tagged_unknown() + RawExpression::Synthetic(Synthetic::String(s.into())).spanned_unknown() } - pub(crate) fn string(inner: impl Into, outer: impl Into) -> Expression { - RawExpression::Literal(Literal::String(inner.into())).tagged(outer.into()) + pub(crate) fn string(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Literal(Literal::String(inner.into())).spanned(outer.into()) } pub(crate) fn path( head: Expression, - tail: Vec>>, - tag: impl Into, + tail: Vec>>, + span: impl Into, ) -> Expression { let tail = tail.into_iter().map(|t| t.map(|s| s.into())).collect(); - RawExpression::Path(Box::new(Path::new(head, tail))).tagged(tag.into()) + RawExpression::Path(Box::new(Path::new(head, tail))).spanned(span.into()) } - pub(crate) fn dot_member(head: Expression, next: Tagged>) -> Expression { - let Tagged { item, tag } = head; - let new_tag = head.tag.until(next.tag); + pub(crate) fn dot_member(head: Expression, next: Spanned>) -> Expression { + let Spanned { item, span } = head; + let new_span = head.span.until(next.span); match item { RawExpression::Path(path) => { let (head, mut tail) = path.parts(); tail.push(next.map(|i| i.into())); - Expression::path(head, tail, new_tag) + Expression::path(head, tail, new_span) } - other => Expression::path(other.tagged(tag), vec![next], new_tag), + other => Expression::path(other.spanned(span), vec![next], new_span), } } pub(crate) fn infix( left: Expression, - op: Tagged>, + op: Spanned>, right: Expression, ) -> Expression { - let new_tag = left.tag.until(right.tag); + let new_span = left.span.until(right.span); RawExpression::Binary(Box::new(Binary::new(left, op.map(|o| o.into()), right))) - .tagged(new_tag) + .spanned(new_span) } - pub(crate) fn file_path(path: impl Into, outer: impl Into) -> Expression { - RawExpression::FilePath(path.into()).tagged(outer) + pub(crate) fn file_path(path: impl Into, outer: impl Into) -> Expression { + RawExpression::FilePath(path.into()).spanned(outer) } - pub(crate) fn list(list: Vec, tag: impl Into) -> Expression { - RawExpression::List(list).tagged(tag) + pub(crate) fn list(list: Vec, span: impl Into) -> Expression { + RawExpression::List(list).spanned(span) } - pub(crate) fn bare(tag: impl Into) -> Expression { - RawExpression::Literal(Literal::Bare).tagged(tag) + pub(crate) fn bare(span: impl Into) -> Expression { + RawExpression::Literal(Literal::Bare).spanned(span) } - pub(crate) fn pattern(tag: impl Into) -> Expression { - RawExpression::Literal(Literal::GlobPattern).tagged(tag.into()) + pub(crate) fn pattern(span: impl Into) -> Expression { + RawExpression::Literal(Literal::GlobPattern).spanned(span.into()) } - pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { - RawExpression::Variable(Variable::Other(inner.into())).tagged(outer) + pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Variable(Variable::Other(inner.into())).spanned(outer) } - pub(crate) fn external_command(inner: impl Into, outer: impl Into) -> Expression { - RawExpression::ExternalCommand(ExternalCommand::new(inner.into())).tagged(outer) + pub(crate) fn external_command(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::ExternalCommand(ExternalCommand::new(inner.into())).spanned(outer) } - pub(crate) fn it_variable(inner: impl Into, outer: impl Into) -> Expression { - RawExpression::Variable(Variable::It(inner.into())).tagged(outer) + pub(crate) fn it_variable(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Variable(Variable::It(inner.into())).spanned(outer) } } impl ToDebug for Expression { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { - match self.item() { - RawExpression::Literal(l) => l.tagged(self.tag()).fmt_debug(f, source), + match &self.item { + RawExpression::Literal(l) => l.spanned(self.span).fmt_debug(f, source), RawExpression::FilePath(p) => write!(f, "{}", p.display()), - RawExpression::ExternalWord => write!(f, "{}", self.tag().slice(source)), + RawExpression::ExternalWord => write!(f, "{}", self.span.slice(source)), RawExpression::Command(tag) => write!(f, "{}", tag.slice(source)), RawExpression::Synthetic(Synthetic::String(s)) => write!(f, "{:?}", s), RawExpression::Variable(Variable::It(_)) => write!(f, "$it"), @@ -281,8 +281,8 @@ impl ToDebug for Expression { } } -impl From> for Expression { - fn from(path: Tagged) -> Expression { +impl From> for Expression { + fn from(path: Spanned) -> Expression { path.map(|p| RawExpression::Path(Box::new(p))) } } @@ -296,14 +296,14 @@ impl From> for Expression { pub enum Literal { Number(Number), Size(Number, Unit), - String(Tag), + String(Span), GlobPattern, Bare, } impl std::fmt::Display for Tagged { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", Tagged::new(self.tag, &self.item)) + write!(f, "{}", Tagged::new(self.tag.clone(), &self.item)) } } @@ -321,14 +321,14 @@ impl std::fmt::Display for Tagged<&Literal> { } } -impl ToDebug for Tagged<&Literal> { +impl ToDebug for Spanned<&Literal> { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { - match self.item() { - Literal::Number(number) => write!(f, "{:?}", *number), + match self.item { + Literal::Number(number) => write!(f, "{:?}", number), Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), Literal::String(tag) => write!(f, "{}", tag.slice(source)), - Literal::GlobPattern => write!(f, "{}", self.tag().slice(source)), - Literal::Bare => write!(f, "{}", self.tag().slice(source)), + Literal::GlobPattern => write!(f, "{}", self.span.slice(source)), + Literal::Bare => write!(f, "{}", self.span.slice(source)), } } } @@ -347,15 +347,15 @@ impl Literal { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)] pub enum Variable { - It(Tag), - Other(Tag), + It(Span), + Other(Span), } impl std::fmt::Display for Variable { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Variable::It(_) => write!(f, "$it"), - Variable::Other(tag) => write!(f, "${{ {}..{} }}", tag.span.start(), tag.span.end()), + Variable::Other(span) => write!(f, "${{ {}..{} }}", span.start(), span.end()), } } } diff --git a/src/parser/hir/baseline_parse/tests.rs b/src/parser/hir/baseline_parse/tests.rs index badb177513..d3b9248496 100644 --- a/src/parser/hir/baseline_parse/tests.rs +++ b/src/parser/hir/baseline_parse/tests.rs @@ -6,15 +6,14 @@ use crate::parser::hir::syntax_shape::*; use crate::parser::hir::TokensIterator; use crate::parser::parse::token_tree_builder::{CurriedToken, TokenTreeBuilder as b}; use crate::parser::TokenNode; -use crate::{Span, Tag, Tagged, TaggedItem, Text}; +use crate::{Span, SpannedItem, Tag, Tagged, Text}; use pretty_assertions::assert_eq; use std::fmt::Debug; -use uuid::Uuid; #[test] fn test_parse_string() { parse_tokens(StringShape, vec![b::string("hello")], |tokens| { - hir::Expression::string(inner_string_tag(tokens[0].tag()), tokens[0].tag()) + hir::Expression::string(inner_string_span(tokens[0].span()), tokens[0].span()) }); } @@ -28,7 +27,7 @@ fn test_parse_path() { let bare = tokens[2].expect_bare(); hir::Expression::path( hir::Expression::it_variable(inner_var, outer_var), - vec!["cpu".tagged(bare)], + vec!["cpu".spanned(bare)], outer_var.until(bare), ) }, @@ -50,7 +49,7 @@ fn test_parse_path() { hir::Expression::path( hir::Expression::variable(inner_var, outer_var), - vec!["amount".tagged(amount), "max ghz".tagged(outer_max_ghz)], + vec!["amount".spanned(amount), "max ghz".spanned(outer_max_ghz)], outer_var.until(outer_max_ghz), ) }, @@ -64,13 +63,16 @@ fn test_parse_command() { vec![b::bare("ls"), b::sp(), b::pattern("*.txt")], |tokens| { let bare = tokens[0].expect_bare(); - let pat = tokens[2].tag(); + let pat = tokens[2].span(); ClassifiedCommand::Internal(InternalCommand::new( "ls".to_string(), - bare, + Tag { + span: bare, + anchor: None, + }, hir::Call { - head: Box::new(hir::RawExpression::Command(bare).tagged(bare)), + head: Box::new(hir::RawExpression::Command(bare).spanned(bare)), positional: Some(vec![hir::Expression::pattern(pat)]), named: None, }, @@ -99,7 +101,7 @@ fn test_parse_command() { hir::Expression::path( hir::Expression::variable(inner_var, outer_var), - vec!["amount".tagged(amount), "max ghz".tagged(outer_max_ghz)], + vec!["amount".spanned(amount), "max ghz".spanned(outer_max_ghz)], outer_var.until(outer_max_ghz), ) }, @@ -112,11 +114,11 @@ fn parse_tokens( expected: impl FnOnce(Tagged<&[TokenNode]>) -> T, ) { let tokens = b::token_list(tokens); - let (tokens, source) = b::build(test_origin(), tokens); + let (tokens, source) = b::build(tokens); ExpandContext::with_empty(&Text::from(source), |context| { let tokens = tokens.expect_list(); - let mut iterator = TokensIterator::all(tokens.item, *context.tag()); + let mut iterator = TokensIterator::all(tokens.item, *context.span()); let expr = expand_syntax(&shape, &mut iterator, &context); @@ -132,13 +134,6 @@ fn parse_tokens( }) } -fn test_origin() -> Uuid { - Uuid::nil() -} - -fn inner_string_tag(tag: Tag) -> Tag { - Tag { - span: Span::new(tag.span.start() + 1, tag.span.end() - 1), - anchor: tag.anchor, - } +fn inner_string_span(span: Span) -> Span { + Span::new(span.start() + 1, span.end() - 1) } diff --git a/src/parser/hir/binary.rs b/src/parser/hir/binary.rs index a44c41d63a..67c597cb86 100644 --- a/src/parser/hir/binary.rs +++ b/src/parser/hir/binary.rs @@ -1,6 +1,6 @@ use crate::parser::{hir::Expression, Operator}; use crate::prelude::*; -use crate::Tagged; + use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; @@ -12,7 +12,7 @@ use std::fmt; #[get = "pub(crate)"] pub struct Binary { left: Expression, - op: Tagged, + op: Spanned, right: Expression, } diff --git a/src/parser/hir/expand_external_tokens.rs b/src/parser/hir/expand_external_tokens.rs index 238cb4b01b..af966945bd 100644 --- a/src/parser/hir/expand_external_tokens.rs +++ b/src/parser/hir/expand_external_tokens.rs @@ -6,17 +6,17 @@ use crate::parser::{ }, FlatShape, TokenNode, TokensIterator, }; -use crate::{Tag, Tagged, Text}; +use crate::{Span, Spanned, Text}; pub fn expand_external_tokens( token_nodes: &mut TokensIterator<'_>, source: &Text, -) -> Result>, ShellError> { - let mut out: Vec> = vec![]; +) -> Result>, ShellError> { + let mut out: Vec> = vec![]; loop { - if let Some(tag) = expand_next_expression(token_nodes)? { - out.push(tag.tagged_string(source)); + if let Some(span) = expand_next_expression(token_nodes)? { + out.push(span.spanned_string(source)); } else { break; } @@ -37,7 +37,7 @@ impl ColorSyntax for ExternalTokensShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Self::Info { loop { // Allow a space @@ -55,7 +55,7 @@ impl ColorSyntax for ExternalTokensShape { pub fn expand_next_expression( token_nodes: &mut TokensIterator<'_>, -) -> Result, ShellError> { +) -> Result, ShellError> { let first = token_nodes.next_non_ws(); let first = match first { @@ -79,14 +79,14 @@ pub fn expand_next_expression( Ok(Some(first.until(last))) } -fn triage_external_head(node: &TokenNode) -> Result { +fn triage_external_head(node: &TokenNode) -> Result { Ok(match node { - TokenNode::Token(token) => token.tag(), + TokenNode::Token(token) => token.span, TokenNode::Call(_call) => unimplemented!("TODO: OMG"), TokenNode::Nodes(_nodes) => unimplemented!("TODO: OMG"), TokenNode::Delimited(_delimited) => unimplemented!("TODO: OMG"), TokenNode::Pipeline(_pipeline) => unimplemented!("TODO: OMG"), - TokenNode::Flag(flag) => flag.tag(), + TokenNode::Flag(flag) => flag.span, TokenNode::Whitespace(_whitespace) => { unreachable!("This function should be called after next_non_ws()") } @@ -96,7 +96,7 @@ fn triage_external_head(node: &TokenNode) -> Result { fn triage_continuation<'a, 'b>( nodes: &'a mut TokensIterator<'b>, -) -> Result, ShellError> { +) -> Result, ShellError> { let mut peeked = nodes.peek_any(); let node = match peeked.node { @@ -116,7 +116,7 @@ fn triage_continuation<'a, 'b>( } peeked.commit(); - Ok(Some(node.tag())) + Ok(Some(node.span())) } #[must_use] @@ -137,7 +137,7 @@ impl ColorSyntax for ExternalExpression { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> ExternalExpressionResult { let atom = match expand_atom( token_nodes, @@ -146,7 +146,7 @@ impl ColorSyntax for ExternalExpression { ExpansionRule::permissive(), ) { Err(_) => unreachable!("TODO: separate infallible expand_atom"), - Ok(Tagged { + Ok(Spanned { item: AtomicToken::Eof { .. }, .. }) => return ExternalExpressionResult::Eof, diff --git a/src/parser/hir/external_command.rs b/src/parser/hir/external_command.rs index 2dd42c1312..df71328cab 100644 --- a/src/parser/hir/external_command.rs +++ b/src/parser/hir/external_command.rs @@ -9,7 +9,7 @@ use std::fmt; )] #[get = "pub(crate)"] pub struct ExternalCommand { - pub(crate) name: Tag, + pub(crate) name: Span, } impl ToDebug for ExternalCommand { diff --git a/src/parser/hir/named.rs b/src/parser/hir/named.rs index 838f643be5..f7387e4fd4 100644 --- a/src/parser/hir/named.rs +++ b/src/parser/hir/named.rs @@ -43,9 +43,13 @@ impl NamedArguments { match switch { None => self.named.insert(name.into(), NamedValue::AbsentSwitch), - Some(flag) => self - .named - .insert(name, NamedValue::PresentSwitch(*flag.name())), + Some(flag) => self.named.insert( + name, + NamedValue::PresentSwitch(Tag { + span: *flag.name(), + anchor: None, + }), + ), }; } diff --git a/src/parser/hir/path.rs b/src/parser/hir/path.rs index a1925102fb..5867132986 100644 --- a/src/parser/hir/path.rs +++ b/src/parser/hir/path.rs @@ -1,6 +1,5 @@ use crate::parser::hir::Expression; use crate::prelude::*; -use crate::Tagged; use derive_new::new; use getset::{Getters, MutGetters}; use serde::{Deserialize, Serialize}; @@ -24,7 +23,7 @@ use std::fmt; pub struct Path { head: Expression, #[get_mut = "pub(crate)"] - tail: Vec>, + tail: Vec>, } impl fmt::Display for Path { @@ -40,7 +39,7 @@ impl fmt::Display for Path { } impl Path { - pub(crate) fn parts(self) -> (Expression, Vec>) { + pub(crate) fn parts(self) -> (Expression, Vec>) { (self.head, self.tail) } } @@ -50,7 +49,7 @@ impl ToDebug for Path { write!(f, "{}", self.head.debug(source))?; for part in &self.tail { - write!(f, ".{}", part.item())?; + write!(f, ".{}", part.item)?; } Ok(()) diff --git a/src/parser/hir/syntax_shape.rs b/src/parser/hir/syntax_shape.rs index 1a140d86bd..8accfbde2b 100644 --- a/src/parser/hir/syntax_shape.rs +++ b/src/parser/hir/syntax_shape.rs @@ -64,7 +64,7 @@ impl FallibleColorSyntax for SyntaxShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { match self { SyntaxShape::Any => { @@ -158,7 +158,7 @@ pub struct ExpandContext<'context> { #[get = "pub(crate)"] registry: &'context CommandRegistry, #[get = "pub(crate)"] - tag: Tag, + span: Span, #[get = "pub(crate)"] source: &'context Text, homedir: Option, @@ -179,7 +179,7 @@ impl<'context> ExpandContext<'context> { callback(ExpandContext { registry: ®istry, - tag: Tag::unknown(), + span: Span::unknown(), source, homedir: None, }) @@ -211,7 +211,7 @@ pub trait FallibleColorSyntax: std::fmt::Debug + Copy { input: &Self::Input, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result; } @@ -224,7 +224,7 @@ pub trait ColorSyntax: std::fmt::Debug + Copy { input: &Self::Input, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Self::Info; } @@ -240,7 +240,7 @@ pub trait ColorSyntax: std::fmt::Debug + Copy { // input: &Self::Input, // token_nodes: &'b mut TokensIterator<'a>, // context: &ExpandContext, -// shapes: &mut Vec>, +// shapes: &mut Vec>, // ) -> Result { // FallibleColorSyntax::color_syntax(self, input, token_nodes, context, shapes) // } @@ -282,7 +282,7 @@ pub fn color_syntax<'a, 'b, T: ColorSyntax, U>( shape: &T, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> ((), U) { trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); @@ -310,7 +310,7 @@ pub fn color_fallible_syntax<'a, 'b, T: FallibleColorSyntax, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result { trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); @@ -344,7 +344,7 @@ pub fn color_syntax_with<'a, 'b, T: ColorSyntax, U, I>( input: &I, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> ((), U) { trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); @@ -373,7 +373,7 @@ pub fn color_fallible_syntax_with<'a, 'b, T: FallibleColorSyntax, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result { trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); @@ -446,15 +446,15 @@ pub trait SkipSyntax: std::fmt::Debug + Copy { enum BarePathState { Initial, - Seen(Tag, Tag), + Seen(Span, Span), Error(ShellError), } impl BarePathState { - pub fn seen(self, tag: Tag) -> BarePathState { + pub fn seen(self, span: Span) -> BarePathState { match self { - BarePathState::Initial => BarePathState::Seen(tag, tag), - BarePathState::Seen(start, _) => BarePathState::Seen(start, tag), + BarePathState::Initial => BarePathState::Seen(span, span), + BarePathState::Seen(start, _) => BarePathState::Seen(start, span), BarePathState::Error(err) => BarePathState::Error(err), } } @@ -467,7 +467,7 @@ impl BarePathState { } } - pub fn into_bare(self) -> Result { + pub fn into_bare(self) -> Result { match self { BarePathState::Initial => unreachable!("into_bare in initial state"), BarePathState::Seen(start, end) => Ok(start.until(end)), @@ -480,7 +480,7 @@ pub fn expand_bare<'a, 'b>( token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, predicate: impl Fn(&TokenNode) -> bool, -) -> Result { +) -> Result { let mut state = BarePathState::Initial; loop { @@ -494,7 +494,7 @@ pub fn expand_bare<'a, 'b>( } Some(node) => { if predicate(node) { - state = state.seen(node.tag()); + state = state.seen(node.span()); peeked.commit(); } else { state = state.end(peeked, "word"); @@ -511,19 +511,19 @@ pub fn expand_bare<'a, 'b>( pub struct BarePathShape; impl ExpandSyntax for BarePathShape { - type Output = Tag; + type Output = Span; fn expand_syntax<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - ) -> Result { + ) -> Result { expand_bare(token_nodes, context, |token| match token { - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Bare, .. }) - | TokenNode::Token(Tagged { + | TokenNode::Token(Spanned { item: RawToken::Operator(Operator::Dot), .. }) => true, @@ -545,15 +545,15 @@ impl FallibleColorSyntax for BareShape { input: &FlatShape, token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { token_nodes.peek_any_token(|token| match token { // If it's a bare token, color it - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Bare, - tag, + span, }) => { - shapes.push((*input).tagged(tag)); + shapes.push((*input).spanned(*span)); Ok(()) } @@ -564,7 +564,7 @@ impl FallibleColorSyntax for BareShape { } impl ExpandSyntax for BareShape { - type Output = Tagged; + type Output = Spanned; fn expand_syntax<'a, 'b>( &self, @@ -574,12 +574,12 @@ impl ExpandSyntax for BareShape { let peeked = token_nodes.peek_any().not_eof("word")?; match peeked.node { - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Bare, - tag, + span, }) => { peeked.commit(); - Ok(tag.tagged_string(context.source)) + Ok(span.spanned_string(context.source)) } other => Err(ShellError::type_error("word", other.tagged_type_name())), @@ -608,9 +608,9 @@ impl TestSyntax for BareShape { #[derive(Debug)] pub enum CommandSignature { - Internal(Tagged>), - LiteralExternal { outer: Tag, inner: Tag }, - External(Tag), + Internal(Spanned>), + LiteralExternal { outer: Span, inner: Span }, + External(Span), Expression(hir::Expression), } @@ -618,14 +618,15 @@ impl CommandSignature { pub fn to_expression(&self) -> hir::Expression { match self { CommandSignature::Internal(command) => { - let tag = command.tag; - hir::RawExpression::Command(tag).tagged(tag) + let span = command.span; + hir::RawExpression::Command(span).spanned(span) } CommandSignature::LiteralExternal { outer, inner } => { - hir::RawExpression::ExternalCommand(hir::ExternalCommand::new(*inner)).tagged(outer) + hir::RawExpression::ExternalCommand(hir::ExternalCommand::new(*inner)) + .spanned(*outer) } - CommandSignature::External(tag) => { - hir::RawExpression::ExternalCommand(hir::ExternalCommand::new(*tag)).tagged(tag) + CommandSignature::External(span) => { + hir::RawExpression::ExternalCommand(hir::ExternalCommand::new(*span)).spanned(*span) } CommandSignature::Expression(expr) => expr.clone(), } @@ -645,7 +646,7 @@ impl FallibleColorSyntax for PipelineShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { // Make sure we're looking at a pipeline let Pipeline { parts, .. } = token_nodes.peek_any_token(|node| node.as_pipeline())?; @@ -654,11 +655,11 @@ impl FallibleColorSyntax for PipelineShape { for part in parts { // If the pipeline part has a prefix `|`, emit a pipe to color if let Some(pipe) = part.pipe { - shapes.push(FlatShape::Pipe.tagged(pipe)); + shapes.push(FlatShape::Pipe.spanned(pipe)); } // Create a new iterator containing the tokens in the pipeline part to color - let mut token_nodes = TokensIterator::new(&part.tokens.item, part.tag, false); + let mut token_nodes = TokensIterator::new(&part.tokens.item, part.span, false); color_syntax(&MaybeSpaceShape, &mut token_nodes, context, shapes); color_syntax(&CommandShape, &mut token_nodes, context, shapes); @@ -685,7 +686,7 @@ impl ExpandSyntax for PipelineShape { let commands: Result, ShellError> = parts .iter() - .map(|item| classify_command(&item, context, &source)) + .map(|item| classify_command(item, context, &source)) .collect(); Ok(ClassifiedPipeline { @@ -711,7 +712,7 @@ impl FallibleColorSyntax for CommandHeadShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result { // If we don't ultimately find a token, roll back token_nodes.atomic(|token_nodes| { @@ -726,7 +727,7 @@ impl FallibleColorSyntax for CommandHeadShape { match atom.item { // If the head is an explicit external command (^cmd), color it as an external command AtomicToken::ExternalCommand { command } => { - shapes.push(FlatShape::ExternalCommand.tagged(command)); + shapes.push(FlatShape::ExternalCommand.spanned(command)); Ok(CommandHeadKind::External) } @@ -736,19 +737,19 @@ impl FallibleColorSyntax for CommandHeadShape { if context.registry.has(name) { // If the registry has the command, color it as an internal command - shapes.push(FlatShape::InternalCommand.tagged(text)); + shapes.push(FlatShape::InternalCommand.spanned(text)); let command = context.registry.expect_command(name); Ok(CommandHeadKind::Internal(command.signature())) } else { // Otherwise, color it as an external command - shapes.push(FlatShape::ExternalCommand.tagged(text)); + shapes.push(FlatShape::ExternalCommand.spanned(text)); Ok(CommandHeadKind::External) } } // Otherwise, we're not actually looking at a command _ => Err(ShellError::syntax_error( - "No command at the head".tagged(atom.tag), + "No command at the head".tagged(atom.span), )), } }) @@ -764,25 +765,25 @@ impl ExpandSyntax for CommandHeadShape { context: &ExpandContext, ) -> Result { let node = - parse_single_node_skipping_ws(token_nodes, "command head1", |token, token_tag, _| { + parse_single_node_skipping_ws(token_nodes, "command head1", |token, token_span, _| { Ok(match token { - RawToken::ExternalCommand(tag) => CommandSignature::LiteralExternal { - outer: token_tag, - inner: tag, + RawToken::ExternalCommand(span) => CommandSignature::LiteralExternal { + outer: token_span, + inner: span, }, RawToken::Bare => { - let name = token_tag.slice(context.source); + let name = token_span.slice(context.source); if context.registry.has(name) { let command = context.registry.expect_command(name); - CommandSignature::Internal(command.tagged(token_tag)) + CommandSignature::Internal(command.spanned(token_span)) } else { - CommandSignature::External(token_tag) + CommandSignature::External(token_span) } } _ => { return Err(ShellError::type_error( "command head2", - token.type_name().tagged(token_tag), + token.type_name().tagged(token_span), )) } }) @@ -813,7 +814,7 @@ impl ExpandSyntax for ClassifiedCommandShape { match &head { CommandSignature::Expression(expr) => Err(ShellError::syntax_error( - "Unexpected expression in command position".tagged(expr.tag), + "Unexpected expression in command position".tagged(expr.span), )), // If the command starts with `^`, treat it as an external command no matter what @@ -831,7 +832,7 @@ impl ExpandSyntax for ClassifiedCommandShape { CommandSignature::Internal(command) => { let tail = - parse_command_tail(&command.signature(), &context, iterator, command.tag)?; + parse_command_tail(&command.signature(), &context, iterator, command.span)?; let (positional, named) = match tail { None => (None, None), @@ -846,7 +847,10 @@ impl ExpandSyntax for ClassifiedCommandShape { Ok(ClassifiedCommand::Internal(InternalCommand::new( command.item.name().to_string(), - command.tag, + Tag { + span: command.span, + anchor: None, + }, call, ))) } @@ -866,7 +870,7 @@ impl FallibleColorSyntax for InternalCommandHeadShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let peeked_head = token_nodes.peek_non_ws().not_eof("command head4"); @@ -876,17 +880,17 @@ impl FallibleColorSyntax for InternalCommandHeadShape { }; let _expr = match peeked_head.node { - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Bare, - tag, - }) => shapes.push(FlatShape::Word.tagged(tag)), + span, + }) => shapes.push(FlatShape::Word.spanned(*span)), - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::String(_inner_tag), - tag, - }) => shapes.push(FlatShape::String.tagged(tag)), + span, + }) => shapes.push(FlatShape::String.spanned(*span)), - _node => shapes.push(FlatShape::Error.tagged(peeked_head.node.tag())), + _node => shapes.push(FlatShape::Error.spanned(peeked_head.node.span())), }; peeked_head.commit(); @@ -905,16 +909,16 @@ impl ExpandExpression for InternalCommandHeadShape { let expr = match peeked_head.node { TokenNode::Token( - spanned @ Tagged { + spanned @ Spanned { item: RawToken::Bare, .. }, ) => spanned.map(|_| hir::RawExpression::Literal(hir::Literal::Bare)), - TokenNode::Token(Tagged { - item: RawToken::String(inner_tag), - tag, - }) => hir::RawExpression::Literal(hir::Literal::String(*inner_tag)).tagged(*tag), + TokenNode::Token(Spanned { + item: RawToken::String(inner_span), + span, + }) => hir::RawExpression::Literal(hir::Literal::String(*inner_span)).spanned(*span), node => { return Err(ShellError::type_error( @@ -932,24 +936,24 @@ impl ExpandExpression for InternalCommandHeadShape { pub(crate) struct SingleError<'token> { expected: &'static str, - node: &'token Tagged, + node: &'token Spanned, } impl<'token> SingleError<'token> { pub(crate) fn error(&self) -> ShellError { - ShellError::type_error(self.expected, self.node.type_name().tagged(self.node.tag)) + ShellError::type_error(self.expected, self.node.type_name().tagged(self.node.span)) } } fn parse_single_node<'a, 'b, T>( token_nodes: &'b mut TokensIterator<'a>, expected: &'static str, - callback: impl FnOnce(RawToken, Tag, SingleError) -> Result, + callback: impl FnOnce(RawToken, Span, SingleError) -> Result, ) -> Result { token_nodes.peek_any_token(|node| match node { TokenNode::Token(token) => callback( token.item, - token.tag(), + token.span, SingleError { expected, node: token, @@ -963,14 +967,14 @@ fn parse_single_node<'a, 'b, T>( fn parse_single_node_skipping_ws<'a, 'b, T>( token_nodes: &'b mut TokensIterator<'a>, expected: &'static str, - callback: impl FnOnce(RawToken, Tag, SingleError) -> Result, + callback: impl FnOnce(RawToken, Span, SingleError) -> Result, ) -> Result { let peeked = token_nodes.peek_non_ws().not_eof(expected)?; let expr = match peeked.node { TokenNode::Token(token) => callback( token.item, - token.tag(), + token.span, SingleError { expected, node: token, @@ -997,7 +1001,7 @@ impl FallibleColorSyntax for WhitespaceShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let peeked = token_nodes.peek_any().not_eof("whitespace"); @@ -1007,7 +1011,7 @@ impl FallibleColorSyntax for WhitespaceShape { }; let _tag = match peeked.node { - TokenNode::Whitespace(tag) => shapes.push(FlatShape::Whitespace.tagged(tag)), + TokenNode::Whitespace(span) => shapes.push(FlatShape::Whitespace.spanned(*span)), _other => return Ok(()), }; @@ -1019,7 +1023,7 @@ impl FallibleColorSyntax for WhitespaceShape { } impl ExpandSyntax for WhitespaceShape { - type Output = Tag; + type Output = Span; fn expand_syntax<'a, 'b>( &self, @@ -1028,7 +1032,7 @@ impl ExpandSyntax for WhitespaceShape { ) -> Result { let peeked = token_nodes.peek_any().not_eof("whitespace")?; - let tag = match peeked.node { + let span = match peeked.node { TokenNode::Whitespace(tag) => *tag, other => { @@ -1041,7 +1045,7 @@ impl ExpandSyntax for WhitespaceShape { peeked.commit(); - Ok(tag) + Ok(span) } } @@ -1094,7 +1098,7 @@ impl ColorSyntax for MaybeSpaceShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Self::Info { let peeked = token_nodes.peek_any().not_eof("whitespace"); @@ -1103,9 +1107,9 @@ impl ColorSyntax for MaybeSpaceShape { Ok(peeked) => peeked, }; - if let TokenNode::Whitespace(tag) = peeked.node { + if let TokenNode::Whitespace(span) = peeked.node { peeked.commit(); - shapes.push(FlatShape::Whitespace.tagged(tag)); + shapes.push(FlatShape::Whitespace.spanned(*span)); } } } @@ -1122,14 +1126,14 @@ impl FallibleColorSyntax for SpaceShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let peeked = token_nodes.peek_any().not_eof("whitespace")?; match peeked.node { - TokenNode::Whitespace(tag) => { + TokenNode::Whitespace(span) => { peeked.commit(); - shapes.push(FlatShape::Whitespace.tagged(tag)); + shapes.push(FlatShape::Whitespace.spanned(*span)); Ok(()) } @@ -1168,26 +1172,26 @@ pub fn spaced(inner: T) -> SpacedExpression { SpacedExpression { inner } } -fn expand_variable(tag: Tag, token_tag: Tag, source: &Text) -> hir::Expression { - if tag.slice(source) == "it" { - hir::Expression::it_variable(tag, token_tag) +fn expand_variable(span: Span, token_span: Span, source: &Text) -> hir::Expression { + if span.slice(source) == "it" { + hir::Expression::it_variable(span, token_span) } else { - hir::Expression::variable(tag, token_tag) + hir::Expression::variable(span, token_span) } } fn classify_command( - command: &Tagged, + command: &Spanned, context: &ExpandContext, source: &Text, ) -> Result { - let mut iterator = TokensIterator::new(&command.tokens.item, command.tag, true); + let mut iterator = TokensIterator::new(&command.tokens.item, command.span, true); let head = CommandHeadShape.expand_syntax(&mut iterator, &context)?; match &head { CommandSignature::Expression(_) => Err(ShellError::syntax_error( - "Unexpected expression in command position".tagged(command.tag), + "Unexpected expression in command position".tagged(command.span), )), // If the command starts with `^`, treat it as an external command no matter what @@ -1205,7 +1209,7 @@ fn classify_command( CommandSignature::Internal(command) => { let tail = - parse_command_tail(&command.signature(), &context, &mut iterator, command.tag)?; + parse_command_tail(&command.signature(), &context, &mut iterator, command.span)?; let (positional, named) = match tail { None => (None, None), @@ -1220,7 +1224,10 @@ fn classify_command( Ok(ClassifiedCommand::Internal(InternalCommand::new( command.name().to_string(), - command.tag, + Tag { + span: command.span, + anchor: None, + }, call, ))) } @@ -1239,7 +1246,7 @@ impl ColorSyntax for CommandShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) { let kind = color_fallible_syntax(&CommandHeadShape, token_nodes, context, shapes); diff --git a/src/parser/hir/syntax_shape/block.rs b/src/parser/hir/syntax_shape/block.rs index 806681691e..7518d8f946 100644 --- a/src/parser/hir/syntax_shape/block.rs +++ b/src/parser/hir/syntax_shape/block.rs @@ -11,7 +11,7 @@ use crate::parser::{ parse::token_tree::Delimiter, RawToken, TokenNode, }; -use crate::{Tag, Tagged, TaggedItem}; +use crate::{Span, Spanned, SpannedItem}; #[derive(Debug, Copy, Clone)] pub struct AnyBlockShape; @@ -25,7 +25,7 @@ impl FallibleColorSyntax for AnyBlockShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let block = token_nodes.peek_non_ws().not_eof("block"); @@ -39,11 +39,11 @@ impl FallibleColorSyntax for AnyBlockShape { match block { // If so, color it as a block - Some((children, tags)) => { - let mut token_nodes = TokensIterator::new(children.item, context.tag, false); + Some((children, spans)) => { + let mut token_nodes = TokensIterator::new(children.item, context.span, false); color_syntax_with( &DelimitedShape, - &(Delimiter::Brace, tags.0, tags.1), + &(Delimiter::Brace, spans.0, spans.1), &mut token_nodes, context, shapes, @@ -72,11 +72,11 @@ impl ExpandExpression for AnyBlockShape { match block { Some((block, _tags)) => { - let mut iterator = TokensIterator::new(&block.item, context.tag, false); + let mut iterator = TokensIterator::new(&block.item, context.span, false); let exprs = expand_syntax(&ExpressionListShape, &mut iterator, context)?; - return Ok(hir::RawExpression::Block(exprs).tagged(block.tag)); + return Ok(hir::RawExpression::Block(exprs).spanned(block.span)); } _ => {} } @@ -97,7 +97,7 @@ impl FallibleColorSyntax for ShorthandBlock { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { // Try to find a shorthand head. If none found, fail color_fallible_syntax(&ShorthandPath, token_nodes, context, shapes)?; @@ -126,10 +126,10 @@ impl ExpandExpression for ShorthandBlock { context: &ExpandContext, ) -> Result { let path = expand_expr(&ShorthandPath, token_nodes, context)?; - let start = path.tag; + let start = path.span; let expr = continue_expression(path, token_nodes, context)?; - let end = expr.tag; - let block = hir::RawExpression::Block(vec![expr]).tagged(start.until(end)); + let end = expr.span; + let block = hir::RawExpression::Block(vec![expr]).spanned(start.until(end)); Ok(block) } @@ -148,7 +148,7 @@ impl FallibleColorSyntax for ShorthandPath { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { token_nodes.atomic(|token_nodes| { let variable = color_fallible_syntax(&VariablePathShape, token_nodes, context, shapes); @@ -232,29 +232,29 @@ impl FallibleColorSyntax for ShorthandHeadShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { // A shorthand path must not be at EOF let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?; match peeked.node { // If the head of a shorthand path is a bare token, it expands to `$it.bare` - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Bare, - tag, + span, }) => { peeked.commit(); - shapes.push(FlatShape::BareMember.tagged(tag)); + shapes.push(FlatShape::BareMember.spanned(*span)); Ok(()) } // If the head of a shorthand path is a string, it expands to `$it."some string"` - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::String(_), - tag: outer, + span: outer, }) => { peeked.commit(); - shapes.push(FlatShape::StringMember.tagged(outer)); + shapes.push(FlatShape::StringMember.spanned(*outer)); Ok(()) } @@ -277,40 +277,40 @@ impl ExpandExpression for ShorthandHeadShape { match peeked.node { // If the head of a shorthand path is a bare token, it expands to `$it.bare` - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Bare, - tag, + span, }) => { // Commit the peeked token peeked.commit(); // Synthesize an `$it` expression - let it = synthetic_it(token_nodes.anchor()); + let it = synthetic_it(); // Make a path out of `$it` and the bare token as a member Ok(hir::Expression::path( it, - vec![tag.tagged_string(context.source)], - tag, + vec![span.spanned_string(context.source)], + *span, )) } // If the head of a shorthand path is a string, it expands to `$it."some string"` - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::String(inner), - tag: outer, + span: outer, }) => { // Commit the peeked token peeked.commit(); // Synthesize an `$it` expression - let it = synthetic_it(token_nodes.anchor()); + let it = synthetic_it(); // Make a path out of `$it` and the bare token as a member Ok(hir::Expression::path( it, - vec![inner.string(context.source).tagged(outer)], - outer, + vec![inner.string(context.source).spanned(*outer)], + *outer, )) } @@ -325,6 +325,6 @@ impl ExpandExpression for ShorthandHeadShape { } } -fn synthetic_it(origin: uuid::Uuid) -> hir::Expression { - hir::Expression::it_variable(Tag::unknown_span(origin), Tag::unknown_span(origin)) +fn synthetic_it() -> hir::Expression { + hir::Expression::it_variable(Span::unknown(), Span::unknown()) } diff --git a/src/parser/hir/syntax_shape/expression.rs b/src/parser/hir/syntax_shape/expression.rs index fc99c38dc3..0be63eaeb6 100644 --- a/src/parser/hir/syntax_shape/expression.rs +++ b/src/parser/hir/syntax_shape/expression.rs @@ -46,7 +46,7 @@ impl FallibleColorSyntax for AnyExpressionShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { // Look for an expression at the cursor color_fallible_syntax(&AnyExpressionStartShape, token_nodes, context, shapes)?; @@ -94,7 +94,7 @@ pub(crate) fn continue_expression( pub(crate) fn continue_coloring_expression( token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { // if there's not even one expression continuation, fail color_fallible_syntax(&ExpressionContinuationShape, token_nodes, context, shapes)?; @@ -131,20 +131,23 @@ impl ExpandExpression for AnyExpressionStartShape { return Ok(hir::Expression::size( number.to_number(context.source), unit.item, - atom.tag, + Tag { + span: atom.span, + anchor: None, + }, )) } AtomicToken::SquareDelimited { nodes, .. } => { - expand_delimited_square(&nodes, atom.tag, context) + expand_delimited_square(&nodes, atom.span.into(), context) } AtomicToken::Word { .. } | AtomicToken::Dot { .. } => { let end = expand_syntax(&BareTailShape, token_nodes, context)?; - Ok(hir::Expression::bare(atom.tag.until_option(end))) + Ok(hir::Expression::bare(atom.span.until_option(end))) } - other => return other.tagged(atom.tag).into_hir(context, "expression"), + other => return other.spanned(atom.span).into_hir(context, "expression"), } } } @@ -158,7 +161,7 @@ impl FallibleColorSyntax for AnyExpressionStartShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let atom = token_nodes.spanned(|token_nodes| { expand_atom( @@ -170,15 +173,15 @@ impl FallibleColorSyntax for AnyExpressionStartShape { }); let atom = match atom { - Tagged { + Spanned { item: Err(_err), - tag, + span, } => { - shapes.push(FlatShape::Error.tagged(tag)); + shapes.push(FlatShape::Error.spanned(span)); return Ok(()); } - Tagged { + Spanned { item: Ok(value), .. } => value, }; @@ -186,18 +189,18 @@ impl FallibleColorSyntax for AnyExpressionStartShape { match atom.item { AtomicToken::Size { number, unit } => shapes.push( FlatShape::Size { - number: number.tag, - unit: unit.tag, + number: number.span.into(), + unit: unit.span.into(), } - .tagged(atom.tag), + .spanned(atom.span), ), - AtomicToken::SquareDelimited { nodes, tags } => { - color_delimited_square(tags, &nodes, atom.tag, context, shapes) + AtomicToken::SquareDelimited { nodes, spans } => { + color_delimited_square(spans, &nodes, atom.span.into(), context, shapes) } AtomicToken::Word { .. } | AtomicToken::Dot { .. } => { - shapes.push(FlatShape::Word.tagged(atom.tag)); + shapes.push(FlatShape::Word.spanned(atom.span)); } _ => atom.color_tokens(shapes), @@ -219,7 +222,7 @@ impl FallibleColorSyntax for BareTailShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let len = shapes.len(); @@ -267,19 +270,19 @@ impl FallibleColorSyntax for BareTailShape { } impl ExpandSyntax for BareTailShape { - type Output = Option; + type Output = Option; fn expand_syntax<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - ) -> Result, ShellError> { - let mut end: Option = None; + ) -> Result, ShellError> { + let mut end: Option = None; loop { match expand_syntax(&BareShape, token_nodes, context) { Ok(bare) => { - end = Some(bare.tag); + end = Some(bare.span); continue; } diff --git a/src/parser/hir/syntax_shape/expression/atom.rs b/src/parser/hir/syntax_shape/expression/atom.rs index 83306da741..bb1b8065ec 100644 --- a/src/parser/hir/syntax_shape/expression/atom.rs +++ b/src/parser/hir/syntax_shape/expression/atom.rs @@ -9,82 +9,83 @@ use crate::parser::{ DelimitedNode, Delimiter, FlatShape, RawToken, TokenNode, Unit, }; use crate::prelude::*; +use crate::{Span, Spanned}; #[derive(Debug)] pub enum AtomicToken<'tokens> { Eof { - tag: Tag, + span: Span, }, Error { - error: Tagged, + error: Spanned, }, Number { number: RawNumber, }, Size { - number: Tagged, - unit: Tagged, + number: Spanned, + unit: Spanned, }, String { - body: Tag, + body: Span, }, ItVariable { - name: Tag, + name: Span, }, Variable { - name: Tag, + name: Span, }, ExternalCommand { - command: Tag, + command: Span, }, ExternalWord { - text: Tag, + text: Span, }, GlobPattern { - pattern: Tag, + pattern: Span, }, FilePath { - path: Tag, + path: Span, }, Word { - text: Tag, + text: Span, }, SquareDelimited { - tags: (Tag, Tag), + spans: (Span, Span), nodes: &'tokens Vec, }, ParenDelimited { - tags: (Tag, Tag), + span: (Span, Span), nodes: &'tokens Vec, }, BraceDelimited { - tags: (Tag, Tag), + spans: (Span, Span), nodes: &'tokens Vec, }, Pipeline { - pipe: Option, - elements: Tagged<&'tokens Vec>, + pipe: Option, + elements: Spanned<&'tokens Vec>, }, ShorthandFlag { - name: Tag, + name: Span, }, LonghandFlag { - name: Tag, + name: Span, }, Dot { - text: Tag, + text: Span, }, Operator { - text: Tag, + text: Span, }, Whitespace { - text: Tag, + text: Span, }, } -pub type TaggedAtomicToken<'tokens> = Tagged>; +pub type SpannedAtomicToken<'tokens> = Spanned>; -impl<'tokens> TaggedAtomicToken<'tokens> { +impl<'tokens> SpannedAtomicToken<'tokens> { pub fn into_hir( &self, context: &ExpandContext, @@ -94,55 +95,55 @@ impl<'tokens> TaggedAtomicToken<'tokens> { AtomicToken::Eof { .. } => { return Err(ShellError::type_error( expected, - "eof atomic token".tagged(self.tag), + "eof atomic token".tagged(self.span), )) } AtomicToken::Error { .. } => { return Err(ShellError::type_error( expected, - "eof atomic token".tagged(self.tag), + "eof atomic token".tagged(self.span), )) } AtomicToken::Operator { .. } => { return Err(ShellError::type_error( expected, - "operator".tagged(self.tag), + "operator".tagged(self.span), )) } AtomicToken::ShorthandFlag { .. } => { return Err(ShellError::type_error( expected, - "shorthand flag".tagged(self.tag), + "shorthand flag".tagged(self.span), )) } AtomicToken::LonghandFlag { .. } => { - return Err(ShellError::type_error(expected, "flag".tagged(self.tag))) + return Err(ShellError::type_error(expected, "flag".tagged(self.span))) } AtomicToken::Whitespace { .. } => { return Err(ShellError::unimplemented("whitespace in AtomicToken")) } AtomicToken::Dot { .. } => { - return Err(ShellError::type_error(expected, "dot".tagged(self.tag))) + return Err(ShellError::type_error(expected, "dot".tagged(self.span))) } AtomicToken::Number { number } => { - Expression::number(number.to_number(context.source), self.tag) + Expression::number(number.to_number(context.source), self.span) } AtomicToken::FilePath { path } => Expression::file_path( expand_file_path(path.slice(context.source), context), - self.tag, + self.span, ), AtomicToken::Size { number, unit } => { - Expression::size(number.to_number(context.source), **unit, self.tag) + Expression::size(number.to_number(context.source), **unit, self.span) } - AtomicToken::String { body } => Expression::string(body, self.tag), - AtomicToken::ItVariable { name } => Expression::it_variable(name, self.tag), - AtomicToken::Variable { name } => Expression::variable(name, self.tag), + AtomicToken::String { body } => Expression::string(*body, self.span), + AtomicToken::ItVariable { name } => Expression::it_variable(*name, self.span), + AtomicToken::Variable { name } => Expression::variable(*name, self.span), AtomicToken::ExternalCommand { command } => { - Expression::external_command(command, self.tag) + Expression::external_command(*command, self.span) } - AtomicToken::ExternalWord { text } => Expression::string(text, self.tag), - AtomicToken::GlobPattern { pattern } => Expression::pattern(pattern), - AtomicToken::Word { text } => Expression::string(text, text), + AtomicToken::ExternalWord { text } => Expression::string(*text, self.span), + AtomicToken::GlobPattern { pattern } => Expression::pattern(*pattern), + AtomicToken::Word { text } => Expression::string(*text, *text), AtomicToken::SquareDelimited { .. } => unimplemented!("into_hir"), AtomicToken::ParenDelimited { .. } => unimplemented!("into_hir"), AtomicToken::BraceDelimited { .. } => unimplemented!("into_hir"), @@ -150,6 +151,33 @@ impl<'tokens> TaggedAtomicToken<'tokens> { }) } + pub fn spanned_type_name(&self) -> Spanned<&'static str> { + match &self.item { + AtomicToken::Eof { .. } => "eof", + AtomicToken::Error { .. } => "error", + AtomicToken::Operator { .. } => "operator", + AtomicToken::ShorthandFlag { .. } => "shorthand flag", + AtomicToken::LonghandFlag { .. } => "flag", + AtomicToken::Whitespace { .. } => "whitespace", + AtomicToken::Dot { .. } => "dot", + AtomicToken::Number { .. } => "number", + AtomicToken::FilePath { .. } => "file path", + AtomicToken::Size { .. } => "size", + AtomicToken::String { .. } => "string", + AtomicToken::ItVariable { .. } => "$it", + AtomicToken::Variable { .. } => "variable", + AtomicToken::ExternalCommand { .. } => "external command", + AtomicToken::ExternalWord { .. } => "external word", + AtomicToken::GlobPattern { .. } => "file pattern", + AtomicToken::Word { .. } => "word", + AtomicToken::SquareDelimited { .. } => "array literal", + AtomicToken::ParenDelimited { .. } => "parenthesized expression", + AtomicToken::BraceDelimited { .. } => "block", + AtomicToken::Pipeline { .. } => "pipeline", + } + .spanned(self.span) + } + pub fn tagged_type_name(&self) -> Tagged<&'static str> { match &self.item { AtomicToken::Eof { .. } => "eof", @@ -174,64 +202,64 @@ impl<'tokens> TaggedAtomicToken<'tokens> { AtomicToken::BraceDelimited { .. } => "block", AtomicToken::Pipeline { .. } => "pipeline", } - .tagged(self.tag) + .tagged(self.span) } - pub(crate) fn color_tokens(&self, shapes: &mut Vec>) { + pub(crate) fn color_tokens(&self, shapes: &mut Vec>) { match &self.item { AtomicToken::Eof { .. } => {} - AtomicToken::Error { .. } => return shapes.push(FlatShape::Error.tagged(self.tag)), + AtomicToken::Error { .. } => return shapes.push(FlatShape::Error.spanned(self.span)), AtomicToken::Operator { .. } => { - return shapes.push(FlatShape::Operator.tagged(self.tag)); + return shapes.push(FlatShape::Operator.spanned(self.span)); } AtomicToken::ShorthandFlag { .. } => { - return shapes.push(FlatShape::ShorthandFlag.tagged(self.tag)); + return shapes.push(FlatShape::ShorthandFlag.spanned(self.span)); } AtomicToken::LonghandFlag { .. } => { - return shapes.push(FlatShape::Flag.tagged(self.tag)); + return shapes.push(FlatShape::Flag.spanned(self.span)); } AtomicToken::Whitespace { .. } => { - return shapes.push(FlatShape::Whitespace.tagged(self.tag)); + return shapes.push(FlatShape::Whitespace.spanned(self.span)); } - AtomicToken::FilePath { .. } => return shapes.push(FlatShape::Path.tagged(self.tag)), - AtomicToken::Dot { .. } => return shapes.push(FlatShape::Dot.tagged(self.tag)), + AtomicToken::FilePath { .. } => return shapes.push(FlatShape::Path.spanned(self.span)), + AtomicToken::Dot { .. } => return shapes.push(FlatShape::Dot.spanned(self.span)), AtomicToken::Number { number: RawNumber::Decimal(_), } => { - return shapes.push(FlatShape::Decimal.tagged(self.tag)); + return shapes.push(FlatShape::Decimal.spanned(self.span)); } AtomicToken::Number { number: RawNumber::Int(_), } => { - return shapes.push(FlatShape::Int.tagged(self.tag)); + return shapes.push(FlatShape::Int.spanned(self.span)); } AtomicToken::Size { number, unit } => { return shapes.push( FlatShape::Size { - number: number.tag, - unit: unit.tag, + number: number.span, + unit: unit.span, } - .tagged(self.tag), + .spanned(self.span), ); } - AtomicToken::String { .. } => return shapes.push(FlatShape::String.tagged(self.tag)), + AtomicToken::String { .. } => return shapes.push(FlatShape::String.spanned(self.span)), AtomicToken::ItVariable { .. } => { - return shapes.push(FlatShape::ItVariable.tagged(self.tag)) + return shapes.push(FlatShape::ItVariable.spanned(self.span)) } AtomicToken::Variable { .. } => { - return shapes.push(FlatShape::Variable.tagged(self.tag)) + return shapes.push(FlatShape::Variable.spanned(self.span)) } AtomicToken::ExternalCommand { .. } => { - return shapes.push(FlatShape::ExternalCommand.tagged(self.tag)); + return shapes.push(FlatShape::ExternalCommand.spanned(self.span)); } AtomicToken::ExternalWord { .. } => { - return shapes.push(FlatShape::ExternalWord.tagged(self.tag)) + return shapes.push(FlatShape::ExternalWord.spanned(self.span)) } AtomicToken::GlobPattern { .. } => { - return shapes.push(FlatShape::GlobPattern.tagged(self.tag)) + return shapes.push(FlatShape::GlobPattern.spanned(self.span)) } - AtomicToken::Word { .. } => return shapes.push(FlatShape::Word.tagged(self.tag)), - _ => return shapes.push(FlatShape::Error.tagged(self.tag)), + AtomicToken::Word { .. } => return shapes.push(FlatShape::Word.spanned(self.span)), + _ => return shapes.push(FlatShape::Error.spanned(self.span)), } } } @@ -350,14 +378,14 @@ pub fn expand_atom<'me, 'content>( expected: &'static str, context: &ExpandContext, rule: ExpansionRule, -) -> Result, ShellError> { +) -> Result, ShellError> { if token_nodes.at_end() { match rule.allow_eof { true => { return Ok(AtomicToken::Eof { - tag: Tag::unknown(), + span: Span::unknown(), } - .tagged_unknown()) + .spanned(Span::unknown())) } false => return Err(ShellError::unexpected_eof("anything", Tag::unknown())), } @@ -376,10 +404,10 @@ pub fn expand_atom<'me, 'content>( Err(_) => {} // But if it was a valid unit, we're done here - Ok(Tagged { + Ok(Spanned { item: (number, unit), - tag, - }) => return Ok(AtomicToken::Size { number, unit }.tagged(tag)), + span, + }) => return Ok(AtomicToken::Size { number, unit }.spanned(span)), }, } @@ -388,7 +416,7 @@ pub fn expand_atom<'me, 'content>( match expand_syntax(&BarePathShape, token_nodes, context) { // If we didn't find a bare path Err(_) => {} - Ok(tag) => { + Ok(span) => { let next = token_nodes.peek_any(); match next.node { @@ -397,7 +425,7 @@ pub fn expand_atom<'me, 'content>( // word, and we should try to parse it as a glob next } - _ => return Ok(AtomicToken::Word { text: tag }.tagged(tag)), + _ => return Ok(AtomicToken::Word { text: span }.spanned(span)), } } } @@ -407,7 +435,7 @@ pub fn expand_atom<'me, 'content>( match expand_syntax(&BarePatternShape, token_nodes, context) { // If we didn't find a bare path Err(_) => {} - Ok(tag) => return Ok(AtomicToken::GlobPattern { pattern: tag }.tagged(tag)), + Ok(span) => return Ok(AtomicToken::GlobPattern { pattern: span }.spanned(span)), } // The next token corresponds to at most one atomic token @@ -427,80 +455,84 @@ pub fn expand_atom<'me, 'content>( return Ok(AtomicToken::Error { error: error.clone(), } - .tagged(error.tag)); + .spanned(error.span)); } // [ ... ] - TokenNode::Delimited(Tagged { + TokenNode::Delimited(Spanned { item: DelimitedNode { delimiter: Delimiter::Square, - tags, + spans, children, }, - tag, + span, }) => { peeked.commit(); + let span = *span; return Ok(AtomicToken::SquareDelimited { nodes: children, - tags: *tags, + spans: *spans, } - .tagged(tag)); + .spanned(span)); } - TokenNode::Flag(Tagged { + TokenNode::Flag(Spanned { item: Flag { kind: FlagKind::Shorthand, name, }, - tag, + span, }) => { peeked.commit(); - return Ok(AtomicToken::ShorthandFlag { name: *name }.tagged(tag)); + return Ok(AtomicToken::ShorthandFlag { name: *name }.spanned(*span)); } - TokenNode::Flag(Tagged { + TokenNode::Flag(Spanned { item: Flag { kind: FlagKind::Longhand, name, }, - tag, + span, }) => { peeked.commit(); - return Ok(AtomicToken::ShorthandFlag { name: *name }.tagged(tag)); + return Ok(AtomicToken::ShorthandFlag { name: *name }.spanned(*span)); } // If we see whitespace, process the whitespace according to the whitespace // handling rules - TokenNode::Whitespace(tag) => match rule.whitespace { + TokenNode::Whitespace(span) => match rule.whitespace { // if whitespace is allowed, return a whitespace token WhitespaceHandling::AllowWhitespace => { peeked.commit(); - return Ok(AtomicToken::Whitespace { text: *tag }.tagged(tag)); + return Ok(AtomicToken::Whitespace { text: *span }.spanned(*span)); } // if whitespace is disallowed, return an error WhitespaceHandling::RejectWhitespace => { - return Err(ShellError::syntax_error( - "Unexpected whitespace".tagged(tag), - )) + return Err(ShellError::syntax_error("Unexpected whitespace".tagged( + Tag { + span: *span, + anchor: None, + }, + ))) } }, other => { - let tag = peeked.node.tag(); + let span = peeked.node.span(); peeked.commit(); return Ok(AtomicToken::Error { - error: ShellError::type_error("token", other.tagged_type_name()).tagged(tag), + error: ShellError::type_error("token", other.tagged_type_name()).spanned(span), } - .tagged(tag)); + .spanned(span)); } } - parse_single_node(token_nodes, expected, |token, token_tag, err| { + parse_single_node(token_nodes, expected, |token, token_span, err| { Ok(match token { // First, the error cases. Each error case corresponds to a expansion rule // flag that can be used to allow the case @@ -511,31 +543,38 @@ pub fn expand_atom<'me, 'content>( RawToken::ExternalCommand(_) if !rule.allow_external_command => { return Err(ShellError::type_error( expected, - token.type_name().tagged(token_tag), + token.type_name().tagged(Tag { + span: token_span, + anchor: None, + }), )) } // rule.allow_external_word RawToken::ExternalWord if !rule.allow_external_word => { - return Err(ShellError::invalid_external_word(token_tag)) + return Err(ShellError::invalid_external_word(Tag { + span: token_span, + anchor: None, + })) } - RawToken::Number(number) => AtomicToken::Number { number }.tagged(token_tag), - RawToken::Operator(_) => AtomicToken::Operator { text: token_tag }.tagged(token_tag), - RawToken::String(body) => AtomicToken::String { body }.tagged(token_tag), + RawToken::Number(number) => AtomicToken::Number { number }.spanned(token_span), + RawToken::Operator(_) => AtomicToken::Operator { text: token_span }.spanned(token_span), + RawToken::String(body) => AtomicToken::String { body }.spanned(token_span), RawToken::Variable(name) if name.slice(context.source) == "it" => { - AtomicToken::ItVariable { name }.tagged(token_tag) + AtomicToken::ItVariable { name }.spanned(token_span) } - RawToken::Variable(name) => AtomicToken::Variable { name }.tagged(token_tag), + RawToken::Variable(name) => AtomicToken::Variable { name }.spanned(token_span), RawToken::ExternalCommand(command) => { - AtomicToken::ExternalCommand { command }.tagged(token_tag) + AtomicToken::ExternalCommand { command }.spanned(token_span) } RawToken::ExternalWord => { - AtomicToken::ExternalWord { text: token_tag }.tagged(token_tag) + AtomicToken::ExternalWord { text: token_span }.spanned(token_span) } - RawToken::GlobPattern => { - AtomicToken::GlobPattern { pattern: token_tag }.tagged(token_tag) + RawToken::GlobPattern => AtomicToken::GlobPattern { + pattern: token_span, } - RawToken::Bare => AtomicToken::Word { text: token_tag }.tagged(token_tag), + .spanned(token_span), + RawToken::Bare => AtomicToken::Word { text: token_span }.spanned(token_span), }) }) } diff --git a/src/parser/hir/syntax_shape/expression/delimited.rs b/src/parser/hir/syntax_shape/expression/delimited.rs index 001e3812f4..b52340ab8f 100644 --- a/src/parser/hir/syntax_shape/expression/delimited.rs +++ b/src/parser/hir/syntax_shape/expression/delimited.rs @@ -6,27 +6,27 @@ use crate::prelude::*; pub fn expand_delimited_square( children: &Vec, - tag: Tag, + span: Span, context: &ExpandContext, ) -> Result { - let mut tokens = TokensIterator::new(&children, tag, false); + let mut tokens = TokensIterator::new(&children, span, false); let list = expand_syntax(&ExpressionListShape, &mut tokens, context); - Ok(hir::Expression::list(list?, tag)) + Ok(hir::Expression::list(list?, Tag { span, anchor: None })) } pub fn color_delimited_square( - (open, close): (Tag, Tag), + (open, close): (Span, Span), children: &Vec, - tag: Tag, + span: Span, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) { - shapes.push(FlatShape::OpenDelimiter(Delimiter::Square).tagged(open)); - let mut tokens = TokensIterator::new(&children, tag, false); + shapes.push(FlatShape::OpenDelimiter(Delimiter::Square).spanned(open)); + let mut tokens = TokensIterator::new(&children, span, false); let _list = color_syntax(&ExpressionListShape, &mut tokens, context, shapes); - shapes.push(FlatShape::CloseDelimiter(Delimiter::Square).tagged(close)); + shapes.push(FlatShape::CloseDelimiter(Delimiter::Square).spanned(close)); } #[derive(Debug, Copy, Clone)] @@ -34,16 +34,16 @@ pub struct DelimitedShape; impl ColorSyntax for DelimitedShape { type Info = (); - type Input = (Delimiter, Tag, Tag); + type Input = (Delimiter, Span, Span); fn color_syntax<'a, 'b>( &self, - (delimiter, open, close): &(Delimiter, Tag, Tag), + (delimiter, open, close): &(Delimiter, Span, Span), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Self::Info { - shapes.push(FlatShape::OpenDelimiter(*delimiter).tagged(open)); + shapes.push(FlatShape::OpenDelimiter(*delimiter).spanned(*open)); color_syntax(&ExpressionListShape, token_nodes, context, shapes); - shapes.push(FlatShape::CloseDelimiter(*delimiter).tagged(close)); + shapes.push(FlatShape::CloseDelimiter(*delimiter).spanned(*close)); } } diff --git a/src/parser/hir/syntax_shape/expression/file_path.rs b/src/parser/hir/syntax_shape/expression/file_path.rs index e73dc8d647..ccb2f8f54b 100644 --- a/src/parser/hir/syntax_shape/expression/file_path.rs +++ b/src/parser/hir/syntax_shape/expression/file_path.rs @@ -17,7 +17,7 @@ impl FallibleColorSyntax for FilePathShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let atom = expand_atom( token_nodes, @@ -36,7 +36,7 @@ impl FallibleColorSyntax for FilePathShape { | AtomicToken::String { .. } | AtomicToken::Number { .. } | AtomicToken::Size { .. } => { - shapes.push(FlatShape::Path.tagged(atom.tag)); + shapes.push(FlatShape::Path.spanned(atom.span)); } _ => atom.color_tokens(shapes), @@ -57,12 +57,12 @@ impl ExpandExpression for FilePathShape { match atom.item { AtomicToken::Word { text: body } | AtomicToken::String { body } => { let path = expand_file_path(body.slice(context.source), context); - return Ok(hir::Expression::file_path(path, atom.tag)); + return Ok(hir::Expression::file_path(path, atom.span)); } AtomicToken::Number { .. } | AtomicToken::Size { .. } => { - let path = atom.tag.slice(context.source); - return Ok(hir::Expression::file_path(path, atom.tag)); + let path = atom.span.slice(context.source); + return Ok(hir::Expression::file_path(path, atom.span)); } _ => return atom.into_hir(context, "file path"), diff --git a/src/parser/hir/syntax_shape/expression/list.rs b/src/parser/hir/syntax_shape/expression/list.rs index 4109108a37..575ae9fcdd 100644 --- a/src/parser/hir/syntax_shape/expression/list.rs +++ b/src/parser/hir/syntax_shape/expression/list.rs @@ -9,7 +9,7 @@ use crate::parser::{ hir::TokensIterator, FlatShape, }; -use crate::Tagged; +use crate::Spanned; #[derive(Debug, Copy, Clone)] pub struct ExpressionListShape; @@ -60,7 +60,7 @@ impl ColorSyntax for ExpressionListShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) { // We encountered a parsing error and will continue with simpler coloring ("backoff // coloring mode") @@ -126,7 +126,7 @@ impl ColorSyntax for BackoffColoringMode { _input: &Self::Input, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Self::Info { loop { if token_nodes.at_end() { @@ -159,7 +159,7 @@ impl ColorSyntax for SimplestExpression { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) { let atom = expand_atom( token_nodes, diff --git a/src/parser/hir/syntax_shape/expression/number.rs b/src/parser/hir/syntax_shape/expression/number.rs index 8d3cb048c6..a4e2a93234 100644 --- a/src/parser/hir/syntax_shape/expression/number.rs +++ b/src/parser/hir/syntax_shape/expression/number.rs @@ -18,20 +18,27 @@ impl ExpandExpression for NumberShape { token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - parse_single_node(token_nodes, "Number", |token, token_tag, err| { + parse_single_node(token_nodes, "Number", |token, token_span, err| { Ok(match token { RawToken::GlobPattern | RawToken::Operator(..) => return Err(err.error()), RawToken::Variable(tag) if tag.slice(context.source) == "it" => { - hir::Expression::it_variable(tag, token_tag) + hir::Expression::it_variable(tag, token_span) } - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), - RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), + RawToken::ExternalCommand(tag) => { + hir::Expression::external_command(tag, token_span) + } + RawToken::ExternalWord => { + return Err(ShellError::invalid_external_word(Tag { + span: token_span, + anchor: None, + })) + } + RawToken::Variable(tag) => hir::Expression::variable(tag, token_span), RawToken::Number(number) => { - hir::Expression::number(number.to_number(context.source), token_tag) + hir::Expression::number(number.to_number(context.source), token_span) } - RawToken::Bare => hir::Expression::bare(token_tag), - RawToken::String(tag) => hir::Expression::string(tag, token_tag), + RawToken::Bare => hir::Expression::bare(token_span), + RawToken::String(tag) => hir::Expression::string(tag, token_span), }) }) } @@ -46,18 +53,18 @@ impl FallibleColorSyntax for NumberShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let atom = token_nodes.spanned(|token_nodes| { expand_atom(token_nodes, "number", context, ExpansionRule::permissive()) }); let atom = match atom { - Tagged { item: Err(_), tag } => { - shapes.push(FlatShape::Error.tagged(tag)); + Spanned { item: Err(_), span } => { + shapes.push(FlatShape::Error.spanned(span)); return Ok(()); } - Tagged { item: Ok(atom), .. } => atom, + Spanned { item: Ok(atom), .. } => atom, }; atom.color_tokens(shapes); @@ -75,21 +82,25 @@ impl ExpandExpression for IntShape { token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - parse_single_node(token_nodes, "Integer", |token, token_tag, err| { + parse_single_node(token_nodes, "Integer", |token, token_span, err| { Ok(match token { RawToken::GlobPattern | RawToken::Operator(..) => return Err(err.error()), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), - RawToken::Variable(tag) if tag.slice(context.source) == "it" => { - hir::Expression::it_variable(tag, token_tag) + RawToken::ExternalWord => { + return Err(ShellError::invalid_external_word(token_span)) } - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), - RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), + RawToken::Variable(span) if span.slice(context.source) == "it" => { + hir::Expression::it_variable(span, token_span) + } + RawToken::ExternalCommand(span) => { + hir::Expression::external_command(span, token_span) + } + RawToken::Variable(span) => hir::Expression::variable(span, token_span), RawToken::Number(number @ RawNumber::Int(_)) => { - hir::Expression::number(number.to_number(context.source), token_tag) + hir::Expression::number(number.to_number(context.source), token_span) } RawToken::Number(_) => return Err(err.error()), - RawToken::Bare => hir::Expression::bare(token_tag), - RawToken::String(tag) => hir::Expression::string(tag, token_tag), + RawToken::Bare => hir::Expression::bare(token_span), + RawToken::String(span) => hir::Expression::string(span, token_span), }) }) } @@ -104,18 +115,18 @@ impl FallibleColorSyntax for IntShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let atom = token_nodes.spanned(|token_nodes| { expand_atom(token_nodes, "integer", context, ExpansionRule::permissive()) }); let atom = match atom { - Tagged { item: Err(_), tag } => { - shapes.push(FlatShape::Error.tagged(tag)); + Spanned { item: Err(_), span } => { + shapes.push(FlatShape::Error.spanned(span)); return Ok(()); } - Tagged { item: Ok(atom), .. } => atom, + Spanned { item: Ok(atom), .. } => atom, }; atom.color_tokens(shapes); diff --git a/src/parser/hir/syntax_shape/expression/pattern.rs b/src/parser/hir/syntax_shape/expression/pattern.rs index 5c863de728..0a11552d5e 100644 --- a/src/parser/hir/syntax_shape/expression/pattern.rs +++ b/src/parser/hir/syntax_shape/expression/pattern.rs @@ -18,14 +18,14 @@ impl FallibleColorSyntax for PatternShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { token_nodes.atomic(|token_nodes| { let atom = expand_atom(token_nodes, "pattern", context, ExpansionRule::permissive())?; match &atom.item { AtomicToken::GlobPattern { .. } | AtomicToken::Word { .. } => { - shapes.push(FlatShape::GlobPattern.tagged(atom.tag)); + shapes.push(FlatShape::GlobPattern.spanned(atom.span)); Ok(()) } @@ -85,23 +85,23 @@ impl ExpandExpression for PatternShape { pub struct BarePatternShape; impl ExpandSyntax for BarePatternShape { - type Output = Tag; + type Output = Span; fn expand_syntax<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - ) -> Result { + ) -> Result { expand_bare(token_nodes, context, |token| match token { - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Bare, .. }) - | TokenNode::Token(Tagged { + | TokenNode::Token(Spanned { item: RawToken::Operator(Operator::Dot), .. }) - | TokenNode::Token(Tagged { + | TokenNode::Token(Spanned { item: RawToken::GlobPattern, .. }) => true, diff --git a/src/parser/hir/syntax_shape/expression/string.rs b/src/parser/hir/syntax_shape/expression/string.rs index 6f33ae5eb1..0dabd70a85 100644 --- a/src/parser/hir/syntax_shape/expression/string.rs +++ b/src/parser/hir/syntax_shape/expression/string.rs @@ -18,7 +18,7 @@ impl FallibleColorSyntax for StringShape { input: &FlatShape, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let atom = expand_atom(token_nodes, "string", context, ExpansionRule::permissive()); @@ -28,10 +28,10 @@ impl FallibleColorSyntax for StringShape { }; match atom { - Tagged { + Spanned { item: AtomicToken::String { .. }, - tag, - } => shapes.push((*input).tagged(tag)), + span, + } => shapes.push((*input).spanned(span)), other => other.color_tokens(shapes), } @@ -45,26 +45,30 @@ impl ExpandExpression for StringShape { token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - parse_single_node(token_nodes, "String", |token, token_tag, _| { + parse_single_node(token_nodes, "String", |token, token_span, _| { Ok(match token { RawToken::GlobPattern => { return Err(ShellError::type_error( "String", - "glob pattern".tagged(token_tag), + "glob pattern".tagged(token_span), )) } RawToken::Operator(..) => { return Err(ShellError::type_error( "String", - "operator".tagged(token_tag), + "operator".tagged(token_span), )) } - RawToken::Variable(tag) => expand_variable(tag, token_tag, &context.source), - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), - RawToken::Number(_) => hir::Expression::bare(token_tag), - RawToken::Bare => hir::Expression::bare(token_tag), - RawToken::String(tag) => hir::Expression::string(tag, token_tag), + RawToken::Variable(span) => expand_variable(span, token_span, &context.source), + RawToken::ExternalCommand(span) => { + hir::Expression::external_command(span, token_span) + } + RawToken::ExternalWord => { + return Err(ShellError::invalid_external_word(token_span)) + } + RawToken::Number(_) => 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/hir/syntax_shape/expression/unit.rs b/src/parser/hir/syntax_shape/expression/unit.rs index 65fca1a468..03602f1088 100644 --- a/src/parser/hir/syntax_shape/expression/unit.rs +++ b/src/parser/hir/syntax_shape/expression/unit.rs @@ -14,24 +14,24 @@ use nom::IResult; pub struct UnitShape; impl ExpandSyntax for UnitShape { - type Output = Tagged<(Tagged, Tagged)>; + type Output = Spanned<(Spanned, Spanned)>; fn expand_syntax<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - ) -> Result, Tagged)>, ShellError> { + ) -> Result, Spanned)>, ShellError> { let peeked = token_nodes.peek_any().not_eof("unit")?; - let tag = match peeked.node { - TokenNode::Token(Tagged { + let span = match peeked.node { + TokenNode::Token(Spanned { item: RawToken::Bare, - tag, - }) => tag, + span, + }) => span, _ => return Err(peeked.type_error("unit")), }; - let unit = unit_size(tag.slice(context.source), *tag); + let unit = unit_size(span.slice(context.source), *span); let (_, (number, unit)) = match unit { Err(_) => { @@ -44,11 +44,11 @@ impl ExpandSyntax for UnitShape { }; peeked.commit(); - Ok((number, unit).tagged(tag)) + Ok((number, unit).spanned(*span)) } } -fn unit_size(input: &str, bare_tag: Tag) -> IResult<&str, (Tagged, Tagged)> { +fn unit_size(input: &str, bare_span: Span) -> IResult<&str, (Spanned, Spanned)> { let (input, digits) = digit1(input)?; let (input, dot) = opt(tag("."))(input)?; @@ -58,20 +58,18 @@ fn unit_size(input: &str, bare_tag: Tag) -> IResult<&str, (Tagged, Ta let (input, rest) = digit1(input)?; ( input, - RawNumber::decimal(( - bare_tag.span.start(), - bare_tag.span.start() + digits.len() + dot.len() + rest.len(), - bare_tag.anchor, + RawNumber::decimal(Span::new( + bare_span.start(), + bare_span.start() + digits.len() + dot.len() + rest.len(), )), ) } None => ( input, - RawNumber::int(( - bare_tag.span.start(), - bare_tag.span.start() + digits.len(), - bare_tag.anchor, + RawNumber::int(Span::new( + bare_span.start(), + bare_span.start() + digits.len(), )), ), }; @@ -85,12 +83,10 @@ fn unit_size(input: &str, bare_tag: Tag) -> IResult<&str, (Tagged, Ta value(Unit::MB, alt((tag("PB"), tag("pb"), tag("Pb")))), )))(input)?; - let start_span = number.tag.span.end(); + let start_span = number.span.end(); - let unit_tag = Tag::new( - bare_tag.anchor, - Span::from((start_span, bare_tag.span.end())), - ); - - Ok((input, (number, unit.tagged(unit_tag)))) + Ok(( + input, + (number, unit.spanned(Span::new(start_span, bare_span.end()))), + )) } diff --git a/src/parser/hir/syntax_shape/expression/variable_path.rs b/src/parser/hir/syntax_shape/expression/variable_path.rs index a7f17a5971..04b511d89a 100644 --- a/src/parser/hir/syntax_shape/expression/variable_path.rs +++ b/src/parser/hir/syntax_shape/expression/variable_path.rs @@ -23,9 +23,9 @@ impl ExpandExpression for VariablePathShape { // 2. consume the next token as a member and push it onto tail let head = expand_expr(&VariableShape, token_nodes, context)?; - let start = head.tag(); + let start = head.span; let mut end = start; - let mut tail: Vec> = vec![]; + let mut tail: Vec> = vec![]; loop { match DotShape.skip(token_nodes, context) { @@ -34,9 +34,9 @@ impl ExpandExpression for VariablePathShape { } let syntax = expand_syntax(&MemberShape, token_nodes, context)?; - let member = syntax.to_tagged_string(context.source); + let member = syntax.to_spanned_string(context.source); - end = member.tag(); + end = member.span; tail.push(member); } @@ -53,7 +53,7 @@ impl FallibleColorSyntax for VariablePathShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { token_nodes.atomic(|token_nodes| { // If the head of the token stream is not a variable, fail @@ -97,7 +97,7 @@ impl FallibleColorSyntax for PathTailShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { token_nodes.atomic(|token_nodes| loop { let result = color_fallible_syntax_with( @@ -120,13 +120,13 @@ impl FallibleColorSyntax for PathTailShape { } impl ExpandSyntax for PathTailShape { - type Output = (Vec>, Tag); + type Output = (Vec>, Span); fn expand_syntax<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result { - let mut end: Option = None; + let mut end: Option = None; let mut tail = vec![]; loop { @@ -136,17 +136,21 @@ impl ExpandSyntax for PathTailShape { } let syntax = expand_syntax(&MemberShape, token_nodes, context)?; - let member = syntax.to_tagged_string(context.source); - end = Some(member.tag()); + let member = syntax.to_spanned_string(context.source); + end = Some(member.span); tail.push(member); } match end { None => { - return Err(ShellError::type_error( - "path tail", - token_nodes.typed_tag_at_cursor(), - )) + return Err(ShellError::type_error("path tail", { + let typed_span = token_nodes.typed_span_at_cursor(); + + Tagged { + tag: typed_span.span.into(), + item: typed_span.item, + } + })) } Some(end) => Ok((tail, end)), @@ -156,8 +160,8 @@ impl ExpandSyntax for PathTailShape { #[derive(Debug)] pub enum ExpressionContinuation { - DotSuffix(Tag, Tagged), - InfixSuffix(Tagged, Expression), + DotSuffix(Span, Spanned), + InfixSuffix(Spanned, Expression), } /// An expression continuation @@ -179,7 +183,7 @@ impl ExpandSyntax for ExpressionContinuationShape { // If a `.` was matched, it's a `Path`, and we expect a `Member` next Ok(dot) => { let syntax = expand_syntax(&MemberShape, token_nodes, context)?; - let member = syntax.to_tagged_string(context.source); + let member = syntax.to_spanned_string(context.source); Ok(ExpressionContinuation::DotSuffix(dot, member)) } @@ -209,7 +213,7 @@ impl FallibleColorSyntax for ExpressionContinuationShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result { token_nodes.atomic(|token_nodes| { // Try to expand a `.` @@ -290,7 +294,7 @@ impl FallibleColorSyntax for VariableShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let atom = expand_atom( token_nodes, @@ -306,11 +310,11 @@ impl FallibleColorSyntax for VariableShape { match &atom.item { AtomicToken::Variable { .. } => { - shapes.push(FlatShape::Variable.tagged(atom.tag)); + shapes.push(FlatShape::Variable.spanned(atom.span)); Ok(()) } AtomicToken::ItVariable { .. } => { - shapes.push(FlatShape::ItVariable.tagged(atom.tag)); + shapes.push(FlatShape::ItVariable.spanned(atom.span)); Ok(()) } _ => Err(ShellError::type_error("variable", atom.tagged_type_name())), @@ -320,50 +324,53 @@ impl FallibleColorSyntax for VariableShape { #[derive(Debug, Clone, Copy)] pub enum Member { - String(/* outer */ Tag, /* inner */ Tag), - Bare(Tag), + String(/* outer */ Span, /* inner */ Span), + Bare(Span), } impl Member { pub(crate) fn to_expr(&self) -> hir::Expression { match self { - Member::String(outer, inner) => hir::Expression::string(inner, outer), - Member::Bare(tag) => hir::Expression::string(tag, tag), + Member::String(outer, inner) => hir::Expression::string(*inner, *outer), + Member::Bare(span) => hir::Expression::string(*span, *span), } } - pub(crate) fn tag(&self) -> Tag { + pub(crate) fn span(&self) -> Span { match self { Member::String(outer, _inner) => *outer, - Member::Bare(tag) => *tag, + Member::Bare(span) => *span, } } - pub(crate) fn to_tagged_string(&self, source: &str) -> Tagged { + pub(crate) fn to_spanned_string(&self, source: &str) -> Spanned { match self { - Member::String(outer, inner) => inner.string(source).tagged(outer), - Member::Bare(tag) => tag.tagged_string(source), + Member::String(outer, inner) => inner.string(source).spanned(*outer), + Member::Bare(span) => span.spanned_string(source), } } pub(crate) fn tagged_type_name(&self) -> Tagged<&'static str> { match self { Member::String(outer, _inner) => "string".tagged(outer), - Member::Bare(tag) => "word".tagged(tag), + Member::Bare(span) => "word".tagged(Tag { + span: *span, + anchor: None, + }), } } } enum ColumnPathState { Initial, - LeadingDot(Tag), - Dot(Tag, Vec, Tag), - Member(Tag, Vec), + LeadingDot(Span), + Dot(Span, Vec, Span), + Member(Span, Vec), Error(ShellError), } impl ColumnPathState { - pub fn dot(self, dot: Tag) -> ColumnPathState { + pub fn dot(self, dot: Span) -> ColumnPathState { match self { ColumnPathState::Initial => ColumnPathState::LeadingDot(dot), ColumnPathState::LeadingDot(_) => { @@ -379,13 +386,13 @@ impl ColumnPathState { pub fn member(self, member: Member) -> ColumnPathState { match self { - ColumnPathState::Initial => ColumnPathState::Member(member.tag(), vec![member]), + ColumnPathState::Initial => ColumnPathState::Member(member.span(), vec![member]), ColumnPathState::LeadingDot(tag) => { - ColumnPathState::Member(tag.until(member.tag()), vec![member]) + ColumnPathState::Member(tag.until(member.span()), vec![member]) } ColumnPathState::Dot(tag, mut tags, _) => { - ColumnPathState::Member(tag.until(member.tag()), { + ColumnPathState::Member(tag.until(member.span()), { tags.push(member); tags }) @@ -449,7 +456,7 @@ impl FallibleColorSyntax for ColumnPathShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { // If there's not even one member shape, fail color_fallible_syntax(&MemberShape, token_nodes, context, shapes)?; @@ -513,7 +520,7 @@ impl FallibleColorSyntax for MemberShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let bare = color_fallible_syntax_with( &BareShape, @@ -552,7 +559,7 @@ impl ExpandSyntax for MemberShape { let bare = BareShape.test(token_nodes, context); if let Some(peeked) = bare { let node = peeked.not_eof("column")?.commit(); - return Ok(Member::Bare(node.tag())); + return Ok(Member::Bare(node.span())); } let string = StringShape.test(token_nodes, context); @@ -583,14 +590,14 @@ impl FallibleColorSyntax for ColorableDotShape { input: &FlatShape, token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Result<(), ShellError> { let peeked = token_nodes.peek_any().not_eof("dot")?; match peeked.node { node if node.is_dot() => { peeked.commit(); - shapes.push((*input).tagged(node.tag())); + shapes.push((*input).spanned(node.span())); Ok(()) } @@ -612,20 +619,20 @@ impl SkipSyntax for DotShape { } impl ExpandSyntax for DotShape { - type Output = Tag; + type Output = Span; fn expand_syntax<'a, 'b>( &self, token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, ) -> Result { - parse_single_node(token_nodes, "dot", |token, token_tag, _| { + parse_single_node(token_nodes, "dot", |token, token_span, _| { Ok(match token { - RawToken::Operator(Operator::Dot) => token_tag, + RawToken::Operator(Operator::Dot) => token_span, _ => { return Err(ShellError::type_error( "dot", - token.type_name().tagged(token_tag), + token.type_name().tagged(token_span), )) } }) @@ -645,7 +652,7 @@ impl FallibleColorSyntax for InfixShape { _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - outer_shapes: &mut Vec>, + outer_shapes: &mut Vec>, ) -> Result<(), ShellError> { let checkpoint = token_nodes.checkpoint(); let mut shapes = vec![]; @@ -657,18 +664,18 @@ impl FallibleColorSyntax for InfixShape { parse_single_node( checkpoint.iterator, "infix operator", - |token, token_tag, _| { + |token, token_span, _| { match token { // If it's an operator (and not `.`), it's a match RawToken::Operator(operator) if operator != Operator::Dot => { - shapes.push(FlatShape::Operator.tagged(token_tag)); + shapes.push(FlatShape::Operator.spanned(token_span)); Ok(()) } // Otherwise, it's not a match _ => Err(ShellError::type_error( "infix operator", - token.type_name().tagged(token_tag), + token.type_name().tagged(token_span), )), } }, @@ -684,7 +691,7 @@ impl FallibleColorSyntax for InfixShape { } impl ExpandSyntax for InfixShape { - type Output = (Tag, Tagged, Tag); + type Output = (Span, Spanned, Span); fn expand_syntax<'a, 'b>( &self, @@ -700,18 +707,18 @@ impl ExpandSyntax for InfixShape { let operator = parse_single_node( checkpoint.iterator, "infix operator", - |token, token_tag, _| { + |token, token_span, _| { Ok(match token { // If it's an operator (and not `.`), it's a match RawToken::Operator(operator) if operator != Operator::Dot => { - operator.tagged(token_tag) + operator.spanned(token_span) } // Otherwise, it's not a match _ => { return Err(ShellError::type_error( "infix operator", - token.type_name().tagged(token_tag), + token.type_name().tagged(token_span), )) } }) diff --git a/src/parser/hir/syntax_shape/flat_shape.rs b/src/parser/hir/syntax_shape/flat_shape.rs index 48e867199e..b961d1f567 100644 --- a/src/parser/hir/syntax_shape/flat_shape.rs +++ b/src/parser/hir/syntax_shape/flat_shape.rs @@ -1,5 +1,5 @@ use crate::parser::{Delimiter, Flag, FlagKind, Operator, RawNumber, RawToken, TokenNode}; -use crate::{Tag, Tagged, TaggedItem, Text}; +use crate::{Span, Spanned, SpannedItem, Text}; #[derive(Debug, Copy, Clone)] pub enum FlatShape { @@ -25,32 +25,34 @@ pub enum FlatShape { Decimal, Whitespace, Error, - Size { number: Tag, unit: Tag }, + Size { number: Span, unit: Span }, } impl FlatShape { - pub fn from(token: &TokenNode, source: &Text, shapes: &mut Vec>) -> () { + pub fn from(token: &TokenNode, source: &Text, shapes: &mut Vec>) -> () { match token { TokenNode::Token(token) => match token.item { RawToken::Number(RawNumber::Int(_)) => { - shapes.push(FlatShape::Int.tagged(token.tag)) + shapes.push(FlatShape::Int.spanned(token.span)) } RawToken::Number(RawNumber::Decimal(_)) => { - shapes.push(FlatShape::Decimal.tagged(token.tag)) + shapes.push(FlatShape::Decimal.spanned(token.span)) } - RawToken::Operator(Operator::Dot) => shapes.push(FlatShape::Dot.tagged(token.tag)), - RawToken::Operator(_) => shapes.push(FlatShape::Operator.tagged(token.tag)), - RawToken::String(_) => shapes.push(FlatShape::String.tagged(token.tag)), + RawToken::Operator(Operator::Dot) => { + shapes.push(FlatShape::Dot.spanned(token.span)) + } + RawToken::Operator(_) => shapes.push(FlatShape::Operator.spanned(token.span)), + RawToken::String(_) => shapes.push(FlatShape::String.spanned(token.span)), RawToken::Variable(v) if v.slice(source) == "it" => { - shapes.push(FlatShape::ItVariable.tagged(token.tag)) + shapes.push(FlatShape::ItVariable.spanned(token.span)) } - RawToken::Variable(_) => shapes.push(FlatShape::Variable.tagged(token.tag)), + RawToken::Variable(_) => shapes.push(FlatShape::Variable.spanned(token.span)), RawToken::ExternalCommand(_) => { - shapes.push(FlatShape::ExternalCommand.tagged(token.tag)) + shapes.push(FlatShape::ExternalCommand.spanned(token.span)) } - RawToken::ExternalWord => shapes.push(FlatShape::ExternalWord.tagged(token.tag)), - RawToken::GlobPattern => shapes.push(FlatShape::GlobPattern.tagged(token.tag)), - RawToken::Bare => shapes.push(FlatShape::Word.tagged(token.tag)), + RawToken::ExternalWord => shapes.push(FlatShape::ExternalWord.spanned(token.span)), + RawToken::GlobPattern => shapes.push(FlatShape::GlobPattern.spanned(token.span)), + RawToken::Bare => shapes.push(FlatShape::Word.spanned(token.span)), }, TokenNode::Call(_) => unimplemented!(), TokenNode::Nodes(nodes) => { @@ -59,37 +61,37 @@ impl FlatShape { } } TokenNode::Delimited(v) => { - shapes.push(FlatShape::OpenDelimiter(v.item.delimiter).tagged(v.item.tags.0)); + shapes.push(FlatShape::OpenDelimiter(v.item.delimiter).spanned(v.item.spans.0)); for token in &v.item.children { FlatShape::from(token, source, shapes); } - shapes.push(FlatShape::CloseDelimiter(v.item.delimiter).tagged(v.item.tags.1)); + shapes.push(FlatShape::CloseDelimiter(v.item.delimiter).spanned(v.item.spans.1)); } TokenNode::Pipeline(pipeline) => { for part in &pipeline.parts { if let Some(_) = part.pipe { - shapes.push(FlatShape::Pipe.tagged(part.tag)); + shapes.push(FlatShape::Pipe.spanned(part.span)); } } } - TokenNode::Flag(Tagged { + TokenNode::Flag(Spanned { item: Flag { kind: FlagKind::Longhand, .. }, - tag, - }) => shapes.push(FlatShape::Flag.tagged(tag)), - TokenNode::Flag(Tagged { + span, + }) => shapes.push(FlatShape::Flag.spanned(*span)), + TokenNode::Flag(Spanned { item: Flag { kind: FlagKind::Shorthand, .. }, - tag, - }) => shapes.push(FlatShape::ShorthandFlag.tagged(tag)), - TokenNode::Whitespace(_) => shapes.push(FlatShape::Whitespace.tagged(token.tag())), - TokenNode::Error(v) => shapes.push(FlatShape::Error.tagged(v.tag)), + span, + }) => shapes.push(FlatShape::ShorthandFlag.spanned(*span)), + TokenNode::Whitespace(_) => shapes.push(FlatShape::Whitespace.spanned(token.span())), + TokenNode::Error(v) => shapes.push(FlatShape::Error.spanned(v.span)), } } } diff --git a/src/parser/hir/tokens_iterator.rs b/src/parser/hir/tokens_iterator.rs index f597c850bd..dbcf5e6c4c 100644 --- a/src/parser/hir/tokens_iterator.rs +++ b/src/parser/hir/tokens_iterator.rs @@ -2,12 +2,12 @@ pub(crate) mod debug; use crate::errors::ShellError; use crate::parser::TokenNode; -use crate::{Tag, Tagged, TaggedItem}; +use crate::{Span, Spanned, SpannedItem}; #[derive(Debug)] pub struct TokensIterator<'content> { tokens: &'content [TokenNode], - tag: Tag, + span: Span, skip_ws: bool, index: usize, seen: indexmap::IndexSet, @@ -65,7 +65,7 @@ impl<'content, 'me> Peeked<'content, 'me> { match self.node { None => Err(ShellError::unexpected_eof( expected, - self.iterator.eof_tag(), + self.iterator.eof_span(), )), Some(node) => Ok(PeekedNode { node, @@ -77,7 +77,7 @@ impl<'content, 'me> Peeked<'content, 'me> { } pub fn type_error(&self, expected: impl Into) -> ShellError { - peek_error(&self.node, self.iterator.eof_tag(), expected) + peek_error(&self.node, self.iterator.eof_span(), expected) } } @@ -105,38 +105,38 @@ impl<'content, 'me> PeekedNode<'content, 'me> { pub fn rollback(self) {} pub fn type_error(&self, expected: impl Into) -> ShellError { - peek_error(&Some(self.node), self.iterator.eof_tag(), expected) + peek_error(&Some(self.node), self.iterator.eof_span(), expected) } } pub fn peek_error( node: &Option<&TokenNode>, - eof_tag: Tag, + eof_span: Span, expected: impl Into, ) -> ShellError { match node { - None => ShellError::unexpected_eof(expected, eof_tag), + None => ShellError::unexpected_eof(expected, eof_span), Some(node) => ShellError::type_error(expected, node.tagged_type_name()), } } impl<'content> TokensIterator<'content> { - pub fn new(items: &'content [TokenNode], tag: Tag, skip_ws: bool) -> TokensIterator<'content> { + pub fn new( + items: &'content [TokenNode], + span: Span, + skip_ws: bool, + ) -> TokensIterator<'content> { TokensIterator { tokens: items, - tag, + span, skip_ws, index: 0, seen: indexmap::IndexSet::new(), } } - pub fn anchor(&self) -> uuid::Uuid { - self.tag.anchor - } - - pub fn all(tokens: &'content [TokenNode], tag: Tag) -> TokensIterator<'content> { - TokensIterator::new(tokens, tag, false) + pub fn all(tokens: &'content [TokenNode], span: Span) -> TokensIterator<'content> { + TokensIterator::new(tokens, span, false) } pub fn len(&self) -> usize { @@ -146,14 +146,14 @@ impl<'content> TokensIterator<'content> { pub fn spanned( &mut self, block: impl FnOnce(&mut TokensIterator<'content>) -> T, - ) -> Tagged { - let start = self.tag_at_cursor(); + ) -> Spanned { + let start = self.span_at_cursor(); let result = block(self); - let end = self.tag_at_cursor(); + let end = self.span_at_cursor(); - result.tagged(start.until(end)) + result.spanned(start.until(end)) } /// Use a checkpoint when you need to peek more than one token ahead, but can't be sure @@ -192,25 +192,25 @@ impl<'content> TokensIterator<'content> { return Ok(value); } - fn eof_tag(&self) -> Tag { - Tag::from((self.tag.span.end(), self.tag.span.end(), self.tag.anchor)) + fn eof_span(&self) -> Span { + Span::new(self.span.end(), self.span.end()) } - pub fn typed_tag_at_cursor(&mut self) -> Tagged<&'static str> { + pub fn typed_span_at_cursor(&mut self) -> Spanned<&'static str> { let next = self.peek_any(); match next.node { - None => "end".tagged(self.eof_tag()), - Some(node) => node.tagged_type_name(), + None => "end".spanned(self.eof_span()), + Some(node) => node.spanned_type_name(), } } - pub fn tag_at_cursor(&mut self) -> Tag { + pub fn span_at_cursor(&mut self) -> Span { let next = self.peek_any(); match next.node { - None => self.eof_tag(), - Some(node) => node.tag(), + None => self.eof_span(), + Some(node) => node.span(), } } @@ -262,7 +262,7 @@ impl<'content> TokensIterator<'content> { pub fn clone(&self) -> TokensIterator<'content> { TokensIterator { tokens: self.tokens, - tag: self.tag, + span: self.span, index: self.index, seen: self.seen.clone(), skip_ws: self.skip_ws, diff --git a/src/parser/parse/files.rs b/src/parser/parse/files.rs index 3c28237f5d..8a2d3c90eb 100644 --- a/src/parser/parse/files.rs +++ b/src/parser/parse/files.rs @@ -1,8 +1,7 @@ -use crate::Tag; +use crate::Span; use derive_new::new; use language_reporting::{FileName, Location}; use log::trace; -use uuid::Uuid; #[derive(new, Debug, Clone)] pub struct Files { @@ -10,20 +9,20 @@ pub struct Files { } impl language_reporting::ReportingFiles for Files { - type Span = Tag; - type FileId = Uuid; + type Span = Span; + type FileId = usize; fn byte_span( &self, - file: Self::FileId, + _file: Self::FileId, from_index: usize, to_index: usize, ) -> Option { - Some(Tag::new(file, (from_index, to_index).into())) + Some(Span::new(from_index, to_index)) } - fn file_id(&self, tag: Self::Span) -> Self::FileId { - tag.anchor + fn file_id(&self, _tag: Self::Span) -> Self::FileId { + 0 } fn file_name(&self, _file: Self::FileId) -> FileName { @@ -68,14 +67,14 @@ impl language_reporting::ReportingFiles for Files { } } - fn line_span(&self, file: Self::FileId, lineno: usize) -> Option { + fn line_span(&self, _file: Self::FileId, lineno: usize) -> Option { let source = &self.snippet; let mut seen_lines = 0; let mut seen_bytes = 0; for (pos, _) in source.match_indices('\n') { if seen_lines == lineno { - return Some(Tag::new(file, (seen_bytes, pos + 1).into())); + return Some(Span::new(seen_bytes, pos + 1)); } else { seen_lines += 1; seen_bytes = pos + 1; @@ -83,20 +82,20 @@ impl language_reporting::ReportingFiles for Files { } if seen_lines == 0 { - Some(Tag::new(file, (0, self.snippet.len() - 1).into())) + Some(Span::new(0, self.snippet.len() - 1)) } else { None } } - fn source(&self, tag: Self::Span) -> Option { - trace!("source(tag={:?}) snippet={:?}", tag, self.snippet); + fn source(&self, span: Self::Span) -> Option { + trace!("source(tag={:?}) snippet={:?}", span, self.snippet); - if tag.span.start() > tag.span.end() { + if span.start() > span.end() { return None; - } else if tag.span.end() > self.snippet.len() { + } else if span.end() > self.snippet.len() { return None; } - Some(tag.slice(&self.snippet).to_string()) + Some(span.slice(&self.snippet).to_string()) } } diff --git a/src/parser/parse/flag.rs b/src/parser/parse/flag.rs index b8995305d2..28b6749f1c 100644 --- a/src/parser/parse/flag.rs +++ b/src/parser/parse/flag.rs @@ -1,5 +1,5 @@ use crate::parser::hir::syntax_shape::flat_shape::FlatShape; -use crate::{Tag, Tagged, TaggedItem}; +use crate::{Span, Spanned, SpannedItem}; use derive_new::new; use getset::Getters; use serde::{Deserialize, Serialize}; @@ -14,14 +14,14 @@ pub enum FlagKind { #[get = "pub(crate)"] pub struct Flag { pub(crate) kind: FlagKind, - pub(crate) name: Tag, + pub(crate) name: Span, } -impl Tagged { - pub fn color(&self) -> Tagged { +impl Spanned { + pub fn color(&self) -> Spanned { match self.item.kind { - FlagKind::Longhand => FlatShape::Flag.tagged(self.tag), - FlagKind::Shorthand => FlatShape::ShorthandFlag.tagged(self.tag), + FlagKind::Longhand => FlatShape::Flag.spanned(self.span), + FlagKind::Shorthand => FlatShape::ShorthandFlag.spanned(self.span), } } } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 73833f7be5..793f7b6cef 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -24,13 +24,11 @@ use nom_tracable::{tracable_parser, HasTracableInfo, TracableInfo}; use serde::{Deserialize, Serialize}; use std::fmt::Debug; use std::str::FromStr; -use uuid::Uuid; pub type NomSpan<'a> = LocatedSpanEx<&'a str, TracableContext>; #[derive(Debug, Clone, Copy, PartialEq, new)] pub struct TracableContext { - pub(crate) origin: Uuid, pub(crate) info: TracableInfo, } @@ -40,10 +38,7 @@ impl HasTracableInfo for TracableContext { } fn set_tracable_info(mut self, info: TracableInfo) -> Self { - TracableContext { - origin: self.origin, - info, - } + TracableContext { info } } } @@ -55,8 +50,8 @@ impl std::ops::Deref for TracableContext { } } -pub fn nom_input(s: &str, anchor: Uuid) -> NomSpan<'_> { - LocatedSpanEx::new_extra(s, TracableContext::new(anchor, TracableInfo::new())) +pub fn nom_input(s: &str) -> NomSpan<'_> { + LocatedSpanEx::new_extra(s, TracableContext::new(TracableInfo::new())) } macro_rules! operator { @@ -69,7 +64,7 @@ macro_rules! operator { Ok(( input, - TokenTreeBuilder::tagged_op(tag.fragment, (start, end, input.extra)), + TokenTreeBuilder::spanned_op(tag.fragment, Span::new(start, end)), )) } }; @@ -175,22 +170,22 @@ pub fn number(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_number(number.item, number.tag), + TokenTreeBuilder::spanned_number(number.item, number.span), )) } #[tracable_parser] -pub fn raw_number(input: NomSpan) -> IResult> { +pub fn raw_number(input: NomSpan) -> IResult> { let anchoral = input; let start = input.offset; let (input, neg) = opt(tag("-"))(input)?; let (input, head) = digit1(input)?; match input.fragment.chars().next() { - None => return Ok((input, RawNumber::int((start, input.offset, input.extra)))), + None => return Ok((input, RawNumber::int(Span::new(start, input.offset)))), Some('.') => (), other if is_boundary(other) => { - return Ok((input, RawNumber::int((start, input.offset, input.extra)))) + return Ok((input, RawNumber::int(Span::new(start, input.offset)))) } _ => { return Err(nom::Err::Error(nom::error::make_error( @@ -206,7 +201,7 @@ pub fn raw_number(input: NomSpan) -> IResult> { Ok((input, dot)) => input, // it's just an integer - Err(_) => return Ok((input, RawNumber::int((start, input.offset, input.extra)))), + Err(_) => return Ok((input, RawNumber::int(Span::new(start, input.offset)))), }; let (input, tail) = digit1(input)?; @@ -216,7 +211,7 @@ pub fn raw_number(input: NomSpan) -> IResult> { let next = input.fragment.chars().next(); if is_boundary(next) { - Ok((input, RawNumber::decimal((start, end, input.extra)))) + Ok((input, RawNumber::decimal(Span::new(start, end)))) } else { Err(nom::Err::Error(nom::error::make_error( input, @@ -243,7 +238,7 @@ pub fn dq_string(input: NomSpan) -> IResult { let end = input.offset; Ok(( input, - TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), + TokenTreeBuilder::spanned_string(Span::new(start1, end1), Span::new(start, end)), )) } @@ -259,7 +254,7 @@ pub fn sq_string(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)), + TokenTreeBuilder::spanned_string(Span::new(start1, end1), Span::new(start, end)), )) } @@ -277,7 +272,7 @@ pub fn external(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_external_command(bare, (start, end, input.extra)), + TokenTreeBuilder::spanned_external_command(bare, Span::new(start, end)), )) } @@ -302,7 +297,7 @@ pub fn pattern(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_pattern((start, end, input.extra)), + TokenTreeBuilder::spanned_pattern(Span::new(start, end)), )) } @@ -335,10 +330,7 @@ pub fn bare(input: NomSpan) -> IResult { let end = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_bare((start, end, input.extra)), - )) + Ok((input, TokenTreeBuilder::spanned_bare(Span::new(start, end)))) } #[tracable_parser] @@ -349,7 +341,7 @@ pub fn external_word(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_external_word((start, end, input.extra)), + TokenTreeBuilder::spanned_external_word(Span::new(start, end)), )) } @@ -362,7 +354,7 @@ pub fn var(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_var(bare, (start, end, input.extra)), + TokenTreeBuilder::spanned_var(bare, Span::new(start, end)), )) } @@ -373,7 +365,7 @@ pub fn ident(input: NomSpan) -> IResult { let (input, _) = take_while(is_bare_char)(input)?; let end = input.offset; - Ok((input, Tag::from((start, end, input.extra.origin)))) + Ok((input, Tag::from((start, end, None)))) } #[tracable_parser] @@ -385,7 +377,7 @@ pub fn flag(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_flag(bare.tag(), (start, end, input.extra)), + TokenTreeBuilder::spanned_flag(bare.span(), Span::new(start, end)), )) } @@ -398,7 +390,7 @@ pub fn shorthand(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_shorthand(bare.tag(), (start, end, input.extra)), + TokenTreeBuilder::spanned_shorthand(bare.span(), Span::new(start, end)), )) } @@ -420,12 +412,12 @@ pub fn token_list(input: NomSpan) -> IResult>> { Ok(( input, - make_token_list(first, list, None).tagged((start, end, input.extra.origin)), + make_token_list(first, list, None).tagged((start, end, None)), )) } #[tracable_parser] -pub fn spaced_token_list(input: NomSpan) -> IResult>> { +pub fn spaced_token_list(input: NomSpan) -> IResult>> { let start = input.offset; let (input, pre_ws) = opt(whitespace)(input)?; let (input, items) = token_list(input)?; @@ -438,7 +430,7 @@ pub fn spaced_token_list(input: NomSpan) -> IResult IResult { let (input, ws1) = space1(input)?; let right = input.offset; - Ok(( - input, - TokenTreeBuilder::tagged_ws((left, right, input.extra)), - )) + Ok((input, TokenTreeBuilder::spanned_ws(Span::new(left, right)))) } pub fn delimited( input: NomSpan, delimiter: Delimiter, -) -> IResult>)> { +) -> IResult>)> { let left = input.offset; - let (input, open_tag) = tag(delimiter.open())(input)?; + let (input, open_span) = tag(delimiter.open())(input)?; let (input, inner_items) = opt(spaced_token_list)(input)?; - let (input, close_tag) = tag(delimiter.close())(input)?; + let (input, close_span) = tag(delimiter.close())(input)?; let right = input.offset; let mut items = vec![]; @@ -493,9 +482,9 @@ pub fn delimited( Ok(( input, ( - Tag::from(open_tag), - Tag::from(close_tag), - items.tagged((left, right, input.extra.origin)), + Span::from(open_span), + Span::from(close_span), + items.spanned(Span::new(left, right)), ), )) } @@ -506,7 +495,7 @@ pub fn delimited_paren(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_parens(tokens.item, (left, right), tokens.tag), + TokenTreeBuilder::spanned_parens(tokens.item, (left, right), tokens.span), )) } @@ -516,7 +505,7 @@ pub fn delimited_square(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_square(tokens.item, (left, right), tokens.tag), + TokenTreeBuilder::spanned_square(tokens.item, (left, right), tokens.span), )) } @@ -526,7 +515,7 @@ pub fn delimited_brace(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_square(tokens.item, (left, right), tokens.tag), + TokenTreeBuilder::spanned_square(tokens.item, (left, right), tokens.span), )) } @@ -637,18 +626,19 @@ pub fn pipeline(input: NomSpan) -> IResult { let end = input.offset; - let head_tag = head.tag(); - let mut all_items: Vec> = - vec![PipelineElement::new(None, head).tagged(head_tag)]; + let head_span = head.span; + let mut all_items: Vec> = + vec![PipelineElement::new(None, head).spanned(head_span)]; all_items.extend(items.into_iter().map(|(pipe, items)| { - let items_tag = items.tag(); - PipelineElement::new(Some(Tag::from(pipe)), items).tagged(Tag::from(pipe).until(items_tag)) + let items_span = items.span; + PipelineElement::new(Some(Span::from(pipe)), items) + .spanned(Span::from(pipe).until(items_span)) })); Ok(( input, - TokenTreeBuilder::tagged_pipeline(all_items, (start, end, input.extra)), + TokenTreeBuilder::spanned_pipeline(all_items, Span::new(start, end)), )) } @@ -757,7 +747,7 @@ mod tests { macro_rules! equal_tokens { ($source:tt -> $tokens:expr) => { let result = apply(pipeline, "pipeline", $source); - let (expected_tree, expected_source) = TokenTreeBuilder::build(uuid::Uuid::nil(), $tokens); + let (expected_tree, expected_source) = TokenTreeBuilder::build($tokens); if result != expected_tree { let debug_result = format!("{}", result.debug($source)); @@ -778,7 +768,7 @@ mod tests { (<$parser:tt> $source:tt -> $tokens:expr) => { let result = apply($parser, stringify!($parser), $source); - let (expected_tree, expected_source) = TokenTreeBuilder::build(uuid::Uuid::nil(), $tokens); + let (expected_tree, expected_source) = TokenTreeBuilder::build($tokens); if result != expected_tree { let debug_result = format!("{}", result.debug($source)); @@ -1241,41 +1231,37 @@ mod tests { desc: &str, string: &str, ) -> TokenNode { - f(nom_input(string, uuid::Uuid::nil())).unwrap().1 + f(nom_input(string)).unwrap().1 } - fn tag(left: usize, right: usize) -> Tag { - Tag::from((left, right, uuid::Uuid::nil())) + fn span((left, right): (usize, usize)) -> Span { + Span::new(left, right) } fn delimited( - delimiter: Tagged, + delimiter: Spanned, children: Vec, left: usize, right: usize, ) -> TokenNode { - let start = Tag::for_char(left, delimiter.tag.anchor); - let end = Tag::for_char(right, delimiter.tag.anchor); + let start = Span::for_char(left); + let end = Span::for_char(right); let node = DelimitedNode::new(delimiter.item, (start, end), children); - let spanned = node.tagged((left, right, delimiter.tag.anchor)); + let spanned = node.spanned(Span::new(left, right)); TokenNode::Delimited(spanned) } fn token(token: RawToken, left: usize, right: usize) -> TokenNode { - TokenNode::Token(token.tagged((left, right, uuid::Uuid::nil()))) + TokenNode::Token(token.spanned(Span::new(left, right))) } fn build(block: CurriedNode) -> T { - let mut builder = TokenTreeBuilder::new(uuid::Uuid::nil()); + let mut builder = TokenTreeBuilder::new(); block(&mut builder) } fn build_token(block: CurriedToken) -> TokenNode { - TokenTreeBuilder::build(uuid::Uuid::nil(), block).0 - } - - fn test_uuid() -> uuid::Uuid { - uuid::Uuid::nil() + TokenTreeBuilder::build(block).0 } } diff --git a/src/parser/parse/pipeline.rs b/src/parser/parse/pipeline.rs index 36813e39c4..73db738078 100644 --- a/src/parser/parse/pipeline.rs +++ b/src/parser/parse/pipeline.rs @@ -1,13 +1,13 @@ use crate::parser::TokenNode; use crate::traits::ToDebug; -use crate::{Tag, Tagged}; +use crate::{Span, Spanned}; use derive_new::new; use getset::Getters; use std::fmt; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)] pub struct Pipeline { - pub(crate) parts: Vec>, + pub(crate) parts: Vec>, // pub(crate) post_ws: Option, } @@ -23,8 +23,8 @@ impl ToDebug for Pipeline { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] pub struct PipelineElement { - pub pipe: Option, - pub tokens: Tagged>, + pub pipe: Option, + pub tokens: Spanned>, } impl ToDebug for PipelineElement { diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index 85961d1dab..3c7e4fc11e 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -2,7 +2,7 @@ use crate::errors::ShellError; use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*}; use crate::prelude::*; use crate::traits::ToDebug; -use crate::{Tag, Tagged, Text}; +use crate::{Tagged, Text}; use derive_new::new; use enum_utils::FromStr; use getset::Getters; @@ -12,14 +12,14 @@ use std::fmt; pub enum TokenNode { Token(Token), - Call(Tagged), - Nodes(Tagged>), - Delimited(Tagged), - Pipeline(Tagged), - Flag(Tagged), - Whitespace(Tag), + Call(Spanned), + Nodes(Spanned>), + Delimited(Spanned), + Pipeline(Spanned), + Flag(Spanned), + Whitespace(Span), - Error(Tagged), + Error(Spanned), } impl ToDebug for TokenNode { @@ -78,28 +78,28 @@ impl fmt::Debug for DebugTokenNode<'_> { } TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)), TokenNode::Error(_) => write!(f, ""), - rest => write!(f, "{}", rest.tag().slice(self.source)), + rest => write!(f, "{}", rest.span().slice(self.source)), } } } -impl From<&TokenNode> for Tag { - fn from(token: &TokenNode) -> Tag { - token.tag() +impl From<&TokenNode> for Span { + fn from(token: &TokenNode) -> Span { + token.span() } } impl TokenNode { - pub fn tag(&self) -> Tag { + pub fn span(&self) -> Span { match self { - TokenNode::Token(t) => t.tag(), - TokenNode::Nodes(t) => t.tag(), - TokenNode::Call(s) => s.tag(), - TokenNode::Delimited(s) => s.tag(), - TokenNode::Pipeline(s) => s.tag(), - TokenNode::Flag(s) => s.tag(), + TokenNode::Token(t) => t.span, + TokenNode::Nodes(t) => t.span, + TokenNode::Call(s) => s.span, + TokenNode::Delimited(s) => s.span, + TokenNode::Pipeline(s) => s.span, + TokenNode::Flag(s) => s.span, TokenNode::Whitespace(s) => *s, - TokenNode::Error(s) => return s.tag, + TokenNode::Error(s) => s.span, } } @@ -116,8 +116,12 @@ impl TokenNode { } } + pub fn spanned_type_name(&self) -> Spanned<&'static str> { + self.type_name().spanned(self.span()) + } + pub fn tagged_type_name(&self) -> Tagged<&'static str> { - self.type_name().tagged(self.tag()) + self.type_name().tagged(self.span()) } pub fn old_debug<'a>(&'a self, source: &'a Text) -> DebugTokenNode<'a> { @@ -125,26 +129,26 @@ impl TokenNode { } pub fn as_external_arg(&self, source: &Text) -> String { - self.tag().slice(source).to_string() + self.span().slice(source).to_string() } pub fn source<'a>(&self, source: &'a Text) -> &'a str { - self.tag().slice(source) + self.span().slice(source) } - pub fn get_variable(&self) -> Result<(Tag, Tag), ShellError> { + pub fn get_variable(&self) -> Result<(Span, Span), ShellError> { match self { - TokenNode::Token(Tagged { - item: RawToken::Variable(inner_tag), - tag: outer_tag, - }) => Ok((*outer_tag, *inner_tag)), + TokenNode::Token(Spanned { + item: RawToken::Variable(inner_span), + span: outer_span, + }) => Ok((*outer_span, *inner_span)), _ => Err(ShellError::type_error("variable", self.tagged_type_name())), } } pub fn is_bare(&self) -> bool { match self { - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Bare, .. }) => true, @@ -154,7 +158,7 @@ impl TokenNode { pub fn is_pattern(&self) -> bool { match self { - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::GlobPattern, .. }) => true, @@ -164,7 +168,7 @@ impl TokenNode { pub fn is_dot(&self) -> bool { match self { - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Operator(Operator::Dot), .. }) => true, @@ -172,24 +176,24 @@ impl TokenNode { } } - pub fn as_block(&self) -> Option<(Tagged<&[TokenNode]>, (Tag, Tag))> { + pub fn as_block(&self) -> Option<(Spanned<&[TokenNode]>, (Span, Span))> { match self { - TokenNode::Delimited(Tagged { + TokenNode::Delimited(Spanned { item: DelimitedNode { delimiter, children, - tags, + spans, }, - tag, - }) if *delimiter == Delimiter::Brace => Some(((&children[..]).tagged(tag), *tags)), + span, + }) if *delimiter == Delimiter::Brace => Some(((&children[..]).spanned(*span), *spans)), _ => None, } } pub fn is_external(&self) -> bool { match self { - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::ExternalCommand(..), .. }) => true, @@ -197,20 +201,20 @@ impl TokenNode { } } - pub fn expect_external(&self) -> Tag { + pub fn expect_external(&self) -> Span { match self { - TokenNode::Token(Tagged { - item: RawToken::ExternalCommand(tag), + TokenNode::Token(Spanned { + item: RawToken::ExternalCommand(span), .. - }) => *tag, + }) => *span, _ => panic!("Only call expect_external if you checked is_external first"), } } - pub(crate) fn as_flag(&self, value: &str, source: &Text) -> Option> { + pub(crate) fn as_flag(&self, value: &str, source: &Text) -> Option> { match self { TokenNode::Flag( - flag @ Tagged { + flag @ Spanned { item: Flag { .. }, .. }, ) if value == flag.name().slice(source) => Some(*flag), @@ -220,7 +224,7 @@ impl TokenNode { pub fn as_pipeline(&self) -> Result { match self { - TokenNode::Pipeline(Tagged { item, .. }) => Ok(item.clone()), + TokenNode::Pipeline(Spanned { item, .. }) => Ok(item.clone()), _ => Err(ShellError::unimplemented("unimplemented")), } } @@ -232,12 +236,12 @@ impl TokenNode { } } - pub fn expect_string(&self) -> (Tag, Tag) { + pub fn expect_string(&self) -> (Span, Span) { match self { - TokenNode::Token(Tagged { - item: RawToken::String(inner_tag), - tag: outer_tag, - }) => (*outer_tag, *inner_tag), + TokenNode::Token(Spanned { + item: RawToken::String(inner_span), + span: outer_span, + }) => (*outer_span, *inner_span), other => panic!("Expected string, found {:?}", other), } } @@ -247,27 +251,30 @@ impl TokenNode { impl TokenNode { pub fn expect_list(&self) -> Tagged<&[TokenNode]> { match self { - TokenNode::Nodes(Tagged { item, tag }) => (&item[..]).tagged(tag), + TokenNode::Nodes(Spanned { item, span }) => (&item[..]).tagged(Tag { + span: *span, + anchor: None, + }), other => panic!("Expected list, found {:?}", other), } } - pub fn expect_var(&self) -> (Tag, Tag) { + pub fn expect_var(&self) -> (Span, Span) { match self { - TokenNode::Token(Tagged { - item: RawToken::Variable(inner_tag), - tag: outer_tag, - }) => (*outer_tag, *inner_tag), + TokenNode::Token(Spanned { + item: RawToken::Variable(inner_span), + span: outer_span, + }) => (*outer_span, *inner_span), other => panic!("Expected var, found {:?}", other), } } - pub fn expect_bare(&self) -> Tag { + pub fn expect_bare(&self) -> Span { match self { - TokenNode::Token(Tagged { + TokenNode::Token(Spanned { item: RawToken::Bare, - tag, - }) => *tag, + span, + }) => *span, other => panic!("Expected var, found {:?}", other), } } @@ -277,7 +284,7 @@ impl TokenNode { #[get = "pub(crate)"] pub struct DelimitedNode { pub(crate) delimiter: Delimiter, - pub(crate) tags: (Tag, Tag), + pub(crate) spans: (Span, Span), pub(crate) children: Vec, } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index 549462a979..891e6b9e16 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -7,7 +7,6 @@ use crate::parser::parse::token_tree::{DelimitedNode, Delimiter, TokenNode}; use crate::parser::parse::tokens::{RawNumber, RawToken}; use crate::parser::CallNode; use derive_new::new; -use uuid::Uuid; #[derive(new)] pub struct TokenTreeBuilder { @@ -16,33 +15,34 @@ pub struct TokenTreeBuilder { #[new(default)] output: String, - - anchor: Uuid, } pub type CurriedToken = Box TokenNode + 'static>; pub type CurriedCall = Box Tagged + 'static>; impl TokenTreeBuilder { - pub fn build(anchor: Uuid, block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { - let mut builder = TokenTreeBuilder::new(anchor); + pub fn build(block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { + let mut builder = TokenTreeBuilder::new(); let node = block(&mut builder); (node, builder.output) } - fn build_tagged(&mut self, callback: impl FnOnce(&mut TokenTreeBuilder) -> T) -> Tagged { + fn build_spanned( + &mut self, + callback: impl FnOnce(&mut TokenTreeBuilder) -> T, + ) -> Spanned { let start = self.pos; let ret = callback(self); let end = self.pos; - ret.tagged((start, end, self.anchor)) + ret.spanned(Span::new(start, end)) } pub fn pipeline(input: Vec>) -> CurriedToken { Box::new(move |b| { let start = b.pos; - let mut out: Vec> = vec![]; + let mut out: Vec> = vec![]; let mut input = input.into_iter().peekable(); let head = input @@ -50,34 +50,37 @@ impl TokenTreeBuilder { .expect("A pipeline must contain at least one element"); let pipe = None; - let head = b.build_tagged(|b| head.into_iter().map(|node| node(b)).collect()); + let head = b.build_spanned(|b| head.into_iter().map(|node| node(b)).collect()); - let head_tag: Tag = head.tag; - out.push(PipelineElement::new(pipe, head).tagged(head_tag)); + let head_span: Span = head.span; + out.push(PipelineElement::new(pipe, head).spanned(head_span)); loop { match input.next() { None => break, Some(node) => { let start = b.pos; - let pipe = Some(b.consume_tag("|")); + let pipe = Some(b.consume_span("|")); let node = - b.build_tagged(|b| node.into_iter().map(|node| node(b)).collect()); + b.build_spanned(|b| node.into_iter().map(|node| node(b)).collect()); let end = b.pos; - out.push(PipelineElement::new(pipe, node).tagged((start, end, b.anchor))); + out.push(PipelineElement::new(pipe, node).spanned(Span::new(start, end))); } } } let end = b.pos; - TokenTreeBuilder::tagged_pipeline(out, (start, end, b.anchor)) + TokenTreeBuilder::spanned_pipeline(out, Span::new(start, end)) }) } - pub fn tagged_pipeline(input: Vec>, tag: impl Into) -> TokenNode { - TokenNode::Pipeline(Pipeline::new(input).tagged(tag.into())) + pub fn spanned_pipeline( + input: Vec>, + span: impl Into, + ) -> TokenNode { + TokenNode::Pipeline(Pipeline::new(input).spanned(span)) } pub fn token_list(input: Vec) -> CurriedToken { @@ -86,12 +89,12 @@ impl TokenTreeBuilder { let tokens = input.into_iter().map(|i| i(b)).collect(); let end = b.pos; - TokenTreeBuilder::tagged_token_list(tokens, (start, end, b.anchor)) + TokenTreeBuilder::tagged_token_list(tokens, (start, end, None)) }) } pub fn tagged_token_list(input: Vec, tag: impl Into) -> TokenNode { - TokenNode::Nodes(input.tagged(tag)) + TokenNode::Nodes(input.spanned(tag.into().span)) } pub fn op(input: impl Into) -> CurriedToken { @@ -102,12 +105,12 @@ impl TokenTreeBuilder { b.pos = end; - TokenTreeBuilder::tagged_op(input, (start, end, b.anchor)) + TokenTreeBuilder::spanned_op(input, Span::new(start, end)) }) } - pub fn tagged_op(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::Operator(input.into()).tagged(tag.into())) + pub fn spanned_op(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Operator(input.into()).spanned(span.into())) } pub fn string(input: impl Into) -> CurriedToken { @@ -119,15 +122,15 @@ impl TokenTreeBuilder { let (_, end) = b.consume("\""); b.pos = end; - TokenTreeBuilder::tagged_string( - (inner_start, inner_end, b.anchor), - (start, end, b.anchor), + TokenTreeBuilder::spanned_string( + Span::new(inner_start, inner_end), + Span::new(start, end), ) }) } - pub fn tagged_string(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::String(input.into()).tagged(tag.into())) + pub fn spanned_string(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Token(RawToken::String(input.into()).spanned(span.into())) } pub fn bare(input: impl Into) -> CurriedToken { @@ -137,12 +140,12 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_bare((start, end, b.anchor)) + TokenTreeBuilder::spanned_bare(Span::new(start, end)) }) } - pub fn tagged_bare(tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::Bare.tagged(tag.into())) + pub fn spanned_bare(span: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Bare.spanned(span)) } pub fn pattern(input: impl Into) -> CurriedToken { @@ -152,12 +155,12 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_pattern((start, end, b.anchor)) + TokenTreeBuilder::spanned_pattern(Span::new(start, end)) }) } - pub fn tagged_pattern(input: impl Into) -> TokenNode { - TokenNode::Token(RawToken::GlobPattern.tagged(input.into())) + pub fn spanned_pattern(input: impl Into) -> TokenNode { + TokenNode::Token(RawToken::GlobPattern.spanned(input.into())) } pub fn external_word(input: impl Into) -> CurriedToken { @@ -167,12 +170,12 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_external_word((start, end, b.anchor)) + TokenTreeBuilder::spanned_external_word(Span::new(start, end)) }) } - pub fn tagged_external_word(input: impl Into) -> TokenNode { - TokenNode::Token(RawToken::ExternalWord.tagged(input.into())) + pub fn spanned_external_word(input: impl Into) -> TokenNode { + TokenNode::Token(RawToken::ExternalWord.spanned(input.into())) } pub fn external_command(input: impl Into) -> CurriedToken { @@ -183,15 +186,15 @@ impl TokenTreeBuilder { let (inner_start, end) = b.consume(&input); b.pos = end; - TokenTreeBuilder::tagged_external_command( - (inner_start, end, b.anchor), - (outer_start, end, b.anchor), + TokenTreeBuilder::spanned_external_command( + Span::new(inner_start, end), + Span::new(outer_start, end), ) }) } - pub fn tagged_external_command(inner: impl Into, outer: impl Into) -> TokenNode { - TokenNode::Token(RawToken::ExternalCommand(inner.into()).tagged(outer.into())) + pub fn spanned_external_command(inner: impl Into, outer: impl Into) -> TokenNode { + TokenNode::Token(RawToken::ExternalCommand(inner.into()).spanned(outer.into())) } pub fn int(input: impl Into) -> CurriedToken { @@ -201,9 +204,9 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&int.to_string()); b.pos = end; - TokenTreeBuilder::tagged_number( - RawNumber::Int((start, end, b.anchor).into()), - (start, end, b.anchor), + TokenTreeBuilder::spanned_number( + RawNumber::Int(Span::new(start, end)), + Span::new(start, end), ) }) } @@ -215,15 +218,15 @@ impl TokenTreeBuilder { let (start, end) = b.consume(&decimal.to_string()); b.pos = end; - TokenTreeBuilder::tagged_number( - RawNumber::Decimal((start, end, b.anchor).into()), - (start, end, b.anchor), + TokenTreeBuilder::spanned_number( + RawNumber::Decimal(Span::new(start, end)), + Span::new(start, end), ) }) } - pub fn tagged_number(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::Number(input.into()).tagged(tag.into())) + pub fn spanned_number(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Number(input.into()).spanned(span.into())) } pub fn var(input: impl Into) -> CurriedToken { @@ -233,12 +236,12 @@ impl TokenTreeBuilder { let (start, _) = b.consume("$"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::tagged_var((inner_start, end, b.anchor), (start, end, b.anchor)) + TokenTreeBuilder::spanned_var(Span::new(inner_start, end), Span::new(start, end)) }) } - pub fn tagged_var(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Token(RawToken::Variable(input.into()).tagged(tag.into())) + pub fn spanned_var(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Token(RawToken::Variable(input.into()).spanned(span.into())) } pub fn flag(input: impl Into) -> CurriedToken { @@ -248,12 +251,12 @@ impl TokenTreeBuilder { let (start, _) = b.consume("--"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::tagged_flag((inner_start, end, b.anchor), (start, end, b.anchor)) + TokenTreeBuilder::spanned_flag(Span::new(inner_start, end), Span::new(start, end)) }) } - pub fn tagged_flag(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Flag(Flag::new(FlagKind::Longhand, input.into()).tagged(tag.into())) + pub fn spanned_flag(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Flag(Flag::new(FlagKind::Longhand, input.into()).spanned(span.into())) } pub fn shorthand(input: impl Into) -> CurriedToken { @@ -263,12 +266,12 @@ impl TokenTreeBuilder { let (start, _) = b.consume("-"); let (inner_start, end) = b.consume(&input); - TokenTreeBuilder::tagged_shorthand((inner_start, end, b.anchor), (start, end, b.anchor)) + TokenTreeBuilder::spanned_shorthand((inner_start, end), (start, end)) }) } - pub fn tagged_shorthand(input: impl Into, tag: impl Into) -> TokenNode { - TokenNode::Flag(Flag::new(FlagKind::Shorthand, input.into()).tagged(tag.into())) + pub fn spanned_shorthand(input: impl Into, span: impl Into) -> TokenNode { + TokenNode::Flag(Flag::new(FlagKind::Shorthand, input.into()).spanned(span.into())) } pub fn call(head: CurriedToken, input: Vec) -> CurriedCall { @@ -284,7 +287,7 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::tagged_call(nodes, (start, end, b.anchor)) + TokenTreeBuilder::tagged_call(nodes, (start, end, None)) }) } @@ -306,7 +309,7 @@ impl TokenTreeBuilder { input: Vec, _open: &str, _close: &str, - ) -> (Tag, Tag, Tag, Vec) { + ) -> (Span, Span, Span, Vec) { let (start_open_paren, end_open_paren) = self.consume("("); let mut output = vec![]; for item in input { @@ -315,9 +318,9 @@ impl TokenTreeBuilder { let (start_close_paren, end_close_paren) = self.consume(")"); - let open = Tag::from((start_open_paren, end_open_paren, self.anchor)); - let close = Tag::from((start_close_paren, end_close_paren, self.anchor)); - let whole = Tag::from((start_open_paren, end_close_paren, self.anchor)); + let open = Span::new(start_open_paren, end_open_paren); + let close = Span::new(start_close_paren, end_close_paren); + let whole = Span::new(start_open_paren, end_close_paren); (open, close, whole, output) } @@ -326,17 +329,17 @@ impl TokenTreeBuilder { Box::new(move |b| { let (open, close, whole, output) = b.consume_delimiter(input, "(", ")"); - TokenTreeBuilder::tagged_parens(output, (open, close), whole) + TokenTreeBuilder::spanned_parens(output, (open, close), whole) }) } - pub fn tagged_parens( + pub fn spanned_parens( input: impl Into>, - tags: (Tag, Tag), - tag: impl Into, + spans: (Span, Span), + span: impl Into, ) -> TokenNode { TokenNode::Delimited( - DelimitedNode::new(Delimiter::Paren, tags, input.into()).tagged(tag.into()), + DelimitedNode::new(Delimiter::Paren, spans, input.into()).spanned(span.into()), ) } @@ -344,17 +347,17 @@ impl TokenTreeBuilder { Box::new(move |b| { let (open, close, whole, tokens) = b.consume_delimiter(input, "[", "]"); - TokenTreeBuilder::tagged_square(tokens, (open, close), whole) + TokenTreeBuilder::spanned_square(tokens, (open, close), whole) }) } - pub fn tagged_square( + pub fn spanned_square( input: impl Into>, - tags: (Tag, Tag), - tag: impl Into, + spans: (Span, Span), + span: impl Into, ) -> TokenNode { TokenNode::Delimited( - DelimitedNode::new(Delimiter::Square, tags, input.into()).tagged(tag.into()), + DelimitedNode::new(Delimiter::Square, spans, input.into()).spanned(span.into()), ) } @@ -362,24 +365,24 @@ impl TokenTreeBuilder { Box::new(move |b| { let (open, close, whole, tokens) = b.consume_delimiter(input, "{", "}"); - TokenTreeBuilder::tagged_brace(tokens, (open, close), whole) + TokenTreeBuilder::spanned_brace(tokens, (open, close), whole) }) } - pub fn tagged_brace( + pub fn spanned_brace( input: impl Into>, - tags: (Tag, Tag), - tag: impl Into, + spans: (Span, Span), + span: impl Into, ) -> TokenNode { TokenNode::Delimited( - DelimitedNode::new(Delimiter::Brace, tags, input.into()).tagged(tag.into()), + DelimitedNode::new(Delimiter::Brace, spans, input.into()).spanned(span.into()), ) } pub fn sp() -> CurriedToken { Box::new(|b| { let (start, end) = b.consume(" "); - TokenNode::Whitespace(Tag::from((start, end, b.anchor))) + TokenNode::Whitespace(Span::new(start, end)) }) } @@ -388,12 +391,12 @@ impl TokenTreeBuilder { Box::new(move |b| { let (start, end) = b.consume(&input); - TokenTreeBuilder::tagged_ws((start, end, b.anchor)) + TokenTreeBuilder::spanned_ws(Span::new(start, end)) }) } - pub fn tagged_ws(tag: impl Into) -> TokenNode { - TokenNode::Whitespace(tag.into()) + pub fn spanned_ws(span: impl Into) -> TokenNode { + TokenNode::Whitespace(span.into()) } fn consume(&mut self, input: &str) -> (usize, usize) { @@ -403,10 +406,10 @@ impl TokenTreeBuilder { (start, self.pos) } - fn consume_tag(&mut self, input: &str) -> Tag { + fn consume_span(&mut self, input: &str) -> Span { let start = self.pos; self.pos += input.len(); self.output.push_str(input); - (start, self.pos, self.anchor).into() + Span::new(start, self.pos) } } diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index 41bdfcebd6..94955d84d9 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -1,6 +1,6 @@ use crate::parser::Operator; use crate::prelude::*; -use crate::{Tagged, Text}; +use crate::Text; use std::fmt; use std::str::FromStr; @@ -8,9 +8,9 @@ use std::str::FromStr; pub enum RawToken { Number(RawNumber), Operator(Operator), - String(Tag), - Variable(Tag), - ExternalCommand(Tag), + String(Span), + Variable(Span), + ExternalCommand(Span), ExternalWord, GlobPattern, Bare, @@ -33,21 +33,21 @@ impl RawToken { #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] pub enum RawNumber { - Int(Tag), - Decimal(Tag), + Int(Span), + Decimal(Span), } impl RawNumber { - pub fn int(tag: impl Into) -> Tagged { - let tag = tag.into(); + pub fn int(span: impl Into) -> Spanned { + let span = span.into(); - RawNumber::Int(tag).tagged(tag) + RawNumber::Int(span).spanned(span) } - pub fn decimal(tag: impl Into) -> Tagged { - let tag = tag.into(); + pub fn decimal(span: impl Into) -> Spanned { + let span = span.into(); - RawNumber::Decimal(tag).tagged(tag) + RawNumber::Decimal(span).spanned(span) } pub(crate) fn to_number(self, source: &Text) -> Number { @@ -60,7 +60,7 @@ impl RawNumber { } } -pub type Token = Tagged; +pub type Token = Spanned; impl Token { pub fn debug<'a>(&self, source: &'a Text) -> DebugToken<'a> { @@ -70,72 +70,72 @@ impl Token { } } - pub fn extract_number(&self) -> Option> { + pub fn extract_number(&self) -> Option> { match self.item { - RawToken::Number(number) => Some((number).tagged(self.tag)), + RawToken::Number(number) => Some((number).spanned(self.span)), _ => None, } } - pub fn extract_int(&self) -> Option<(Tag, Tag)> { + pub fn extract_int(&self) -> Option<(Span, Span)> { match self.item { - RawToken::Number(RawNumber::Int(int)) => Some((int, self.tag)), + RawToken::Number(RawNumber::Int(int)) => Some((int, self.span)), _ => None, } } - pub fn extract_decimal(&self) -> Option<(Tag, Tag)> { + pub fn extract_decimal(&self) -> Option<(Span, Span)> { match self.item { - RawToken::Number(RawNumber::Decimal(decimal)) => Some((decimal, self.tag)), + RawToken::Number(RawNumber::Decimal(decimal)) => Some((decimal, self.span)), _ => None, } } - pub fn extract_operator(&self) -> Option> { + pub fn extract_operator(&self) -> Option> { match self.item { - RawToken::Operator(operator) => Some(operator.tagged(self.tag)), + RawToken::Operator(operator) => Some(operator.spanned(self.span)), _ => None, } } - pub fn extract_string(&self) -> Option<(Tag, Tag)> { + pub fn extract_string(&self) -> Option<(Span, Span)> { match self.item { - RawToken::String(tag) => Some((tag, self.tag)), + RawToken::String(span) => Some((span, self.span)), _ => None, } } - pub fn extract_variable(&self) -> Option<(Tag, Tag)> { + pub fn extract_variable(&self) -> Option<(Span, Span)> { match self.item { - RawToken::Variable(tag) => Some((tag, self.tag)), + RawToken::Variable(span) => Some((span, self.span)), _ => None, } } - pub fn extract_external_command(&self) -> Option<(Tag, Tag)> { + pub fn extract_external_command(&self) -> Option<(Span, Span)> { match self.item { - RawToken::ExternalCommand(tag) => Some((tag, self.tag)), + RawToken::ExternalCommand(span) => Some((span, self.span)), _ => None, } } - pub fn extract_external_word(&self) -> Option { + pub fn extract_external_word(&self) -> Option { match self.item { - RawToken::ExternalWord => Some(self.tag), + RawToken::ExternalWord => Some(self.span), _ => None, } } - pub fn extract_glob_pattern(&self) -> Option { + pub fn extract_glob_pattern(&self) -> Option { match self.item { - RawToken::GlobPattern => Some(self.tag), + RawToken::GlobPattern => Some(self.span), _ => None, } } - pub fn extract_bare(&self) -> Option { + pub fn extract_bare(&self) -> Option { match self.item { - RawToken::Bare => Some(self.tag), + RawToken::Bare => Some(self.span), _ => None, } } @@ -148,6 +148,6 @@ pub struct DebugToken<'a> { impl fmt::Debug for DebugToken<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.node.tag().slice(self.source)) + write!(f, "{}", self.node.span.slice(self.source)) } } diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index 603ff2956d..935794f3c1 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -10,14 +10,14 @@ use crate::parser::{ Flag, }; use crate::traits::ToDebug; -use crate::{Tag, Tagged, Text}; +use crate::{Span, Spanned, Tag, Text}; use log::trace; pub fn parse_command_tail( config: &Signature, context: &ExpandContext, tail: &mut TokensIterator, - command_tag: Tag, + command_span: Span, ) -> Result>, Option)>, ShellError> { let mut named = NamedArguments::new(); trace_remaining("nodes", tail.clone(), context.source()); @@ -32,7 +32,7 @@ pub fn parse_command_tail( named.insert_switch(name, flag); } NamedType::Mandatory(syntax_type) => { - match extract_mandatory(config, name, tail, context.source(), command_tag) { + match extract_mandatory(config, name, tail, context.source(), command_span) { Err(err) => return Err(err), // produce a correct diagnostic Ok((pos, flag)) => { tail.move_to(pos); @@ -41,7 +41,7 @@ pub fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), - flag.tag(), + flag.span, )); } @@ -62,7 +62,7 @@ pub fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingValueForName(name.to_string()), - flag.tag(), + flag.span, )); } @@ -98,7 +98,10 @@ pub fn parse_command_tail( return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryPositional(arg.name().to_string()), - command_tag, + Tag { + span: command_span, + anchor: None, + }, )); } } @@ -158,7 +161,7 @@ pub fn parse_command_tail( #[derive(Debug)] struct ColoringArgs { - vec: Vec>>>, + vec: Vec>>>, } impl ColoringArgs { @@ -167,11 +170,11 @@ impl ColoringArgs { ColoringArgs { vec } } - fn insert(&mut self, pos: usize, shapes: Vec>) { + fn insert(&mut self, pos: usize, shapes: Vec>) { self.vec[pos] = Some(shapes); } - fn spread_shapes(self, shapes: &mut Vec>) { + fn spread_shapes(self, shapes: &mut Vec>) { for item in self.vec { match item { None => {} @@ -195,7 +198,7 @@ impl ColorSyntax for CommandTailShape { signature: &Signature, token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, - shapes: &mut Vec>, + shapes: &mut Vec>, ) -> Self::Info { let mut args = ColoringArgs::new(token_nodes.len()); trace_remaining("nodes", token_nodes.clone(), context.source()); @@ -216,7 +219,7 @@ impl ColorSyntax for CommandTailShape { name, token_nodes, context.source(), - Tag::unknown(), + Span::unknown(), ) { Err(_) => { // The mandatory flag didn't exist at all, so there's nothing to color @@ -378,7 +381,7 @@ impl ColorSyntax for CommandTailShape { // Consume any remaining tokens with backoff coloring mode color_syntax(&BackoffColoringMode, token_nodes, context, shapes); - shapes.sort_by(|a, b| a.tag.span.start().cmp(&b.tag.span.start())); + shapes.sort_by(|a, b| a.span.start().cmp(&b.span.start())); } } @@ -393,15 +396,15 @@ fn extract_mandatory( name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Text, - tag: Tag, -) -> Result<(usize, Tagged), ShellError> { + span: Span, +) -> Result<(usize, Spanned), ShellError> { let flag = tokens.extract(|t| t.as_flag(name, source)); match flag { None => Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryFlag(name.to_string()), - tag, + span, )), Some((pos, flag)) => { @@ -415,7 +418,7 @@ fn extract_optional( name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Text, -) -> Result<(Option<(usize, Tagged)>), ShellError> { +) -> Result<(Option<(usize, Spanned)>), ShellError> { let flag = tokens.extract(|t| t.as_flag(name, source)); match flag { diff --git a/src/parser/registry.rs b/src/parser/registry.rs index 888e5ae1e9..790925e800 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -298,7 +298,7 @@ pub(crate) fn evaluate_args( for (name, value) in n.named.iter() { match value { hir::named::NamedValue::PresentSwitch(tag) => { - results.insert(name.clone(), Value::boolean(true).tagged(*tag)); + results.insert(name.clone(), Value::boolean(true).tagged(tag)); } hir::named::NamedValue::Value(expr) => { results.insert( diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 6fc034226c..98cf3819b3 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -22,7 +22,7 @@ impl Add { let value_tag = value.tag(); match (value.item, self.value.clone()) { (obj @ Value::Row(_), Some(v)) => match &self.field { - Some(f) => match obj.insert_data_at_column_path(value_tag, &f, v) { + Some(f) => match obj.insert_data_at_column_path(value_tag.clone(), &f, v) { Some(v) => return Ok(v), None => { return Err(ShellError::labeled_error( @@ -32,7 +32,7 @@ impl Add { f.iter().map(|i| &i.item).join(".") ), "column name", - value_tag, + &value_tag, )) } }, diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index d5488d3241..b834f440e2 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -24,8 +24,7 @@ impl Plugin for BinaryView { let value_anchor = v.anchor(); match v.item { Value::Primitive(Primitive::Binary(b)) => { - let source = call_info.source_map.get(&value_anchor); - let _ = view_binary(&b, source, call_info.args.has("lores")); + let _ = view_binary(&b, value_anchor.as_ref(), call_info.args.has("lores")); } _ => {} } diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index c0f6dfbedd..34653bd66d 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -27,7 +27,7 @@ impl Edit { return Err(ShellError::labeled_error( "edit could not find place to insert column", "column name", - f.tag, + &f.tag, )) } }, diff --git a/src/plugins/embed.rs b/src/plugins/embed.rs index 4e3545d055..97dd6a2713 100644 --- a/src/plugins/embed.rs +++ b/src/plugins/embed.rs @@ -28,7 +28,7 @@ impl Embed { None => Err(ShellError::labeled_error( "embed needs a field when embedding a value", "original value", - value.tag, + &tag, )), }, } diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index c58ca89369..38788014ad 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -82,9 +82,7 @@ impl Inc { Value::Primitive(Primitive::Bytes(b)) => { Ok(Value::bytes(b + 1 as u64).tagged(value.tag())) } - Value::Primitive(Primitive::String(ref s)) => { - Ok(Tagged::from_item(self.apply(&s)?, value.tag())) - } + Value::Primitive(Primitive::String(ref s)) => Ok(self.apply(&s)?.tagged(value.tag())), Value::Row(_) => match self.field { Some(ref f) => { let replacement = match value.item.get_data_by_column_path(value.tag(), f) { @@ -93,7 +91,7 @@ impl Inc { return Err(ShellError::labeled_error( "inc could not find field to replace", "column name", - f.tag, + &f.tag, )) } }; @@ -107,7 +105,7 @@ impl Inc { return Err(ShellError::labeled_error( "inc could not find field to replace", "column name", - f.tag, + &f.tag, )) } } @@ -191,20 +189,18 @@ mod tests { use super::{Inc, SemVerAction}; use indexmap::IndexMap; use nu::{ - CallInfo, EvaluatedArgs, Plugin, ReturnSuccess, SourceMap, Tag, Tagged, TaggedDictBuilder, - TaggedItem, Value, + CallInfo, EvaluatedArgs, Plugin, ReturnSuccess, Tag, Tagged, TaggedDictBuilder, TaggedItem, + Value, }; struct CallStub { - anchor: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } impl CallStub { - fn new(anchor: uuid::Uuid) -> CallStub { + fn new() -> CallStub { CallStub { - anchor, positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -221,19 +217,18 @@ mod tests { fn with_parameter(&mut self, name: &str) -> &mut Self { let fields: Vec> = name .split(".") - .map(|s| Value::string(s.to_string()).tagged(Tag::unknown_span(self.anchor))) + .map(|s| Value::string(s.to_string()).tagged(Tag::unknown())) .collect(); self.positionals - .push(Value::Table(fields).tagged(Tag::unknown_span(self.anchor))); + .push(Value::Table(fields).tagged(Tag::unknown())); self } fn create(&self) -> CallInfo { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), - source_map: SourceMap::new(), - name_tag: Tag::unknown_span(self.anchor), + name_tag: Tag::unknown(), } } } @@ -260,7 +255,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new(test_uuid()).with_long_flag("major").create()) + .begin_filter(CallStub::new().with_long_flag("major").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -270,7 +265,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new(test_uuid()).with_long_flag("minor").create()) + .begin_filter(CallStub::new().with_long_flag("minor").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -280,7 +275,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter(CallStub::new(test_uuid()).with_long_flag("patch").create()) + .begin_filter(CallStub::new().with_long_flag("patch").create()) .is_ok()); assert!(plugin.action.is_some()); } @@ -291,7 +286,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new(test_uuid()) + CallStub::new() .with_long_flag("major") .with_long_flag("minor") .create(), @@ -305,11 +300,7 @@ mod tests { let mut plugin = Inc::new(); assert!(plugin - .begin_filter( - CallStub::new(test_uuid()) - .with_parameter("package.version") - .create() - ) + .begin_filter(CallStub::new().with_parameter("package.version").create()) .is_ok()); assert_eq!( @@ -347,7 +338,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new(test_uuid()) + CallStub::new() .with_long_flag("major") .with_parameter("version") .create() @@ -375,7 +366,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new(test_uuid()) + CallStub::new() .with_long_flag("minor") .with_parameter("version") .create() @@ -404,7 +395,7 @@ mod tests { assert!(plugin .begin_filter( - CallStub::new(test_uuid()) + CallStub::new() .with_long_flag("patch") .with_parameter(&field) .create() @@ -425,8 +416,4 @@ mod tests { _ => {} } } - - fn test_uuid() -> uuid::Uuid { - uuid::Uuid::nil() - } } diff --git a/src/plugins/ps.rs b/src/plugins/ps.rs index 1ae9938d34..2db73d395a 100644 --- a/src/plugins/ps.rs +++ b/src/plugins/ps.rs @@ -40,7 +40,7 @@ async fn ps(tag: Tag) -> Vec> { let mut output = vec![]; while let Some(res) = processes.next().await { if let Ok((process, usage)) = res { - let mut dict = TaggedDictBuilder::new(tag); + let mut dict = TaggedDictBuilder::new(&tag); dict.insert("pid", Value::int(process.pid())); if let Ok(name) = process.name().await { dict.insert("name", Value::string(name)); diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 4635d60c35..60625e7f17 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -89,14 +89,12 @@ impl Str { impl Str { fn strutils(&self, value: Tagged) -> Result, ShellError> { match value.item { - Value::Primitive(Primitive::String(ref s)) => { - Ok(Tagged::from_item(self.apply(&s)?, value.tag())) - } + Value::Primitive(Primitive::String(ref s)) => Ok(self.apply(&s)?.tagged(value.tag())), Value::Row(_) => match self.field { Some(ref f) => { let replacement = match value.item.get_data_by_column_path(value.tag(), f) { Some(result) => self.strutils(result.map(|x| x.clone()))?, - None => return Ok(Tagged::from_item(Value::nothing(), value.tag)), + None => return Ok(Value::nothing().tagged(value.tag)), }; match value.item.replace_data_at_column_path( value.tag(), @@ -174,7 +172,7 @@ impl Plugin for Str { return Err(ShellError::labeled_error( "Unrecognized type in params", possible_field.type_name(), - possible_field.tag, + &possible_field.tag, )) } } @@ -216,13 +214,12 @@ mod tests { use super::{Action, Str}; use indexmap::IndexMap; use nu::{ - CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, SourceMap, Tag, Tagged, - TaggedDictBuilder, TaggedItem, Value, + CallInfo, EvaluatedArgs, Plugin, Primitive, ReturnSuccess, Tag, Tagged, TaggedDictBuilder, + TaggedItem, Value, }; use num_bigint::BigInt; struct CallStub { - anchor: uuid::Uuid, positionals: Vec>, flags: IndexMap>, } @@ -230,7 +227,6 @@ mod tests { impl CallStub { fn new() -> CallStub { CallStub { - anchor: uuid::Uuid::nil(), positionals: vec![], flags: indexmap::IndexMap::new(), } @@ -247,19 +243,18 @@ mod tests { fn with_parameter(&mut self, name: &str) -> &mut Self { let fields: Vec> = name .split(".") - .map(|s| Value::string(s.to_string()).tagged(Tag::unknown_span(self.anchor))) + .map(|s| Value::string(s.to_string()).tagged(Tag::unknown())) .collect(); self.positionals - .push(Value::Table(fields).tagged(Tag::unknown_span(self.anchor))); + .push(Value::Table(fields).tagged(Tag::unknown())); self } fn create(&self) -> CallInfo { CallInfo { args: EvaluatedArgs::new(Some(self.positionals.clone()), Some(self.flags.clone())), - source_map: SourceMap::new(), - name_tag: Tag::unknown_span(self.anchor), + name_tag: Tag::unknown(), } } } @@ -271,7 +266,7 @@ mod tests { } fn unstructured_sample_record(value: &str) -> Tagged { - Tagged::from_item(Value::string(value), Tag::unknown()) + Value::string(value).tagged(Tag::unknown()) } #[test] diff --git a/src/plugins/sum.rs b/src/plugins/sum.rs index 2bb89b74e1..d08d45713d 100644 --- a/src/plugins/sum.rs +++ b/src/plugins/sum.rs @@ -21,7 +21,7 @@ impl Sum { tag, }) => { //TODO: handle overflow - self.total = Some(Value::int(i + j).tagged(*tag)); + self.total = Some(Value::int(i + j).tagged(tag)); Ok(()) } None => { @@ -36,7 +36,7 @@ impl Sum { } } Value::Primitive(Primitive::Bytes(b)) => { - match self.total { + match &self.total { Some(Tagged { item: Value::Primitive(Primitive::Bytes(j)), tag, diff --git a/src/plugins/sys.rs b/src/plugins/sys.rs index 1f86b51d7e..55bf5028bf 100644 --- a/src/plugins/sys.rs +++ b/src/plugins/sys.rs @@ -80,7 +80,7 @@ async fn mem(tag: Tag) -> Tagged { } async fn host(tag: Tag) -> Tagged { - let mut dict = TaggedDictBuilder::with_capacity(tag, 6); + let mut dict = TaggedDictBuilder::with_capacity(&tag, 6); let (platform_result, uptime_result) = futures::future::join(host::platform(), host::uptime()).await; @@ -95,7 +95,7 @@ async fn host(tag: Tag) -> Tagged { // Uptime if let Ok(uptime) = uptime_result { - let mut uptime_dict = TaggedDictBuilder::with_capacity(tag, 4); + let mut uptime_dict = TaggedDictBuilder::with_capacity(&tag, 4); let uptime = uptime.get::().round() as i64; let days = uptime / (60 * 60 * 24); @@ -116,7 +116,10 @@ async fn host(tag: Tag) -> Tagged { let mut user_vec = vec![]; while let Some(user) = users.next().await { if let Ok(user) = user { - user_vec.push(Tagged::from_item(Value::string(user.username()), tag)); + user_vec.push(Tagged { + item: Value::string(user.username()), + tag: tag.clone(), + }); } } let user_list = Value::Table(user_vec); @@ -130,7 +133,7 @@ async fn disks(tag: Tag) -> Option { let mut partitions = disk::partitions_physical(); while let Some(part) = partitions.next().await { if let Ok(part) = part { - let mut dict = TaggedDictBuilder::with_capacity(tag, 6); + let mut dict = TaggedDictBuilder::with_capacity(&tag, 6); dict.insert( "device", Value::string( @@ -176,7 +179,7 @@ async fn battery(tag: Tag) -> Option { if let Ok(batteries) = manager.batteries() { for battery in batteries { if let Ok(battery) = battery { - let mut dict = TaggedDictBuilder::new(tag); + let mut dict = TaggedDictBuilder::new(&tag); if let Some(vendor) = battery.vendor() { dict.insert("vendor", Value::string(vendor)); } @@ -217,7 +220,7 @@ async fn temp(tag: Tag) -> Option { let mut sensors = sensors::temperatures(); while let Some(sensor) = sensors.next().await { if let Ok(sensor) = sensor { - let mut dict = TaggedDictBuilder::new(tag); + let mut dict = TaggedDictBuilder::new(&tag); dict.insert("unit", Value::string(sensor.unit())); if let Some(label) = sensor.label() { dict.insert("label", Value::string(label)); @@ -259,7 +262,7 @@ async fn net(tag: Tag) -> Option { let mut io_counters = net::io_counters(); while let Some(nic) = io_counters.next().await { if let Ok(nic) = nic { - let mut network_idx = TaggedDictBuilder::with_capacity(tag, 3); + let mut network_idx = TaggedDictBuilder::with_capacity(&tag, 3); network_idx.insert("name", Value::string(nic.interface())); network_idx.insert( "sent", @@ -280,11 +283,17 @@ async fn net(tag: Tag) -> Option { } async fn sysinfo(tag: Tag) -> Vec> { - let mut sysinfo = TaggedDictBuilder::with_capacity(tag, 7); + let mut sysinfo = TaggedDictBuilder::with_capacity(&tag, 7); - let (host, cpu, disks, memory, temp) = - futures::future::join5(host(tag), cpu(tag), disks(tag), mem(tag), temp(tag)).await; - let (net, battery) = futures::future::join(net(tag), battery(tag)).await; + let (host, cpu, disks, memory, temp) = futures::future::join5( + host(tag.clone()), + cpu(tag.clone()), + disks(tag.clone()), + mem(tag.clone()), + temp(tag.clone()), + ) + .await; + let (net, battery) = futures::future::join(net(tag.clone()), battery(tag.clone())).await; sysinfo.insert_tagged("host", host); if let Some(cpu) = cpu { diff --git a/src/plugins/textview.rs b/src/plugins/textview.rs index cce8bd7084..88507183e0 100644 --- a/src/plugins/textview.rs +++ b/src/plugins/textview.rs @@ -1,8 +1,7 @@ use crossterm::{cursor, terminal, RawScreen}; use crossterm::{InputEvent, KeyEvent}; use nu::{ - serve_plugin, AnchorLocation, CallInfo, Plugin, Primitive, ShellError, Signature, SourceMap, - Tagged, Value, + serve_plugin, AnchorLocation, CallInfo, Plugin, Primitive, ShellError, Signature, Tagged, Value, }; use syntect::easy::HighlightLines; @@ -29,8 +28,8 @@ impl Plugin for TextView { Ok(Signature::build("textview").desc("Autoview of text data.")) } - fn sink(&mut self, call_info: CallInfo, input: Vec>) { - view_text_value(&input[0], &call_info.source_map); + fn sink(&mut self, _call_info: CallInfo, input: Vec>) { + view_text_value(&input[0]); } } @@ -215,20 +214,18 @@ fn scroll_view(s: &str) { scroll_view_lines_if_needed(v, false); } -fn view_text_value(value: &Tagged, source_map: &SourceMap) { +fn view_text_value(value: &Tagged) { let value_anchor = value.anchor(); match value.item { Value::Primitive(Primitive::String(ref s)) => { - let source = source_map.get(&value_anchor); - - if let Some(source) = source { + if let Some(source) = value_anchor { let extension: Option = match source { AnchorLocation::File(file) => { - let path = Path::new(file); + let path = Path::new(&file); path.extension().map(|x| x.to_string_lossy().to_string()) } AnchorLocation::Url(url) => { - let url = url::Url::parse(url); + let url = url::Url::parse(&url); if let Ok(url) = url { let url = url.clone(); if let Some(mut segments) = url.path_segments() { diff --git a/src/prelude.rs b/src/prelude.rs index 1f80126a4f..4b12a07bda 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -66,7 +66,7 @@ pub(crate) use crate::commands::RawCommandArgs; pub(crate) use crate::context::CommandRegistry; pub(crate) use crate::context::{AnchorLocation, Context}; pub(crate) use crate::data::base as value; -pub(crate) use crate::data::meta::{Tag, Tagged, TaggedItem}; +pub(crate) use crate::data::meta::{Span, Spanned, SpannedItem, Tag, Tagged, TaggedItem}; pub(crate) use crate::data::types::ExtractType; pub(crate) use crate::data::{Primitive, Value}; pub(crate) use crate::env::host::handle_unexpected; @@ -109,6 +109,22 @@ where } } +pub trait ToInputStream { + fn to_input_stream(self) -> InputStream; +} + +impl ToInputStream for T +where + T: Stream + Send + 'static, + U: Into, ShellError>>, +{ + fn to_input_stream(self) -> InputStream { + InputStream { + values: self.map(|item| item.into().unwrap()).boxed(), + } + } +} + pub trait ToOutputStream { fn to_output_stream(self) -> OutputStream; } diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index aec736ec0f..72a0c241f3 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -3,7 +3,6 @@ use crate::commands::cp::CopyArgs; use crate::commands::mkdir::MkdirArgs; use crate::commands::mv::MoveArgs; use crate::commands::rm::RemoveArgs; -use crate::context::SourceMap; use crate::data::dir_entry_dict; use crate::prelude::*; use crate::shell::completer::NuCompleter; @@ -12,6 +11,7 @@ use crate::utils::FileStructure; use rustyline::completion::FilenameCompleter; use rustyline::hint::{Hinter, HistoryHinter}; use std::path::{Path, PathBuf}; +use std::sync::atomic::Ordering; pub struct FilesystemShell { pub(crate) path: String, @@ -73,7 +73,7 @@ impl FilesystemShell { } impl Shell for FilesystemShell { - fn name(&self, _source_map: &SourceMap) -> String { + fn name(&self) -> String { "filesystem".to_string() } @@ -84,7 +84,7 @@ impl Shell for FilesystemShell { fn ls( &self, pattern: Option>, - command_tag: Tag, + context: &RunnableContext, ) -> Result { let cwd = self.path(); let mut full_path = PathBuf::from(self.path()); @@ -94,7 +94,8 @@ impl Shell for FilesystemShell { _ => {} } - let mut shell_entries = VecDeque::new(); + let ctrl_c = context.ctrl_c.clone(); + let name_tag = context.name.clone(); //If it's not a glob, try to display the contents of the entry if it's a directory let lossy_path = full_path.to_string_lossy(); @@ -114,24 +115,30 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( e.to_string(), e.to_string(), - command_tag, + name_tag, )); } } Ok(o) => o, }; - for entry in entries { - let entry = entry?; - let filepath = entry.path(); - let filename = if let Ok(fname) = filepath.strip_prefix(&cwd) { - fname - } else { - Path::new(&filepath) - }; - let value = dir_entry_dict(filename, &entry.metadata()?, command_tag)?; - shell_entries.push_back(ReturnSuccess::value(value)) - } - return Ok(shell_entries.to_output_stream()); + let stream = async_stream! { + for entry in entries { + if ctrl_c.load(Ordering::SeqCst) { + break; + } + if let Ok(entry) = entry { + let filepath = entry.path(); + let filename = if let Ok(fname) = filepath.strip_prefix(&cwd) { + fname + } else { + Path::new(&filepath) + }; + let value = dir_entry_dict(filename, &entry.metadata().unwrap(), &name_tag)?; + yield ReturnSuccess::value(value); + } + } + }; + return Ok(stream.to_output_stream()); } } @@ -151,20 +158,25 @@ impl Shell for FilesystemShell { }; // Enumerate the entries from the glob and add each - for entry in entries { - if let Ok(entry) = entry { - let filename = if let Ok(fname) = entry.strip_prefix(&cwd) { - fname - } else { - Path::new(&entry) - }; - let metadata = std::fs::metadata(&entry)?; - let value = dir_entry_dict(filename, &metadata, command_tag)?; - shell_entries.push_back(ReturnSuccess::value(value)) + let stream = async_stream! { + for entry in entries { + if ctrl_c.load(Ordering::SeqCst) { + break; + } + if let Ok(entry) = entry { + let filename = if let Ok(fname) = entry.strip_prefix(&cwd) { + fname + } else { + Path::new(&entry) + }; + let metadata = std::fs::metadata(&entry).unwrap(); + if let Ok(value) = dir_entry_dict(filename, &metadata, &name_tag) { + yield ReturnSuccess::value(value); + } + } } - } - - Ok(shell_entries.to_output_stream()) + }; + Ok(stream.to_output_stream()) } fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { @@ -175,7 +187,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "Can not change to home directory", "can not go to home", - args.call_info.name_tag, + &args.call_info.name_tag, )) } }, @@ -957,7 +969,7 @@ impl Shell for FilesystemShell { return Err(ShellError::labeled_error( "unable to show current directory", "pwd command failed", - args.call_info.name_tag, + &args.call_info.name_tag, )); } }; @@ -965,7 +977,7 @@ impl Shell for FilesystemShell { let mut stream = VecDeque::new(); stream.push_back(ReturnSuccess::value( Value::Primitive(Primitive::String(p.to_string_lossy().to_string())) - .tagged(args.call_info.name_tag), + .tagged(&args.call_info.name_tag), )); Ok(stream.into()) diff --git a/src/shell/help_shell.rs b/src/shell/help_shell.rs index 0fedd9ad79..7c0e74bde4 100644 --- a/src/shell/help_shell.rs +++ b/src/shell/help_shell.rs @@ -3,7 +3,6 @@ use crate::commands::cp::CopyArgs; use crate::commands::mkdir::MkdirArgs; use crate::commands::mv::MoveArgs; use crate::commands::rm::RemoveArgs; -use crate::context::SourceMap; use crate::data::{command_dict, TaggedDictBuilder}; use crate::prelude::*; use crate::shell::shell::Shell; @@ -98,8 +97,8 @@ impl HelpShell { } impl Shell for HelpShell { - fn name(&self, source_map: &SourceMap) -> String { - let anchor_name = self.value.anchor_name(source_map); + fn name(&self) -> String { + let anchor_name = self.value.anchor_name(); format!( "{}", match anchor_name { @@ -129,7 +128,7 @@ impl Shell for HelpShell { fn ls( &self, _pattern: Option>, - _command_tag: Tag, + _context: &RunnableContext, ) -> Result { Ok(self .commands() diff --git a/src/shell/helper.rs b/src/shell/helper.rs index b590d82826..dc3ab96dc1 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -3,7 +3,7 @@ use crate::parser::hir::syntax_shape::{color_fallible_syntax, FlatShape, Pipelin use crate::parser::hir::TokensIterator; use crate::parser::nom_input; use crate::parser::parse::token_tree::TokenNode; -use crate::{Tag, Tagged, TaggedItem, Text}; +use crate::{Span, Spanned, SpannedItem, Tag, Tagged, Text}; use ansi_term::Color; use log::trace; use rustyline::completion::Completer; @@ -67,7 +67,7 @@ impl Highlighter for Helper { } fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> { - let tokens = crate::parser::pipeline(nom_input(line, uuid::Uuid::nil())); + let tokens = crate::parser::pipeline(nom_input(line)); match tokens { Err(_) => Cow::Borrowed(line), @@ -78,13 +78,13 @@ impl Highlighter for Helper { Ok(v) => v, }; - let tokens = vec![TokenNode::Pipeline(pipeline.clone().tagged(v.tag()))]; - let mut tokens = TokensIterator::all(&tokens[..], v.tag()); + let tokens = vec![TokenNode::Pipeline(pipeline.clone().spanned(v.span()))]; + let mut tokens = TokensIterator::all(&tokens[..], v.span()); let text = Text::from(line); let expand_context = self .context - .expand_context(&text, Tag::from((0, line.len() - 1, uuid::Uuid::nil()))); + .expand_context(&text, Span::new(0, line.len() - 1)); let mut shapes = vec![]; // We just constructed a token list that only contains a pipeline, so it can't fail @@ -126,16 +126,16 @@ impl Highlighter for Helper { #[allow(unused)] fn vec_tag(input: Vec>) -> Option { let mut iter = input.iter(); - let first = iter.next()?.tag; + let first = iter.next()?.tag.clone(); let last = iter.last(); Some(match last { None => first, - Some(last) => first.until(last.tag), + Some(last) => first.until(&last.tag), }) } -fn paint_flat_shape(flat_shape: Tagged, line: &str) -> String { +fn paint_flat_shape(flat_shape: Spanned, line: &str) -> String { let style = match &flat_shape.item { FlatShape::OpenDelimiter(_) => Color::White.normal(), FlatShape::CloseDelimiter(_) => Color::White.normal(), @@ -170,7 +170,7 @@ fn paint_flat_shape(flat_shape: Tagged, line: &str) -> String { } }; - let body = flat_shape.tag.slice(line); + let body = flat_shape.span.slice(line); style.paint(body).to_string() } diff --git a/src/shell/shell.rs b/src/shell/shell.rs index c567e474a3..507fc0517b 100644 --- a/src/shell/shell.rs +++ b/src/shell/shell.rs @@ -3,20 +3,19 @@ use crate::commands::cp::CopyArgs; use crate::commands::mkdir::MkdirArgs; use crate::commands::mv::MoveArgs; use crate::commands::rm::RemoveArgs; -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 name(&self) -> String; fn homedir(&self) -> Option; fn ls( &self, pattern: Option>, - command_tag: Tag, + context: &RunnableContext, ) -> Result; fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result; fn cp(&self, args: CopyArgs, name: Tag, path: &str) -> Result; diff --git a/src/shell/shell_manager.rs b/src/shell/shell_manager.rs index c4c42367ed..149fdd58d1 100644 --- a/src/shell/shell_manager.rs +++ b/src/shell/shell_manager.rs @@ -10,18 +10,19 @@ use crate::shell::shell::Shell; use crate::stream::OutputStream; use std::error::Error; use std::path::PathBuf; +use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; #[derive(Clone, Debug)] pub struct ShellManager { - pub(crate) current_shell: usize, + pub(crate) current_shell: Arc, pub(crate) shells: Arc>>>, } impl ShellManager { pub fn basic(commands: CommandRegistry) -> Result> { Ok(ShellManager { - current_shell: 0, + current_shell: Arc::new(AtomicUsize::new(0)), shells: Arc::new(Mutex::new(vec![Box::new(FilesystemShell::basic( commands, )?)])), @@ -30,24 +31,29 @@ impl ShellManager { pub fn insert_at_current(&mut self, shell: Box) { self.shells.lock().unwrap().push(shell); - self.current_shell = self.shells.lock().unwrap().len() - 1; + self.current_shell + .store(self.shells.lock().unwrap().len() - 1, Ordering::SeqCst); self.set_path(self.path()); } + pub fn current_shell(&self) -> usize { + self.current_shell.load(Ordering::SeqCst) + } + pub fn remove_at_current(&mut self) { { let mut shells = self.shells.lock().unwrap(); if shells.len() > 0 { - if self.current_shell == shells.len() - 1 { + if self.current_shell() == shells.len() - 1 { shells.pop(); let new_len = shells.len(); if new_len > 0 { - self.current_shell = new_len - 1; + self.current_shell.store(new_len - 1, Ordering::SeqCst); } else { return; } } else { - shells.remove(self.current_shell); + shells.remove(self.current_shell()); } } } @@ -59,17 +65,17 @@ impl ShellManager { } pub fn path(&self) -> String { - self.shells.lock().unwrap()[self.current_shell].path() + self.shells.lock().unwrap()[self.current_shell()].path() } pub fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let env = self.shells.lock().unwrap(); - env[self.current_shell].pwd(args) + env[self.current_shell()].pwd(args) } pub fn set_path(&mut self, path: String) { - self.shells.lock().unwrap()[self.current_shell].set_path(path) + self.shells.lock().unwrap()[self.current_shell()].set_path(path) } pub fn complete( @@ -78,20 +84,21 @@ impl ShellManager { pos: usize, ctx: &rustyline::Context<'_>, ) -> Result<(usize, Vec), rustyline::error::ReadlineError> { - self.shells.lock().unwrap()[self.current_shell].complete(line, pos, ctx) + self.shells.lock().unwrap()[self.current_shell()].complete(line, pos, ctx) } pub fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { - self.shells.lock().unwrap()[self.current_shell].hint(line, pos, ctx) + self.shells.lock().unwrap()[self.current_shell()].hint(line, pos, ctx) } pub fn next(&mut self) { { let shell_len = self.shells.lock().unwrap().len(); - if self.current_shell == (shell_len - 1) { - self.current_shell = 0; + if self.current_shell() == (shell_len - 1) { + self.current_shell.store(0, Ordering::SeqCst); } else { - self.current_shell += 1; + self.current_shell + .store(self.current_shell() + 1, Ordering::SeqCst); } } self.set_path(self.path()); @@ -100,10 +107,11 @@ impl ShellManager { pub fn prev(&mut self) { { let shell_len = self.shells.lock().unwrap().len(); - if self.current_shell == 0 { - self.current_shell = shell_len - 1; + if self.current_shell() == 0 { + self.current_shell.store(shell_len - 1, Ordering::SeqCst); } else { - self.current_shell -= 1; + self.current_shell + .store(self.current_shell() - 1, Ordering::SeqCst); } } self.set_path(self.path()); @@ -112,23 +120,23 @@ impl ShellManager { pub fn homedir(&self) -> Option { let env = self.shells.lock().unwrap(); - env[self.current_shell].homedir() + env[self.current_shell()].homedir() } pub fn ls( &self, path: Option>, - command_tag: Tag, + context: &RunnableContext, ) -> Result { let env = self.shells.lock().unwrap(); - env[self.current_shell].ls(path, command_tag) + env[self.current_shell()].ls(path, context) } pub fn cd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let env = self.shells.lock().unwrap(); - env[self.current_shell].cd(args) + env[self.current_shell()].cd(args) } pub fn cp( @@ -140,13 +148,13 @@ impl ShellManager { match env { Ok(x) => { - let path = x[self.current_shell].path(); - x[self.current_shell].cp(args, context.name, &path) + let path = x[self.current_shell()].path(); + x[self.current_shell()].cp(args, context.name.clone(), &path) } Err(e) => Err(ShellError::labeled_error( format!("Internal error: could not lock {}", e), "Internal error: could not lock", - context.name, + &context.name, )), } } @@ -160,13 +168,13 @@ impl ShellManager { match env { Ok(x) => { - let path = x[self.current_shell].path(); - x[self.current_shell].rm(args, context.name, &path) + let path = x[self.current_shell()].path(); + x[self.current_shell()].rm(args, context.name.clone(), &path) } Err(e) => Err(ShellError::labeled_error( format!("Internal error: could not lock {}", e), "Internal error: could not lock", - context.name, + &context.name, )), } } @@ -180,13 +188,13 @@ impl ShellManager { match env { Ok(x) => { - let path = x[self.current_shell].path(); - x[self.current_shell].mkdir(args, context.name, &path) + let path = x[self.current_shell()].path(); + x[self.current_shell()].mkdir(args, context.name.clone(), &path) } Err(e) => Err(ShellError::labeled_error( format!("Internal error: could not lock {}", e), "Internal error: could not lock", - context.name, + &context.name, )), } } @@ -200,13 +208,13 @@ impl ShellManager { match env { Ok(x) => { - let path = x[self.current_shell].path(); - x[self.current_shell].mv(args, context.name, &path) + let path = x[self.current_shell()].path(); + x[self.current_shell()].mv(args, context.name.clone(), &path) } Err(e) => Err(ShellError::labeled_error( format!("Internal error: could not lock {}", e), "Internal error: could not lock", - context.name, + &context.name, )), } } diff --git a/src/shell/value_shell.rs b/src/shell/value_shell.rs index d95d07cb97..0aa9e341bb 100644 --- a/src/shell/value_shell.rs +++ b/src/shell/value_shell.rs @@ -3,7 +3,6 @@ use crate::commands::cp::CopyArgs; use crate::commands::mkdir::MkdirArgs; use crate::commands::mv::MoveArgs; use crate::commands::rm::RemoveArgs; -use crate::context::SourceMap; use crate::prelude::*; use crate::shell::shell::Shell; use crate::utils::ValueStructure; @@ -72,8 +71,8 @@ impl ValueShell { } impl Shell for ValueShell { - fn name(&self, source_map: &SourceMap) -> String { - let anchor_name = self.value.anchor_name(source_map); + fn name(&self) -> String { + let anchor_name = self.value.anchor_name(); format!( "{}", match anchor_name { @@ -90,9 +89,10 @@ impl Shell for ValueShell { fn ls( &self, target: Option>, - command_name: Tag, + context: &RunnableContext, ) -> Result { let mut full_path = PathBuf::from(self.path()); + let name_tag = context.name.clone(); match &target { Some(value) => full_path.push(value.as_ref()), @@ -114,7 +114,7 @@ impl Shell for ValueShell { return Err(ShellError::labeled_error( "Can not list entries inside", "No such path exists", - command_name, + name_tag, )); } @@ -166,7 +166,7 @@ impl Shell for ValueShell { return Err(ShellError::labeled_error( "Can not change to path inside", "No such path exists", - args.call_info.name_tag, + &args.call_info.name_tag, )); } @@ -213,10 +213,9 @@ impl Shell for ValueShell { fn pwd(&self, args: EvaluatedWholeStreamCommandArgs) -> Result { let mut stream = VecDeque::new(); - stream.push_back(ReturnSuccess::value(Tagged::from_item( - Value::string(self.path()), - args.call_info.name_tag, - ))); + stream.push_back(ReturnSuccess::value( + Value::string(self.path()).tagged(&args.call_info.name_tag), + )); Ok(stream.into()) } diff --git a/src/stream.rs b/src/stream.rs index 066acb74a1..f6f2d5e2e1 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -23,6 +23,17 @@ impl InputStream { } } +impl Stream for InputStream { + type Item = Tagged; + + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> core::task::Poll> { + Stream::poll_next(std::pin::Pin::new(&mut self.values), cx) + } +} + impl From>> for InputStream { fn from(input: BoxStream<'static, Tagged>) -> InputStream { InputStream { values: input } diff --git a/tests/command_config_test.rs b/tests/command_config_test.rs index dd0f4e0ebb..8a45be47c5 100644 --- a/tests/command_config_test.rs +++ b/tests/command_config_test.rs @@ -86,30 +86,30 @@ fn sets_configuration_value() { h::delete_file_at(nu::config_path().unwrap().join("test_4.toml")); } -#[test] -fn removes_configuration_value() { - Playground::setup("config_test_5", |dirs, sandbox| { - sandbox.with_files(vec![FileWithContent( - "test_5.toml", - r#" - caballeros = [1, 1, 1] - podershell = [1, 1, 1] - "#, - )]); +// #[test] +// fn removes_configuration_value() { +// Playground::setup("config_test_5", |dirs, sandbox| { +// sandbox.with_files(vec![FileWithContent( +// "test_5.toml", +// r#" +// caballeros = [1, 1, 1] +// podershell = [1, 1, 1] +// "#, +// )]); - nu!( - cwd: dirs.test(), - "config --load test_5.toml --remove podershell" - ); +// nu!( +// cwd: dirs.test(), +// "config --load test_5.toml --remove podershell" +// ); - let actual = nu_error!( - cwd: dirs.root(), - r#"open "{}/test_5.toml" | get podershell | echo $it"#, - dirs.config_path() - ); +// let actual = nu_error!( +// cwd: dirs.root(), +// r#"open "{}/test_5.toml" | get podershell | echo $it"#, +// dirs.config_path() +// ); - assert!(actual.contains("Unknown column")); - }); +// assert!(actual.contains("Unknown column")); +// }); - h::delete_file_at(nu::config_path().unwrap().join("test_5.toml")); -} +// h::delete_file_at(nu::config_path().unwrap().join("test_5.toml")); +// } diff --git a/tests/command_open_tests.rs b/tests/command_open_tests.rs index e9047883cf..53e393eef4 100644 --- a/tests/command_open_tests.rs +++ b/tests/command_open_tests.rs @@ -222,7 +222,7 @@ fn open_can_parse_utf16_ini() { fn errors_if_file_not_found() { let actual = nu_error!( cwd: "tests/fixtures/formats", - "open i_dont_exist.txt | echo $it" + "open i_dont_exist.txt" ); assert!(actual.contains("File could not be opened")); From 2716bb020f537470511f1036b1ef95c029a455d7 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sun, 13 Oct 2019 17:53:58 +1300 Subject: [PATCH 252/342] Fix #811 (#813) --- Cargo.lock | 1046 ++++++++++++++------------------ Cargo.toml | 4 +- src/parser/parse/token_tree.rs | 3 +- 3 files changed, 474 insertions(+), 579 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 765f42d637..da47189204 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,7 +2,7 @@ # It is not intended for manual editing. [[package]] name = "adler32" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -47,10 +47,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "arrayvec" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", + "nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -67,9 +67,9 @@ name = "async-stream-impl" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -77,24 +77,24 @@ name = "atty" version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "autocfg" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" -version = "0.3.34" +version = "0.3.38" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -103,7 +103,7 @@ version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -119,10 +119,10 @@ name = "battery" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -143,27 +143,27 @@ dependencies = [ [[package]] name = "bincode" -version = "1.1.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bitflags" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "blake2b_simd" -version = "0.5.6" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -180,21 +180,21 @@ dependencies = [ "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "decimal 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "md5 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "bstr" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", @@ -202,12 +202,12 @@ dependencies = [ [[package]] name = "bumpalo" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "byte-unit" -version = "3.0.1" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -226,7 +226,7 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -234,7 +234,7 @@ name = "c2-chacha" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -243,13 +243,13 @@ name = "cc" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "jobserver 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", + "jobserver 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "cfg-if" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -257,7 +257,7 @@ name = "chrono" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", @@ -279,7 +279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -311,7 +311,7 @@ name = "cloudabi" version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -319,19 +319,19 @@ name = "config" version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "constant_time_eq" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -340,7 +340,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -353,7 +353,7 @@ name = "crc32fast" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -374,8 +374,8 @@ name = "crossbeam-utils" version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -409,7 +409,7 @@ dependencies = [ "crossterm_screen 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -441,7 +441,7 @@ dependencies = [ "crossterm_cursor 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "crossterm_utils 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -450,7 +450,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -467,10 +467,10 @@ name = "csv" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bstr 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -484,11 +484,11 @@ dependencies = [ [[package]] name = "ctor" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -502,30 +502,29 @@ dependencies = [ [[package]] name = "curl" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "curl-sys 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "curl-sys 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "curl-sys" -version = "0.4.20" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "libnghttp2-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -536,7 +535,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "darwin-libproc-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -545,7 +544,7 @@ name = "darwin-libproc-sys" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -553,9 +552,9 @@ name = "decimal" version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "ord_subset 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", @@ -566,7 +565,7 @@ name = "deflate" version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -575,9 +574,9 @@ name = "derive-new" version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -590,7 +589,7 @@ name = "directories" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -599,7 +598,7 @@ name = "dirs" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -609,7 +608,7 @@ name = "dirs" version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -618,8 +617,8 @@ name = "dirs-sys" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -636,66 +635,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "either" -version = "1.5.2" +version = "1.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "encode_unicode" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "enum-utils" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "enum-utils-from-str 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.24.1 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "enum-utils-from-str" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "env_logger" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", + "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "failure_derive" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -715,13 +692,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "flate2" -version = "1.0.9" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -736,7 +713,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "futures" -version = "0.1.28" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -804,7 +781,7 @@ name = "futures-util-preview" version = "0.3.0-alpha.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-io-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -826,11 +803,12 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.1.8" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -838,9 +816,9 @@ name = "getset" version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -848,8 +826,8 @@ name = "git2" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "libgit2-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -870,33 +848,33 @@ dependencies = [ [[package]] name = "heim" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-cpu 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-disk 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-host 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-memory 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-net 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-process 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-sensors 0.0.3-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-virt 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-cpu 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-disk 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-host 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-memory 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-net 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-process 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-sensors 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-virt 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-common" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -906,41 +884,41 @@ dependencies = [ [[package]] name = "heim-cpu" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-derive" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-disk" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -948,66 +926,66 @@ dependencies = [ [[package]] name = "heim-host" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "platforms 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "platforms 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-memory" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-net" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "macaddr 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-process" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "darwin-libproc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-cpu 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-host 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-net 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-cpu 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-host 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-net 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "mach 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1017,35 +995,35 @@ dependencies = [ [[package]] name = "heim-runtime" -version = "0.0.4-alpha.1" +version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "threadpool 1.7.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-sensors" -version = "0.0.3-alpha.1" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-derive 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "heim-virt" -version = "0.0.8-alpha.1" +version = "0.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", - "heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", + "heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "raw-cpuid 7.0.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1071,7 +1049,7 @@ dependencies = [ [[package]] name = "humantime" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1089,11 +1067,11 @@ dependencies = [ [[package]] name = "image" -version = "0.22.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", + "jpeg-decoder 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1113,35 +1091,34 @@ name = "inflate" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "iovec" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "isahc" -version = "0.7.1" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-channel 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", - "curl 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)", - "curl-sys 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)", + "curl 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)", + "curl-sys 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)", "futures-io-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sluice 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "sluice 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1149,8 +1126,8 @@ name = "isatty" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1160,7 +1137,7 @@ name = "itertools" version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1168,7 +1145,7 @@ name = "itertools" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1178,17 +1155,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "jobserver" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "jpeg-decoder" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1196,10 +1173,10 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1221,7 +1198,7 @@ dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "render-tree 0.1.1 (git+https://github.com/wycats/language-reporting)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1232,7 +1209,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazy_static" -version = "1.3.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1242,19 +1219,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lexical-core" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "stackvector 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "libc" -version = "0.2.60" +version = "0.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1263,9 +1240,9 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1274,7 +1251,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1283,7 +1260,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1293,8 +1270,8 @@ version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1303,7 +1280,7 @@ name = "line-wrap" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1325,7 +1302,7 @@ name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1346,7 +1323,7 @@ name = "mach" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1354,7 +1331,7 @@ name = "mach" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1362,7 +1339,7 @@ name = "malloc_buf" version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1380,7 +1357,7 @@ name = "memchr" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1394,35 +1371,15 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", - "unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz-sys" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "miniz_oxide" -version = "0.3.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "miniz_oxide_c_api" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "miniz_oxide 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1435,12 +1392,12 @@ name = "neso" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1448,10 +1405,10 @@ name = "nix" version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1460,16 +1417,16 @@ name = "nix" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "nodrop" -version = "0.1.13" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1483,10 +1440,10 @@ dependencies = [ [[package]] name = "nom" -version = "5.0.0" +version = "5.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lexical-core 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", + "lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1496,7 +1453,7 @@ name = "nom-tracable" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "nom-tracable-macros 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1507,7 +1464,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1517,7 +1474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1539,7 +1496,7 @@ dependencies = [ "battery 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", "bigdecimal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "bson 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", - "byte-unit 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "byte-unit 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1551,16 +1508,15 @@ dependencies = [ "derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "enum-utils 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "getset 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "git2 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "heim 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)", + "heim 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "image 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)", + "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "language-reporting 0.3.1 (git+https://github.com/wycats/language-reporting)", @@ -1568,21 +1524,21 @@ dependencies = [ "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "natural 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "nom-tracable 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)", - "pretty-hex 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "pretty-hex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "ptree 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rawkey 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "roxmltree 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1590,9 +1546,9 @@ dependencies = [ "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)", "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_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)", "shellexpand 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "sublime_fuzzy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1612,7 +1568,7 @@ name = "num-bigint" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1623,7 +1579,7 @@ name = "num-integer" version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1632,7 +1588,7 @@ name = "num-iter" version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1642,7 +1598,7 @@ name = "num-rational" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1660,7 +1616,7 @@ name = "num-traits" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1668,7 +1624,7 @@ name = "num_cpus" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1708,12 +1664,12 @@ dependencies = [ [[package]] name = "onig" -version = "4.3.2" +version = "4.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1723,7 +1679,7 @@ version = "69.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1733,13 +1689,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.49" +version = "0.9.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1790,12 +1746,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pkg-config" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "platforms" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1805,7 +1761,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "line-wrap 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1816,7 +1772,7 @@ name = "png" version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "deflate 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)", "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1829,7 +1785,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "pretty-hex" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1838,7 +1794,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", + "ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "difference 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "output_vt100 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1860,23 +1816,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)", "csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "proc-macro2" -version = "0.4.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "proc-macro2" -version = "1.0.1" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1894,7 +1842,7 @@ dependencies = [ "petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde-value 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "tint 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1903,31 +1851,23 @@ name = "quick-error" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "quote" -version = "0.6.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rand" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1937,7 +1877,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1955,10 +1895,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rand_core" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1966,7 +1906,7 @@ name = "rand_hc" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1976,7 +1916,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)", "fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rdrand 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1987,7 +1927,7 @@ name = "raw-cpuid" version = "7.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2026,7 +1966,7 @@ name = "redox_users" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2034,12 +1974,12 @@ dependencies = [ [[package]] name = "regex" -version = "1.2.1" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2053,7 +1993,7 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.11" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2081,10 +2021,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "roxmltree" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "xmlparser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "xmlparser 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2092,7 +2032,7 @@ name = "rusqlite" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", "libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2107,7 +2047,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", - "blake2b_simd 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", + "blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "crossbeam-utils 0.6.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2118,7 +2058,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc-demangle" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2140,7 +2080,7 @@ version = "5.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2152,12 +2092,12 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "safemem" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2170,10 +2110,10 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2200,7 +2140,7 @@ name = "serde" version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2211,7 +2151,7 @@ dependencies = [ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2220,10 +2160,10 @@ name = "serde-hjson" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2246,21 +2186,12 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.98" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "serde_derive_internals" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2275,12 +2206,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", - "ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2305,7 +2236,7 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.9" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2335,7 +2266,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "sluice" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2353,8 +2284,8 @@ name = "socket2" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2364,18 +2295,9 @@ name = "sourcefile" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "stackvector" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", - "unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "static_assertions" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2394,7 +2316,7 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-utils 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2405,49 +2327,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)", - "isahc 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "isahc 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-futures 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)", - "web-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-futures 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "syn" -version = "0.15.43" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "syn" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "synstructure" -version = "0.10.2" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2455,18 +2367,18 @@ name = "syntect" version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "onig 4.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "onig 4.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "plist 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)", "walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2476,9 +2388,9 @@ name = "tempfile" version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2500,7 +2412,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2509,7 +2421,7 @@ name = "termcolor" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2526,7 +2438,7 @@ name = "thread_local" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2542,7 +2454,7 @@ name = "time" version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2561,7 +2473,7 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2583,12 +2495,12 @@ dependencies = [ [[package]] name = "typenum" -version = "1.10.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicase" -version = "2.4.0" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2620,31 +2532,18 @@ name = "unicode-width" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "unicode-xid" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "unreachable" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "uom" version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2654,7 +2553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-rational 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2711,93 +2610,100 @@ dependencies = [ "winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "wasi" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "wasm-bindgen" -version = "0.2.50" +version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.50" +version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bumpalo 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-futures" -version = "0.3.25" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", "futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.50" +version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-macro-support 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-macro-support 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.50" +version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-shared 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.50" +version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wasm-bindgen-webidl" -version = "0.2.50" +version = "0.2.51" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-backend 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", "weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "web-sys" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)", "sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", - "wasm-bindgen-webidl 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", + "wasm-bindgen-webidl 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2813,8 +2719,8 @@ name = "which" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2861,7 +2767,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wincolor" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2873,8 +2779,8 @@ name = "x11" version = "2.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", - "pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2890,7 +2796,7 @@ name = "xcb" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2906,7 +2812,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "xmlparser" -version = "0.9.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2918,36 +2824,36 @@ dependencies = [ ] [metadata] -"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" +"checksum adler32 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" "checksum app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e73a24bad9bd6a94d6395382a6c69fe071708ae4409f763c5475e14ee896313d" "checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee" -"checksum arrayvec 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b8d73f9beda665eaa98ab9e4f7442bd4e7de6652587de55b2525e52e29c1b0ba" +"checksum arrayvec 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" "checksum async-stream 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "650be9b667e47506c42ee53034fb1935443cb2447a3a5c0a75e303d2e756fa73" "checksum async-stream-impl 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4f0d8c5b411e36dcfb04388bacfec54795726b1f0148adcb0f377a96d6747e0e" "checksum atty 0.2.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1803c647a3ec87095e7ae7acfca019e98de5ec9a7d01343f611cf3152ed71a90" -"checksum autocfg 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "22130e92352b948e7e82a49cdb0aa94f2211761117f29e052dd397c1ac33542b" -"checksum backtrace 0.3.34 (registry+https://github.com/rust-lang/crates.io-index)" = "b5164d292487f037ece34ec0de2fcede2faa162f085dd96d2385ab81b12765ba" +"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" +"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5" "checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum battery 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6d6fe5630049e900227cd89afce4c1204b88ec8e61a2581bb96fcce26f047b" "checksum bigdecimal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "460825c9e21708024d67c07057cd5560e5acdccac85de0de624a81d3de51bacb" -"checksum bincode 1.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7" -"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd" -"checksum blake2b_simd 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "461f4b879a8eb70c1debf7d0788a9a5ff15f1ea9d25925fea264ef4258bed6b2" +"checksum bincode 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8ab639324e3ee8774d296864fbc0dbbb256cf1a41c490b94cba90c082915f92" +"checksum bitflags 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8a606a02debe2813760609f57a64a2ffd27d9fdf5b2f133eaca0b248dd92cdd2" +"checksum blake2b_simd 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5850aeee1552f495dd0250014cf64b82b7c8879a89d83b33bbdace2cc4f63182" "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" "checksum bson 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d61895d21e2194d1ce1d434cff69025daac1e49a8b4698eb04b05722dbc08b33" -"checksum bstr 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "e0a692f1c740e7e821ca71a22cf99b9b2322dfa94d10f71443befb1797b3946a" -"checksum bumpalo 2.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2cd43d82f27d68911e6ee11ee791fb248f138f5d69424dc02e098d4f152b0b05" -"checksum byte-unit 3.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90139954ec9776c4832d44f212e558ccdacbe915a881bf3de3a1a487fa8d1e87" +"checksum bstr 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8d6c2c5b58ab920a4f5aeaaca34b4488074e8cc7596af94e6f8c6ff247c60245" +"checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708" +"checksum byte-unit 3.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6894a79550807490d9f19a138a6da0f8830e70c83e83402dd23f16fd6c479056" "checksum bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f861d9ce359f56dbcb6e0c2a1cb84e52ad732cadb57b806adeb3c7668caccbd8" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)" = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" "checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" "checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be" -"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" +"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e8493056968583b0193c1bb04d6f7684586f3726992d6c573261941a895dbd68" "checksum chrono-humanize 0.0.11 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2ff48a655fe8d2dae9a39e66af7fd8ff32a879e8c4e27422c25596a8b5e90d" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" @@ -2955,7 +2861,7 @@ dependencies = [ "checksum clipboard-win 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e3a093d6fed558e5fe24c3dfc85a68bb68f1c824f440d3ba5aca189e2998786b" "checksum cloudabi 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" "checksum config 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9107d78ed62b3fa5a86e7d18e647abed48cfd8f8fab6c72f4cdb982d196f7e6" -"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e" +"checksum constant_time_eq 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "995a44c877f9212528ccc74b21a232f66ad69001e40ede5bcee2ac9ef2657120" "checksum core-foundation 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "25b9e03f145fd4f2bf705e07b900cd41fc636598fe5dc452fd0db1441c3f496d" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" @@ -2972,10 +2878,10 @@ dependencies = [ "checksum crossterm_winapi 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b055e7cc627c452e6a9b977022f48a2db6f0ff73df446ca970f95eef9c381d45" "checksum csv 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "37519ccdfd73a75821cac9319d4fce15a81b9fcf75f951df5b9988aa3a0af87d" "checksum csv-core 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9b5cadb6b25c77aeff80ba701712494213f4a8418fcda2ee11b6560c3ad0bf4c" -"checksum ctor 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3b4c17619643c1252b5f690084b82639dd7fac141c57c8e77a00e0148132092c" +"checksum ctor 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "cd8ce37ad4184ab2ce004c33bf6379185d3b1c95801cab51026bd271bf68eedc" "checksum ctrlc 3.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7dfd2d8b4c82121dfdff120f818e09fc4380b0b7e17a742081a89b94853e87f" -"checksum curl 0.4.22 (registry+https://github.com/rust-lang/crates.io-index)" = "f8ed9a22aa8c4e49ac0c896279ef532a43a7df2f54fcd19fa36960de029f965f" -"checksum curl-sys 0.4.20 (registry+https://github.com/rust-lang/crates.io-index)" = "5e90ae10f635645cba9cad1023535f54915a95c58c44751c6ed70dbaeb17a408" +"checksum curl 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)" = "06aa71e9208a54def20792d877bc663d6aae0732b9852e612c4a933177c31283" +"checksum curl-sys 0.4.23 (registry+https://github.com/rust-lang/crates.io-index)" = "f71cd2dbddb49c744c1c9e0b96106f50a634e8759ec51bcd5399a578700a3ab3" "checksum darwin-libproc 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ade5a88af8d9646bf770687321a9488a0f2b4610aa08b0373016cd1af37f0a31" "checksum darwin-libproc-sys 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c30d1a078d74da1183b02fed8a8b07afc412d3998334b53b750d0ed03b031541" "checksum decimal 2.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e6458723bc760383275fbc02f4c769b2e5f3de782abaf5e7e0b9b7f0368a63ed" @@ -2988,20 +2894,18 @@ dependencies = [ "checksum dirs-sys 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b" "checksum dtoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "ea57b42383d091c85abcc2706240b94ab2a8fa1fc81c10ff23c4de06e2a90b5e" "checksum dunce 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0ad6bf6a88548d1126045c413548df1453d9be094a8ab9fd59bf1fdd338da4f" -"checksum either 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5527cfe0d098f36e3f8839852688e63c8fff1c90b2b405aef730615f9a7bcf7b" -"checksum encode_unicode 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90b2c9496c001e8cb61827acdefad780795c42264c137744cae6f7d9e3450abd" -"checksum enum-utils 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f1ae672d9891879fb93e17ab6015c4e3bbe63fbeb23a41b9ac39ffa845b8836" -"checksum enum-utils-from-str 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b5669381f76d7320e122abdd4a8307f986634f6d067fb69e31179422175801a" +"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" +"checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" "checksum env_logger 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aafcde04e90a5226a6443b7aabdb016ba2f8307c847d524724bd9b346dd1a2d3" -"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2" -"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1" +"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" +"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" "checksum fallible-streaming-iterator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" "checksum fixedbitset 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "86d4de0081402f5e88cdac65c8dcdcc73118c1a7a465e2a05f0da05843a8ea33" -"checksum flate2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "550934ad4808d5d39365e5d61727309bf18b3b02c6c56b729cb92e7dd84bc3d8" +"checksum flate2 1.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "ad3c5233c9a940c8719031b423d7e6c16af66e031cb0420b0896f5245bf181d3" "checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" -"checksum futures 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "45dc39533a6cae6da2b56da48edae506bb767ec07370f86f70fc062e9d435869" +"checksum futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)" = "1b980f2816d6ee8673b6517b52cb0e808a180efc92e5c19d02cdda79066703ef" "checksum futures-channel-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "f477fd0292c4a4ae77044454e7f2b413207942ad405f759bb0b4698b7ace5b12" "checksum futures-core-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "4a2f26f774b81b3847dcda0c81bd4b6313acfb4f69e5a0390c7cb12c058953e9" "checksum futures-executor-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "80705612926df8a1bc05f0057e77460e29318801f988bf7d803a734cf54e7528" @@ -3011,47 +2915,47 @@ dependencies = [ "checksum futures-timer 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "878f1d2fc31355fa02ed2372e741b0c17e58373341e6a122569b4623a14a7d33" "checksum futures-util-preview 0.3.0-alpha.18 (registry+https://github.com/rust-lang/crates.io-index)" = "7df53daff1e98cc024bf2720f3ceb0414d96fbb0a94f3cad3a5c3bf3be1d261c" "checksum futures_codec 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "36552cd31353fd135114510d53b8d120758120c36aa636a9341970f9efb1e4a0" -"checksum getrandom 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "34f33de6f0ae7c9cb5e574502a562e2b512799e32abb801cd1e79ad952b62b49" +"checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" "checksum getset 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "117a5b13aecd4e10161bb3feb22dda898e8552836c2391d8e4645d5e703ab866" "checksum git2 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39f27186fbb5ec67ece9a56990292bc5aed3c3fc51b9b07b0b52446b1dfb4a82" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -"checksum heim 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "02692a4aa3bed77933da9ae7915aef7fcceb65eff9d9251be189b1acc0b77f65" -"checksum heim-common 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "559807533108e09863125eeccb38a7213cef5a7a7deadd3fac2674e1f8d3db70" -"checksum heim-cpu 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "60c237652eaa091b39f996deb41aa7baae67cab5f25204154c14414f46ef69c1" -"checksum heim-derive 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b3f326db96a03106afcea6839b13f7d95b09cffd063eaa94ef0fd3e796214a66" -"checksum heim-disk 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bd75c64f2d054ce1297ad08f2ca41bf7db7e9ca868221b2fb7427210579e85a1" -"checksum heim-host 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6401c858723568a09e0f09e09bda833e0019c34aa512ccdeba236fce45e4eeb1" -"checksum heim-memory 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "424a549b6c3faecc2492cd3d49f1f89ed9f191c7995741b89e674b85a262e303" -"checksum heim-net 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0ebbcbabe86dbc1c8713ecc1f54630549f82fa07520083cf9a0edcdd77d329a" -"checksum heim-process 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "564f0d9d123c708688721fb2c2aacc198bd5eec3d995eb8c25d369500c66ca7d" -"checksum heim-runtime 0.0.4-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "df59b2a6e00b7f4532dc00736d74bf721a4587d4dbf90793c524ed0a7eddfa19" -"checksum heim-sensors 0.0.3-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "512afc3c0562aa26ae4e236a4b371901fbf7ddac843c961b2ef201936e79a7cd" -"checksum heim-virt 0.0.8-alpha.1 (registry+https://github.com/rust-lang/crates.io-index)" = "95372a84d2a0a5709899449fbb8ed296a9ce5b9fc0ba4729f0c26f7d5ebdf155" +"checksum heim 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "de848466ae9659d5ab634615bdd0b7d558a41ae524ee4d59c880d12499af5b77" +"checksum heim-common 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "63f408c31e695732096a0383df16cd3efee4adb32ba3ad086fb85a7dc8f53100" +"checksum heim-cpu 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "5785004dfdbd68a814d504b27b8ddc16c748a856835dfb6e65b15142090664ef" +"checksum heim-derive 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "9573bedf4673c1b254bce7f1521559329d2b27995b693b695fa13be2b15c188b" +"checksum heim-disk 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "c84980e62564828ae4ca70a8bfbdb0f139cc89abb6c91b8b4809518346a72366" +"checksum heim-host 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1de019d5969f6bab766311be378788bd1bb068b59c4f3861c539a420fc258ed3" +"checksum heim-memory 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "a9cdbe6433197da8387dcd0cf1afd9184db4385d55f8a76355b28ceabe99cdc5" +"checksum heim-net 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7b0f5e590eb2f8b23229ff4b06f7e7aee0e229837d3697f362014343682ae073" +"checksum heim-process 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "a64874316339b9c0c7953e7a87d2b32e2400bf6778650ac11b76b05d3c37e121" +"checksum heim-runtime 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "13ef10b5ab5a501e6537b1414db0e3c488425d88bb131bd4e9ff7c0e61e5fbd1" +"checksum heim-sensors 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ad8b3c9032bca1a76dd43e1eb5c8044e0c505343cb21949dc7acd1bc55b408b" +"checksum heim-virt 0.0.8 (registry+https://github.com/rust-lang/crates.io-index)" = "bb2dda5314da10a8fbcdf130c065abc65f02c3ace72c6f143ad4537520536e2b" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" "checksum hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "023b39be39e3a2da62a94feb433e91e8bcd37676fbc8bea371daf52b7a769a3e" "checksum http 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "372bcb56f939e449117fb0869c2e8fd8753a8223d92a172c6e808cf123a5b6e4" -"checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" +"checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" "checksum idna 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -"checksum image 0.22.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ee0665404aa0f2ad154021777b785878b0e5b1c1da030455abc3d9ed257c2c67" +"checksum image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b4be8aaefbe7545dc42ae925afb55a0098f226a3fe5ef721872806f44f57826" "checksum indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a61202fbe46c4a951e9404a720a0180bcf3212c750d735cb5c4ba4dc551299f3" "checksum inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" -"checksum iovec 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dbe6e417e7d0975db6512b90796e8ce223145ac4e33c377e4a42882a0e88bb08" -"checksum isahc 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e1b971511b5d8de4a51d4da4bc8e374bf60ce841e91b116f46ae06ae2e2a8e9b" +"checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +"checksum isahc 0.7.4 (registry+https://github.com/rust-lang/crates.io-index)" = "769f5071e5bf0b45489eefe0ec96b97328675db38d02ea5e923519d52e690cb8" "checksum isatty 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e31a8281fc93ec9693494da65fbf28c0c2aa60a2eaec25dc58e2f31952e95edc" "checksum itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)" = "0d47946d458e94a1b7bcabbf6521ea7c037062c81f534615abcad76e84d4970d" "checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum jobserver 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "f74e73053eaf95399bf926e48fc7a2a3ce50bd0eaaa2357d391e95b2dcdd4f10" -"checksum jpeg-decoder 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b7d43206b34b3f94ea9445174bda196e772049b9bddbc620c9d29b2d20110d" -"checksum js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "1efc4f2a556c58e79c5500912e221dd826bec64ff4aabd8ce71ccef6da02d7d4" +"checksum jobserver 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "f2b1d42ef453b30b7387e113da1c83ab1605d90c5b4e0eb8e96d016ed3b8c160" +"checksum jpeg-decoder 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "c1aae18ffeeae409c6622c3b6a7ee49792a7e5a062eea1b135fbb74e301792ba" +"checksum js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc9a97d7cec30128fd8b28a7c1f9df1c001ceb9b441e2b755e24130a6b43c79" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" "checksum language-reporting 0.3.1 (git+https://github.com/wycats/language-reporting)" = "" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" -"checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" +"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" -"checksum lexical-core 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b0f90c979adde96d19eb10eb6431ba0c441e2f9e9bdff868b2f6f5114ff519" -"checksum libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)" = "d44e80633f007889c7eff624b709ab43c92d708caad982295768a7b13ca3b5eb" +"checksum lexical-core 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2304bccb228c4b020f3a4835d247df0a02a7c4686098d4167762cfbbe4c5cb14" +"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libgit2-sys 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a30f8637eb59616ee3b8a00f6adff781ee4ddd8343a615b8238de756060cc1b3" "checksum libnghttp2-sys 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02254d44f4435dd79e695f2c2b83cd06a47919adea30216ceaf0c57ca0a72463" "checksum libsqlite3-sys 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5e5b95e89c330291768dc840238db7f9e204fd208511ab6319b56193a7f2ae25" @@ -3070,16 +2974,14 @@ dependencies = [ "checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e" "checksum mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "dd1d63acd1b78403cc0c325605908475dd9b9a3acbf65ed8bcab97e27014afcf" "checksum mime_guess 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1a0ed03949aef72dbdf3116a383d7b38b4768e6f960528cd6a6044aa9ed68599" -"checksum miniz-sys 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "1e9e3ae51cea1576ceba0dde3d484d30e6e5b86dee0b2d412fe3a16a15c98202" -"checksum miniz_oxide 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe2959c5a0747a8d7a56b4444c252ffd2dda5d452cfd147cdfdda73b1c3ece5b" -"checksum miniz_oxide_c_api 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6c675792957b0d19933816c4e1d56663c341dd9bfa31cb2140ff2267c1d8ecf4" +"checksum miniz_oxide 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "304f66c19be2afa56530fa7c39796192eef38618da8d19df725ad7c6d6b2aaae" "checksum natural 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd659d7d6b4554da2c0e7a486d5952b24dfce0e0bac88ab53b270f4efe1010a6" "checksum neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b3c31defbcb081163db18437fd88c2a267cb3e26f7bd5e4b186e4b1b38fe8c8" "checksum nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" "checksum nix 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" -"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum nodrop 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" "checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" -"checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b" +"checksum nom 5.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c618b63422da4401283884e6668d39f819a106ef51f5f59b81add00075da35ca" "checksum nom-tracable 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "edaa64ad2837d831d4a17966c9a83aa5101cc320730f5b724811c8f7442a2528" "checksum nom-tracable-macros 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fd25f70877a9fe68bd406b3dd3ff99e94ce9de776cf2a96e0d99de90b53d4765" "checksum nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f932834fd8e391fc7710e2ba17e8f9f8645d846b55aa63207e17e110a1e1ce35" @@ -3095,10 +2997,10 @@ dependencies = [ "checksum objc-foundation 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" "checksum objc_id 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" "checksum ole32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5d2c49021782e5233cd243168edfa8037574afed4eba4bbaf538b3d8d1789d8c" -"checksum onig 4.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a646989adad8a19f49be2090374712931c3a59835cb5277b4530f48b417f26e7" +"checksum onig 4.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8518fcb2b1b8c2f45f0ad499df4fda6087fc3475ca69a185c173b8315d2fb383" "checksum onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388410bf5fa341f10e58e6db3975f4bea1ac30247dd79d37a9e5ced3cb4cc3b0" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.49 (registry+https://github.com/rust-lang/crates.io-index)" = "f4fad9e54bd23bd4cbbe48fdc08a1b8091707ac869ef8508edea2fec77dcc884" +"checksum openssl-sys 0.9.51 (registry+https://github.com/rust-lang/crates.io-index)" = "ba24190c8f0805d3bd2ce028f439fe5af1d55882bbe6261bed1dbc93b50dd6b1" "checksum ord_subset 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d7ce14664caf5b27f5656ff727defd68ae1eb75ef3c4d95259361df1eb376bef" "checksum ordered-float 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "18869315e81473c951eb56ad5558bbc56978562d3ecfb87abb7a1e944cea4518" "checksum ordermap 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a86ed3f5f244b372d6b1a00b72ef7f8876d0bc6a78a4c9985c53614041512063" @@ -3106,26 +3008,24 @@ dependencies = [ "checksum percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" "checksum petgraph 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)" = "9c3659d1ee90221741f65dd128d9998311b0e40c5d3c23a62445938214abce4f" "checksum pin-utils 0.1.0-alpha.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587" -"checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" -"checksum platforms 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6cfec0daac55b13af394ceaaad095d17c790f77bdc9329264f06e49d6cd3206c" +"checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" +"checksum platforms 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "feb3b2b1033b8a60b4da6ee470325f887758c95d5320f52f9ce0df055a55940e" "checksum plist 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5f2a9f075f6394100e7c105ed1af73fb1859d6fd14e49d4290d578120beb167f" "checksum png 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8422b27bb2c013dd97b9aef69e161ce262236f49aaf46a0489011c8ff0264602" "checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" -"checksum pretty-hex 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "119929a2a3b731bb3d888f7a1b5dc3c1db28b6c134def5d99f7e16e2da16b8f7" +"checksum pretty-hex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be91bcc43e73799dc46a6c194a55e7aae1d86cc867c860fd4a436019af21bd8c" "checksum pretty_assertions 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f81e1644e1b54f5a68959a29aa86cde704219254669da328ecfdf6a1f09d427" "checksum pretty_env_logger 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "717ee476b1690853d222af4634056d830b5197ffd747726a9a1eee6da9f49074" "checksum prettytable-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0fd04b170004fa2daccf418a7f8253aaf033c27760b5f225889024cf66d7ac2e" -"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" -"checksum proc-macro2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c5c2380ae88876faae57698be9e9775e3544decad214599c3a6266cca6ac802" +"checksum proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90cf5f418035b98e655e9cdb225047638296b862b42411c4e45bb88d700f7fc0" "checksum ptree 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0a3be00b19ee7bd33238c1c523a7ab4df697345f6b36f90827a7860ea938d4" "checksum quick-error 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9274b940887ce9addde99c4eee6b5c44cc494b182b97e73dc8ffdcb3397fd3f0" -"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" -"checksum rand 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d47eab0e83d9693d40f825f86948aa16eff6750ead4bdffc4ab95b8b3a7f052c" +"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" "checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" -"checksum rand_core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "615e683324e75af5d43d8f7a39ffe3ee4a9dc42c5c701167a71dc59c3a493aca" +"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" "checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" "checksum rand_os 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" "checksum raw-cpuid 7.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4a349ca83373cfa5d6dbb66fd76e58b2cca08da71a5f6400de0a0a6a9bceeaf" @@ -3134,24 +3034,24 @@ dependencies = [ "checksum readkey 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d98db94bb4f3e926c8d8186547cd9366d958d753aff5801214d93d38214e8f0f" "checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84" "checksum redox_users 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ecedbca3bf205f8d8f5c2b44d83cd0690e39ee84b951ed649e9f1841132b66d" -"checksum regex 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88c3d9193984285d544df4a30c23a4e62ead42edf70a4452ceb76dac1ce05c26" +"checksum regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dc220bd33bdce8f093101afe22a037b8eb0e5af33592e6a9caafff0d4cb81cbd" "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" -"checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f" +"checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum render-tree 0.1.1 (git+https://github.com/wycats/language-reporting)" = "" "checksum result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560" -"checksum roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "153c367ce9fb8ef7afe637ef92bd083ba0f88b03ef3fcf0287d40be05ae0a61c" +"checksum roxmltree 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1a3193e568c6e262f817fd07af085c7f79241a947aedd3779d47eadc170e174" "checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051" "checksum rust-argon2 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4ca4eaef519b494d1f2848fc602d18816fed808a981aedf4f1f00ceb7c9d32cf" "checksum rust-ini 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e52c148ef37f8c375d49d5a73aa70713125b7f19095948a923f80afdeb22ec2" -"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" +"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4795e277e6e57dec9df62b515cd4991371daa80e8dc8d80d596e58722b89c417" -"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" -"checksum safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e133ccc4f4d1cd4f89cc8a7ff618287d56dc7f638b8e38fc32c5fdcadc339dd5" +"checksum ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19d2271fa48eaf61e53cc88b4ad9adcbafa2d512c531e7fadb6dc11a4d3656c5" +"checksum safemem 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b08423011dae9a5ca23f07cf57dac3857f5c885d352b76f6d95f4aea9434d0" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" -"checksum schannel 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f6abf258d99c3c1c5c2131d99d064e94b7b3dd5f416483057f308fea253339" +"checksum schannel 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "87f550b06b6cba9c8b8be3ee73f391990116bf527450d2556e9b9ce263b9a021" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" "checksum serde 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "9dad3f759919b92c3068c696c15c3d17238234498bbdcc80f2c469606f948ac8" @@ -3160,29 +3060,26 @@ dependencies = [ "checksum serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6a3a4e0ea8a88553209f6cc6cfe8724ecad22e1acf372793c27d995290fe74f8" "checksum serde-value 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7a663f873dedc4eac1a559d4c6bc0d0b2c34dc5ac4702e105014b8281489e44f" "checksum serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "45af0182ff64abaeea290235eb67da3825a576c5d53e642c4d5b652e12e6effc" -"checksum serde_derive 1.0.98 (registry+https://github.com/rust-lang/crates.io-index)" = "01e69e1b8a631f245467ee275b8c757b818653c6d704cdbcaeb56b56767b529c" -"checksum serde_derive_internals 0.24.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8a80c6c0b1ebbcea4ec2c7e9e2e9fa197a425d17f1afec8ba79fcd1352b18ffb" +"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e" "checksum serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "eb236687e2bb073a7521c021949be944641e671b8505a94069ca37b656c81139" -"checksum serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)" = "051c49229f282f7c6f3813f8286cc1e3323e8051823fce42c7ea80fe13521704" +"checksum serde_json 1.0.41 (registry+https://github.com/rust-lang/crates.io-index)" = "2f72eb2a68a7dc3f9a691bfda9305a1c017a6215e5a4545c258500d2099a37c2" "checksum serde_test 0.8.23 (registry+https://github.com/rust-lang/crates.io-index)" = "110b3dbdf8607ec493c22d5d947753282f3bae73c0f56d322af1e8c78e4c23d5" "checksum serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" -"checksum serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)" = "38b08a9a90e5260fe01c6480ec7c811606df6d3a660415808c3c3fa8ed95b582" +"checksum serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)" = "691b17f19fc1ec9d94ec0b5864859290dff279dbd7b03f017afda54eb36c3c35" "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 sluice 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "0a7d06dfb3e8743bc19e6de8a302277471d08077d68946b307280496dc5a3531" "checksum smallvec 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ab606a9c5e214920bb66c458cd7be8ef094f813f20fe77a54cc7dbfff220d4b7" "checksum socket2 0.3.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e8b74de517221a2cb01a53349cf54182acdc31a074727d3079068448c0676d85" "checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3" -"checksum stackvector 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1c4725650978235083241fab0fdc8e694c3de37821524e7534a1a9061d1068af" -"checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241" +"checksum static_assertions 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum sublime_fuzzy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97bd7ad698ea493a3a7f60c2ffa117c234f341e09f8cc2d39cef10cdde077acf" "checksum subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "28fc0f40f0c0da73339d347aa7d6d2b90341a95683a47722bc4eebed71ff3c00" "checksum surf 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "018eed64aede455beb88505d50c5c64882bebbe0996d4b660c272e3d8bb6f883" -"checksum syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ee06ea4b620ab59a2267c6b48be16244a3389f8bfa0986bdd15c35b890b00af3" -"checksum syn 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c65d951ab12d976b61a41cf9ed4531fc19735c6e6d84a4bb1453711e762ec731" -"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" +"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf" +"checksum synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3f085a5855930c0441ca1288cf044ea4aecf4f43a91668abdb870b4ba546a203" "checksum syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e80b8831c5a543192ffc3727f01cf0e57579c6ac15558e3048bfb5708892167b" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" @@ -3196,15 +3093,13 @@ dependencies = [ "checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" -"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" -"checksum unicase 2.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a84e5511b2a947f3ae965dcb29b13b7b1691b6e7332cf5dbc1744138d5acb7f6" +"checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "141339a08b982d942be2ca06ff8b076563cbe223d1befd5450716790d44e2426" "checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9" "checksum unicode-width 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "7007dbd421b92cc6e28410fe7362e2e0a2503394908f417b68ec8d1c364c4e20" -"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" -"checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" "checksum uom 0.23.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ef5bbe8385736e498dbb0033361f764ab43a435192513861447b9f7714b3fec" "checksum uom 0.25.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3198c29f199fa8a23d732f4aa21ddc4f4d0a257cb0c2a44afea30145ce2575c1" "checksum url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "75b414f6c464c879d7f9babf951f23bc3743fb7313c081b2e6ca719067ea9d61" @@ -3215,14 +3110,15 @@ dependencies = [ "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum walkdir 2.2.9 (registry+https://github.com/rust-lang/crates.io-index)" = "9658c94fa8b940eab2250bd5a457f9c48b748420d71293b165c8cdbe2f55f71e" -"checksum wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "dcddca308b16cd93c2b67b126c688e5467e4ef2e28200dc7dfe4ae284f2faefc" -"checksum wasm-bindgen-backend 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "f805d9328b5fc7e5c6399960fd1889271b9b58ae17bdb2417472156cc9fafdd0" -"checksum wasm-bindgen-futures 0.3.25 (registry+https://github.com/rust-lang/crates.io-index)" = "73c25810ee684c909488c214f55abcbc560beb62146d352b9588519e73c2fed9" -"checksum wasm-bindgen-macro 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "3ff88201a482abfc63921621f6cb18eb1efd74f136b05e5841e7f8ca434539e9" -"checksum wasm-bindgen-macro-support 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "6a433d89ecdb9f77d46fcf00c8cf9f3467b7de9954d8710c175f61e2e245bb0e" -"checksum wasm-bindgen-shared 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "d41fc1bc3570cdf8d108c15e014045fd45a95bb5eb36605f96a90461fc34027d" -"checksum wasm-bindgen-webidl 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "be53d289bf2fa7645a089cfd5c7a34bf4fe94221f58cf86ee42a7b4bc854ff14" -"checksum web-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "6435c477200ad486089a7a72c2bd6c9bdf9740bd7fff868806076218076d8c51" +"checksum wasi 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b89c3ce4ce14bdc6fb6beaf9ec7928ca331de5df7e5ea278375642a2f478570d" +"checksum wasm-bindgen 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "cd34c5ba0d228317ce388e87724633c57edca3e7531feb4e25e35aaa07a656af" +"checksum wasm-bindgen-backend 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "927196b315c23eed2748442ba675a4c54a1a079d90d9bdc5ad16ce31cf90b15b" +"checksum wasm-bindgen-futures 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "83420b37346c311b9ed822af41ec2e82839bfe99867ec6c54e2da43b7538771c" +"checksum wasm-bindgen-macro 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "92c2442bf04d89792816650820c3fb407af8da987a9f10028d5317f5b04c2b4a" +"checksum wasm-bindgen-macro-support 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "9c075d27b7991c68ca0f77fe628c3513e64f8c477d422b859e03f28751b46fc5" +"checksum wasm-bindgen-shared 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "83d61fe986a7af038dd8b5ec660e5849cbd9f38e7492b9404cc48b2b4df731d1" +"checksum wasm-bindgen-webidl 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "9b979afb0535fe4749906a674082db1211de8aef466331d43232f63accb7c07c" +"checksum web-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "c84440699cd02ca23bed6f045ffb1497bc18a3c2628bd13e2093186faaaacf6b" "checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164" "checksum which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b57acb10231b9493c8472b20cb57317d0679a49e0bdbee44b3b803a6473af164" "checksum widestring 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "effc0e4ff8085673ea7b9b2e3c73f6bd4d118810c9009ed8f1e16bd96c331db6" @@ -3232,11 +3128,11 @@ dependencies = [ "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-util 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7168bab6e1daee33b4557efd0e95d5ca70a03706d39fa5f3fe7a236f584b03c9" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "561ed901ae465d6185fa7864d63fbd5720d0ef718366c9a4dc83cf6170d7e9ba" +"checksum wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "96f5016b18804d24db43cebf3c77269e7569b8954a8464501c216cc5e070eaa9" "checksum x11 2.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39697e3123f715483d311b5826e254b6f3cfebdd83cf7ef3358f579c3d68e235" "checksum x11-clipboard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89bd49c06c9eb5d98e6ba6536cf64ac9f7ee3a009b2f53996d405b3944f6bcea" "checksum xcb 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5e917a3f24142e9ff8be2414e36c649d47d6cc2ba81f16201cdef96e533e02de" "checksum xdg 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d089681aa106a86fade1b0128fb5daf07d5867a509ab036d99988dec80429a57" "checksum xml-rs 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "541b12c998c5b56aa2b4e6f18f03664eef9a4fd0a246a55594efae6cc2d964b5" -"checksum xmlparser 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ecec95f00fb0ff019153e64ea520f87d1409769db3e8f4db3ea588638a3e1cee" +"checksum xmlparser 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8110496c5bcc0d966b0b2da38d5a791aa139eeb0b80e7840a7463c2b806921eb" "checksum yaml-rust 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "65923dd1784f44da1d2c3dbbc5e822045628c590ba72123e1c73d3c230c4434d" diff --git a/Cargo.toml b/Cargo.toml index 9ae1ada021..e9a0d013ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,6 @@ url = "2.1.0" roxmltree = "0.7.0" nom_locate = "1.0.0" nom-tracable = "0.4.0" -enum-utils = "0.1.1" unicode-xid = "0.2.0" serde_ini = "0.2.0" subprocess = "0.1.18" @@ -75,8 +74,8 @@ bigdecimal = { version = "0.1.0", features = ["serde"] } natural = "0.3.0" serde_urlencoded = "0.6.1" sublime_fuzzy = "0.5" -regex = "1" +regex = {version = "1", optional = true } neso = { version = "0.5.0", optional = true } crossterm = { version = "0.10.2", optional = true } syntect = {version = "3.2.0", optional = true } @@ -144,6 +143,7 @@ path = "src/plugins/skip.rs" [[bin]] name = "nu_plugin_match" path = "src/plugins/match.rs" +required-features = ["regex"] [[bin]] name = "nu_plugin_sys" diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index 3c7e4fc11e..c3c1df652a 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -4,7 +4,6 @@ use crate::prelude::*; use crate::traits::ToDebug; use crate::{Tagged, Text}; use derive_new::new; -use enum_utils::FromStr; use getset::Getters; use std::fmt; @@ -298,7 +297,7 @@ impl DelimitedNode { } } -#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, FromStr)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] pub enum Delimiter { Paren, Brace, From 341cc1ea63b17e6acc6d3494f66feb96cd79fcb5 Mon Sep 17 00:00:00 2001 From: Jason Gedge Date: Sun, 13 Oct 2019 12:00:30 -0400 Subject: [PATCH 253/342] Ignore errors in `ls`. `std::fs::metadata` will attempt to follow symlinks, which results in a "No such file or directory" error if the path pointed to by the symlink does not exist. This shouldn't prevent `ls` from succeeding, so we ignore errors. Also, switching to use of `symlink_metadata` means we get stat info on the symlink itself, not what it points to. This means `ls` will now include broken symlinks in its listing. --- src/shell/filesystem_shell.rs | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 72a0c241f3..f0adeebeb8 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -128,13 +128,16 @@ impl Shell for FilesystemShell { } if let Ok(entry) = entry { let filepath = entry.path(); - let filename = if let Ok(fname) = filepath.strip_prefix(&cwd) { - fname - } else { - Path::new(&filepath) - }; - let value = dir_entry_dict(filename, &entry.metadata().unwrap(), &name_tag)?; - yield ReturnSuccess::value(value); + if let Ok(metadata) = std::fs::symlink_metadata(&filepath) { + let filename = if let Ok(fname) = filepath.strip_prefix(&cwd) { + fname + } else { + Path::new(&filepath) + }; + + let value = dir_entry_dict(filename, &metadata, &name_tag)?; + yield ReturnSuccess::value(value); + } } } }; @@ -164,14 +167,16 @@ impl Shell for FilesystemShell { break; } if let Ok(entry) = entry { - let filename = if let Ok(fname) = entry.strip_prefix(&cwd) { - fname - } else { - Path::new(&entry) - }; - let metadata = std::fs::metadata(&entry).unwrap(); - if let Ok(value) = dir_entry_dict(filename, &metadata, &name_tag) { - yield ReturnSuccess::value(value); + if let Ok(metadata) = std::fs::symlink_metadata(&entry) { + let filename = if let Ok(fname) = entry.strip_prefix(&cwd) { + fname + } else { + Path::new(&entry) + }; + + if let Ok(value) = dir_entry_dict(filename, &metadata, &name_tag) { + yield ReturnSuccess::value(value); + } } } } From 0f7e73646f80d0918c30ccdc511d7cd4f45606c6 Mon Sep 17 00:00:00 2001 From: Jason Gedge Date: Sun, 13 Oct 2019 14:21:44 -0400 Subject: [PATCH 254/342] Bump heim in Cargo.toml to match Cargo.lock --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index e9a0d013ca..955beeddf9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -80,7 +80,7 @@ neso = { version = "0.5.0", optional = true } crossterm = { version = "0.10.2", optional = true } syntect = {version = "3.2.0", optional = true } onig_sys = {version = "=69.1.0", optional = true } -heim = {version = "0.0.8-alpha.1", optional = true } +heim = {version = "0.0.8", optional = true } battery = {version = "0.7.4", optional = true } rawkey = {version = "0.1.2", optional = true } clipboard = {version = "0.5", optional = true } From 648d4865b18786ecfb1b31eefc5786632e9fe9bb Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Sun, 13 Oct 2019 21:15:30 +0200 Subject: [PATCH 255/342] Adds unimplemented module, tests. --- src/commands.rs | 2 ++ src/commands/from_ssv.rs | 43 ++++++++++++++++++++++++++++++ tests/filters_test.rs | 56 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 src/commands/from_ssv.rs diff --git a/src/commands.rs b/src/commands.rs index 72c07e38e6..93729aef68 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -22,6 +22,7 @@ pub(crate) mod from_csv; pub(crate) mod from_ini; pub(crate) mod from_json; pub(crate) mod from_sqlite; +pub(crate) mod from_ssv; pub(crate) mod from_toml; pub(crate) mod from_tsv; pub(crate) mod from_url; @@ -91,6 +92,7 @@ pub(crate) use from_ini::FromINI; pub(crate) use from_json::FromJSON; pub(crate) use from_sqlite::FromDB; pub(crate) use from_sqlite::FromSQLite; +pub(crate) use from_ssv::FromSSV; pub(crate) use from_toml::FromTOML; pub(crate) use from_tsv::FromTSV; pub(crate) use from_url::FromURL; diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs new file mode 100644 index 0000000000..0da2b6f562 --- /dev/null +++ b/src/commands/from_ssv.rs @@ -0,0 +1,43 @@ +use crate::commands::WholeStreamCommand; +use crate::data::{Primitive, TaggedDictBuilder, Value}; +use crate::prelude::*; + +pub struct FromSSV; + +#[derive(Deserialize)] +pub struct FromSSVArgs { + headerless: bool, +} + +const STRING_REPRESENTATION: &str = "from-ssv"; + +impl WholeStreamCommand for FromSSV { + fn name(&self) -> &str { + STRING_REPRESENTATION + } + + fn signature(&self) -> Signature { + Signature::build(STRING_REPRESENTATION).switch("headerless") + } + + fn usage(&self) -> &str { + "Parse text as .ssv and create a table." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, from_ssv)?.run() + } +} + +fn from_ssv( + FromSSVArgs { + headerless: headerless, + }: FromSSVArgs, + RunnableContext { input, name, .. }: RunnableContext, +) -> Result { + unimplemented!() +} diff --git a/tests/filters_test.rs b/tests/filters_test.rs index f994fa4494..70fd752967 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -355,6 +355,62 @@ fn converts_from_tsv_text_skipping_headers_to_structured_table() { }) } +#[test] +fn converts_from_ssv_text_to_structured_table() { + Playground::setup("filter_from_ssv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "oc_get_svc.ssv", + r#" + NAME LABELS SELECTOR IP PORT(S) + docker-registry docker-registry=default docker-registry=default 172.30.78.158 5000/TCP + kubernetes component=apiserver,provider=kubernetes 172.30.0.2 443/TCP + kubernetes-ro component=apiserver,provider=kubernetes 172.30.0.1 80/TCP + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + open oc_get_svc.ssv + | from-ssv + | nth 0 + | get NAME + | echo $it + "# + )); + + assert_eq!(actual, "docker-registry"); + }) +} + +#[test] +fn converts_from_ssv_text_skipping_headers_to_structured_table() { + Playground::setup("filter_from_ssv_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "oc_get_svc.ssv", + r#" + NAME LABELS SELECTOR IP PORT(S) + docker-registry docker-registry=default docker-registry=default 172.30.78.158 5000/TCP + kubernetes component=apiserver,provider=kubernetes 172.30.0.2 443/TCP + kubernetes-ro component=apiserver,provider=kubernetes 172.30.0.1 80/TCP + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + open oc_get_svc.ssv + | from-ssv --headerless + | nth 2 + | get Column2 + | echo $it + "# + )); + + assert_eq!(actual, "component=apiserver,provider=kubernetes"); + }) +} + #[test] fn can_convert_table_to_bson_and_back_into_table() { let actual = nu!( From de1c4e6c8894915825d6bd67cc7ed85d26b25be6 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Sun, 13 Oct 2019 22:50:45 +0200 Subject: [PATCH 256/342] Implements from-ssv --- src/cli.rs | 1 + src/commands/from_ssv.rs | 88 ++++++++++++++++++++++++++++++++++++++-- tests/filters_test.rs | 12 +++--- 3 files changed, 91 insertions(+), 10 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 38e2474faf..5bfd7ff681 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -272,6 +272,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Env), whole_stream_command(FromCSV), whole_stream_command(FromTSV), + whole_stream_command(FromSSV), whole_stream_command(FromINI), whole_stream_command(FromBSON), whole_stream_command(FromJSON), diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 0da2b6f562..59ba2f2f8f 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -33,11 +33,91 @@ impl WholeStreamCommand for FromSSV { } } +fn from_ssv_string_to_value( + s: &str, + headerless: bool, + tag: impl Into, +) -> Result, &str> { + let mut lines = s.lines(); + let tag = tag.into(); + + let headers = lines + .next() + .expect("No content.") + .split_whitespace() + .map(|s| s.to_owned()) + .collect::>(); + + let header_row = if headerless { + (0..headers.len()) + .map(|i| format!("Column{}", i + 1)) + .collect::>() + } else { + headers + }; + + let rows = lines + .map(|l| { + let mut row = TaggedDictBuilder::new(tag); + for (column, value) in header_row.iter().zip(l.split_whitespace()) { + row.insert_tagged( + column.to_owned(), + Value::Primitive(Primitive::String(String::from(value))).tagged(tag), + ) + } + row.into_tagged_value() + }) + .collect(); + + Ok(Tagged::from_item(Value::Table(rows), tag)) +} + fn from_ssv( - FromSSVArgs { - headerless: headerless, - }: FromSSVArgs, + FromSSVArgs { headerless }: FromSSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { - unimplemented!() + let stream = async_stream! { + let values: Vec> = input.values.collect().await; + let mut concat_string = String::new(); + let mut latest_tag: Option = None; + + for value in values { + let value_tag = value.tag(); + latest_tag = Some(value_tag); + match value.item { + Value::Primitive(Primitive::String(s)) => { + concat_string.push_str(&s); + concat_string.push_str("\n"); + + } + _ => yield Err(ShellError::labeled_error_with_secondary ( + "Expected a string from pipeline", + "requires string input", + name, + "value originates from here", + value_tag + )), + } + } + + match from_ssv_string_to_value(&concat_string, headerless, name) { + Ok(x) => match x { + Tagged { item: Value::Table(list), ..} => { + for l in list { yield ReturnSuccess::value(l) } + } + x => yield ReturnSuccess::value(x) + }, + Err(_) => if let Some(last_tag) = latest_tag { + yield Err(ShellError::labeled_error_with_secondary( + "Could not parse as SSV", + "input cannot be parsed ssv", + name, + "value originates from here", + last_tag, + )) + } + } + }; + + Ok(stream.to_output_stream()) } diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 70fd752967..ed841af4ca 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -359,7 +359,7 @@ fn converts_from_tsv_text_skipping_headers_to_structured_table() { fn converts_from_ssv_text_to_structured_table() { Playground::setup("filter_from_ssv_test_1", |dirs, sandbox| { sandbox.with_files(vec![FileWithContentToBeTrimmed( - "oc_get_svc.ssv", + "oc_get_svc.txt", r#" NAME LABELS SELECTOR IP PORT(S) docker-registry docker-registry=default docker-registry=default 172.30.78.158 5000/TCP @@ -371,15 +371,15 @@ fn converts_from_ssv_text_to_structured_table() { let actual = nu!( cwd: dirs.test(), h::pipeline( r#" - open oc_get_svc.ssv + open oc_get_svc.txt | from-ssv | nth 0 - | get NAME + | get IP | echo $it "# )); - assert_eq!(actual, "docker-registry"); + assert_eq!(actual, "172.30.78.158"); }) } @@ -387,7 +387,7 @@ fn converts_from_ssv_text_to_structured_table() { fn converts_from_ssv_text_skipping_headers_to_structured_table() { Playground::setup("filter_from_ssv_test_2", |dirs, sandbox| { sandbox.with_files(vec![FileWithContentToBeTrimmed( - "oc_get_svc.ssv", + "oc_get_svc.txt", r#" NAME LABELS SELECTOR IP PORT(S) docker-registry docker-registry=default docker-registry=default 172.30.78.158 5000/TCP @@ -399,7 +399,7 @@ fn converts_from_ssv_text_skipping_headers_to_structured_table() { let actual = nu!( cwd: dirs.test(), h::pipeline( r#" - open oc_get_svc.ssv + open oc_get_svc.txt | from-ssv --headerless | nth 2 | get Column2 From 8422d40e2c36edc31bf42f8d38b8305abdfbe19c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Sun, 13 Oct 2019 23:09:10 +0200 Subject: [PATCH 257/342] Add from-ssv to readme. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 7df2c92ec9..45867f1daf 100644 --- a/README.md +++ b/README.md @@ -284,6 +284,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | from-ini | Parse text as .ini and create table | | from-json | Parse text as .json and create table | | from-sqlite | Parse binary data as sqlite .db and create table | +| from-ssv | Parse text as whitespace-separated values and create table| | from-toml | Parse text as .toml and create table | | from-tsv | Parse text as .tsv and create table | | from-url | Parse urlencoded string and create a table | From 38b5979881ed974824f4c94e07403d089ea8b424 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Sun, 13 Oct 2019 23:09:24 +0200 Subject: [PATCH 258/342] Make usage string clearer. --- src/commands/from_ssv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 59ba2f2f8f..ede06da2da 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -21,7 +21,7 @@ impl WholeStreamCommand for FromSSV { } fn usage(&self) -> &str { - "Parse text as .ssv and create a table." + "Parse text as whitespace-separated values and create a table." } fn run( From 20e891db6e59c02e00ae5f0fbfa61c7f843a4295 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Sun, 13 Oct 2019 23:09:40 +0200 Subject: [PATCH 259/342] Move variable assignment to clarify use. --- src/commands/from_ssv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index ede06da2da..2f3f574fb3 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -39,7 +39,6 @@ fn from_ssv_string_to_value( tag: impl Into, ) -> Result, &str> { let mut lines = s.lines(); - let tag = tag.into(); let headers = lines .next() @@ -56,6 +55,7 @@ fn from_ssv_string_to_value( headers }; + let tag = tag.into(); let rows = lines .map(|l| { let mut row = TaggedDictBuilder::new(tag); From 6c0bf6e0abc3a0f2f84bb17b748b63ed7e4ddb91 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 14 Oct 2019 17:48:27 +1300 Subject: [PATCH 260/342] Fix panic if external is not found --- src/commands/classified.rs | 65 ++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 28 deletions(-) diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 105daff771..c2380d4ffe 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -317,41 +317,50 @@ impl ExternalCommand { trace!(target: "nu::run::external", "set up stdin pipe"); trace!(target: "nu::run::external", "built process {:?}", process); - let mut popen = process.popen().unwrap(); + let popen = process.popen(); trace!(target: "nu::run::external", "next = {:?}", stream_next); - match stream_next { - StreamNext::Last => { - let _ = popen.detach(); - loop { - match popen.poll() { - None => { - let _ = std::thread::sleep(std::time::Duration::new(0, 100000000)); - } - _ => { - let _ = popen.terminate(); - break; + if let Ok(mut popen) = popen { + match stream_next { + StreamNext::Last => { + let _ = popen.detach(); + loop { + match popen.poll() { + None => { + let _ = std::thread::sleep(std::time::Duration::new(0, 100000000)); + } + _ => { + let _ = popen.terminate(); + break; + } } } + Ok(ClassifiedInputStream::new()) + } + StreamNext::External => { + let _ = popen.detach(); + let stdout = popen.stdout.take().unwrap(); + Ok(ClassifiedInputStream::from_stdout(stdout)) + } + StreamNext::Internal => { + let _ = popen.detach(); + let stdout = popen.stdout.take().unwrap(); + let file = futures::io::AllowStdIo::new(stdout); + let stream = Framed::new(file, LinesCodec {}); + let stream = + stream.map(move |line| Value::string(line.unwrap()).tagged(&name_tag)); + Ok(ClassifiedInputStream::from_input_stream( + stream.boxed() as BoxStream<'static, Tagged> + )) } - Ok(ClassifiedInputStream::new()) - } - StreamNext::External => { - let _ = popen.detach(); - let stdout = popen.stdout.take().unwrap(); - Ok(ClassifiedInputStream::from_stdout(stdout)) - } - StreamNext::Internal => { - let _ = popen.detach(); - let stdout = popen.stdout.take().unwrap(); - let file = futures::io::AllowStdIo::new(stdout); - let stream = Framed::new(file, LinesCodec {}); - let stream = stream.map(move |line| Value::string(line.unwrap()).tagged(&name_tag)); - Ok(ClassifiedInputStream::from_input_stream( - stream.boxed() as BoxStream<'static, Tagged> - )) } + } else { + return Err(ShellError::labeled_error( + "Command not found", + "command not found", + name_tag, + )); } } } From 7c40aed73878c84fcd9cbd0d2bf9bf50ce6f83ec Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 14 Oct 2019 18:00:10 +1300 Subject: [PATCH 261/342] Don't panick of no suggestions are found --- src/evaluate/evaluator.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 1e19c31e78..75eb2f4667 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -117,11 +117,19 @@ pub(crate) fn evaluate_baseline_expr( possible_matches.sort(); - return Err(ShellError::labeled_error( - "Unknown column", - format!("did you mean '{}'?", possible_matches[0].1), - &tag, - )); + if possible_matches.len() > 0 { + return Err(ShellError::labeled_error( + "Unknown column", + format!("did you mean '{}'?", possible_matches[0].1), + &tag, + )); + } else { + return Err(ShellError::labeled_error( + "Unknown column", + "row does not have this column", + &tag, + )); + } } Some(next) => { item = next.clone().item.tagged(&tag); From a4a1588fbcebd93ad855930fb87fde91dc668676 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 14 Oct 2019 18:28:54 +1300 Subject: [PATCH 262/342] Fix confusing unnamed column and crash --- src/commands/get.rs | 18 +++++++++++++----- src/format/table.rs | 2 +- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/commands/get.rs b/src/commands/get.rs index e8fc62ca64..21dbe6b0a7 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -60,11 +60,19 @@ pub fn get_column_path( possible_matches.sort(); - return Err(ShellError::labeled_error( - "Unknown column", - format!("did you mean '{}'?", possible_matches[0].1), - tag_for_tagged_list(path.iter().map(|p| p.tag())), - )); + if possible_matches.len() > 0 { + return Err(ShellError::labeled_error( + "Unknown column", + format!("did you mean '{}'?", possible_matches[0].1), + tag_for_tagged_list(path.iter().map(|p| p.tag())), + )); + } else { + return Err(ShellError::labeled_error( + "Unknown column", + "row does not contain this column", + tag_for_tagged_list(path.iter().map(|p| p.tag())), + )); + } } } } diff --git a/src/format/table.rs b/src/format/table.rs index b2680a6c96..f4b318dae8 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -42,7 +42,7 @@ impl TableView { let mut headers = TableView::merge_descriptors(values); if headers.len() == 0 { - headers.push("value".to_string()); + headers.push("".to_string()); } let mut entries = vec![]; From 63039666b0aa36f40b15b8bdd740154e7bdf6d51 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 07:37:34 +0200 Subject: [PATCH 263/342] Changes from_ssv_to_string_value to return an Option. --- src/commands/from_ssv.rs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 2f3f574fb3..3232fd1e3e 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -37,12 +37,11 @@ fn from_ssv_string_to_value( s: &str, headerless: bool, tag: impl Into, -) -> Result, &str> { +) -> Option> { let mut lines = s.lines(); let headers = lines - .next() - .expect("No content.") + .next()? .split_whitespace() .map(|s| s.to_owned()) .collect::>(); @@ -69,7 +68,7 @@ fn from_ssv_string_to_value( }) .collect(); - Ok(Tagged::from_item(Value::Table(rows), tag)) + Some(Tagged::from_item(Value::Table(rows), tag)) } fn from_ssv( @@ -101,13 +100,13 @@ fn from_ssv( } match from_ssv_string_to_value(&concat_string, headerless, name) { - Ok(x) => match x { + Some(x) => match x { Tagged { item: Value::Table(list), ..} => { for l in list { yield ReturnSuccess::value(l) } } x => yield ReturnSuccess::value(x) }, - Err(_) => if let Some(last_tag) = latest_tag { + None => if let Some(last_tag) = latest_tag { yield Err(ShellError::labeled_error_with_secondary( "Could not parse as SSV", "input cannot be parsed ssv", @@ -115,7 +114,7 @@ fn from_ssv( "value originates from here", last_tag, )) - } + }, } }; From 38225d0dbadd2e8af530085e0a137d98fbf1d37e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 07:48:10 +0200 Subject: [PATCH 264/342] Removes extra newline --- src/commands/from_ssv.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 3232fd1e3e..a47a8662f5 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -86,8 +86,6 @@ fn from_ssv( match value.item { Value::Primitive(Primitive::String(s)) => { concat_string.push_str(&s); - concat_string.push_str("\n"); - } _ => yield Err(ShellError::labeled_error_with_secondary ( "Expected a string from pipeline", From 0b210ce5bf55b91ce88a8706724deeb844953634 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 07:48:19 +0200 Subject: [PATCH 265/342] Filters out empty lines before table creation. --- src/commands/from_ssv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index a47a8662f5..41a611f8cf 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -38,7 +38,7 @@ fn from_ssv_string_to_value( headerless: bool, tag: impl Into, ) -> Option> { - let mut lines = s.lines(); + let mut lines = s.lines().filter(|l| !l.is_empty()); let headers = lines .next()? From a9293f62a8fd9565a67e5b01ada5d120bf5ff37e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 09:43:54 +0200 Subject: [PATCH 266/342] Adds some initial ideas for refactoring. --- src/commands/from_ssv.rs | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 41a611f8cf..354d2cb2d1 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -33,6 +33,27 @@ impl WholeStreamCommand for FromSSV { } } +fn string_to_table(s: &str, headerless: bool) -> std::iter::Map> { + let mut lines = s.lines().filter(|l| !l.trim().is_empty()); + + let headers = lines + .next() + .unwrap() + .split_whitespace() + .map(|s| s.to_owned()) + .collect::>(); + + let header_row = if headerless { + (0..headers.len()) + .map(|i| format!("Column{}", i + 1)) + .collect::>() + } else { + headers + }; + + lines.map(|l| header_row.iter().zip(l.split_whitespace())) +} + fn from_ssv_string_to_value( s: &str, headerless: bool, @@ -118,3 +139,22 @@ fn from_ssv( Ok(stream.to_output_stream()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_trims_empty_and_whitespace_only_lines() { + let input = r#" + + a b + + 1 2 + + 3 4 + "#; + + let +} +} \ No newline at end of file From 104b7824f58631d8dcfef96493827d9375ea7937 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 16:34:06 +0200 Subject: [PATCH 267/342] Updates return types. --- src/commands/from_ssv.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 354d2cb2d1..5f88147afe 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -33,7 +33,7 @@ impl WholeStreamCommand for FromSSV { } } -fn string_to_table(s: &str, headerless: bool) -> std::iter::Map> { +fn string_to_table(s: &str, headerless: bool) -> Vec> { let mut lines = s.lines().filter(|l| !l.trim().is_empty()); let headers = lines @@ -51,7 +51,7 @@ fn string_to_table(s: &str, headerless: bool) -> std::iter::Map Date: Mon, 14 Oct 2019 13:46:37 -0500 Subject: [PATCH 268/342] Color escaped externals. --- src/parser/hir/syntax_shape.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/hir/syntax_shape.rs b/src/parser/hir/syntax_shape.rs index 8accfbde2b..72fcf9ecb4 100644 --- a/src/parser/hir/syntax_shape.rs +++ b/src/parser/hir/syntax_shape.rs @@ -726,8 +726,8 @@ impl FallibleColorSyntax for CommandHeadShape { match atom.item { // If the head is an explicit external command (^cmd), color it as an external command - AtomicToken::ExternalCommand { command } => { - shapes.push(FlatShape::ExternalCommand.spanned(command)); + AtomicToken::ExternalCommand { .. } => { + shapes.push(FlatShape::ExternalCommand.spanned(atom.span)); Ok(CommandHeadKind::External) } From 22d2360c4b0760208cf582a7eaa1301be819949b Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 22:00:25 +0200 Subject: [PATCH 269/342] Adds conversion test for leading whitespace. Refactors string parsing into a separate function. --- src/commands/from_ssv.rs | 81 +++++++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 5f88147afe..56a3f10868 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -44,14 +44,22 @@ fn string_to_table(s: &str, headerless: bool) -> Vec> { .collect::>(); let header_row = if headerless { - (0..headers.len()) - .map(|i| format!("Column{}", i + 1)) + (1..=headers.len()) + .map(|i| format!("Column{}", i)) .collect::>() } else { headers }; - lines.map(|l| header_row.iter().zip(l.split_whitespace())).collect() + lines + .map(|l| { + header_row + .iter() + .zip(l.split_whitespace()) + .map(|(a, b)| (String::from(a), String::from(b))) + .collect() + }) + .collect() } fn from_ssv_string_to_value( @@ -59,33 +67,18 @@ fn from_ssv_string_to_value( headerless: bool, tag: impl Into, ) -> Option> { - let mut lines = s.lines().filter(|l| !l.is_empty()); - - let headers = lines - .next()? - .split_whitespace() - .map(|s| s.to_owned()) - .collect::>(); - - let header_row = if headerless { - (0..headers.len()) - .map(|i| format!("Column{}", i + 1)) - .collect::>() - } else { - headers - }; - let tag = tag.into(); - let rows = lines - .map(|l| { - let mut row = TaggedDictBuilder::new(tag); - for (column, value) in header_row.iter().zip(l.split_whitespace()) { - row.insert_tagged( - column.to_owned(), - Value::Primitive(Primitive::String(String::from(value))).tagged(tag), + let rows = string_to_table(s, headerless) + .iter() + .map(|row| { + let mut tagged_dict = TaggedDictBuilder::new(tag); + for (col, entry) in row { + tagged_dict.insert_tagged( + col, + Value::Primitive(Primitive::String(String::from(entry))).tagged(tag), ) } - row.into_tagged_value() + tagged_dict.into_tagged_value() }) .collect(); @@ -143,18 +136,44 @@ fn from_ssv( #[cfg(test)] mod tests { use super::*; + fn owned(x: &str, y: &str) -> (String, String) { + (String::from(x), String::from(y)) + } #[test] fn it_trims_empty_and_whitespace_only_lines() { let input = r#" - a b + a b - 1 2 + 1 2 3 4 "#; + let result = string_to_table(input, false); + assert_eq!( + result, + vec![ + vec![owned("a", "1"), owned("b", "2")], + vec![owned("a", "3"), owned("b", "4")] + ] + ); + } - let + #[test] + fn it_ignores_headers_when_headerless() { + let input = r#" + a b + 1 2 + 3 4 + "#; + let result = string_to_table(input, true); + assert_eq!( + result, + vec![ + vec![owned("Column1", "1"), owned("Column2", "2")], + vec![owned("Column1", "3"), owned("Column2", "4")] + ] + ); + } } -} \ No newline at end of file From 43ead45db6755cfdcc2e65dc367c7de2ba6f611c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 22:03:17 +0200 Subject: [PATCH 270/342] Removes rust_src_path and ssl_cert_file vars. --- shell.nix | 2 -- 1 file changed, 2 deletions(-) diff --git a/shell.nix b/shell.nix index f5c61ac0a8..d528cf8491 100644 --- a/shell.nix +++ b/shell.nix @@ -27,6 +27,4 @@ let in stdenv.mkDerivation { name = "nushell-rust"; buildInputs = nu-deps ++ rust; - RUST_SRC_PATH = "${nightly}/lib/rustlib/src/rust/src"; - SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; } From ee8cd671cb206f412c5e5b25b07b9feb9661f632 Mon Sep 17 00:00:00 2001 From: Jason Gedge Date: Mon, 14 Oct 2019 16:30:23 -0400 Subject: [PATCH 271/342] Fix bug with multiple input objects to an external command. Previously, we would build a command that looked something like this: "$it" "&&" "" "$it" So that the "&&" and "" would also be arguments to the command, instead of a chained command. This commit builds up a command string that can be passed to an external shell. --- src/commands/classified.rs | 74 ++++++++++++++++++-------------------- tests/commands_test.rs | 22 ++++++++++++ tests/helpers/mod.rs | 54 ++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 40 deletions(-) diff --git a/src/commands/classified.rs b/src/commands/classified.rs index c2380d4ffe..440413ddd4 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -225,7 +225,6 @@ impl ExternalCommand { ) -> Result { let stdin = input.stdin; let inputs: Vec> = input.objects.into_vec().await; - let name_tag = self.name_tag.clone(); trace!(target: "nu::run::external", "-> {}", self.name); trace!(target: "nu::run::external", "inputs = {:?}", inputs); @@ -235,53 +234,47 @@ impl ExternalCommand { arg_string.push_str(&arg); } + trace!(target: "nu::run::external", "command = {:?}", self.name); + let mut process; - - process = Exec::cmd(&self.name); - - trace!(target: "nu::run::external", "command = {:?}", process); - if arg_string.contains("$it") { - let mut first = true; - - for i in &inputs { - if i.as_string().is_err() { - let mut tag = None; - for arg in &self.args { - if arg.item.contains("$it") { - tag = Some(arg.tag()); + let input_strings = inputs + .iter() + .map(|i| { + i.as_string().map_err(|_| { + let arg = self.args.iter().find(|arg| arg.item.contains("$it")); + if let Some(arg) = arg { + ShellError::labeled_error( + "External $it needs string data", + "given row instead of string data", + arg.tag(), + ) + } else { + ShellError::labeled_error( + "Error: $it needs string data", + "given something else", + self.name_tag.clone(), + ) } - } - if let Some(tag) = tag { - return Err(ShellError::labeled_error( - "External $it needs string data", - "given row instead of string data", - tag, - )); - } else { - return Err(ShellError::labeled_error( - "Error: $it needs string data", - "given something else", - name_tag, - )); - } - } - if !first { - process = process.arg("&&"); - process = process.arg(&self.name); - } else { - first = false; - } + }) + }) + .collect::, ShellError>>()?; - for arg in &self.args { + let commands = input_strings.iter().map(|i| { + let args = self.args.iter().filter_map(|arg| { if arg.chars().all(|c| c.is_whitespace()) { - continue; + None + } else { + Some(arg.replace("$it", &i)) } + }); - process = process.arg(&arg.replace("$it", &i.as_string()?)); - } - } + format!("{} {}", self.name, itertools::join(args, " ")) + }); + + process = Exec::shell(itertools::join(commands, " && ")) } else { + process = Exec::cmd(&self.name); for arg in &self.args { let arg_chars: Vec<_> = arg.chars().collect(); if arg_chars.len() > 1 @@ -321,6 +314,7 @@ impl ExternalCommand { trace!(target: "nu::run::external", "next = {:?}", stream_next); + let name_tag = self.name_tag.clone(); if let Ok(mut popen) = popen { match stream_next { StreamNext::Last => { diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 30636eafca..cfa6f74334 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -164,6 +164,28 @@ fn save_figures_out_intelligently_where_to_write_out_with_metadata() { }) } +#[test] +fn it_arg_works_with_many_inputs_to_external_command() { + Playground::setup("it_arg_works_with_many_inputs", |dirs, sandbox| { + sandbox.with_files(vec![ + FileWithContent("file1", "text"), + FileWithContent("file2", " and more text"), + ]); + + let (stdout, stderr) = nu_combined!( + cwd: dirs.test(), h::pipeline( + r#" + echo file1 file2 + | split-row " " + | cat $it + "# + )); + + assert_eq!("text and more text", stdout); + assert!(!stderr.contains("No such file or directory")); + }) +} + #[test] fn save_can_write_out_csv() { Playground::setup("save_test_2", |dirs, _| { diff --git a/tests/helpers/mod.rs b/tests/helpers/mod.rs index 199038b531..86c8a10e7f 100644 --- a/tests/helpers/mod.rs +++ b/tests/helpers/mod.rs @@ -155,6 +155,60 @@ macro_rules! nu_error { }}; } +#[macro_export] +macro_rules! nu_combined { + (cwd: $cwd:expr, $path:expr, $($part:expr),*) => {{ + use $crate::helpers::DisplayPath; + + let path = format!($path, $( + $part.display_path() + ),*); + + nu_combined!($cwd, &path) + }}; + + (cwd: $cwd:expr, $path:expr) => {{ + nu_combined!($cwd, $path) + }}; + + ($cwd:expr, $path:expr) => {{ + pub use std::error::Error; + pub use std::io::prelude::*; + pub use std::process::{Command, Stdio}; + + let commands = &*format!( + " + cd {} + {} + exit", + $crate::helpers::in_directory($cwd), + $crate::helpers::DisplayPath::display_path(&$path) + ); + + let mut process = Command::new(helpers::executable_path()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .expect("couldn't run test"); + + let stdin = process.stdin.as_mut().expect("couldn't open stdin"); + stdin + .write_all(commands.as_bytes()) + .expect("couldn't write to stdin"); + + let output = process + .wait_with_output() + .expect("couldn't read from stdout/stderr"); + + let err = String::from_utf8_lossy(&output.stderr).into_owned(); + let out = String::from_utf8_lossy(&output.stdout).into_owned(); + let out = out.replace("\r\n", ""); + let out = out.replace("\n", ""); + (out, err) + }}; +} + pub enum Stub<'a> { FileWithContent(&'a str, &'a str), FileWithContentToBeTrimmed(&'a str, &'a str), From b2c53a09672f86f6f6e35001f05a4aa6f8fd8a1e Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 23:14:45 +0200 Subject: [PATCH 272/342] Updates commands to work after tag is no longer copy. --- src/commands/from_ssv.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 56a3f10868..782fb531f4 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -71,18 +71,18 @@ fn from_ssv_string_to_value( let rows = string_to_table(s, headerless) .iter() .map(|row| { - let mut tagged_dict = TaggedDictBuilder::new(tag); + let mut tagged_dict = TaggedDictBuilder::new(&tag); for (col, entry) in row { tagged_dict.insert_tagged( col, - Value::Primitive(Primitive::String(String::from(entry))).tagged(tag), + Value::Primitive(Primitive::String(String::from(entry))).tagged(&tag), ) } tagged_dict.into_tagged_value() }) .collect(); - Some(Tagged::from_item(Value::Table(rows), tag)) + Some(Value::Table(rows).tagged(&tag)) } fn from_ssv( @@ -96,7 +96,7 @@ fn from_ssv( for value in values { let value_tag = value.tag(); - latest_tag = Some(value_tag); + latest_tag = Some(value_tag.clone()); match value.item { Value::Primitive(Primitive::String(s)) => { concat_string.push_str(&s); @@ -104,27 +104,27 @@ fn from_ssv( _ => yield Err(ShellError::labeled_error_with_secondary ( "Expected a string from pipeline", "requires string input", - name, + &name, "value originates from here", - value_tag + &value_tag )), } } - match from_ssv_string_to_value(&concat_string, headerless, name) { + match from_ssv_string_to_value(&concat_string, headerless, name.clone()) { Some(x) => match x { Tagged { item: Value::Table(list), ..} => { for l in list { yield ReturnSuccess::value(l) } } x => yield ReturnSuccess::value(x) }, - None => if let Some(last_tag) = latest_tag { + None => if let Some(tag) = latest_tag { yield Err(ShellError::labeled_error_with_secondary( "Could not parse as SSV", "input cannot be parsed ssv", - name, + &name, "value originates from here", - last_tag, + &tag, )) }, } From de12393eaf497286288d95c23431104fbc87fa16 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 23:25:52 +0200 Subject: [PATCH 273/342] Updates shell.nix. --- shell.nix | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/shell.nix b/shell.nix index d528cf8491..4e287ab115 100644 --- a/shell.nix +++ b/shell.nix @@ -8,7 +8,7 @@ with pkgs; let nightly = (pkgs.rustChannelOf { - date = "2019-09-01"; + date = "2019-10-14"; channel = "nightly"; }).rust.override { extensions = [ @@ -27,4 +27,5 @@ let in stdenv.mkDerivation { name = "nushell-rust"; buildInputs = nu-deps ++ rust; + SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; } From d21389d549109324a4487d59a2b9d114df38d326 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 00:24:32 +0200 Subject: [PATCH 274/342] Removes unwrap. A rogue unwrap had been left in the code, but has now been replaced by an option. --- src/commands/from_ssv.rs | 42 ++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 782fb531f4..1be9b4567a 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -33,12 +33,11 @@ impl WholeStreamCommand for FromSSV { } } -fn string_to_table(s: &str, headerless: bool) -> Vec> { +fn string_to_table(s: &str, headerless: bool) -> Option>> { let mut lines = s.lines().filter(|l| !l.trim().is_empty()); let headers = lines - .next() - .unwrap() + .next()? .split_whitespace() .map(|s| s.to_owned()) .collect::>(); @@ -51,15 +50,17 @@ fn string_to_table(s: &str, headerless: bool) -> Vec> { headers }; - lines - .map(|l| { - header_row - .iter() - .zip(l.split_whitespace()) - .map(|(a, b)| (String::from(a), String::from(b))) - .collect() - }) - .collect() + Some( + lines + .map(|l| { + header_row + .iter() + .zip(l.split_whitespace()) + .map(|(a, b)| (String::from(a), String::from(b))) + .collect() + }) + .collect(), + ) } fn from_ssv_string_to_value( @@ -68,7 +69,7 @@ fn from_ssv_string_to_value( tag: impl Into, ) -> Option> { let tag = tag.into(); - let rows = string_to_table(s, headerless) + let rows = string_to_table(s, headerless)? .iter() .map(|row| { let mut tagged_dict = TaggedDictBuilder::new(&tag); @@ -153,10 +154,10 @@ mod tests { let result = string_to_table(input, false); assert_eq!( result, - vec![ + Some(vec![ vec![owned("a", "1"), owned("b", "2")], vec![owned("a", "3"), owned("b", "4")] - ] + ]) ); } @@ -170,10 +171,17 @@ mod tests { let result = string_to_table(input, true); assert_eq!( result, - vec![ + Some(vec![ vec![owned("Column1", "1"), owned("Column2", "2")], vec![owned("Column1", "3"), owned("Column2", "4")] - ] + ]) ); } + + #[test] + fn it_returns_none_given_an_empty_string() { + let input = ""; + let result = string_to_table(input, true); + assert_eq!(result, None); + } } From 65008bb912076f288d0c0dbf8ba0ae9f3641d374 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Mon, 14 Oct 2019 23:46:19 +0200 Subject: [PATCH 275/342] Deletes nix-specific configuration. --- .envrc | 1 - shell.nix | 31 ------------------------------- 2 files changed, 32 deletions(-) delete mode 100644 .envrc delete mode 100644 shell.nix diff --git a/.envrc b/.envrc deleted file mode 100644 index 65326bb6dd..0000000000 --- a/.envrc +++ /dev/null @@ -1 +0,0 @@ -use nix \ No newline at end of file diff --git a/shell.nix b/shell.nix deleted file mode 100644 index 4e287ab115..0000000000 --- a/shell.nix +++ /dev/null @@ -1,31 +0,0 @@ -{ pkgs ? import { - overlays = [ - (import (builtins.fetchTarball - "https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz")) - ]; -} }: -with pkgs; -let - - nightly = (pkgs.rustChannelOf { - date = "2019-10-14"; - channel = "nightly"; - }).rust.override { - extensions = [ - "clippy-preview" - "rls-preview" - "rust-analysis" - "rust-src" - "rustfmt-preview" - ]; - }; - - nu-deps = [ openssl_1_1 pkg-config x11 python3 ]; - - rust = [ nightly rustracer cargo-watch ]; - -in stdenv.mkDerivation { - name = "nushell-rust"; - buildInputs = nu-deps ++ rust; - SSL_CERT_FILE = "/etc/ssl/certs/ca-certificates.crt"; -} From f20f3f56c7c967d2be19d11c99c69f9ff3017852 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 14 Oct 2019 16:11:00 -0700 Subject: [PATCH 276/342] Start moving coloring into the token stream The benefit of this is that coloring can be made atomic alongside token stream forwarding. I put the feature behind a flag so I can continue to iterate on it without possibly regressing existing functionality. It's a lot of places where the flags have to go, but I expect it to be a short-lived flag, and the flags are fully contained in the parser. --- features.toml | 9 + src/parser/hir/expand_external_tokens.rs | 61 ++- src/parser/hir/syntax_shape.rs | 468 ++++++++++++++++++ src/parser/hir/syntax_shape/block.rs | 170 ++++++- src/parser/hir/syntax_shape/expression.rs | 163 ++++++ .../hir/syntax_shape/expression/delimited.rs | 30 ++ .../hir/syntax_shape/expression/file_path.rs | 39 ++ .../hir/syntax_shape/expression/list.rs | 128 ++++- .../hir/syntax_shape/expression/number.rs | 60 +++ .../hir/syntax_shape/expression/pattern.rs | 27 + .../hir/syntax_shape/expression/string.rs | 31 ++ .../syntax_shape/expression/variable_path.rs | 307 ++++++++++++ src/parser/hir/tokens_iterator.rs | 98 +++- src/parser/parse_command.rs | 201 ++++++++ src/shell/helper.rs | 26 +- 15 files changed, 1808 insertions(+), 10 deletions(-) diff --git a/features.toml b/features.toml index 290f673d26..f7cea6d9e9 100644 --- a/features.toml +++ b/features.toml @@ -2,3 +2,12 @@ description = "Adding hints based upon error states in the syntax highlighter" enabled = false + +[coloring_in_tokens] + +description = "Move coloring into the TokensIterator so they can be atomic with the rest of the iterator" +reason = """ +This is laying the groundwork for merging coloring and parsing. It also makes token_nodes.atomic() naturally +work with coloring, which is pretty useful on its own. +""" +enabled = false \ No newline at end of file diff --git a/src/parser/hir/expand_external_tokens.rs b/src/parser/hir/expand_external_tokens.rs index af966945bd..e277efe2e8 100644 --- a/src/parser/hir/expand_external_tokens.rs +++ b/src/parser/hir/expand_external_tokens.rs @@ -1,10 +1,12 @@ use crate::errors::ShellError; +#[cfg(not(coloring_in_tokens))] +use crate::parser::hir::syntax_shape::FlatShape; use crate::parser::{ hir::syntax_shape::{ color_syntax, expand_atom, AtomicToken, ColorSyntax, ExpandContext, ExpansionRule, MaybeSpaceShape, }, - FlatShape, TokenNode, TokensIterator, + TokenNode, TokensIterator, }; use crate::{Span, Spanned, Text}; @@ -28,6 +30,7 @@ pub fn expand_external_tokens( #[derive(Debug, Copy, Clone)] pub struct ExternalTokensShape; +#[cfg(not(coloring_in_tokens))] impl ColorSyntax for ExternalTokensShape { type Info = (); type Input = (); @@ -53,6 +56,31 @@ impl ColorSyntax for ExternalTokensShape { } } +#[cfg(coloring_in_tokens)] +impl ColorSyntax for ExternalTokensShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Self::Info { + loop { + // Allow a space + color_syntax(&MaybeSpaceShape, token_nodes, context); + + // Process an external expression. External expressions are mostly words, with a + // few exceptions (like $variables and path expansion rules) + match color_syntax(&ExternalExpression, token_nodes, context).1 { + ExternalExpressionResult::Eof => break, + ExternalExpressionResult::Processed => continue, + } + } + } +} + pub fn expand_next_expression( token_nodes: &mut TokensIterator<'_>, ) -> Result, ShellError> { @@ -128,6 +156,7 @@ enum ExternalExpressionResult { #[derive(Debug, Copy, Clone)] struct ExternalExpression; +#[cfg(not(coloring_in_tokens))] impl ColorSyntax for ExternalExpression { type Info = ExternalExpressionResult; type Input = (); @@ -157,3 +186,33 @@ impl ColorSyntax for ExternalExpression { return ExternalExpressionResult::Processed; } } + +#[cfg(coloring_in_tokens)] +impl ColorSyntax for ExternalExpression { + type Info = ExternalExpressionResult; + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> ExternalExpressionResult { + let atom = match expand_atom( + token_nodes, + "external word", + context, + ExpansionRule::permissive(), + ) { + Err(_) => unreachable!("TODO: separate infallible expand_atom"), + Ok(Spanned { + item: AtomicToken::Eof { .. }, + .. + }) => return ExternalExpressionResult::Eof, + Ok(atom) => atom, + }; + + atom.color_tokens(token_nodes.mut_shapes()); + return ExternalExpressionResult::Processed; + } +} diff --git a/src/parser/hir/syntax_shape.rs b/src/parser/hir/syntax_shape.rs index 8accfbde2b..37bb8abed4 100644 --- a/src/parser/hir/syntax_shape.rs +++ b/src/parser/hir/syntax_shape.rs @@ -55,6 +55,7 @@ pub enum SyntaxShape { Block, } +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for SyntaxShape { type Info = (); type Input = (); @@ -104,6 +105,39 @@ impl FallibleColorSyntax for SyntaxShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for SyntaxShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + match self { + SyntaxShape::Any => color_fallible_syntax(&AnyExpressionShape, token_nodes, context), + SyntaxShape::List => { + color_syntax(&ExpressionListShape, token_nodes, context); + Ok(()) + } + SyntaxShape::Int => color_fallible_syntax(&IntShape, token_nodes, context), + SyntaxShape::String => { + color_fallible_syntax_with(&StringShape, &FlatShape::String, token_nodes, context) + } + SyntaxShape::Member => color_fallible_syntax(&MemberShape, token_nodes, context), + SyntaxShape::ColumnPath => { + color_fallible_syntax(&ColumnPathShape, token_nodes, context) + } + SyntaxShape::Number => color_fallible_syntax(&NumberShape, token_nodes, context), + SyntaxShape::Path => color_fallible_syntax(&FilePathShape, token_nodes, context), + SyntaxShape::Pattern => color_fallible_syntax(&PatternShape, token_nodes, context), + SyntaxShape::Block => color_fallible_syntax(&AnyBlockShape, token_nodes, context), + } + } +} + impl ExpandExpression for SyntaxShape { fn expand_expr<'a, 'b>( &self, @@ -202,6 +236,20 @@ pub trait ExpandExpression: std::fmt::Debug + Copy { ) -> Result; } +#[cfg(coloring_in_tokens)] +pub trait FallibleColorSyntax: std::fmt::Debug + Copy { + type Info; + type Input; + + fn color_syntax<'a, 'b>( + &self, + input: &Self::Input, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result; +} + +#[cfg(not(coloring_in_tokens))] pub trait FallibleColorSyntax: std::fmt::Debug + Copy { type Info; type Input; @@ -215,6 +263,7 @@ pub trait FallibleColorSyntax: std::fmt::Debug + Copy { ) -> Result; } +#[cfg(not(coloring_in_tokens))] pub trait ColorSyntax: std::fmt::Debug + Copy { type Info; type Input; @@ -228,6 +277,19 @@ pub trait ColorSyntax: std::fmt::Debug + Copy { ) -> Self::Info; } +#[cfg(coloring_in_tokens)] +pub trait ColorSyntax: std::fmt::Debug + Copy { + type Info; + type Input; + + fn color_syntax<'a, 'b>( + &self, + input: &Self::Input, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Self::Info; +} + // impl ColorSyntax for T // where // T: FallibleColorSyntax, @@ -278,6 +340,7 @@ pub(crate) fn expand_syntax<'a, 'b, T: ExpandSyntax>( } } +#[cfg(not(coloring_in_tokens))] pub fn color_syntax<'a, 'b, T: ColorSyntax, U>( shape: &T, token_nodes: &'b mut TokensIterator<'a>, @@ -306,6 +369,35 @@ pub fn color_syntax<'a, 'b, T: ColorSyntax, U>( ((), result) } +#[cfg(coloring_in_tokens)] +pub fn color_syntax<'a, 'b, T: ColorSyntax, U>( + shape: &T, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, +) -> ((), U) { + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + let len = token_nodes.shapes().len(); + let result = shape.color_syntax(&(), token_nodes, context); + + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + + if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { + trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); + + if len < token_nodes.shapes().len() { + for i in len..(token_nodes.shapes().len()) { + trace!(target: "nu::color_syntax", "new shape :: {:?}", token_nodes.shapes()[i]); + } + } else { + trace!(target: "nu::color_syntax", "no new shapes"); + } + } + + ((), result) +} + +#[cfg(not(coloring_in_tokens))] pub fn color_fallible_syntax<'a, 'b, T: FallibleColorSyntax, U>( shape: &T, token_nodes: &'b mut TokensIterator<'a>, @@ -339,6 +431,40 @@ pub fn color_fallible_syntax<'a, 'b, T: FallibleColorSyntax, U>( + shape: &T, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, +) -> Result { + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + if token_nodes.at_end() { + trace!(target: "nu::color_syntax", "at eof"); + return Err(ShellError::unexpected_eof("coloring", Tag::unknown())); + } + + let len = token_nodes.shapes().len(); + let result = shape.color_syntax(&(), token_nodes, context); + + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + + if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { + trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); + + if len < token_nodes.shapes().len() { + for i in len..(token_nodes.shapes().len()) { + trace!(target: "nu::color_syntax", "new shape :: {:?}", token_nodes.shapes()[i]); + } + } else { + trace!(target: "nu::color_syntax", "no new shapes"); + } + } + + result +} + +#[cfg(not(coloring_in_tokens))] pub fn color_syntax_with<'a, 'b, T: ColorSyntax, U, I>( shape: &T, input: &I, @@ -368,6 +494,36 @@ pub fn color_syntax_with<'a, 'b, T: ColorSyntax, U, I>( ((), result) } +#[cfg(coloring_in_tokens)] +pub fn color_syntax_with<'a, 'b, T: ColorSyntax, U, I>( + shape: &T, + input: &I, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, +) -> ((), U) { + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + let len = token_nodes.shapes().len(); + let result = shape.color_syntax(input, token_nodes, context); + + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + + if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { + trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); + + if len < token_nodes.shapes().len() { + for i in len..(token_nodes.shapes().len()) { + trace!(target: "nu::color_syntax", "new shape :: {:?}", token_nodes.shapes()[i]); + } + } else { + trace!(target: "nu::color_syntax", "no new shapes"); + } + } + + ((), result) +} + +#[cfg(not(coloring_in_tokens))] pub fn color_fallible_syntax_with<'a, 'b, T: FallibleColorSyntax, U, I>( shape: &T, input: &I, @@ -402,6 +558,40 @@ pub fn color_fallible_syntax_with<'a, 'b, T: FallibleColorSyntax, U, I>( + shape: &T, + input: &I, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, +) -> Result { + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + + if token_nodes.at_end() { + trace!(target: "nu::color_syntax", "at eof"); + return Err(ShellError::unexpected_eof("coloring", Tag::unknown())); + } + + let len = token_nodes.shapes().len(); + let result = shape.color_syntax(input, token_nodes, context); + + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + + if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { + trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); + + if len < token_nodes.shapes().len() { + for i in len..(token_nodes.shapes().len()) { + trace!(target: "nu::color_syntax", "new shape :: {:?}", token_nodes.shapes()[i]); + } + } else { + trace!(target: "nu::color_syntax", "no new shapes"); + } + } + + result +} + pub(crate) fn expand_expr<'a, 'b, T: ExpandExpression>( shape: &T, token_nodes: &'b mut TokensIterator<'a>, @@ -536,6 +726,7 @@ impl ExpandSyntax for BarePathShape { #[derive(Debug, Copy, Clone)] pub struct BareShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for BareShape { type Info = (); type Input = FlatShape; @@ -563,6 +754,37 @@ impl FallibleColorSyntax for BareShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for BareShape { + type Info = (); + type Input = FlatShape; + + fn color_syntax<'a, 'b>( + &self, + input: &FlatShape, + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Result<(), ShellError> { + let span = token_nodes.peek_any_token(|token| match token { + // If it's a bare token, color it + TokenNode::Token(Spanned { + item: RawToken::Bare, + span, + }) => { + // token_nodes.color_shape((*input).spanned(*span)); + Ok(span) + } + + // otherwise, fail + other => Err(ShellError::type_error("word", other.tagged_type_name())), + })?; + + token_nodes.color_shape((*input).spanned(*span)); + + Ok(()) + } +} + impl ExpandSyntax for BareShape { type Output = Spanned; @@ -636,6 +858,7 @@ impl CommandSignature { #[derive(Debug, Copy, Clone)] pub struct PipelineShape; +#[cfg(not(coloring_in_tokens))] // The failure mode is if the head of the token stream is not a pipeline impl FallibleColorSyntax for PipelineShape { type Info = (); @@ -669,6 +892,39 @@ impl FallibleColorSyntax for PipelineShape { } } +#[cfg(coloring_in_tokens)] +// The failure mode is if the head of the token stream is not a pipeline +impl FallibleColorSyntax for PipelineShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + // Make sure we're looking at a pipeline + let Pipeline { parts, .. } = token_nodes.peek_any_token(|node| node.as_pipeline())?; + + // Enumerate the pipeline parts + for part in parts { + // If the pipeline part has a prefix `|`, emit a pipe to color + if let Some(pipe) = part.pipe { + token_nodes.color_shape(FlatShape::Pipe.spanned(pipe)) + } + + // Create a new iterator containing the tokens in the pipeline part to color + let mut token_nodes = TokensIterator::new(&part.tokens.item, part.span, false); + + color_syntax(&MaybeSpaceShape, &mut token_nodes, context); + color_syntax(&CommandShape, &mut token_nodes, context); + } + + Ok(()) + } +} + impl ExpandSyntax for PipelineShape { type Output = ClassifiedPipeline; fn expand_syntax<'a, 'b>( @@ -703,6 +959,7 @@ pub enum CommandHeadKind { #[derive(Debug, Copy, Clone)] pub struct CommandHeadShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for CommandHeadShape { type Info = CommandHeadKind; type Input = (); @@ -756,6 +1013,59 @@ impl FallibleColorSyntax for CommandHeadShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for CommandHeadShape { + type Info = CommandHeadKind; + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + // If we don't ultimately find a token, roll back + token_nodes.atomic(|token_nodes| { + // First, take a look at the next token + let atom = expand_atom( + token_nodes, + "command head", + context, + ExpansionRule::permissive(), + )?; + + match atom.item { + // If the head is an explicit external command (^cmd), color it as an external command + AtomicToken::ExternalCommand { command } => { + token_nodes.color_shape(FlatShape::ExternalCommand.spanned(command)); + Ok(CommandHeadKind::External) + } + + // If the head is a word, it depends on whether it matches a registered internal command + AtomicToken::Word { text } => { + let name = text.slice(context.source); + + if context.registry.has(name) { + // If the registry has the command, color it as an internal command + token_nodes.color_shape(FlatShape::InternalCommand.spanned(text)); + let command = context.registry.expect_command(name); + Ok(CommandHeadKind::Internal(command.signature())) + } else { + // Otherwise, color it as an external command + token_nodes.color_shape(FlatShape::ExternalCommand.spanned(text)); + Ok(CommandHeadKind::External) + } + } + + // Otherwise, we're not actually looking at a command + _ => Err(ShellError::syntax_error( + "No command at the head".tagged(atom.span), + )), + } + }) + } +} + impl ExpandSyntax for CommandHeadShape { type Output = CommandSignature; @@ -861,6 +1171,7 @@ impl ExpandSyntax for ClassifiedCommandShape { #[derive(Debug, Copy, Clone)] pub struct InternalCommandHeadShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for InternalCommandHeadShape { type Info = (); type Input = (); @@ -899,6 +1210,44 @@ impl FallibleColorSyntax for InternalCommandHeadShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for InternalCommandHeadShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Result<(), ShellError> { + let peeked_head = token_nodes.peek_non_ws().not_eof("command head4"); + + let peeked_head = match peeked_head { + Err(_) => return Ok(()), + Ok(peeked_head) => peeked_head, + }; + + let node = peeked_head.commit(); + + let _expr = match node { + TokenNode::Token(Spanned { + item: RawToken::Bare, + span, + }) => token_nodes.color_shape(FlatShape::Word.spanned(*span)), + + TokenNode::Token(Spanned { + item: RawToken::String(_inner_tag), + span, + }) => token_nodes.color_shape(FlatShape::String.spanned(*span)), + + _node => token_nodes.color_shape(FlatShape::Error.spanned(node.span())), + }; + + Ok(()) + } +} + impl ExpandExpression for InternalCommandHeadShape { fn expand_expr( &self, @@ -992,6 +1341,7 @@ fn parse_single_node_skipping_ws<'a, 'b, T>( #[derive(Debug, Copy, Clone)] pub struct WhitespaceShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for WhitespaceShape { type Info = (); type Input = (); @@ -1022,6 +1372,38 @@ impl FallibleColorSyntax for WhitespaceShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for WhitespaceShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Result<(), ShellError> { + let peeked = token_nodes.peek_any().not_eof("whitespace"); + + let peeked = match peeked { + Err(_) => return Ok(()), + Ok(peeked) => peeked, + }; + + let node = peeked.commit(); + + let _ = match node { + TokenNode::Whitespace(span) => { + token_nodes.color_shape(FlatShape::Whitespace.spanned(*span)) + } + + _other => return Ok(()), + }; + + Ok(()) + } +} + impl ExpandSyntax for WhitespaceShape { type Output = Span; @@ -1089,6 +1471,7 @@ pub struct MaybeSpacedExpression { #[derive(Debug, Copy, Clone)] pub struct MaybeSpaceShape; +#[cfg(not(coloring_in_tokens))] impl ColorSyntax for MaybeSpaceShape { type Info = (); type Input = (); @@ -1114,9 +1497,35 @@ impl ColorSyntax for MaybeSpaceShape { } } +#[cfg(coloring_in_tokens)] +impl ColorSyntax for MaybeSpaceShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Self::Info { + let peeked = token_nodes.peek_any().not_eof("whitespace"); + + let peeked = match peeked { + Err(_) => return, + Ok(peeked) => peeked, + }; + + if let TokenNode::Whitespace(span) = peeked.node { + peeked.commit(); + token_nodes.color_shape(FlatShape::Whitespace.spanned(*span)); + } + } +} + #[derive(Debug, Copy, Clone)] pub struct SpaceShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for SpaceShape { type Info = (); type Input = (); @@ -1145,6 +1554,34 @@ impl FallibleColorSyntax for SpaceShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for SpaceShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Result<(), ShellError> { + let peeked = token_nodes.peek_any().not_eof("whitespace")?; + + match peeked.node { + TokenNode::Whitespace(span) => { + peeked.commit(); + token_nodes.color_shape(FlatShape::Whitespace.spanned(*span)); + Ok(()) + } + + other => Err(ShellError::type_error( + "whitespace", + other.tagged_type_name(), + )), + } + } +} + impl ExpandExpression for MaybeSpacedExpression { fn expand_expr<'a, 'b>( &self, @@ -1237,6 +1674,7 @@ fn classify_command( #[derive(Debug, Copy, Clone)] pub struct CommandShape; +#[cfg(not(coloring_in_tokens))] impl ColorSyntax for CommandShape { type Info = (); type Input = (); @@ -1266,3 +1704,33 @@ impl ColorSyntax for CommandShape { }; } } + +#[cfg(coloring_in_tokens)] +impl ColorSyntax for CommandShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) { + let kind = color_fallible_syntax(&CommandHeadShape, token_nodes, context); + + match kind { + Err(_) => { + // We didn't find a command, so we'll have to fall back to parsing this pipeline part + // as a blob of undifferentiated expressions + color_syntax(&ExpressionListShape, token_nodes, context); + } + + Ok(CommandHeadKind::External) => { + color_syntax(&ExternalTokensShape, token_nodes, context); + } + Ok(CommandHeadKind::Internal(signature)) => { + color_syntax_with(&CommandTailShape, &signature, token_nodes, context); + } + }; + } +} diff --git a/src/parser/hir/syntax_shape/block.rs b/src/parser/hir/syntax_shape/block.rs index 7518d8f946..fdf2ecb3f8 100644 --- a/src/parser/hir/syntax_shape/block.rs +++ b/src/parser/hir/syntax_shape/block.rs @@ -1,11 +1,12 @@ use crate::errors::ShellError; +#[cfg(not(coloring_in_tokens))] +use crate::parser::hir::syntax_shape::FlatShape; use crate::parser::{ hir, hir::syntax_shape::{ color_fallible_syntax, color_syntax_with, continue_expression, expand_expr, expand_syntax, DelimitedShape, ExpandContext, ExpandExpression, ExpressionContinuationShape, - ExpressionListShape, FallibleColorSyntax, FlatShape, MemberShape, PathTailShape, - VariablePathShape, + ExpressionListShape, FallibleColorSyntax, MemberShape, PathTailShape, VariablePathShape, }, hir::tokens_iterator::TokensIterator, parse::token_tree::Delimiter, @@ -16,6 +17,7 @@ use crate::{Span, Spanned, SpannedItem}; #[derive(Debug, Copy, Clone)] pub struct AnyBlockShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for AnyBlockShape { type Info = (); type Input = (); @@ -59,6 +61,48 @@ impl FallibleColorSyntax for AnyBlockShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for AnyBlockShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let block = token_nodes.peek_non_ws().not_eof("block"); + + let block = match block { + Err(_) => return Ok(()), + Ok(block) => block, + }; + + // is it just a block? + let block = block.node.as_block(); + + match block { + // If so, color it as a block + Some((children, spans)) => { + let mut token_nodes = TokensIterator::new(children.item, context.span, false); + color_syntax_with( + &DelimitedShape, + &(Delimiter::Brace, spans.0, spans.1), + &mut token_nodes, + context, + ); + + return Ok(()); + } + _ => {} + } + + // Otherwise, look for a shorthand block. If none found, fail + color_fallible_syntax(&ShorthandBlock, token_nodes, context) + } +} + impl ExpandExpression for AnyBlockShape { fn expand_expr<'a, 'b>( &self, @@ -88,6 +132,7 @@ impl ExpandExpression for AnyBlockShape { #[derive(Debug, Copy, Clone)] pub struct ShorthandBlock; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for ShorthandBlock { type Info = (); type Input = (); @@ -119,6 +164,36 @@ impl FallibleColorSyntax for ShorthandBlock { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for ShorthandBlock { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + // Try to find a shorthand head. If none found, fail + color_fallible_syntax(&ShorthandPath, token_nodes, context)?; + + loop { + // Check to see whether there's any continuation after the head expression + let result = color_fallible_syntax(&ExpressionContinuationShape, token_nodes, context); + + match result { + // if no continuation was found, we're done + Err(_) => break, + // if a continuation was found, look for another one + Ok(_) => continue, + } + } + + Ok(()) + } +} + impl ExpandExpression for ShorthandBlock { fn expand_expr<'a, 'b>( &self, @@ -139,6 +214,7 @@ impl ExpandExpression for ShorthandBlock { #[derive(Debug, Copy, Clone)] pub struct ShorthandPath; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for ShorthandPath { type Info = (); type Input = (); @@ -183,6 +259,50 @@ impl FallibleColorSyntax for ShorthandPath { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for ShorthandPath { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + token_nodes.atomic(|token_nodes| { + let variable = color_fallible_syntax(&VariablePathShape, token_nodes, context); + + match variable { + Ok(_) => { + // if it's a variable path, that's the head part + return Ok(()); + } + + Err(_) => { + // otherwise, we'll try to find a member path + } + } + + // look for a member (`` -> `$it.`) + color_fallible_syntax(&MemberShape, token_nodes, context)?; + + // Now that we've synthesized the head, of the path, proceed to expand the tail of the path + // like any other path. + let tail = color_fallible_syntax(&PathTailShape, token_nodes, context); + + match tail { + Ok(_) => {} + Err(_) => { + // It's ok if there's no path tail; a single member is sufficient + } + } + + Ok(()) + }) + } +} + impl ExpandExpression for ShorthandPath { fn expand_expr<'a, 'b>( &self, @@ -223,6 +343,52 @@ impl ExpandExpression for ShorthandPath { #[derive(Debug, Copy, Clone)] pub struct ShorthandHeadShape; +#[cfg(not(coloring_in_tokens))] +impl FallibleColorSyntax for ShorthandHeadShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + shapes: &mut Vec>, + ) -> Result<(), ShellError> { + // A shorthand path must not be at EOF + let peeked = token_nodes.peek_non_ws().not_eof("shorthand path")?; + + match peeked.node { + // If the head of a shorthand path is a bare token, it expands to `$it.bare` + TokenNode::Token(Spanned { + item: RawToken::Bare, + span, + }) => { + peeked.commit(); + shapes.push(FlatShape::BareMember.spanned(*span)); + Ok(()) + } + + // If the head of a shorthand path is a string, it expands to `$it."some string"` + TokenNode::Token(Spanned { + item: RawToken::String(_), + span: outer, + }) => { + peeked.commit(); + shapes.push(FlatShape::StringMember.spanned(*outer)); + Ok(()) + } + + other => Err(ShellError::type_error( + "shorthand head", + other.tagged_type_name(), + )), + } + } +} + +#[cfg(coloring_in_tokens)] +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for ShorthandHeadShape { type Info = (); type Input = (); diff --git a/src/parser/hir/syntax_shape/expression.rs b/src/parser/hir/syntax_shape/expression.rs index 0be63eaeb6..eccebf7516 100644 --- a/src/parser/hir/syntax_shape/expression.rs +++ b/src/parser/hir/syntax_shape/expression.rs @@ -37,6 +37,7 @@ impl ExpandExpression for AnyExpressionShape { } } +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for AnyExpressionShape { type Info = (); type Input = (); @@ -63,6 +64,32 @@ impl FallibleColorSyntax for AnyExpressionShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for AnyExpressionShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + // Look for an expression at the cursor + color_fallible_syntax(&AnyExpressionStartShape, token_nodes, context)?; + + match continue_coloring_expression(token_nodes, context) { + Err(_) => { + // it's fine for there to be no continuation + } + + Ok(()) => {} + } + + Ok(()) + } +} + pub(crate) fn continue_expression( mut head: hir::Expression, token_nodes: &mut TokensIterator<'_>, @@ -91,6 +118,7 @@ pub(crate) fn continue_expression( } } +#[cfg(not(coloring_in_tokens))] pub(crate) fn continue_coloring_expression( token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, @@ -115,6 +143,29 @@ pub(crate) fn continue_coloring_expression( } } +#[cfg(coloring_in_tokens)] +pub(crate) fn continue_coloring_expression( + token_nodes: &mut TokensIterator<'_>, + context: &ExpandContext, +) -> Result<(), ShellError> { + // if there's not even one expression continuation, fail + color_fallible_syntax(&ExpressionContinuationShape, token_nodes, context)?; + + loop { + // Check to see whether there's any continuation after the head expression + let result = color_fallible_syntax(&ExpressionContinuationShape, token_nodes, context); + + match result { + Err(_) => { + // We already saw one continuation, so just return + return Ok(()); + } + + Ok(_) => {} + } + } +} + #[derive(Debug, Copy, Clone)] pub struct AnyExpressionStartShape; @@ -152,6 +203,7 @@ impl ExpandExpression for AnyExpressionStartShape { } } +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for AnyExpressionStartShape { type Info = (); type Input = (); @@ -210,9 +262,70 @@ impl FallibleColorSyntax for AnyExpressionStartShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for AnyExpressionStartShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let atom = token_nodes.spanned(|token_nodes| { + expand_atom( + token_nodes, + "expression", + context, + ExpansionRule::permissive(), + ) + }); + + let atom = match atom { + Spanned { + item: Err(_err), + span, + } => { + token_nodes.color_shape(FlatShape::Error.spanned(span)); + return Ok(()); + } + + Spanned { + item: Ok(value), .. + } => value, + }; + + match atom.item { + AtomicToken::Size { number, unit } => token_nodes.color_shape( + FlatShape::Size { + number: number.span.into(), + unit: unit.span.into(), + } + .spanned(atom.span), + ), + + AtomicToken::SquareDelimited { nodes, spans } => { + token_nodes.child((&nodes[..]).spanned(atom.span), |tokens| { + color_delimited_square(spans, tokens, atom.span.into(), context); + }); + } + + AtomicToken::Word { .. } | AtomicToken::Dot { .. } => { + token_nodes.color_shape(FlatShape::Word.spanned(atom.span)); + } + + _ => atom.color_tokens(token_nodes.mut_shapes()), + } + + Ok(()) + } +} + #[derive(Debug, Copy, Clone)] pub struct BareTailShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for BareTailShape { type Info = (); type Input = (); @@ -269,6 +382,56 @@ impl FallibleColorSyntax for BareTailShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for BareTailShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let len = token_nodes.shapes().len(); + + loop { + let word = + color_fallible_syntax_with(&BareShape, &FlatShape::Word, token_nodes, context); + + match word { + // if a word was found, continue + Ok(_) => continue, + // if a word wasn't found, try to find a dot + Err(_) => {} + } + + // try to find a dot + let dot = color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Word, + token_nodes, + context, + ); + + match dot { + // if a dot was found, try to find another word + Ok(_) => continue, + // otherwise, we're done + Err(_) => break, + } + } + + if token_nodes.shapes().len() > len { + Ok(()) + } else { + Err(ShellError::syntax_error( + "No tokens matched BareTailShape".tagged_unknown(), + )) + } + } +} + impl ExpandSyntax for BareTailShape { type Output = Option; diff --git a/src/parser/hir/syntax_shape/expression/delimited.rs b/src/parser/hir/syntax_shape/expression/delimited.rs index b52340ab8f..5f8406c6cb 100644 --- a/src/parser/hir/syntax_shape/expression/delimited.rs +++ b/src/parser/hir/syntax_shape/expression/delimited.rs @@ -16,6 +16,7 @@ pub fn expand_delimited_square( Ok(hir::Expression::list(list?, Tag { span, anchor: None })) } +#[cfg(not(coloring_in_tokens))] pub fn color_delimited_square( (open, close): (Span, Span), children: &Vec, @@ -29,9 +30,22 @@ pub fn color_delimited_square( shapes.push(FlatShape::CloseDelimiter(Delimiter::Square).spanned(close)); } +#[cfg(coloring_in_tokens)] +pub fn color_delimited_square( + (open, close): (Span, Span), + token_nodes: &mut TokensIterator, + _span: Span, + context: &ExpandContext, +) { + token_nodes.color_shape(FlatShape::OpenDelimiter(Delimiter::Square).spanned(open)); + let _list = color_syntax(&ExpressionListShape, token_nodes, context); + token_nodes.color_shape(FlatShape::CloseDelimiter(Delimiter::Square).spanned(close)); +} + #[derive(Debug, Copy, Clone)] pub struct DelimitedShape; +#[cfg(not(coloring_in_tokens))] impl ColorSyntax for DelimitedShape { type Info = (); type Input = (Delimiter, Span, Span); @@ -47,3 +61,19 @@ impl ColorSyntax for DelimitedShape { shapes.push(FlatShape::CloseDelimiter(*delimiter).spanned(*close)); } } + +#[cfg(coloring_in_tokens)] +impl ColorSyntax for DelimitedShape { + type Info = (); + type Input = (Delimiter, Span, Span); + fn color_syntax<'a, 'b>( + &self, + (delimiter, open, close): &(Delimiter, Span, Span), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Self::Info { + token_nodes.color_shape(FlatShape::OpenDelimiter(*delimiter).spanned(*open)); + color_syntax(&ExpressionListShape, token_nodes, context); + token_nodes.color_shape(FlatShape::CloseDelimiter(*delimiter).spanned(*close)); + } +} diff --git a/src/parser/hir/syntax_shape/expression/file_path.rs b/src/parser/hir/syntax_shape/expression/file_path.rs index ccb2f8f54b..acde8fba13 100644 --- a/src/parser/hir/syntax_shape/expression/file_path.rs +++ b/src/parser/hir/syntax_shape/expression/file_path.rs @@ -8,6 +8,7 @@ use crate::prelude::*; #[derive(Debug, Copy, Clone)] pub struct FilePathShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for FilePathShape { type Info = (); type Input = (); @@ -46,6 +47,44 @@ impl FallibleColorSyntax for FilePathShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for FilePathShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let atom = expand_atom( + token_nodes, + "file path", + context, + ExpansionRule::permissive(), + ); + + let atom = match atom { + Err(_) => return Ok(()), + Ok(atom) => atom, + }; + + match atom.item { + AtomicToken::Word { .. } + | AtomicToken::String { .. } + | AtomicToken::Number { .. } + | AtomicToken::Size { .. } => { + token_nodes.color_shape(FlatShape::Path.spanned(atom.span)); + } + + _ => atom.color_tokens(token_nodes.mut_shapes()), + } + + Ok(()) + } +} + impl ExpandExpression for FilePathShape { fn expand_expr<'a, 'b>( &self, diff --git a/src/parser/hir/syntax_shape/expression/list.rs b/src/parser/hir/syntax_shape/expression/list.rs index 575ae9fcdd..5a1ea8e383 100644 --- a/src/parser/hir/syntax_shape/expression/list.rs +++ b/src/parser/hir/syntax_shape/expression/list.rs @@ -1,4 +1,6 @@ use crate::errors::ShellError; +#[cfg(not(coloring_in_tokens))] +use crate::parser::hir::syntax_shape::FlatShape; use crate::parser::{ hir, hir::syntax_shape::{ @@ -7,8 +9,8 @@ use crate::parser::{ MaybeSpaceShape, SpaceShape, }, hir::TokensIterator, - FlatShape, }; +#[cfg(not(coloring_in_tokens))] use crate::Spanned; #[derive(Debug, Copy, Clone)] @@ -44,6 +46,7 @@ impl ExpandSyntax for ExpressionListShape { } } +#[cfg(not(coloring_in_tokens))] impl ColorSyntax for ExpressionListShape { type Info = (); type Input = (); @@ -113,10 +116,80 @@ impl ColorSyntax for ExpressionListShape { } } +#[cfg(coloring_in_tokens)] +impl ColorSyntax for ExpressionListShape { + type Info = (); + type Input = (); + + /// The intent of this method is to fully color an expression list shape infallibly. + /// This means that if we can't expand a token into an expression, we fall back to + /// a simpler coloring strategy. + /// + /// This would apply to something like `where x >`, which includes an incomplete + /// binary operator. Since we will fail to process it as a binary operator, we'll + /// fall back to a simpler coloring and move on. + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) { + // We encountered a parsing error and will continue with simpler coloring ("backoff + // coloring mode") + let mut backoff = false; + + // Consume any leading whitespace + color_syntax(&MaybeSpaceShape, token_nodes, context); + + loop { + // If we reached the very end of the token stream, we're done + if token_nodes.at_end() { + return; + } + + if backoff { + let len = token_nodes.shapes().len(); + + // If we previously encountered a parsing error, use backoff coloring mode + color_syntax(&SimplestExpression, token_nodes, context); + + if len == token_nodes.shapes().len() && !token_nodes.at_end() { + // This should never happen, but if it does, a panic is better than an infinite loop + panic!("Unexpected tokens left that couldn't be colored even with SimplestExpression") + } + } else { + // Try to color the head of the stream as an expression + match color_fallible_syntax(&AnyExpressionShape, token_nodes, context) { + // If no expression was found, switch to backoff coloring mode + Err(_) => { + backoff = true; + continue; + } + Ok(_) => {} + } + + // If an expression was found, consume a space + match color_fallible_syntax(&SpaceShape, token_nodes, context) { + Err(_) => { + // If no space was found, we're either at the end or there's an error. + // Either way, switch to backoff coloring mode. If we're at the end + // it won't have any consequences. + backoff = true; + } + Ok(_) => { + // Otherwise, move on to the next expression + } + } + } + } + } +} + /// BackoffColoringMode consumes all of the remaining tokens in an infallible way #[derive(Debug, Copy, Clone)] pub struct BackoffColoringMode; +#[cfg(not(coloring_in_tokens))] impl ColorSyntax for BackoffColoringMode { type Info = (); type Input = (); @@ -144,12 +217,40 @@ impl ColorSyntax for BackoffColoringMode { } } +#[cfg(coloring_in_tokens)] +impl ColorSyntax for BackoffColoringMode { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &Self::Input, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Self::Info { + loop { + if token_nodes.at_end() { + break; + } + + let len = token_nodes.shapes().len(); + color_syntax(&SimplestExpression, token_nodes, context); + + if len == token_nodes.shapes().len() && !token_nodes.at_end() { + // This shouldn't happen, but if it does, a panic is better than an infinite loop + panic!("SimplestExpression failed to consume any tokens, but it's not at the end. This is unexpected\n== token nodes==\n{:#?}\n\n== shapes ==\n{:#?}", token_nodes, token_nodes.shapes()); + } + } + } +} + /// The point of `SimplestExpression` is to serve as an infallible base case for coloring. /// As a last ditch effort, if we can't find any way to parse the head of the stream as an /// expression, fall back to simple coloring. #[derive(Debug, Copy, Clone)] pub struct SimplestExpression; +#[cfg(not(coloring_in_tokens))] impl ColorSyntax for SimplestExpression { type Info = (); type Input = (); @@ -174,3 +275,28 @@ impl ColorSyntax for SimplestExpression { } } } + +#[cfg(coloring_in_tokens)] +impl ColorSyntax for SimplestExpression { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) { + let atom = expand_atom( + token_nodes, + "any token", + context, + ExpansionRule::permissive(), + ); + + match atom { + Err(_) => {} + Ok(atom) => atom.color_tokens(token_nodes.mut_shapes()), + } + } +} diff --git a/src/parser/hir/syntax_shape/expression/number.rs b/src/parser/hir/syntax_shape/expression/number.rs index a4e2a93234..d1475cbaf3 100644 --- a/src/parser/hir/syntax_shape/expression/number.rs +++ b/src/parser/hir/syntax_shape/expression/number.rs @@ -44,6 +44,7 @@ impl ExpandExpression for NumberShape { } } +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for NumberShape { type Info = (); type Input = (); @@ -73,6 +74,35 @@ impl FallibleColorSyntax for NumberShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for NumberShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let atom = token_nodes.spanned(|token_nodes| { + expand_atom(token_nodes, "number", context, ExpansionRule::permissive()) + }); + + let atom = match atom { + Spanned { item: Err(_), span } => { + token_nodes.color_shape(FlatShape::Error.spanned(span)); + return Ok(()); + } + Spanned { item: Ok(atom), .. } => atom, + }; + + atom.color_tokens(token_nodes.mut_shapes()); + + Ok(()) + } +} + #[derive(Debug, Copy, Clone)] pub struct IntShape; @@ -106,6 +136,7 @@ impl ExpandExpression for IntShape { } } +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for IntShape { type Info = (); type Input = (); @@ -134,3 +165,32 @@ impl FallibleColorSyntax for IntShape { Ok(()) } } + +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for IntShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let atom = token_nodes.spanned(|token_nodes| { + expand_atom(token_nodes, "integer", context, ExpansionRule::permissive()) + }); + + let atom = match atom { + Spanned { item: Err(_), span } => { + token_nodes.color_shape(FlatShape::Error.spanned(span)); + return Ok(()); + } + Spanned { item: Ok(atom), .. } => atom, + }; + + atom.color_tokens(token_nodes.mut_shapes()); + + Ok(()) + } +} diff --git a/src/parser/hir/syntax_shape/expression/pattern.rs b/src/parser/hir/syntax_shape/expression/pattern.rs index 0a11552d5e..328e8f795e 100644 --- a/src/parser/hir/syntax_shape/expression/pattern.rs +++ b/src/parser/hir/syntax_shape/expression/pattern.rs @@ -9,6 +9,7 @@ use crate::prelude::*; #[derive(Debug, Copy, Clone)] pub struct PatternShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for PatternShape { type Info = (); type Input = (); @@ -35,6 +36,32 @@ impl FallibleColorSyntax for PatternShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for PatternShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + token_nodes.atomic(|token_nodes| { + let atom = expand_atom(token_nodes, "pattern", context, ExpansionRule::permissive())?; + + match &atom.item { + AtomicToken::GlobPattern { .. } | AtomicToken::Word { .. } => { + token_nodes.color_shape(FlatShape::GlobPattern.spanned(atom.span)); + Ok(()) + } + + _ => Err(ShellError::type_error("pattern", atom.tagged_type_name())), + } + }) + } +} + impl ExpandExpression for PatternShape { fn expand_expr<'a, 'b>( &self, diff --git a/src/parser/hir/syntax_shape/expression/string.rs b/src/parser/hir/syntax_shape/expression/string.rs index 0dabd70a85..e74fa0a6a7 100644 --- a/src/parser/hir/syntax_shape/expression/string.rs +++ b/src/parser/hir/syntax_shape/expression/string.rs @@ -9,6 +9,7 @@ use crate::prelude::*; #[derive(Debug, Copy, Clone)] pub struct StringShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for StringShape { type Info = (); type Input = FlatShape; @@ -39,6 +40,36 @@ impl FallibleColorSyntax for StringShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for StringShape { + type Info = (); + type Input = FlatShape; + + fn color_syntax<'a, 'b>( + &self, + input: &FlatShape, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let atom = expand_atom(token_nodes, "string", context, ExpansionRule::permissive()); + + let atom = match atom { + Err(_) => return Ok(()), + Ok(atom) => atom, + }; + + match atom { + Spanned { + item: AtomicToken::String { .. }, + span, + } => token_nodes.color_shape((*input).spanned(span)), + other => other.color_tokens(token_nodes.mut_shapes()), + } + + Ok(()) + } +} + impl ExpandExpression for StringShape { fn expand_expr<'a, 'b>( &self, diff --git a/src/parser/hir/syntax_shape/expression/variable_path.rs b/src/parser/hir/syntax_shape/expression/variable_path.rs index 04b511d89a..380b3f936c 100644 --- a/src/parser/hir/syntax_shape/expression/variable_path.rs +++ b/src/parser/hir/syntax_shape/expression/variable_path.rs @@ -44,6 +44,7 @@ impl ExpandExpression for VariablePathShape { } } +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for VariablePathShape { type Info = (); type Input = (); @@ -84,9 +85,49 @@ impl FallibleColorSyntax for VariablePathShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for VariablePathShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + token_nodes.atomic(|token_nodes| { + // If the head of the token stream is not a variable, fail + color_fallible_syntax(&VariableShape, token_nodes, context)?; + + loop { + // look for a dot at the head of a stream + let dot = color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Dot, + token_nodes, + context, + ); + + // if there's no dot, we're done + match dot { + Err(_) => break, + Ok(_) => {} + } + + // otherwise, look for a member, and if you don't find one, fail + color_fallible_syntax(&MemberShape, token_nodes, context)?; + } + + Ok(()) + }) + } +} + #[derive(Debug, Copy, Clone)] pub struct PathTailShape; +#[cfg(not(coloring_in_tokens))] /// The failure mode of `PathTailShape` is a dot followed by a non-member impl FallibleColorSyntax for PathTailShape { type Info = (); @@ -119,6 +160,37 @@ impl FallibleColorSyntax for PathTailShape { } } +#[cfg(coloring_in_tokens)] +/// The failure mode of `PathTailShape` is a dot followed by a non-member +impl FallibleColorSyntax for PathTailShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + token_nodes.atomic(|token_nodes| loop { + let result = color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Dot, + token_nodes, + context, + ); + + match result { + Err(_) => return Ok(()), + Ok(_) => {} + } + + // If we've seen a dot but not a member, fail + color_fallible_syntax(&MemberShape, token_nodes, context)?; + }) + } +} + impl ExpandSyntax for PathTailShape { type Output = (Vec>, Span); fn expand_syntax<'a, 'b>( @@ -204,6 +276,7 @@ pub enum ContinuationInfo { Infix, } +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for ExpressionContinuationShape { type Info = ContinuationInfo; type Input = (); @@ -256,6 +329,51 @@ impl FallibleColorSyntax for ExpressionContinuationShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for ExpressionContinuationShape { + type Info = ContinuationInfo; + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result { + token_nodes.atomic(|token_nodes| { + // Try to expand a `.` + let dot = color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Dot, + token_nodes, + context, + ); + + match dot { + Ok(_) => { + // we found a dot, so let's keep looking for a member; if no member was found, fail + color_fallible_syntax(&MemberShape, token_nodes, context)?; + + Ok(ContinuationInfo::Dot) + } + Err(_) => { + let result = token_nodes.atomic(|token_nodes| { + // we didn't find a dot, so let's see if we're looking at an infix. If not found, fail + color_fallible_syntax(&InfixShape, token_nodes, context)?; + + // now that we've seen an infix shape, look for any expression. If not found, fail + color_fallible_syntax(&AnyExpressionShape, token_nodes, context)?; + + Ok(ContinuationInfo::Infix) + })?; + + Ok(result) + } + } + }) + } +} + #[derive(Debug, Copy, Clone)] pub struct VariableShape; @@ -285,6 +403,7 @@ impl ExpandExpression for VariableShape { } } +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for VariableShape { type Info = (); type Input = (); @@ -322,6 +441,43 @@ impl FallibleColorSyntax for VariableShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for VariableShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let atom = expand_atom( + token_nodes, + "variable", + context, + ExpansionRule::permissive(), + ); + + let atom = match atom { + Err(err) => return Err(err), + Ok(atom) => atom, + }; + + match &atom.item { + AtomicToken::Variable { .. } => { + token_nodes.color_shape(FlatShape::Variable.spanned(atom.span)); + Ok(()) + } + AtomicToken::ItVariable { .. } => { + token_nodes.color_shape(FlatShape::ItVariable.spanned(atom.span)); + Ok(()) + } + _ => Err(ShellError::type_error("variable", atom.tagged_type_name())), + } + } +} + #[derive(Debug, Clone, Copy)] pub enum Member { String(/* outer */ Span, /* inner */ Span), @@ -447,6 +603,7 @@ pub fn expand_column_path<'a, 'b>( #[derive(Debug, Copy, Clone)] pub struct ColumnPathShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for ColumnPathShape { type Info = (); type Input = (); @@ -496,6 +653,53 @@ impl FallibleColorSyntax for ColumnPathShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for ColumnPathShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + // If there's not even one member shape, fail + color_fallible_syntax(&MemberShape, token_nodes, context)?; + + loop { + let checkpoint = token_nodes.checkpoint(); + + match color_fallible_syntax_with( + &ColorableDotShape, + &FlatShape::Dot, + checkpoint.iterator, + context, + ) { + Err(_) => { + // we already saw at least one member shape, so return successfully + return Ok(()); + } + + Ok(_) => { + match color_fallible_syntax(&MemberShape, checkpoint.iterator, context) { + Err(_) => { + // we saw a dot but not a member (but we saw at least one member), + // so don't commit the dot but return successfully + return Ok(()); + } + + Ok(_) => { + // we saw a dot and a member, so commit it and continue on + checkpoint.commit(); + } + } + } + } + } + } +} + impl ExpandSyntax for ColumnPathShape { type Output = Tagged>; @@ -511,6 +715,7 @@ impl ExpandSyntax for ColumnPathShape { #[derive(Debug, Copy, Clone)] pub struct MemberShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for MemberShape { type Info = (); type Input = (); @@ -548,6 +753,32 @@ impl FallibleColorSyntax for MemberShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for MemberShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let bare = + color_fallible_syntax_with(&BareShape, &FlatShape::BareMember, token_nodes, context); + + match bare { + Ok(_) => return Ok(()), + Err(_) => { + // If we don't have a bare word, we'll look for a string + } + } + + // Look for a string token. If we don't find one, fail + color_fallible_syntax_with(&StringShape, &FlatShape::StringMember, token_nodes, context) + } +} + impl ExpandSyntax for MemberShape { type Output = Member; @@ -581,6 +812,7 @@ pub struct DotShape; #[derive(Debug, Copy, Clone)] pub struct ColorableDotShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for ColorableDotShape { type Info = (); type Input = FlatShape; @@ -606,6 +838,31 @@ impl FallibleColorSyntax for ColorableDotShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for ColorableDotShape { + type Info = (); + type Input = FlatShape; + + fn color_syntax<'a, 'b>( + &self, + input: &FlatShape, + token_nodes: &'b mut TokensIterator<'a>, + _context: &ExpandContext, + ) -> Result<(), ShellError> { + let peeked = token_nodes.peek_any().not_eof("dot")?; + + match peeked.node { + node if node.is_dot() => { + peeked.commit(); + token_nodes.color_shape((*input).spanned(node.span())); + Ok(()) + } + + other => Err(ShellError::type_error("dot", other.tagged_type_name())), + } + } +} + impl SkipSyntax for DotShape { fn skip<'a, 'b>( &self, @@ -643,6 +900,7 @@ impl ExpandSyntax for DotShape { #[derive(Debug, Copy, Clone)] pub struct InfixShape; +#[cfg(not(coloring_in_tokens))] impl FallibleColorSyntax for InfixShape { type Info = (); type Input = (); @@ -690,6 +948,55 @@ impl FallibleColorSyntax for InfixShape { } } +#[cfg(coloring_in_tokens)] +impl FallibleColorSyntax for InfixShape { + type Info = (); + type Input = (); + + fn color_syntax<'a, 'b>( + &self, + _input: &(), + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Result<(), ShellError> { + let checkpoint = token_nodes.checkpoint(); + + // An infix operator must be prefixed by whitespace. If no whitespace was found, fail + color_fallible_syntax(&WhitespaceShape, checkpoint.iterator, context)?; + + // Parse the next TokenNode after the whitespace + let operator_span = parse_single_node( + checkpoint.iterator, + "infix operator", + |token, token_span, _| { + match token { + // If it's an operator (and not `.`), it's a match + RawToken::Operator(operator) if operator != Operator::Dot => { + // token_nodes.color_shape(FlatShape::Operator.spanned(token_span)); + Ok(token_span) + } + + // Otherwise, it's not a match + _ => Err(ShellError::type_error( + "infix operator", + token.type_name().tagged(token_span), + )), + } + }, + )?; + + checkpoint + .iterator + .color_shape(FlatShape::Operator.spanned(operator_span)); + + // An infix operator must be followed by whitespace. If no whitespace was found, fail + color_fallible_syntax(&WhitespaceShape, checkpoint.iterator, context)?; + + checkpoint.commit(); + Ok(()) + } +} + impl ExpandSyntax for InfixShape { type Output = (Span, Spanned, Span); diff --git a/src/parser/hir/tokens_iterator.rs b/src/parser/hir/tokens_iterator.rs index dbcf5e6c4c..094c5af8c6 100644 --- a/src/parser/hir/tokens_iterator.rs +++ b/src/parser/hir/tokens_iterator.rs @@ -1,16 +1,23 @@ pub(crate) mod debug; use crate::errors::ShellError; +#[cfg(coloring_in_tokens)] +use crate::parser::hir::syntax_shape::FlatShape; use crate::parser::TokenNode; use crate::{Span, Spanned, SpannedItem}; +#[allow(unused)] +use getset::Getters; -#[derive(Debug)] +#[derive(Getters, Debug)] pub struct TokensIterator<'content> { tokens: &'content [TokenNode], span: Span, skip_ws: bool, index: usize, seen: indexmap::IndexSet, + #[cfg(coloring_in_tokens)] + #[get = "pub"] + shapes: Vec>, } #[derive(Debug)] @@ -18,6 +25,8 @@ pub struct Checkpoint<'content, 'me> { pub(crate) iterator: &'me mut TokensIterator<'content>, index: usize, seen: indexmap::IndexSet, + #[cfg(coloring_in_tokens)] + shape_start: usize, committed: bool, } @@ -32,6 +41,8 @@ impl<'content, 'me> std::ops::Drop for Checkpoint<'content, 'me> { if !self.committed { self.iterator.index = self.index; self.iterator.seen = self.seen.clone(); + #[cfg(coloring_in_tokens)] + self.iterator.shapes.truncate(self.shape_start); } } } @@ -132,6 +143,8 @@ impl<'content> TokensIterator<'content> { skip_ws, index: 0, seen: indexmap::IndexSet::new(), + #[cfg(coloring_in_tokens)] + shapes: vec![], } } @@ -156,10 +169,47 @@ impl<'content> TokensIterator<'content> { result.spanned(start.until(end)) } + #[cfg(coloring_in_tokens)] + pub fn color_shape(&mut self, shape: Spanned) { + self.shapes.push(shape); + } + + #[cfg(coloring_in_tokens)] + pub fn mut_shapes(&mut self) -> &mut Vec> { + &mut self.shapes + } + + #[cfg(coloring_in_tokens)] + pub fn child( + &mut self, + tokens: Spanned<&'content [TokenNode]>, + block: impl FnOnce(&mut TokensIterator) -> T, + ) -> T { + let mut shapes = vec![]; + std::mem::swap(&mut shapes, &mut self.shapes); + + let mut iterator = TokensIterator { + tokens: tokens.item, + span: tokens.span, + skip_ws: false, + index: 0, + seen: indexmap::IndexSet::new(), + shapes, + }; + + let result = block(&mut iterator); + + std::mem::swap(&mut iterator.shapes, &mut self.shapes); + + result + } + /// Use a checkpoint when you need to peek more than one token ahead, but can't be sure /// that you'll succeed. pub fn checkpoint<'me>(&'me mut self) -> Checkpoint<'content, 'me> { let index = self.index; + #[cfg(coloring_in_tokens)] + let shape_start = self.shapes.len(); let seen = self.seen.clone(); Checkpoint { @@ -167,6 +217,8 @@ impl<'content> TokensIterator<'content> { index, seen, committed: false, + #[cfg(coloring_in_tokens)] + shape_start, } } @@ -177,6 +229,8 @@ impl<'content> TokensIterator<'content> { block: impl FnOnce(&mut TokensIterator<'content>) -> Result, ) -> Result { let index = self.index; + #[cfg(coloring_in_tokens)] + let shape_start = self.shapes.len(); let seen = self.seen.clone(); let checkpoint = Checkpoint { @@ -184,6 +238,8 @@ impl<'content> TokensIterator<'content> { index, seen, committed: false, + #[cfg(coloring_in_tokens)] + shape_start, }; let value = block(checkpoint.iterator)?; @@ -192,6 +248,44 @@ impl<'content> TokensIterator<'content> { return Ok(value); } + #[cfg(coloring_in_tokens)] + /// Use a checkpoint when you need to peek more than one token ahead, but can't be sure + /// that you'll succeed. + pub fn atomic_returning_shapes<'me, T>( + &'me mut self, + block: impl FnOnce(&mut TokensIterator<'content>) -> Result, + ) -> (Result, Vec>) { + let index = self.index; + let mut shapes = vec![]; + + let seen = self.seen.clone(); + std::mem::swap(&mut self.shapes, &mut shapes); + + let checkpoint = Checkpoint { + iterator: self, + index, + seen, + committed: false, + shape_start: 0, + }; + + let value = block(checkpoint.iterator); + + let value = match value { + Err(err) => { + drop(checkpoint); + std::mem::swap(&mut self.shapes, &mut shapes); + return (Err(err), vec![]); + } + + Ok(value) => value, + }; + + checkpoint.commit(); + std::mem::swap(&mut self.shapes, &mut shapes); + return (Ok(value), shapes); + } + fn eof_span(&self) -> Span { Span::new(self.span.end(), self.span.end()) } @@ -266,6 +360,8 @@ impl<'content> TokensIterator<'content> { index: self.index, seen: self.seen.clone(), skip_ws: self.skip_ws, + #[cfg(coloring_in_tokens)] + shapes: self.shapes.clone(), } } diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index 935794f3c1..a4365db247 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -189,6 +189,7 @@ impl ColoringArgs { #[derive(Debug, Copy, Clone)] pub struct CommandTailShape; +#[cfg(not(coloring_in_tokens))] impl ColorSyntax for CommandTailShape { type Info = (); type Input = Signature; @@ -385,6 +386,206 @@ impl ColorSyntax for CommandTailShape { } } +#[cfg(coloring_in_tokens)] +impl ColorSyntax for CommandTailShape { + type Info = (); + type Input = Signature; + + fn color_syntax<'a, 'b>( + &self, + signature: &Signature, + token_nodes: &'b mut TokensIterator<'a>, + context: &ExpandContext, + ) -> Self::Info { + let mut args = ColoringArgs::new(token_nodes.len()); + trace_remaining("nodes", token_nodes.clone(), context.source()); + + for (name, kind) in &signature.named { + trace!(target: "nu::color_syntax", "looking for {} : {:?}", name, kind); + + match kind { + NamedType::Switch => { + match token_nodes.extract(|t| t.as_flag(name, context.source())) { + Some((pos, flag)) => args.insert(pos, vec![flag.color()]), + None => {} + } + } + NamedType::Mandatory(syntax_type) => { + match extract_mandatory( + signature, + name, + token_nodes, + context.source(), + Span::unknown(), + ) { + Err(_) => { + // The mandatory flag didn't exist at all, so there's nothing to color + } + Ok((pos, flag)) => { + let (_, shapes) = token_nodes.atomic_returning_shapes(|token_nodes| { + token_nodes.color_shape(flag.color()); + token_nodes.move_to(pos); + + if token_nodes.at_end() { + // args.insert(pos, shapes); + // token_nodes.restart(); + return Ok(()); + // continue; + } + + // We still want to color the flag even if the following tokens don't match, so don't + // propagate the error to the parent atomic block if it fails + let _ = token_nodes.atomic(|token_nodes| { + // We can live with unmatched syntax after a mandatory flag + color_syntax(&MaybeSpaceShape, token_nodes, context); + + // If the part after a mandatory flag isn't present, that's ok, but we + // should roll back any whitespace we chomped + color_fallible_syntax(syntax_type, token_nodes, context)?; + + Ok(()) + }); + + Ok(()) + }); + + args.insert(pos, shapes); + token_nodes.restart(); + } + } + } + NamedType::Optional(syntax_type) => { + match extract_optional(name, token_nodes, context.source()) { + Err(_) => { + // The optional flag didn't exist at all, so there's nothing to color + } + Ok(Some((pos, flag))) => { + let (_, shapes) = token_nodes.atomic_returning_shapes(|token_nodes| { + token_nodes.color_shape(flag.color()); + token_nodes.move_to(pos); + + if token_nodes.at_end() { + // args.insert(pos, shapes); + // token_nodes.restart(); + return Ok(()); + // continue; + } + + // We still want to color the flag even if the following tokens don't match, so don't + // propagate the error to the parent atomic block if it fails + let _ = token_nodes.atomic(|token_nodes| { + // We can live with unmatched syntax after a mandatory flag + color_syntax(&MaybeSpaceShape, token_nodes, context); + + // If the part after a mandatory flag isn't present, that's ok, but we + // should roll back any whitespace we chomped + color_fallible_syntax(syntax_type, token_nodes, context)?; + + Ok(()) + }); + + Ok(()) + }); + + args.insert(pos, shapes); + token_nodes.restart(); + } + + Ok(None) => { + token_nodes.restart(); + } + } + } + }; + } + + trace_remaining("after named", token_nodes.clone(), context.source()); + + for arg in &signature.positional { + trace!("Processing positional {:?}", arg); + + match arg { + PositionalType::Mandatory(..) => { + if token_nodes.at_end() { + break; + } + } + + PositionalType::Optional(..) => { + if token_nodes.at_end() { + break; + } + } + } + + let pos = token_nodes.pos(false); + + match pos { + None => break, + Some(pos) => { + // We can live with an unmatched positional argument. Hopefully it will be + // matched by a future token + let (_, shapes) = token_nodes.atomic_returning_shapes(|token_nodes| { + color_syntax(&MaybeSpaceShape, token_nodes, context); + + // If no match, we should roll back any whitespace we chomped + color_fallible_syntax(&arg.syntax_type(), token_nodes, context)?; + + Ok(()) + }); + + args.insert(pos, shapes); + } + } + } + + trace_remaining("after positional", token_nodes.clone(), context.source()); + + if let Some(syntax_type) = signature.rest_positional { + loop { + if token_nodes.at_end_possible_ws() { + break; + } + + let pos = token_nodes.pos(false); + + match pos { + None => break, + Some(pos) => { + // If any arguments don't match, we'll fall back to backoff coloring mode + let (result, shapes) = token_nodes.atomic_returning_shapes(|token_nodes| { + color_syntax(&MaybeSpaceShape, token_nodes, context); + + // If no match, we should roll back any whitespace we chomped + color_fallible_syntax(&syntax_type, token_nodes, context)?; + + Ok(()) + }); + + args.insert(pos, shapes); + + match result { + Err(_) => break, + Ok(_) => continue, + } + } + } + } + } + + args.spread_shapes(token_nodes.mut_shapes()); + + // Consume any remaining tokens with backoff coloring mode + color_syntax(&BackoffColoringMode, token_nodes, context); + + // This is pretty dubious, but it works. We should look into a better algorithm that doesn't end up requiring + // this solution. + token_nodes + .mut_shapes() + .sort_by(|a, b| a.span.start().cmp(&b.span.start())); + } +} + fn extract_switch(name: &str, tokens: &mut hir::TokensIterator<'_>, source: &Text) -> Option { tokens .extract(|t| t.as_flag(name, source)) diff --git a/src/shell/helper.rs b/src/shell/helper.rs index dc3ab96dc1..9b5446f5df 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -85,11 +85,27 @@ impl Highlighter for Helper { let expand_context = self .context .expand_context(&text, Span::new(0, line.len() - 1)); - let mut shapes = vec![]; - // We just constructed a token list that only contains a pipeline, so it can't fail - color_fallible_syntax(&PipelineShape, &mut tokens, &expand_context, &mut shapes) + #[cfg(not(coloring_in_tokens))] + let shapes = { + let mut shapes = vec![]; + color_fallible_syntax( + &PipelineShape, + &mut tokens, + &expand_context, + &mut shapes, + ) .unwrap(); + shapes + }; + + #[cfg(coloring_in_tokens)] + let shapes = { + // We just constructed a token list that only contains a pipeline, so it can't fail + color_fallible_syntax(&PipelineShape, &mut tokens, &expand_context).unwrap(); + + tokens.shapes() + }; trace!(target: "nu::shapes", "SHAPES :: {:?}", @@ -97,7 +113,7 @@ impl Highlighter for Helper { ); for shape in shapes { - let styled = paint_flat_shape(shape, line); + let styled = paint_flat_shape(&shape, line); out.push_str(&styled); } @@ -135,7 +151,7 @@ fn vec_tag(input: Vec>) -> Option { }) } -fn paint_flat_shape(flat_shape: Spanned, line: &str) -> String { +fn paint_flat_shape(flat_shape: &Spanned, line: &str) -> String { let style = match &flat_shape.item { FlatShape::OpenDelimiter(_) => Color::White.normal(), FlatShape::CloseDelimiter(_) => Color::White.normal(), From 452b5c58e8d3680bfdf12626a2052effffacf900 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 15 Oct 2019 15:38:22 +1300 Subject: [PATCH 277/342] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7df2c92ec9..48fae584a5 100644 --- a/README.md +++ b/README.md @@ -46,16 +46,16 @@ Optional dependencies: * To use Nu with all possible optional features enabled, you'll also need the following: * on Linux (on Debian/Ubuntu): `apt install libxcb-composite0-dev libx11-dev` -To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs/) and the nightly compiler via `rustup install nightly`): +To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs/) and the beta compiler via `rustup install beta`): ``` -cargo +nightly install nu +cargo +beta install nu ``` You can also install Nu with all the bells and whistles (be sure to have installed the [dependencies](https://book.nushell.sh/en/installation#dependencies) for your platform): ``` -cargo +nightly install nu --all-features +cargo +beta install nu --all-features ``` ## Docker From 3a9945637193f7350c79e7c20f220eeccd6464fe Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 15 Oct 2019 18:41:05 +1300 Subject: [PATCH 278/342] Bump the version ahead of release --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 955beeddf9..e4a89f5d06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nu" -version = "0.3.0" +version = "0.4.0" authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] description = "A shell for the GitHub era" license = "MIT" From e250a3f213428412c6121f628075e0dc08af1bf3 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 15 Oct 2019 18:52:15 +1300 Subject: [PATCH 279/342] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index bf6b65585b..3cc54584f6 100644 --- a/README.md +++ b/README.md @@ -173,7 +173,7 @@ We can pipeline this into a command that gets the contents of one of the columns ━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━┯━━━━━━┯━━━━━━━━━ authors │ description │ edition │ license │ name │ version ─────────────────┼────────────────────────────┼─────────┼─────────┼──────┼───────── - [table: 3 rows] │ A shell for the GitHub era │ 2018 │ ISC │ nu │ 0.3.0 + [table: 3 rows] │ A shell for the GitHub era │ 2018 │ MIT │ nu │ 0.4.0 ━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━┷━━━━━━┷━━━━━━━━━ ``` @@ -181,7 +181,7 @@ Finally, we can use commands outside of Nu once we have the data we want: ``` /home/jonathan/Source/nushell(master)> open Cargo.toml | get package.version | echo $it -0.3.0 +0.4.0 ``` Here we use the variable `$it` to refer to the value being piped to the external command. @@ -200,7 +200,7 @@ Nu supports plugins that offer additional functionality to the shell and follow There are a few examples in the `plugins` directory. -Plugins are binaries that are available in your path and follow a "nu_plugin_*" naming convention. These binaries interact with nu via a simple JSON-RPC protocol where the command identifies itself and passes along its configuration, which then makes it available for use. If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout. If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases. +Plugins are binaries that are available in your path and follow a `nu_plugin_*` naming convention. These binaries interact with nu via a simple JSON-RPC protocol where the command identifies itself and passes along its configuration, which then makes it available for use. If the plugin is a filter, data streams to it one element at a time, and it can stream data back in return via stdin/stdout. If the plugin is a sink, it is given the full vector of final data and is given free reign over stdin/stdout to use as it pleases. # Goals From 3f60c9d4169bb7ffb83c1b80190a1eb3e575aa82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 15 Oct 2019 04:17:55 -0500 Subject: [PATCH 280/342] 'first' gets first row if no amount desired given. --- src/commands/first.rs | 15 +++++++++++---- tests/commands_test.rs | 22 ++++++++++++++++------ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/src/commands/first.rs b/src/commands/first.rs index 71d05be7e1..face12bddc 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -7,7 +7,7 @@ pub struct First; #[derive(Deserialize)] pub struct FirstArgs { - amount: Tagged, + rows: Option>, } impl WholeStreamCommand for First { @@ -16,7 +16,7 @@ impl WholeStreamCommand for First { } fn signature(&self) -> Signature { - Signature::build("first").required("amount", SyntaxShape::Int) + Signature::build("first").optional("rows", SyntaxShape::Int) } fn usage(&self) -> &str { @@ -33,8 +33,15 @@ impl WholeStreamCommand for First { } fn first( - FirstArgs { amount }: FirstArgs, + FirstArgs { rows }: FirstArgs, context: RunnableContext, ) -> Result { - Ok(OutputStream::from_input(context.input.values.take(*amount))) + + let rows_desired = if let Some(quantity) = rows { + *quantity + } else { + 1 + }; + + Ok(OutputStream::from_input(context.input.values.take(rows_desired))) } diff --git a/tests/commands_test.rs b/tests/commands_test.rs index cfa6f74334..2a58dd0d13 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -32,13 +32,23 @@ fn first_gets_first_rows_by_amount() { } #[test] -fn first_requires_an_amount() { - Playground::setup("first_test_2", |dirs, _| { - let actual = nu_error!( - cwd: dirs.test(), "ls | first" - ); +fn first_gets_first_row_when_no_amount_given() { + Playground::setup("first_test_2", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("los-tres-amigos.PASSTEST.txt")]); - assert!(actual.contains("requires amount parameter")); + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + ls + | get name + | first + | split-column "." + | get Column2 + | echo $it + "# + )); + + assert_eq!(actual, "PASSTEST"); }) } From 96ef478fbc3ecff3884dcb25ebaa9a357e56c531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 15 Oct 2019 04:18:35 -0500 Subject: [PATCH 281/342] Better error messages. --- src/commands/classified.rs | 2 +- src/commands/config.rs | 4 ++-- src/plugins/docker.rs | 17 ++++++----------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/commands/classified.rs b/src/commands/classified.rs index 440413ddd4..7204af77c6 100644 --- a/src/commands/classified.rs +++ b/src/commands/classified.rs @@ -251,7 +251,7 @@ impl ExternalCommand { ) } else { ShellError::labeled_error( - "Error: $it needs string data", + "$it needs string data", "given something else", self.name_tag.clone(), ) diff --git a/src/commands/config.rs b/src/commands/config.rs index 82fbbf1db6..4f9625b211 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -71,7 +71,7 @@ pub fn config( if let Some(v) = get { let key = v.to_string(); let value = result.get(&key).ok_or_else(|| { - ShellError::labeled_error(&format!("Missing key in config"), "key", v.tag()) + ShellError::labeled_error("Missing key in config", "key", v.tag()) })?; let mut results = VecDeque::new(); @@ -121,7 +121,7 @@ pub fn config( config::write(&result, &configuration)?; } else { return Err(ShellError::labeled_error( - "{} does not exist in config", + "Key does not exist in config", "key", v.tag(), )); diff --git a/src/plugins/docker.rs b/src/plugins/docker.rs index 9cb8a52e80..e0a06ab3d4 100644 --- a/src/plugins/docker.rs +++ b/src/plugins/docker.rs @@ -21,8 +21,8 @@ async fn docker(sub_command: &String, name: Tag) -> Result>, S "images" => docker_images(name), _ => Err(ShellError::labeled_error( "Unsupported Docker command", - format!("'{}'?", sub_command), - name.span, + "unknown docker command", + name, )), } } @@ -46,7 +46,7 @@ fn process_docker_output(cmd_output: &str, tag: Tag) -> Result .filter(|s| s.trim() != "") .collect(); - let mut dict = TaggedDictBuilder::new(tag); + let mut dict = TaggedDictBuilder::new(&tag); for (i, v) in values.iter().enumerate() { dict.insert(header[i].to_string(), Value::string(v.trim().to_string())); } @@ -92,18 +92,13 @@ impl Plugin for Docker { if let Some(args) = callinfo.args.positional { match &args[0] { Tagged { - item: Value::Primitive(Primitive::String(s)), + item: Value::Primitive(Primitive::String(command)), .. - } => match block_on(docker(&s, callinfo.name_tag)) { + } => match block_on(docker(&command, args[0].tag())) { Ok(v) => return Ok(v.into_iter().map(ReturnSuccess::value).collect()), Err(e) => return Err(e), }, - _ => { - return Err(ShellError::string(format!( - "Unrecognized type in params: {:?}", - args[0] - ))) - } + _ => return Err(ShellError::type_error("string", args[0].tagged_type_name())), } } From 5ed1ed54a6eea351f4852a5777dc8a75586ffd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 15 Oct 2019 05:16:47 -0500 Subject: [PATCH 282/342] Move off 'sum' to internal command 'count' for tests. --- tests/command_ls_tests.rs | 39 +++++++++------------------- tests/commands_test.rs | 54 ++++++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 45 deletions(-) diff --git a/tests/command_ls_tests.rs b/tests/command_ls_tests.rs index f6f5f39f86..a0ae959e12 100644 --- a/tests/command_ls_tests.rs +++ b/tests/command_ls_tests.rs @@ -7,26 +7,21 @@ use helpers::{Playground, Stub::*}; fn ls_lists_regular_files() { Playground::setup("ls_test_1", |dirs, sandbox| { sandbox.with_files(vec![ - EmptyFile("yehuda.10.txt"), - EmptyFile("jonathan.10.txt"), - EmptyFile("andres.10.txt"), + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), ]); let actual = nu!( cwd: dirs.test(), h::pipeline( r#" ls - | get name - | lines - | split-column "." - | get Column2 - | str --to-int - | sum + | count | echo $it "# )); - assert_eq!(actual, "30"); + assert_eq!(actual, "3"); }) } @@ -34,22 +29,17 @@ fn ls_lists_regular_files() { fn ls_lists_regular_files_using_asterisk_wildcard() { Playground::setup("ls_test_2", |dirs, sandbox| { sandbox.with_files(vec![ - EmptyFile("los.1.txt"), - EmptyFile("tres.1.txt"), - EmptyFile("amigos.1.txt"), - EmptyFile("arepas.1.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), ]); let actual = nu!( cwd: dirs.test(), h::pipeline( r#" ls *.txt - | get name - | lines - | split-column "." - | get Column2 - | str --to-int - | sum + | count | echo $it "# )); @@ -72,16 +62,11 @@ fn ls_lists_regular_files_using_question_mark_wildcard() { cwd: dirs.test(), h::pipeline( r#" ls *.??.txt - | get name - | lines - | split-column "." - | get Column2 - | str --to-int - | sum + | count | echo $it "# )); - assert_eq!(actual, "30"); + assert_eq!(actual, "3"); }) } diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 2a58dd0d13..6544042f66 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -7,48 +7,66 @@ use helpers::{Playground, Stub::*}; fn first_gets_first_rows_by_amount() { Playground::setup("first_test_1", |dirs, sandbox| { sandbox.with_files(vec![ - EmptyFile("los.1.txt"), - EmptyFile("tres.1.txt"), - EmptyFile("amigos.1.txt"), - EmptyFile("arepas.1.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), ]); let actual = nu!( cwd: dirs.test(), h::pipeline( r#" ls - | get name - | first 2 - | split-column "." - | get Column2 - | str --to-int - | sum + | first 3 + | count | echo $it "# )); - assert_eq!(actual, "2"); + assert_eq!(actual, "3"); }) } #[test] -fn first_gets_first_row_when_no_amount_given() { +fn first_gets_all_rows_if_amount_higher_than_all_rows() { Playground::setup("first_test_2", |dirs, sandbox| { - sandbox.with_files(vec![EmptyFile("los-tres-amigos.PASSTEST.txt")]); + sandbox.with_files(vec![ + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + ]); let actual = nu!( cwd: dirs.test(), h::pipeline( r#" ls - | get name - | first - | split-column "." - | get Column2 + | first 99 + | count | echo $it "# )); - assert_eq!(actual, "PASSTEST"); + assert_eq!(actual, "4"); + }) +} + +#[test] +fn first_gets_first_row_when_no_amount_given() { + Playground::setup("first_test_3", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("caballeros.txt"), EmptyFile("arepas.clu")]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + ls + | first + | count + | echo $it + "# + )); + + assert_eq!(actual, "1"); }) } From 821ee5e726508ac35e299f29e740dffd0a3114e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 15 Oct 2019 05:19:06 -0500 Subject: [PATCH 283/342] count command introduced. --- src/cli.rs | 2 +- src/commands.rs | 2 ++ src/commands/count.rs | 46 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 src/commands/count.rs diff --git a/src/cli.rs b/src/cli.rs index 0182ad1002..ad3eb8d39b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -258,7 +258,6 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Next), whole_stream_command(Previous), whole_stream_command(Debug), - whole_stream_command(Lines), whole_stream_command(Shells), whole_stream_command(SplitColumn), whole_stream_command(SplitRow), @@ -277,6 +276,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(ToYAML), whole_stream_command(SortBy), whole_stream_command(Tags), + whole_stream_command(Count), whole_stream_command(First), whole_stream_command(Last), whole_stream_command(Env), diff --git a/src/commands.rs b/src/commands.rs index 61a45dbb3a..0b155891cc 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -8,6 +8,7 @@ pub(crate) mod classified; pub(crate) mod clip; pub(crate) mod command; pub(crate) mod config; +pub(crate) mod count; pub(crate) mod cp; pub(crate) mod date; pub(crate) mod debug; @@ -78,6 +79,7 @@ pub(crate) use command::{ pub(crate) use classified::ClassifiedCommand; pub(crate) use config::Config; +pub(crate) use count::Count; pub(crate) use cp::Cpy; pub(crate) use date::Date; pub(crate) use debug::Debug; diff --git a/src/commands/count.rs b/src/commands/count.rs new file mode 100644 index 0000000000..5e44283737 --- /dev/null +++ b/src/commands/count.rs @@ -0,0 +1,46 @@ +use crate::commands::WholeStreamCommand; +use crate::data::Value; +use crate::errors::ShellError; +use crate::parser::CommandRegistry; +use crate::prelude::*; +use futures::stream::StreamExt; + +pub struct Count; + +#[derive(Deserialize)] +pub struct CountArgs {} + +impl WholeStreamCommand for Count { + fn name(&self) -> &str { + "count" + } + + fn signature(&self) -> Signature { + Signature::build("count") + } + + fn usage(&self) -> &str { + "Show the total number of rows." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, count)?.run() + } +} + +pub fn count( + CountArgs {}: CountArgs, + RunnableContext { input, name, .. }: RunnableContext, +) -> Result { + let stream = async_stream! { + let rows: Vec> = input.values.collect().await; + + yield ReturnSuccess::value(Value::int(rows.len()).tagged(name)) + }; + + Ok(stream.to_output_stream()) +} From ec2e35ad81d2212b49bc6df0ab1a52d61ba23e67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 15 Oct 2019 05:41:34 -0500 Subject: [PATCH 284/342] 'last' gets last row if no amount desired given. --- src/commands/last.rs | 18 +++++++++++------- tests/commands_test.rs | 43 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/src/commands/last.rs b/src/commands/last.rs index 321506846b..04db0f4c48 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -7,7 +7,7 @@ pub struct Last; #[derive(Deserialize)] pub struct LastArgs { - amount: Tagged, + rows: Option>, } impl WholeStreamCommand for Last { @@ -16,7 +16,7 @@ impl WholeStreamCommand for Last { } fn signature(&self) -> Signature { - Signature::build("last").required("amount", SyntaxShape::Number) + Signature::build("last").optional("rows", SyntaxShape::Number) } fn usage(&self) -> &str { @@ -32,13 +32,17 @@ impl WholeStreamCommand for Last { } } -fn last( - LastArgs { amount }: LastArgs, - context: RunnableContext, -) -> Result { +fn last(LastArgs { rows }: LastArgs, context: RunnableContext) -> Result { let stream = async_stream! { let v: Vec<_> = context.input.into_vec().await; - let count = (*amount as usize); + + let rows_desired = if let Some(quantity) = rows { + *quantity + } else { + 1 + }; + + let count = (rows_desired as usize); if count < v.len() { let k = v.len() - count; for x in v[k..].iter() { diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 6544042f66..4d6fa84a65 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -70,6 +70,49 @@ fn first_gets_first_row_when_no_amount_given() { }) } +#[test] +fn last_gets_last_rows_by_amount() { + Playground::setup("last_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + ]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + ls + | last 3 + | count + | echo $it + "# + )); + + assert_eq!(actual, "3"); + }) +} + +#[test] +fn last_gets_last_row_when_no_amount_given() { + Playground::setup("last_test_2", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("caballeros.txt"), EmptyFile("arepas.clu")]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + ls + | last + | count + | echo $it + "# + )); + + assert_eq!(actual, "1"); + }) +} + #[test] fn get() { Playground::setup("get_test_1", |dirs, sandbox| { From 0373006710ac23c71af4af1f3a201529d0e9e98c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 15 Oct 2019 05:42:24 -0500 Subject: [PATCH 285/342] Formatting. --- src/commands/config.rs | 6 +++--- src/commands/first.rs | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/commands/config.rs b/src/commands/config.rs index 4f9625b211..9cde5213de 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -70,9 +70,9 @@ pub fn config( if let Some(v) = get { let key = v.to_string(); - let value = result.get(&key).ok_or_else(|| { - ShellError::labeled_error("Missing key in config", "key", v.tag()) - })?; + let value = result + .get(&key) + .ok_or_else(|| ShellError::labeled_error("Missing key in config", "key", v.tag()))?; let mut results = VecDeque::new(); diff --git a/src/commands/first.rs b/src/commands/first.rs index face12bddc..4c1c3b8c35 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -36,12 +36,13 @@ fn first( FirstArgs { rows }: FirstArgs, context: RunnableContext, ) -> Result { - let rows_desired = if let Some(quantity) = rows { *quantity } else { 1 }; - Ok(OutputStream::from_input(context.input.values.take(rows_desired))) + Ok(OutputStream::from_input( + context.input.values.take(rows_desired), + )) } From 81affaa584079c8e981accee9ae390c1e0e32c23 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 19:10:38 +0200 Subject: [PATCH 286/342] Adds tests for allowed-spaces option. --- src/commands/from_ssv.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 1be9b4567a..3c61e211f4 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -184,4 +184,43 @@ mod tests { let result = string_to_table(input, true); assert_eq!(result, None); } + + #[test] + fn it_allows_a_predefined_number_of_spaces() { + let input = r#" + column a column b + entry 1 entry number 2 + 3 four + "#; + + let result = string_to_table(input, false); + assert_eq!( + result, + Some(vec![ + vec![ + owned("column a", "entry 1"), + owned("column b", "entry number 2") + ], + vec![owned("column a", "3"), owned("column b", "four")] + ]) + ); + } + + #[test] + fn it_trims_remaining_separator_space() { + let input = r#" + colA colB colC + val1 val2 val3 + "#; + + let trimmed = |s: &str| s.trim() == s; + + let result = string_to_table(input, false).unwrap(); + assert_eq!( + true, + result + .iter() + .all(|row| row.iter().all(|(a, b)| trimmed(a) && trimmed(b))) + ) + } } From d32e97b81288297d02e1f044fc1216765ee78bf5 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 19:52:12 +0200 Subject: [PATCH 287/342] Implements variable space separator length, version 1. --- src/commands/from_ssv.rs | 44 +++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 3c61e211f4..6d2dcfda57 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -7,9 +7,11 @@ pub struct FromSSV; #[derive(Deserialize)] pub struct FromSSVArgs { headerless: bool, + n: Option>, } const STRING_REPRESENTATION: &str = "from-ssv"; +const DEFAULT_ALLOWED_SPACES: usize = 0; impl WholeStreamCommand for FromSSV { fn name(&self) -> &str { @@ -17,7 +19,9 @@ impl WholeStreamCommand for FromSSV { } fn signature(&self) -> Signature { - Signature::build(STRING_REPRESENTATION).switch("headerless") + Signature::build(STRING_REPRESENTATION) + .switch("headerless") + .named("n", SyntaxShape::Int) } fn usage(&self) -> &str { @@ -33,12 +37,19 @@ impl WholeStreamCommand for FromSSV { } } -fn string_to_table(s: &str, headerless: bool) -> Option>> { +fn string_to_table( + s: &str, + headerless: bool, + split_at: usize, +) -> Option>> { let mut lines = s.lines().filter(|l| !l.trim().is_empty()); + let separator = " ".repeat(std::cmp::max(split_at, 1)); let headers = lines .next()? - .split_whitespace() + .split(&separator) + .map(|s| s.trim()) + .filter(|s| !s.is_empty()) .map(|s| s.to_owned()) .collect::>(); @@ -55,7 +66,11 @@ fn string_to_table(s: &str, headerless: bool) -> Option Option, ) -> Option> { let tag = tag.into(); - let rows = string_to_table(s, headerless)? + let rows = string_to_table(s, headerless, split_at)? .iter() .map(|row| { let mut tagged_dict = TaggedDictBuilder::new(&tag); @@ -87,13 +103,17 @@ fn from_ssv_string_to_value( } fn from_ssv( - FromSSVArgs { headerless }: FromSSVArgs, + FromSSVArgs { headerless, n }: FromSSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { let stream = async_stream! { let values: Vec> = input.values.collect().await; let mut concat_string = String::new(); let mut latest_tag: Option = None; + let split_at = match n { + Some(number) => number.item, + None => DEFAULT_ALLOWED_SPACES + }; for value in values { let value_tag = value.tag(); @@ -112,7 +132,7 @@ fn from_ssv( } } - match from_ssv_string_to_value(&concat_string, headerless, name.clone()) { + match from_ssv_string_to_value(&concat_string, headerless, split_at, name.clone()) { Some(x) => match x { Tagged { item: Value::Table(list), ..} => { for l in list { yield ReturnSuccess::value(l) } @@ -151,7 +171,7 @@ mod tests { 3 4 "#; - let result = string_to_table(input, false); + let result = string_to_table(input, false, 1); assert_eq!( result, Some(vec![ @@ -168,7 +188,7 @@ mod tests { 1 2 3 4 "#; - let result = string_to_table(input, true); + let result = string_to_table(input, true, 1); assert_eq!( result, Some(vec![ @@ -181,7 +201,7 @@ mod tests { #[test] fn it_returns_none_given_an_empty_string() { let input = ""; - let result = string_to_table(input, true); + let result = string_to_table(input, true, 1); assert_eq!(result, None); } @@ -193,7 +213,7 @@ mod tests { 3 four "#; - let result = string_to_table(input, false); + let result = string_to_table(input, false, 3); assert_eq!( result, Some(vec![ @@ -215,7 +235,7 @@ mod tests { let trimmed = |s: &str| s.trim() == s; - let result = string_to_table(input, false).unwrap(); + let result = string_to_table(input, false, 2).unwrap(); assert_eq!( true, result From e7b37bee08d8d15af22a9116240d10f617f0be8f Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 20:58:46 +0200 Subject: [PATCH 288/342] Adds filter test for named param. --- tests/filters_test.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/filters_test.rs b/tests/filters_test.rs index ed841af4ca..a84622c37f 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -383,6 +383,34 @@ fn converts_from_ssv_text_to_structured_table() { }) } +#[test] +fn converts_from_ssv_text_to_structured_table_with_separator_specified() { + Playground::setup("filter_from_ssv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "oc_get_svc.txt", + r#" + NAME LABELS SELECTOR IP PORT(S) + docker-registry docker-registry=default docker-registry=default 172.30.78.158 5000/TCP + kubernetes component=apiserver,provider=kubernetes 172.30.0.2 443/TCP + kubernetes-ro component=apiserver,provider=kubernetes 172.30.0.1 80/TCP + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + open oc_get_svc.txt + | from-ssv -n 3 + | nth 0 + | get IP + | echo $it + "# + )); + + assert_eq!(actual, "172.30.78.158"); + }) +} + #[test] fn converts_from_ssv_text_skipping_headers_to_structured_table() { Playground::setup("filter_from_ssv_test_2", |dirs, sandbox| { From b4c639a5d9b286a74e822a800f5fc27e48140ee6 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 21:01:14 +0200 Subject: [PATCH 289/342] Updates description of command in readme. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3cc54584f6..33d381d710 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | from-ini | Parse text as .ini and create table | | from-json | Parse text as .json and create table | | from-sqlite | Parse binary data as sqlite .db and create table | -| from-ssv | Parse text as whitespace-separated values and create table| +| from-ssv -n | Parse text as space-separated values and create table | | from-toml | Parse text as .toml and create table | | from-tsv | Parse text as .tsv and create table | | from-url | Parse urlencoded string and create a table | From 294c2c600ddf098b2106a0634922b559338ee463 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 21:10:15 +0200 Subject: [PATCH 290/342] Update the usage string to match the readme. --- src/commands/from_ssv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 6d2dcfda57..0d56c20e0a 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -25,7 +25,7 @@ impl WholeStreamCommand for FromSSV { } fn usage(&self) -> &str { - "Parse text as whitespace-separated values and create a table." + "Parse text as space-separated values and create a table." } fn run( From 5635b8378df40c9b05f2c72597b2926a459cf4b9 Mon Sep 17 00:00:00 2001 From: sdfnz <30536578+sdfnz@users.noreply.github.com> Date: Tue, 15 Oct 2019 14:23:32 -0500 Subject: [PATCH 291/342] Added documentation for the sum command --- docs/commands/sum.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/commands/sum.md diff --git a/docs/commands/sum.md b/docs/commands/sum.md new file mode 100644 index 0000000000..f5c59848dd --- /dev/null +++ b/docs/commands/sum.md @@ -0,0 +1,35 @@ +# sum + +This command allows you to calculate the sum of values in a column. + +## Examples + +To get the sum of the file sizes in a directory, simply pipe the size column from the ls command to the sum command. + +```shell +> ls | get size | sum +━━━━━━━━━ + value +━━━━━━━━━ + 51.0 MB +━━━━━━━━━ +``` + +Note that sum only works for integer and byte values at the moment, and if the shell doesn't recognize the values in a column as one of those types, it will return an error. + +```shell +> open example.csv +━━━┯━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━ + # │ fruit │ amount │ quality +───┼─────────┼────────┼────────── + 0 │ apples │ 1 │ fresh + 1 │ bananas │ 2 │ old + 2 │ oranges │ 7 │ fresh + 3 │ kiwis │ 25 │ rotten +━━━┷━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━ +``` + +```shell +> open example.csv | get amount | sum +error: Unrecognized type in stream: Primitive(String("1")) +``` From 1bb301aafa6a2fdebc035301764214e9a3e5bcb6 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 16 Oct 2019 08:54:46 +1300 Subject: [PATCH 292/342] Bump dep for language-reporting --- Cargo.toml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e4a89f5d06..cd6be5d9fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,8 +41,7 @@ serde-hjson = "0.9.1" serde_yaml = "0.8" serde_bytes = "0.11.2" getset = "0.0.8" -#language-reporting = "0.3.1" -language-reporting = { git = "https://github.com/wycats/language-reporting" } +language-reporting = "0.4.0" app_dirs = "1.2.1" csv = "1.1" toml = "0.5.3" From 0d2044e72e21d3a44a034b9eeb64de783cdd323c Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 22:05:32 +0200 Subject: [PATCH 293/342] Changes flag to `minimum-spaces`. --- src/commands/from_ssv.rs | 12 ++++++++---- tests/filters_test.rs | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 0d56c20e0a..4cbb3c78f6 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -7,7 +7,8 @@ pub struct FromSSV; #[derive(Deserialize)] pub struct FromSSVArgs { headerless: bool, - n: Option>, + #[serde(rename(deserialize = "minimum-spaces"))] + minimum_spaces: Option>, } const STRING_REPRESENTATION: &str = "from-ssv"; @@ -21,7 +22,7 @@ impl WholeStreamCommand for FromSSV { fn signature(&self) -> Signature { Signature::build(STRING_REPRESENTATION) .switch("headerless") - .named("n", SyntaxShape::Int) + .named("minimum-spaces", SyntaxShape::Int) } fn usage(&self) -> &str { @@ -103,14 +104,17 @@ fn from_ssv_string_to_value( } fn from_ssv( - FromSSVArgs { headerless, n }: FromSSVArgs, + FromSSVArgs { + headerless, + minimum_spaces, + }: FromSSVArgs, RunnableContext { input, name, .. }: RunnableContext, ) -> Result { let stream = async_stream! { let values: Vec> = input.values.collect().await; let mut concat_string = String::new(); let mut latest_tag: Option = None; - let split_at = match n { + let split_at = match minimum_spaces { Some(number) => number.item, None => DEFAULT_ALLOWED_SPACES }; diff --git a/tests/filters_test.rs b/tests/filters_test.rs index a84622c37f..f0d5dead61 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -400,7 +400,7 @@ fn converts_from_ssv_text_to_structured_table_with_separator_specified() { cwd: dirs.test(), h::pipeline( r#" open oc_get_svc.txt - | from-ssv -n 3 + | from-ssv --minimum-spaces 3 | nth 0 | get IP | echo $it From f8d44e732bc103caf4c963f1c0ef34511704a206 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 22:05:47 +0200 Subject: [PATCH 294/342] Updates default minimum spaces to allow single spaces by default. --- src/commands/from_ssv.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 4cbb3c78f6..001ea8d0c6 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -12,7 +12,7 @@ pub struct FromSSVArgs { } const STRING_REPRESENTATION: &str = "from-ssv"; -const DEFAULT_ALLOWED_SPACES: usize = 0; +const DEFAULT_MINIMUM_SPACES: usize = 2; impl WholeStreamCommand for FromSSV { fn name(&self) -> &str { @@ -116,7 +116,7 @@ fn from_ssv( let mut latest_tag: Option = None; let split_at = match minimum_spaces { Some(number) => number.item, - None => DEFAULT_ALLOWED_SPACES + None => DEFAULT_MINIMUM_SPACES }; for value in values { From 587bb13be55f030d83459d78c88841886453ffbc Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 23:19:16 +0200 Subject: [PATCH 295/342] Updates readme with new name of flag. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33d381d710..cf36fd2cb1 100644 --- a/README.md +++ b/README.md @@ -284,7 +284,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | from-ini | Parse text as .ini and create table | | from-json | Parse text as .json and create table | | from-sqlite | Parse binary data as sqlite .db and create table | -| from-ssv -n | Parse text as space-separated values and create table | +| from-ssv --minimum-spaces | Parse text as space-separated values and create table | | from-toml | Parse text as .toml and create table | | from-tsv | Parse text as .tsv and create table | | from-url | Parse urlencoded string and create a table | From 74b0e4e5413fdb75b037630f52277540fcfa8270 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Tue, 15 Oct 2019 23:20:06 +0200 Subject: [PATCH 296/342] Adds more info to the usage string. --- src/commands/from_ssv.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 001ea8d0c6..7aca350964 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -26,7 +26,7 @@ impl WholeStreamCommand for FromSSV { } fn usage(&self) -> &str { - "Parse text as space-separated values and create a table." + "Parse text as space-separated values and create a table. The default minimum number of spaces counted as a separator is 2." } fn run( From d91b73544275b1a889547311d0c7ad647f762f20 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 16 Oct 2019 15:09:47 +1300 Subject: [PATCH 297/342] Update cargo.lock --- Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index da47189204..763ab16798 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1190,13 +1190,13 @@ dependencies = [ [[package]] name = "language-reporting" -version = "0.3.1" -source = "git+https://github.com/wycats/language-reporting#1e2100290fec96f69646e1e61482d80f7a8e7855" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "derive-new 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "render-tree 0.1.1 (git+https://github.com/wycats/language-reporting)", + "render-tree 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1487,7 +1487,7 @@ dependencies = [ [[package]] name = "nu" -version = "0.3.0" +version = "0.4.0" dependencies = [ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1519,7 +1519,7 @@ dependencies = [ "image 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", "indexmap 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "language-reporting 0.3.1 (git+https://github.com/wycats/language-reporting)", + "language-reporting 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "mime 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "natural 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2007,7 +2007,7 @@ dependencies = [ [[package]] name = "render-tree" version = "0.1.1" -source = "git+https://github.com/wycats/language-reporting#1e2100290fec96f69646e1e61482d80f7a8e7855" +source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "itertools 0.7.11 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2950,7 +2950,7 @@ dependencies = [ "checksum jpeg-decoder 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "c1aae18ffeeae409c6622c3b6a7ee49792a7e5a062eea1b135fbb74e301792ba" "checksum js-sys 0.3.28 (registry+https://github.com/rust-lang/crates.io-index)" = "2cc9a97d7cec30128fd8b28a7c1f9df1c001ceb9b441e2b755e24130a6b43c79" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -"checksum language-reporting 0.3.1 (git+https://github.com/wycats/language-reporting)" = "" +"checksum language-reporting 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4e6a84e1e6cccd818617d299427ad1519f127af2738b1d3a581835ef56ae298b" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" @@ -3038,7 +3038,7 @@ dependencies = [ "checksum regex-automata 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "92b73c2a1770c255c240eaa4ee600df1704a38dc3feaa6e949e7fcd4f8dc09f9" "checksum regex-syntax 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "11a7e20d1cce64ef2fed88b66d347f88bd9babb82845b2b858f3edbf59a4f716" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" -"checksum render-tree 0.1.1 (git+https://github.com/wycats/language-reporting)" = "" +"checksum render-tree 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "68ed587df09cfb7ce1bc6fe8f77e24db219f222c049326ccbfb948ec67e31664" "checksum result 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "194d8e591e405d1eecf28819740abed6d719d1a2db87fc0bcdedee9a26d55560" "checksum roxmltree 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b1a3193e568c6e262f817fd07af085c7f79241a947aedd3779d47eadc170e174" "checksum rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2a194373ef527035645a1bc21b10dc2125f73497e6e155771233eb187aedd051" From 9a02fac0e5b2e90412d18d7356121ba84af47ec9 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 17 Oct 2019 07:28:49 +1300 Subject: [PATCH 298/342] Rename to --- src/format/table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/format/table.rs b/src/format/table.rs index f4b318dae8..a59e1adafb 100644 --- a/src/format/table.rs +++ b/src/format/table.rs @@ -42,7 +42,7 @@ impl TableView { let mut headers = TableView::merge_descriptors(values); if headers.len() == 0 { - headers.push("".to_string()); + headers.push("".to_string()); } let mut entries = vec![]; From a0ed6ea3c8f1c774163886eb4ebbbe324aeca527 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 17 Oct 2019 00:17:58 +0200 Subject: [PATCH 299/342] Adds new tests and updates old ones. New tests are added to test for additional cases that might be trickier to handle with the new logic. Old tests are updated where their expectations are no longer expected to hold true. For instance: previously, lines would be treated separately, allowing any index offset between columns on different rows, as long as they had the same row index as decided by a separator. When this is no longer the case, some things need to be adjusted. --- src/commands/from_ssv.rs | 75 +++++++++++++++++++++++++++++++++++----- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 7aca350964..39ad1f7c73 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -171,9 +171,9 @@ mod tests { a b - 1 2 + 1 2 - 3 4 + 3 4 "#; let result = string_to_table(input, false, 1); assert_eq!( @@ -185,6 +185,20 @@ mod tests { ); } + #[test] + fn it_deals_with_single_column_input() { + let input = r#" + a + 1 + 2 + "#; + let result = string_to_table(input, false, 1); + assert_eq!( + result, + Some(vec![vec![owned("a", "1")], vec![owned("a", "2")]]) + ); + } + #[test] fn it_ignores_headers_when_headerless() { let input = r#" @@ -206,15 +220,15 @@ mod tests { fn it_returns_none_given_an_empty_string() { let input = ""; let result = string_to_table(input, true, 1); - assert_eq!(result, None); + assert!(result.is_none()); } #[test] fn it_allows_a_predefined_number_of_spaces() { let input = r#" column a column b - entry 1 entry number 2 - 3 four + entry 1 entry number 2 + 3 four "#; let result = string_to_table(input, false, 3); @@ -239,12 +253,55 @@ mod tests { let trimmed = |s: &str| s.trim() == s; + let result = string_to_table(input, false, 2).unwrap(); + assert!(result + .iter() + .all(|row| row.iter().all(|(a, b)| trimmed(a) && trimmed(b)))) + } + + #[test] + fn it_keeps_empty_columns() { + let input = r#" + colA col B col C + val2 val3 + val4 val 5 val 6 + val7 val8 + "#; + let result = string_to_table(input, false, 2).unwrap(); assert_eq!( - true, - result - .iter() - .all(|row| row.iter().all(|(a, b)| trimmed(a) && trimmed(b))) + result, + vec![ + vec![ + owned("colA", ""), + owned("col B", "val2"), + owned("col C", "val3") + ], + vec![ + owned("colA", "val4"), + owned("col B", "val 5"), + owned("col C", "val 6") + ], + vec![ + owned("colA", "val7"), + owned("col B", ""), + owned("col C", "val8") + ], + ] + ) + } + + #[test] + fn it_drops_trailing_values() { + let input = r#" + colA col B + val1 val2 trailing value that should be ignored + "#; + + let result = string_to_table(input, false, 2).unwrap(); + assert_eq!( + result, + vec![vec![owned("colA", "val1"), owned("col B", "val2"),],] ) } } From 9b1ff9b5667f864212efa69e51716fbd47cdd157 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 17 Oct 2019 00:20:48 +0200 Subject: [PATCH 300/342] Updates the table creation logic. The table parsing/creation logic has changed from treating every line the same to processing each line in context of the column header's placement. Previously, lines on separate rows would go towards the same column as long as they were the same index based on separator alone. Now, each item's index is based on vertical alignment to the column header. This may seem brittle, but it solves the problem of some tables operating with empty cells that would cause remaining values to be paired with the wrong column. Based on kubernetes output (get pods, events), the new method has shown to have much greater success rates for parsing. --- src/commands/from_ssv.rs | 44 +++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 39ad1f7c73..3af9e76084 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -46,33 +46,39 @@ fn string_to_table( let mut lines = s.lines().filter(|l| !l.trim().is_empty()); let separator = " ".repeat(std::cmp::max(split_at, 1)); - let headers = lines - .next()? - .split(&separator) - .map(|s| s.trim()) - .filter(|s| !s.is_empty()) - .map(|s| s.to_owned()) - .collect::>(); + let headers_raw = lines.next()?; - let header_row = if headerless { - (1..=headers.len()) - .map(|i| format!("Column{}", i)) - .collect::>() - } else { + let headers = headers_raw + .trim() + .split(&separator) + .map(str::trim) + .filter(|s| !s.is_empty()) + .map(|s| (headers_raw.find(s).unwrap(), s.to_owned())); + + let columns = if headerless { headers + .enumerate() + .map(|(header_no, (string_index, _))| { + (string_index, format!("Column{}", header_no + 1)) + }) + .collect::>() + } else { + headers.collect::>() }; Some( lines .map(|l| { - header_row + columns .iter() - .zip( - l.split(&separator) - .map(|s| s.trim()) - .filter(|s| !s.is_empty()), - ) - .map(|(a, b)| (String::from(a), String::from(b))) + .enumerate() + .filter_map(|(i, (start, col))| { + (match columns.get(i + 1) { + Some((end, _)) => l.get(*start..*end), + None => l.get(*start..)?.split(&separator).next(), + }) + .and_then(|s| Some((col.clone(), String::from(s.trim())))) + }) .collect() }) .collect(), From 305ca11eb57801ec491984f336294c3331e04903 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 17 Oct 2019 09:40:00 +0200 Subject: [PATCH 301/342] Changes the parsing to use the full value of the final column. Previously it would split the last column on the first separator value found between the start of the column and the end of the row. Changing this to using everything from the start of the column to the end of the string makes it behave more similarly to the other columns, making it less surprising. --- src/commands/from_ssv.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 3af9e76084..913df9981a 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -75,7 +75,7 @@ fn string_to_table( .filter_map(|(i, (start, col))| { (match columns.get(i + 1) { Some((end, _)) => l.get(*start..*end), - None => l.get(*start..)?.split(&separator).next(), + None => l.get(*start..), }) .and_then(|s| Some((col.clone(), String::from(s.trim())))) }) @@ -298,16 +298,16 @@ mod tests { } #[test] - fn it_drops_trailing_values() { + fn it_uses_the_full_final_column() { let input = r#" colA col B - val1 val2 trailing value that should be ignored + val1 val2 trailing value that should be included "#; let result = string_to_table(input, false, 2).unwrap(); assert_eq!( result, - vec![vec![owned("colA", "val1"), owned("col B", "val2"),],] + vec![vec![owned("colA", "val1"), owned("col B", "val2 trailing value that should be included"),],] ) } } From f21405399cb60df969158abe7a40abc2aee780cd Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 17 Oct 2019 09:56:06 +0200 Subject: [PATCH 302/342] Formats file. --- src/commands/from_ssv.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index 913df9981a..f14d89356a 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -307,7 +307,10 @@ mod tests { let result = string_to_table(input, false, 2).unwrap(); assert_eq!( result, - vec![vec![owned("colA", "val1"), owned("col B", "val2 trailing value that should be included"),],] + vec![vec![ + owned("colA", "val1"), + owned("col B", "val2 trailing value that should be included"), + ],] ) } } From 321629a6932bc5f311383c10d48f908df8acbde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Ker=C3=A4nen?= Date: Thu, 17 Oct 2019 22:57:02 +0300 Subject: [PATCH 303/342] Fix size comparison in 'where size' Fixes #840 --- src/parser/hir/syntax_shape/expression/unit.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parser/hir/syntax_shape/expression/unit.rs b/src/parser/hir/syntax_shape/expression/unit.rs index 03602f1088..2c01038ebc 100644 --- a/src/parser/hir/syntax_shape/expression/unit.rs +++ b/src/parser/hir/syntax_shape/expression/unit.rs @@ -78,9 +78,9 @@ fn unit_size(input: &str, bare_span: Span) -> IResult<&str, (Spanned, value(Unit::B, alt((tag("B"), tag("b")))), value(Unit::KB, alt((tag("KB"), tag("kb"), tag("Kb")))), value(Unit::MB, alt((tag("MB"), tag("mb"), tag("Mb")))), - value(Unit::MB, alt((tag("GB"), tag("gb"), tag("Gb")))), - value(Unit::MB, alt((tag("TB"), tag("tb"), tag("Tb")))), - value(Unit::MB, alt((tag("PB"), tag("pb"), tag("Pb")))), + value(Unit::GB, alt((tag("GB"), tag("gb"), tag("Gb")))), + value(Unit::TB, alt((tag("TB"), tag("tb"), tag("Tb")))), + value(Unit::PB, alt((tag("PB"), tag("pb"), tag("Pb")))), )))(input)?; let start_span = number.span.end(); From 5ce4b12cc18c25a615bec30bf824f11dc4bf51d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Fri, 18 Oct 2019 07:08:04 -0500 Subject: [PATCH 304/342] Inc plugin increments appropiately given a table containing a version in it. --- src/data/base.rs | 130 ++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 4 ++ src/plugins/embed.rs | 44 ++++++--------- src/plugins/inc.rs | 19 +++++-- 4 files changed, 164 insertions(+), 33 deletions(-) diff --git a/src/data/base.rs b/src/data/base.rs index f7b875ef53..c95ee26e86 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -8,6 +8,7 @@ use crate::Text; use chrono::{DateTime, Utc}; use chrono_humanize::Humanize; use derive_new::new; +use indexmap::IndexMap; use log::trace; use serde::{Deserialize, Serialize}; use std::fmt; @@ -452,7 +453,7 @@ impl Value { match self { Value::Primitive(p) => p.type_name(), Value::Row(_) => format!("row"), - Value::Table(_) => format!("list"), + Value::Table(_) => format!("table"), Value::Block(_) => format!("block"), Value::Error(_) => format!("error"), } @@ -684,6 +685,15 @@ impl Value { Value::Row(ref mut o) => { current = o; } + Value::Table(ref mut l) => match l.get_mut(0) { + Some(Tagged { + item: Value::Row(ref mut dict), + .. + }) => { + current = dict; + } + _ => return None, + }, _ => return None, } } @@ -769,6 +779,21 @@ impl Value { } } + #[allow(unused)] + pub fn row(entries: IndexMap>) -> Value { + Value::Row(entries.into()) + } + + pub fn table(list: &Vec>) -> Value { + let mut out = vec![]; + + for v in list { + out.push(v.clone()); + } + + Value::Table(out) + } + pub fn string(s: impl Into) -> Value { Value::Primitive(Primitive::String(s.into())) } @@ -927,3 +952,106 @@ fn coerce_compare_primitive( _ => return Err((left.type_name(), right.type_name())), }) } +#[cfg(test)] +mod tests { + + use crate::data::meta::*; + use crate::Value; + use indexmap::IndexMap; + + fn string(input: impl Into) -> Tagged { + Value::string(input.into()).tagged_unknown() + } + + fn row(entries: IndexMap>) -> Tagged { + Value::row(entries).tagged_unknown() + } + + fn table(list: &Vec>) -> Tagged { + Value::table(list).tagged_unknown() + } + + fn column_path(paths: &Vec>) -> Tagged>> { + let paths = paths + .iter() + .map(|p| string(p.as_string().unwrap())) + .collect(); + let table = table(&paths); + table.as_column_path().unwrap() + } + #[test] + fn gets_the_matching_field_from_a_row() { + let field = "amigos"; + + let row = Value::row(indexmap! { + field.into() => table(&vec![ + string("andres"), + string("jonathan"), + string("yehuda"), + ]), + }); + + assert_eq!( + table(&vec![ + string("andres"), + string("jonathan"), + string("yehuda") + ]), + *row.get_data_by_key(field).unwrap() + ); + } + + #[test] + fn gets_the_first_row_with_matching_field_from_rows_inside_a_table() { + let field = "name"; + + let table = Value::table(&vec![ + row(indexmap! {field.into() => string("andres")}), + row(indexmap! {field.into() => string("jonathan")}), + row(indexmap! {field.into() => string("yehuda")}), + ]); + + assert_eq!(string("andres"), *table.get_data_by_key(field).unwrap()); + } + + #[test] + fn gets_the_matching_field_from_nested_rows_inside_a_row() { + let _field = "package.version"; + let field = vec![string("package"), string("version")]; + let field = column_path(&field); + + let (version, tag) = string("0.4.0").into_parts(); + + let row = Value::row(indexmap! { + "package".into() => row(indexmap!{ + "name".into() => string("nu"), + "version".into() => string("0.4.0"), + }) + }); + + assert_eq!(version, **row.get_data_by_column_path(tag, &field).unwrap()) + } + + #[test] + fn gets_the_first_row_with_matching_field_from_nested_rows_inside_a_table() { + let _field = "package.authors.name"; + let field = vec![string("package"), string("authors"), string("name")]; + let field = column_path(&field); + + let (name, tag) = string("Andrés N. Robalino").into_parts(); + + let row = Value::row(indexmap! { + "package".into() => row(indexmap!{ + "authors".into() => table(&vec![ + row(indexmap!{"name".into()=> string("Andrés N. Robalino")}), + row(indexmap!{"name".into()=> string("Jonathan Turner")}), + row(indexmap!{"name".into() => string("Yehuda Katz")}) + ]), + "name".into() => string("nu"), + "version".into() => string("0.4.0"), + }) + }); + + assert_eq!(name, **row.get_data_by_column_path(tag, &field).unwrap()) + } +} diff --git a/src/lib.rs b/src/lib.rs index bfcaa4510f..520e08a136 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,9 @@ #![recursion_limit = "1024"] +#[cfg(test)] +#[macro_use] +extern crate indexmap; + #[macro_use] mod prelude; diff --git a/src/plugins/embed.rs b/src/plugins/embed.rs index 97dd6a2713..e659bfeb3b 100644 --- a/src/plugins/embed.rs +++ b/src/plugins/embed.rs @@ -1,6 +1,9 @@ +#[macro_use] +extern crate indexmap; + use nu::{ serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - SyntaxShape, Tag, Tagged, TaggedDictBuilder, Value, + SyntaxShape, Tag, Tagged, TaggedItem, Value, }; struct Embed { @@ -16,22 +19,8 @@ impl Embed { } fn embed(&mut self, value: Tagged) -> Result<(), ShellError> { - match value { - Tagged { item, tag } => match &self.field { - Some(_) => { - self.values.push(Tagged { - item: item, - tag: tag, - }); - Ok(()) - } - None => Err(ShellError::labeled_error( - "embed needs a field when embedding a value", - "original value", - &tag, - )), - }, - } + self.values.push(value); + Ok(()) } } @@ -39,8 +28,7 @@ impl Plugin for Embed { fn config(&mut self) -> Result { Ok(Signature::build("embed") .desc("Embeds a new field to the table.") - .required("Field", SyntaxShape::String) - .rest(SyntaxShape::String) + .optional("field", SyntaxShape::String) .filter()) } @@ -67,15 +55,15 @@ impl Plugin for Embed { } fn end_filter(&mut self) -> Result, ShellError> { - let mut root = TaggedDictBuilder::new(Tag::unknown()); - root.insert_tagged( - self.field.as_ref().unwrap(), - Tagged { - item: Value::Table(self.values.clone()), - tag: Tag::unknown(), - }, - ); - Ok(vec![ReturnSuccess::value(root.into_tagged_value())]) + let row = Value::row(indexmap! { + match &self.field { + Some(key) => key.clone(), + None => "root".into(), + } => Value::table(&self.values).tagged(Tag::unknown()), + }) + .tagged(Tag::unknown()); + + Ok(vec![ReturnSuccess::value(row)]) } } diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 38788014ad..1cb6cb2b97 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -14,7 +14,7 @@ pub enum SemVerAction { Patch, } -pub type ColumnPath = Tagged>>; +pub type ColumnPath = Vec>; struct Inc { field: Option, @@ -83,6 +83,16 @@ impl Inc { Ok(Value::bytes(b + 1 as u64).tagged(value.tag())) } Value::Primitive(Primitive::String(ref s)) => Ok(self.apply(&s)?.tagged(value.tag())), + Value::Table(values) => { + if values.len() == 1 { + return Ok(Value::Table(vec![self.inc(values[0].clone())?]).tagged(value.tag())); + } else { + return Err(ShellError::type_error( + "incrementable value", + value.tagged_type_name(), + )); + } + } Value::Row(_) => match self.field { Some(ref f) => { let replacement = match value.item.get_data_by_column_path(value.tag(), f) { @@ -91,10 +101,11 @@ impl Inc { return Err(ShellError::labeled_error( "inc could not find field to replace", "column name", - &f.tag, + value.tag(), )) } }; + match value.item.replace_data_at_column_path( value.tag(), f, @@ -105,7 +116,7 @@ impl Inc { return Err(ShellError::labeled_error( "inc could not find field to replace", "column name", - &f.tag, + value.tag(), )) } } @@ -151,7 +162,7 @@ impl Plugin for Inc { item: Value::Table(_), .. } => { - self.field = Some(table.as_column_path()?); + self.field = Some(table.as_column_path()?.item().to_vec()); } value => return Err(ShellError::type_error("table", value.tagged_type_name())), } From e913e26c01a77e207b5619302ccb0e1d036632c3 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 18 Oct 2019 20:02:24 +0200 Subject: [PATCH 305/342] Deletes impl From<&str> The code still compiles, so this doesn't seem to break anything. That also means it's not critical to fix it, but having dead code around isn't great either. --- src/parser/parse/unit.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/parser/parse/unit.rs b/src/parser/parse/unit.rs index aa19580ac2..e89986f8ac 100644 --- a/src/parser/parse/unit.rs +++ b/src/parser/parse/unit.rs @@ -39,12 +39,6 @@ impl Unit { } } -impl From<&str> for Unit { - fn from(input: &str) -> Unit { - Unit::from_str(input).unwrap() - } -} - impl FromStr for Unit { type Err = (); fn from_str(input: &str) -> Result::Err> { From fc1301c92d191ba7cc2b825edcd04f2aa37d4ac6 Mon Sep 17 00:00:00 2001 From: jdvr Date: Sat, 19 Oct 2019 00:41:24 +0200 Subject: [PATCH 306/342] #194 Added trash crate and send files to the trash using a flag --- Cargo.lock | 10 ++++++++++ Cargo.toml | 1 + src/commands/rm.rs | 4 +++- src/shell/filesystem_shell.rs | 9 +++++++-- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 763ab16798..510cc4d8ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1558,6 +1558,7 @@ dependencies = [ "term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)", + "trash 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "which 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2493,6 +2494,14 @@ dependencies = [ "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "trash" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "typenum" version = "1.11.2" @@ -3093,6 +3102,7 @@ dependencies = [ "checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum toml 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c7aabe75941d914b72bf3e5d3932ed92ce0664d49d8432305a8b547c37227724" +"checksum trash 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f2f24d31505f49e989b1ee2c03c323251f6763d5907d471b71192dac92e323f8" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" "checksum unicase 2.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2e2e6bd1e59e56598518beb94fd6db628ded570326f0a98c679a304bd9f00150" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" diff --git a/Cargo.toml b/Cargo.toml index cd6be5d9fa..cf1e5d791d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ bigdecimal = { version = "0.1.0", features = ["serde"] } natural = "0.3.0" serde_urlencoded = "0.6.1" sublime_fuzzy = "0.5" +trash = "1.0.0" regex = {version = "1", optional = true } neso = { version = "0.5.0", optional = true } diff --git a/src/commands/rm.rs b/src/commands/rm.rs index ac5aeaae7d..c1e671f4b0 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -11,6 +11,7 @@ pub struct Remove; pub struct RemoveArgs { pub target: Tagged, pub recursive: Tagged, + pub trash: Tagged, } impl PerItemCommand for Remove { @@ -21,11 +22,12 @@ impl PerItemCommand for Remove { fn signature(&self) -> Signature { Signature::build("rm") .required("path", SyntaxShape::Pattern) + .switch("trash") .switch("recursive") } fn usage(&self) -> &str { - "Remove a file, (for removing directory append '--recursive')" + "Remove a file. Append '--recursive' to remove directories and '--trash' for seding it to system recycle bin" } fn run( diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index f0adeebeb8..3cf9afab21 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -8,6 +8,7 @@ use crate::prelude::*; use crate::shell::completer::NuCompleter; use crate::shell::shell::Shell; use crate::utils::FileStructure; +use trash as SendToTrash; use rustyline::completion::FilenameCompleter; use rustyline::hint::{Hinter, HistoryHinter}; use std::path::{Path, PathBuf}; @@ -860,7 +861,7 @@ impl Shell for FilesystemShell { fn rm( &self, - RemoveArgs { target, recursive }: RemoveArgs, + RemoveArgs { target, recursive, trash }: RemoveArgs, name: Tag, path: &str, ) -> Result { @@ -946,7 +947,11 @@ impl Shell for FilesystemShell { if path.is_dir() { std::fs::remove_dir_all(&path)?; } else if path.is_file() { - std::fs::remove_file(&path)?; + if trash.item { + SendToTrash::remove(path).unwrap(); + } else { + std::fs::remove_file(&path)?; + } } } Err(e) => { From 0e86430ea34fba749c8d7da02f89f1659cc5e264 Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Tue, 8 Oct 2019 22:17:02 -0400 Subject: [PATCH 307/342] get very basic average working --- Cargo.toml | 4 ++ src/plugins/average.rs | 106 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/plugins/average.rs diff --git a/Cargo.toml b/Cargo.toml index cd6be5d9fa..16b8c85863 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,6 +119,10 @@ path = "src/plugins/inc.rs" name = "nu_plugin_sum" path = "src/plugins/sum.rs" +[[bin]] +name = "nu_plugin_average" +path = "src/plugins/average.rs" + [[bin]] name = "nu_plugin_embed" path = "src/plugins/embed.rs" diff --git a/src/plugins/average.rs b/src/plugins/average.rs new file mode 100644 index 0000000000..5e76560d40 --- /dev/null +++ b/src/plugins/average.rs @@ -0,0 +1,106 @@ +use nu::{ + serve_plugin, CoerceInto, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, + Tagged, TaggedItem, Value, +}; + +#[derive(Debug)] +struct Average { + total: Option>, + count: u64, +} + +impl Average { + fn new() -> Average { + Average { total: None, count: 1 } + } + + fn average(&mut self, value: Tagged) -> Result<(), ShellError> { + match value.item() { + Value::Primitive(Primitive::Nothing) => Ok(()), + Value::Primitive(Primitive::Int(i)) => { + match &self.total { + Some(Tagged { + item: Value::Primitive(Primitive::Int(j)), + tag, + }) => { + self.total = Some(Value::int(i + j).tagged(tag)); + self.count = self.count + 1; + Ok(()) + } + None => { + self.total = Some(value.clone()); + Ok(()) + } + _ => Err(ShellError::string(format!( + "Could not calculate average of non-integer or unrelated types" + ))), + } + } + Value::Primitive(Primitive::Bytes(b)) => { + match self.total { + Some(Tagged { + item: Value::Primitive(Primitive::Bytes(j)), + tag, + }) => { + self.total = Some(Value::int(b + j).tagged(tag)); + self.count = self.count + 1; + Ok(()) + } + None => { + self.total = Some(value); + Ok(()) + } + _ => Err(ShellError::string(format!( + "Could not calculate average of non-integer or unrelated types" + ))), + } + } + x => Err(ShellError::string(format!( + "Unrecognized type in stream: {:?}", + x + ))), + } + + } +} + +impl Plugin for Average { + fn config(&mut self) -> Result { + Ok(Signature::build("average") + .desc("Compute the average of a column of numerical values.") + .filter()) + } + + fn begin_filter(&mut self, _: CallInfo) -> Result, ShellError> { + Ok(vec![]) + } + + fn filter(&mut self, input: Tagged) -> Result, ShellError> { + self.average(input)?; + Ok(vec![]) + } + + fn end_filter(&mut self) -> Result, ShellError> { + match self.total { + None => Ok(vec![]), + Some(ref v) => { + match v.item() { + Value::Primitive(Primitive::Int(i)) => { + let total: u64 = i.tagged(v.tag).coerce_into("converting for average")?; + let avg = total as f64 / self.count as f64; + let decimal_value: Value= Primitive::from(avg).into(); + let tagged_value = decimal_value.tagged(v.tag); + Ok(vec![ReturnSuccess::value(tagged_value)]) + } + _ => unreachable!() + + } + }, + } + } +} + +fn main() { + serve_plugin(&mut Average::new()); +} + From 8262c2dd333137886480fc331bf3cbe800bf1afd Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Sun, 13 Oct 2019 21:08:14 -0400 Subject: [PATCH 308/342] add support for average on byte columns and fmt the code --- src/plugins/average.rs | 118 +++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 53 deletions(-) diff --git a/src/plugins/average.rs b/src/plugins/average.rs index 5e76560d40..8f82a23d10 100644 --- a/src/plugins/average.rs +++ b/src/plugins/average.rs @@ -1,6 +1,6 @@ use nu::{ - serve_plugin, CoerceInto, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, - Tagged, TaggedItem, Value, + serve_plugin, CallInfo, CoerceInto, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, + Signature, Tagged, TaggedItem, Value, }; #[derive(Debug)] @@ -11,56 +11,61 @@ struct Average { impl Average { fn new() -> Average { - Average { total: None, count: 1 } + Average { + total: None, + count: 0, + } } fn average(&mut self, value: Tagged) -> Result<(), ShellError> { match value.item() { Value::Primitive(Primitive::Nothing) => Ok(()), - Value::Primitive(Primitive::Int(i)) => { - match &self.total { - Some(Tagged { - item: Value::Primitive(Primitive::Int(j)), - tag, - }) => { - self.total = Some(Value::int(i + j).tagged(tag)); - self.count = self.count + 1; - Ok(()) - } - None => { - self.total = Some(value.clone()); - Ok(()) - } - _ => Err(ShellError::string(format!( - "Could not calculate average of non-integer or unrelated types" - ))), + Value::Primitive(Primitive::Int(i)) => match &self.total { + Some(Tagged { + item: Value::Primitive(Primitive::Int(j)), + tag, + }) => { + self.total = Some(Value::int(i + j).tagged(tag)); + self.count += 1; + Ok(()) } - } - Value::Primitive(Primitive::Bytes(b)) => { - match self.total { - Some(Tagged { - item: Value::Primitive(Primitive::Bytes(j)), - tag, - }) => { - self.total = Some(Value::int(b + j).tagged(tag)); - self.count = self.count + 1; - Ok(()) - } - None => { - self.total = Some(value); - Ok(()) - } - _ => Err(ShellError::string(format!( - "Could not calculate average of non-integer or unrelated types" - ))), + None => { + self.total = Some(value.clone()); + self.count += 1; + Ok(()) } - } - x => Err(ShellError::string(format!( - "Unrecognized type in stream: {:?}", - x - ))), + _ => Err(ShellError::labeled_error( + "Could calculate average of non-integer or unrelated types", + "source", + value.tag, + )), + }, + Value::Primitive(Primitive::Bytes(b)) => match &self.total { + Some(Tagged { + item: Value::Primitive(Primitive::Bytes(j)), + tag, + }) => { + self.total = Some(Value::bytes(b + j).tagged(tag)); + self.count += 1; + Ok(()) + } + None => { + self.total = Some(value); + self.count += 1; + Ok(()) + } + _ => Err(ShellError::labeled_error( + "Could calculate average of non-integer or unrelated types", + "source", + value.tag, + )), + }, + x => Err(ShellError::labeled_error( + format!("Unrecognized type in stream: {:?}", x), + "source", + value.tag, + )), } - } } @@ -83,19 +88,27 @@ impl Plugin for Average { fn end_filter(&mut self) -> Result, ShellError> { match self.total { None => Ok(vec![]), - Some(ref v) => { - match v.item() { + Some(ref inner) => { + match inner.item() { Value::Primitive(Primitive::Int(i)) => { - let total: u64 = i.tagged(v.tag).coerce_into("converting for average")?; + let total: u64 = i + .tagged(inner.tag.clone()) + .coerce_into("converting for average")?; let avg = total as f64 / self.count as f64; - let decimal_value: Value= Primitive::from(avg).into(); - let tagged_value = decimal_value.tagged(v.tag); + let primitive_value: Value = Primitive::from(avg).into(); + let tagged_value = primitive_value.tagged(inner.tag.clone()); Ok(vec![ReturnSuccess::value(tagged_value)]) } - _ => unreachable!() - + Value::Primitive(Primitive::Bytes(bytes)) => { + // let total: u64 = b.tagged(inner.tag.clone()).coerce_into("converting for average")?; + let avg = *bytes as f64 / self.count as f64; + let primitive_value: Value = Primitive::from(avg).into(); + let tagged_value = primitive_value.tagged(inner.tag.clone()); + Ok(vec![ReturnSuccess::value(tagged_value)]) + } + _ => Ok(vec![]), } - }, + } } } } @@ -103,4 +116,3 @@ impl Plugin for Average { fn main() { serve_plugin(&mut Average::new()); } - From 43fbf4345d626bc1d0d6fbeb0c03c8ffc9c538fa Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Mon, 14 Oct 2019 17:55:42 -0400 Subject: [PATCH 309/342] remove comment and add test for averaging integers --- src/plugins/average.rs | 37 +++++++++++++++++-------------------- tests/filters_test.rs | 15 +++++++++++++++ 2 files changed, 32 insertions(+), 20 deletions(-) diff --git a/src/plugins/average.rs b/src/plugins/average.rs index 8f82a23d10..f78078450a 100644 --- a/src/plugins/average.rs +++ b/src/plugins/average.rs @@ -88,27 +88,24 @@ impl Plugin for Average { fn end_filter(&mut self) -> Result, ShellError> { match self.total { None => Ok(vec![]), - Some(ref inner) => { - match inner.item() { - Value::Primitive(Primitive::Int(i)) => { - let total: u64 = i - .tagged(inner.tag.clone()) - .coerce_into("converting for average")?; - let avg = total as f64 / self.count as f64; - let primitive_value: Value = Primitive::from(avg).into(); - let tagged_value = primitive_value.tagged(inner.tag.clone()); - Ok(vec![ReturnSuccess::value(tagged_value)]) - } - Value::Primitive(Primitive::Bytes(bytes)) => { - // let total: u64 = b.tagged(inner.tag.clone()).coerce_into("converting for average")?; - let avg = *bytes as f64 / self.count as f64; - let primitive_value: Value = Primitive::from(avg).into(); - let tagged_value = primitive_value.tagged(inner.tag.clone()); - Ok(vec![ReturnSuccess::value(tagged_value)]) - } - _ => Ok(vec![]), + Some(ref inner) => match inner.item() { + Value::Primitive(Primitive::Int(i)) => { + let total: u64 = i + .tagged(inner.tag.clone()) + .coerce_into("converting for average")?; + let avg = total as f64 / self.count as f64; + let primitive_value: Value = Primitive::from(avg).into(); + let tagged_value = primitive_value.tagged(inner.tag.clone()); + Ok(vec![ReturnSuccess::value(tagged_value)]) } - } + Value::Primitive(Primitive::Bytes(bytes)) => { + let avg = *bytes as f64 / self.count as f64; + let primitive_value: Value = Primitive::from(avg).into(); + let tagged_value = primitive_value.tagged(inner.tag.clone()); + Ok(vec![ReturnSuccess::value(tagged_value)]) + } + _ => Ok(vec![]), + }, } } } diff --git a/tests/filters_test.rs b/tests/filters_test.rs index f0d5dead61..7696b2f80b 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -579,6 +579,21 @@ fn can_sum() { assert_eq!(actual, "203") } +#[test] +fn can_average() { + let actual = nu!( + cwd: "tests/fixtures/formats", h::pipeline( + r#" + open sgml_description.json + | get glossary.GlossDiv.GlossList.GlossEntry.Sections + | average + | echo $it + "# + )); + + assert_eq!(actual, "101.5000000000000") +} + #[test] fn can_filter_by_unit_size_comparison() { let actual = nu!( From f9fbb0eb3c3a4482afd5eaaebb90c6a743fa6aaf Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Wed, 16 Oct 2019 20:40:19 -0400 Subject: [PATCH 310/342] add docs for average and give more specific examples for sum --- docs/commands/average.md | 42 ++++++++++++++++++++++++++++++++++++++++ docs/commands/sum.md | 42 ++++++++++++++++++++++++---------------- 2 files changed, 67 insertions(+), 17 deletions(-) create mode 100644 docs/commands/average.md diff --git a/docs/commands/average.md b/docs/commands/average.md new file mode 100644 index 0000000000..701ad6091b --- /dev/null +++ b/docs/commands/average.md @@ -0,0 +1,42 @@ +# average This command allows you to calculate the average of values in a column. ## Examples +To get the average of the file sizes in a directory, simply pipe the size column from the ls command to the sum command. + +```shell +> ls | get size | average +━━━━━━━━━ + +━━━━━━━━━ +2282.727272727273 +━━━━━━━━━ +``` + +```shell +> pwd | split-row / | size | get chars | average +━━━━━━━━━ + +━━━━━━━━━ +5.250000000000000 +━━━━━━━━━ +``` + +Note that average only works for integer and byte values at the moment, and if the shell doesn't recognize the values in a column as one of those types, it will return an error. +One way to solve this is to convert each row to an integer and then pipe the result to `average` + +```shell +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average +error: Unrecognized type in stream: Primitive(String("2509000000")) +- shell:1:0 +1 | open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average + | ^^^^ source +``` + +```shell +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | str --to-int | average +━━━━━━━━━━━━━━━━━━━ + +─────────────────── + 3239404444.000000 +━━━━━━━━━━━━━━━━━━━ +``` + + diff --git a/docs/commands/sum.md b/docs/commands/sum.md index f5c59848dd..f20dcb5f37 100644 --- a/docs/commands/sum.md +++ b/docs/commands/sum.md @@ -1,9 +1,4 @@ -# sum - -This command allows you to calculate the sum of values in a column. - -## Examples - +# sum This command allows you to calculate the sum of values in a column. ## Examples To get the sum of the file sizes in a directory, simply pipe the size column from the ls command to the sum command. ```shell @@ -15,21 +10,34 @@ To get the sum of the file sizes in a directory, simply pipe the size column fro ━━━━━━━━━ ``` +To get the sum of the characters in your present working directory. +```shell +> pwd | split-row / | size | get chars | sum +━━━━━━━━━ + +━━━━━━━━━ +21 +━━━━━━━━━ +``` + + + Note that sum only works for integer and byte values at the moment, and if the shell doesn't recognize the values in a column as one of those types, it will return an error. +One way to solve this is to convert each row to an integer and then pipe the result to `sum` ```shell -> open example.csv -━━━┯━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━ - # │ fruit │ amount │ quality -───┼─────────┼────────┼────────── - 0 │ apples │ 1 │ fresh - 1 │ bananas │ 2 │ old - 2 │ oranges │ 7 │ fresh - 3 │ kiwis │ 25 │ rotten -━━━┷━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━ +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average +error: Unrecognized type in stream: Primitive(String("2509000000")) +- shell:1:0 +1 | open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | sum + | ^^^^ source ``` ```shell -> open example.csv | get amount | sum -error: Unrecognized type in stream: Primitive(String("1")) +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | str --to-int | sum +━━━━━━━━━━━━━ + +───────────── + 29154639996 +━━━━━━━━━━━━━ ``` From 2f5eeab56745ba96662f05aade730e45bf74d796 Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Wed, 16 Oct 2019 20:45:23 -0400 Subject: [PATCH 311/342] fix typos and incorrect commands --- docs/commands/average.md | 11 +++++++---- docs/commands/sum.md | 15 ++++++++------- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/commands/average.md b/docs/commands/average.md index 701ad6091b..d4095e518f 100644 --- a/docs/commands/average.md +++ b/docs/commands/average.md @@ -1,5 +1,8 @@ -# average This command allows you to calculate the average of values in a column. ## Examples -To get the average of the file sizes in a directory, simply pipe the size column from the ls command to the sum command. +# average +This command allows you to calculate the average of values in a column. + +## Examples +To get the average of the file sizes in a directory, simply pipe the size column from the ls command to the average command. ```shell > ls | get size | average @@ -19,8 +22,8 @@ To get the average of the file sizes in a directory, simply pipe the size column ━━━━━━━━━ ``` -Note that average only works for integer and byte values at the moment, and if the shell doesn't recognize the values in a column as one of those types, it will return an error. -One way to solve this is to convert each row to an integer and then pipe the result to `average` +Note that average only works for integer and byte values. If the shell doesn't recognize the values in a column as one of those types, it will return an error. +One way to solve this is to convert each row to an integer when possible and then pipe the result to `average` ```shell > open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average diff --git a/docs/commands/sum.md b/docs/commands/sum.md index f20dcb5f37..7482ca0c54 100644 --- a/docs/commands/sum.md +++ b/docs/commands/sum.md @@ -1,4 +1,7 @@ -# sum This command allows you to calculate the sum of values in a column. ## Examples +# sum +This command allows you to calculate the sum of values in a column. + +## Examples To get the sum of the file sizes in a directory, simply pipe the size column from the ls command to the sum command. ```shell @@ -10,7 +13,7 @@ To get the sum of the file sizes in a directory, simply pipe the size column fro ━━━━━━━━━ ``` -To get the sum of the characters in your present working directory. +To get the sum of the characters that make up your present working directory. ```shell > pwd | split-row / | size | get chars | sum ━━━━━━━━━ @@ -20,13 +23,11 @@ To get the sum of the characters in your present working directory. ━━━━━━━━━ ``` - - -Note that sum only works for integer and byte values at the moment, and if the shell doesn't recognize the values in a column as one of those types, it will return an error. -One way to solve this is to convert each row to an integer and then pipe the result to `sum` +Note that sum only works for integer and byte values. If the shell doesn't recognize the values in a column as one of those types, it will return an error. +One way to solve this is to convert each row to an integer when possible and then pipe the result to `sum` ```shell -> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | average +> open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | sum error: Unrecognized type in stream: Primitive(String("2509000000")) - shell:1:0 1 | open tests/fixtures/formats/caco3_plastics.csv | get tariff_item | sum From 4f91d2512a8952be06738d9797a084dc32c75734 Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Wed, 16 Oct 2019 20:45:37 -0400 Subject: [PATCH 312/342] add a test to calculate average of bytes --- tests/filters_test.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 7696b2f80b..0754f76d20 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -580,7 +580,7 @@ fn can_sum() { } #[test] -fn can_average() { +fn can_average_numbers() { let actual = nu!( cwd: "tests/fixtures/formats", h::pipeline( r#" @@ -594,6 +594,16 @@ fn can_average() { assert_eq!(actual, "101.5000000000000") } +#[test] +fn can_average_bytes() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "ls | get size | average | echo $it" + ); + + assert_eq!(actual, "2282.727272727273"); +} + #[test] fn can_filter_by_unit_size_comparison() { let actual = nu!( From 9eda573a434bc4d9fe41d04815c9662256eba1d0 Mon Sep 17 00:00:00 2001 From: "notryanb@gmail.com" Date: Fri, 18 Oct 2019 20:43:07 -0400 Subject: [PATCH 313/342] filter out the files that have the same size on multiple operating systems --- tests/filters_test.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/filters_test.rs b/tests/filters_test.rs index 0754f76d20..1eb55448b7 100644 --- a/tests/filters_test.rs +++ b/tests/filters_test.rs @@ -598,10 +598,10 @@ fn can_average_numbers() { fn can_average_bytes() { let actual = nu!( cwd: "tests/fixtures/formats", - "ls | get size | average | echo $it" + "ls | sort-by name | skip 1 | first 2 | get size | average | echo $it" ); - assert_eq!(actual, "2282.727272727273"); + assert_eq!(actual, "1600.000000000000"); } #[test] From 74dddc880d451b879dd43beae087c7ffa643a377 Mon Sep 17 00:00:00 2001 From: jdvr Date: Sat, 19 Oct 2019 12:25:48 +0200 Subject: [PATCH 314/342] "#194 Added trash switch checked before normal rm command action" --- src/shell/filesystem_shell.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index 3cf9afab21..a7d6a42248 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -944,14 +944,12 @@ impl Shell for FilesystemShell { )); } - if path.is_dir() { + if trash.item { + SendToTrash::remove(path).unwrap(); + } else if path.is_dir() { std::fs::remove_dir_all(&path)?; } else if path.is_file() { - if trash.item { - SendToTrash::remove(path).unwrap(); - } else { - std::fs::remove_file(&path)?; - } + std::fs::remove_file(&path)?; } } Err(e) => { From c209d0d487dd72882af52ffa9850b5dbccb63978 Mon Sep 17 00:00:00 2001 From: jdvr Date: Sat, 19 Oct 2019 22:52:39 +0200 Subject: [PATCH 315/342] 194 Fixed file format --- src/shell/filesystem_shell.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/shell/filesystem_shell.rs b/src/shell/filesystem_shell.rs index a7d6a42248..7b8310141c 100644 --- a/src/shell/filesystem_shell.rs +++ b/src/shell/filesystem_shell.rs @@ -8,11 +8,11 @@ use crate::prelude::*; use crate::shell::completer::NuCompleter; use crate::shell::shell::Shell; use crate::utils::FileStructure; -use trash as SendToTrash; use rustyline::completion::FilenameCompleter; use rustyline::hint::{Hinter, HistoryHinter}; use std::path::{Path, PathBuf}; use std::sync::atomic::Ordering; +use trash as SendToTrash; pub struct FilesystemShell { pub(crate) path: String, @@ -861,7 +861,11 @@ impl Shell for FilesystemShell { fn rm( &self, - RemoveArgs { target, recursive, trash }: RemoveArgs, + RemoveArgs { + target, + recursive, + trash, + }: RemoveArgs, name: Tag, path: &str, ) -> Result { From f24bc5c826a7fe67ed0e514a43eb92103e0d6e29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Sun, 20 Oct 2019 06:55:56 -0500 Subject: [PATCH 316/342] Improvements to Value mutable operations. --- src/data/base.rs | 336 ++++++++++++++++++++++++++++------------------- src/data/dict.rs | 11 ++ 2 files changed, 210 insertions(+), 137 deletions(-) diff --git a/src/data/base.rs b/src/data/base.rs index c95ee26e86..2cf1f2cedb 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -497,6 +497,28 @@ impl Value { } } + pub(crate) fn get_mut_data_by_key(&mut self, name: &str) -> Option<&mut Tagged> { + match self { + Value::Row(ref mut o) => o.get_mut_data_by_key(name), + Value::Table(ref mut l) => { + for item in l { + match item { + Tagged { + item: Value::Row(ref mut o), + .. + } => match o.get_mut_data_by_key(name) { + Some(v) => return Some(v), + None => {} + }, + _ => {} + } + } + None + } + _ => None, + } + } + pub fn get_data_by_column_path( &self, tag: Tag, @@ -513,18 +535,6 @@ impl Value { Some(current.tagged(tag)) } - pub fn get_data_by_path(&self, tag: Tag, path: &str) -> Option> { - let mut current = self; - for p in path.split(".") { - match current.get_data_by_key(p) { - Some(v) => current = v, - None => return None, - } - } - - Some(current.tagged(tag)) - } - pub fn insert_data_at_path( &self, tag: Tag, @@ -629,41 +639,6 @@ impl Value { None } - pub fn replace_data_at_path( - &self, - tag: Tag, - path: &str, - replaced_value: Value, - ) -> Option> { - let mut new_obj = self.clone(); - - let split_path: Vec<_> = path.split(".").collect(); - - if let Value::Row(ref mut o) = new_obj { - let mut current = o; - for idx in 0..split_path.len() { - match current.entries.get_mut(split_path[idx]) { - Some(next) => { - if idx == (split_path.len() - 1) { - *next = replaced_value.tagged(&tag); - return Some(new_obj.tagged(&tag)); - } else { - match next.item { - Value::Row(ref mut o) => { - current = o; - } - _ => return None, - } - } - } - _ => return None, - } - } - } - - None - } - pub fn replace_data_at_column_path( &self, tag: Tag, @@ -671,34 +646,20 @@ impl Value { replaced_value: Value, ) -> Option> { let mut new_obj = self.clone(); + let mut current = &mut new_obj; - if let Value::Row(ref mut o) = new_obj { - let mut current = o; - for idx in 0..split_path.len() { - match current.entries.get_mut(&split_path[idx].item) { - Some(next) => { - if idx == (split_path.len() - 1) { - *next = replaced_value.tagged(&tag); - return Some(new_obj.tagged(&tag)); - } else { - match next.item { - Value::Row(ref mut o) => { - current = o; - } - Value::Table(ref mut l) => match l.get_mut(0) { - Some(Tagged { - item: Value::Row(ref mut dict), - .. - }) => { - current = dict; - } - _ => return None, - }, - _ => return None, - } - } + for idx in 0..split_path.len() { + match current.get_mut_data_by_key(&split_path[idx].item) { + Some(next) => { + if idx == (split_path.len() - 1) { + *next = replaced_value.tagged(&tag); + return Some(new_obj.tagged(&tag)); + } else { + current = &mut next.item; } - _ => return None, + } + None => { + return None; } } } @@ -785,13 +746,7 @@ impl Value { } pub fn table(list: &Vec>) -> Value { - let mut out = vec![]; - - for v in list { - out.push(v.clone()); - } - - Value::Table(out) + Value::Table(list.to_vec()) } pub fn string(s: impl Into) -> Value { @@ -972,86 +927,193 @@ mod tests { } fn column_path(paths: &Vec>) -> Tagged>> { - let paths = paths - .iter() - .map(|p| string(p.as_string().unwrap())) - .collect(); - let table = table(&paths); - table.as_column_path().unwrap() + table( + &paths + .iter() + .map(|p| string(p.as_string().unwrap())) + .collect(), + ) + .as_column_path() + .unwrap() } + #[test] - fn gets_the_matching_field_from_a_row() { - let field = "amigos"; + fn gets_matching_field_from_a_row() { + let row = Value::row(indexmap! { + "amigos".into() => table(&vec![string("andres"),string("jonathan"),string("yehuda")]) + }); + + assert_eq!( + *row.get_data_by_key("amigos").unwrap(), + table(&vec![ + string("andres"), + string("jonathan"), + string("yehuda") + ]) + ); + } + + #[test] + fn gets_matching_field_from_nested_rows_inside_a_row() { + let field_path = column_path(&vec![string("package"), string("version")]); + + let (version, tag) = string("0.4.0").into_parts(); let row = Value::row(indexmap! { - field.into() => table(&vec![ + "package".into() => + row(indexmap! { + "name".into() => string("nu"), + "version".into() => string("0.4.0") + }) + }); + + assert_eq!( + **row.get_data_by_column_path(tag, &field_path).unwrap(), + version + ) + } + + #[test] + fn gets_first_matching_field_from_rows_with_same_field_inside_a_table() { + let field_path = column_path(&vec![string("package"), string("authors"), string("name")]); + + let (name, tag) = string("Andrés N. Robalino").into_parts(); + + let row = Value::row(indexmap! { + "package".into() => row(indexmap! { + "name".into() => string("nu"), + "version".into() => string("0.4.0"), + "authors".into() => table(&vec![ + row(indexmap!{"name".into() => string("Andrés N. Robalino")}), + row(indexmap!{"name".into() => string("Jonathan Turner")}), + row(indexmap!{"name".into() => string("Yehuda Katz")}) + ]) + }) + }); + + assert_eq!( + **row.get_data_by_column_path(tag, &field_path).unwrap(), + name + ) + } + + #[test] + fn replaces_matching_field_from_a_row() { + let field_path = column_path(&vec![string("amigos")]); + + let sample = Value::row(indexmap! { + "amigos".into() => table(&vec![ string("andres"), string("jonathan"), string("yehuda"), ]), }); - assert_eq!( - table(&vec![ - string("andres"), - string("jonathan"), - string("yehuda") - ]), - *row.get_data_by_key(field).unwrap() - ); + let (replacement, tag) = string("jonas").into_parts(); + + let actual = sample + .replace_data_at_column_path(tag, &field_path, replacement) + .unwrap(); + + assert_eq!(actual, row(indexmap! {"amigos".into() => string("jonas")})); } #[test] - fn gets_the_first_row_with_matching_field_from_rows_inside_a_table() { - let field = "name"; - - let table = Value::table(&vec![ - row(indexmap! {field.into() => string("andres")}), - row(indexmap! {field.into() => string("jonathan")}), - row(indexmap! {field.into() => string("yehuda")}), + fn replaces_matching_field_from_nested_rows_inside_a_row() { + let field_path = column_path(&vec![ + string("package"), + string("authors"), + string("los.3.caballeros"), ]); - assert_eq!(string("andres"), *table.get_data_by_key(field).unwrap()); - } - - #[test] - fn gets_the_matching_field_from_nested_rows_inside_a_row() { - let _field = "package.version"; - let field = vec![string("package"), string("version")]; - let field = column_path(&field); - - let (version, tag) = string("0.4.0").into_parts(); - - let row = Value::row(indexmap! { - "package".into() => row(indexmap!{ - "name".into() => string("nu"), - "version".into() => string("0.4.0"), + let sample = Value::row(indexmap! { + "package".into() => row(indexmap! { + "authors".into() => row(indexmap! { + "los.3.mosqueteros".into() => table(&vec![string("andres::yehuda::jonathan")]), + "los.3.amigos".into() => table(&vec![string("andres::yehuda::jonathan")]), + "los.3.caballeros".into() => table(&vec![string("andres::yehuda::jonathan")]) + }) }) }); - assert_eq!(version, **row.get_data_by_column_path(tag, &field).unwrap()) + let (replacement, tag) = table(&vec![string("yehuda::jonathan::andres")]).into_parts(); + + let actual = sample + .replace_data_at_column_path(tag.clone(), &field_path, replacement.clone()) + .unwrap(); + + assert_eq!( + actual, + Value::row(indexmap! { + "package".into() => row(indexmap! { + "authors".into() => row(indexmap! { + "los.3.mosqueteros".into() => table(&vec![string("andres::yehuda::jonathan")]), + "los.3.amigos".into() => table(&vec![string("andres::yehuda::jonathan")]), + "los.3.caballeros".into() => replacement.tagged(&tag)})})}) + .tagged(tag) + ); } - #[test] - fn gets_the_first_row_with_matching_field_from_nested_rows_inside_a_table() { - let _field = "package.authors.name"; - let field = vec![string("package"), string("authors"), string("name")]; - let field = column_path(&field); + fn replaces_matching_field_from_rows_inside_a_table() { + let field_path = column_path(&vec![ + string("shell_policy"), + string("releases"), + string("nu.version.arepa"), + ]); - let (name, tag) = string("Andrés N. Robalino").into_parts(); - - let row = Value::row(indexmap! { - "package".into() => row(indexmap!{ - "authors".into() => table(&vec![ - row(indexmap!{"name".into()=> string("Andrés N. Robalino")}), - row(indexmap!{"name".into()=> string("Jonathan Turner")}), - row(indexmap!{"name".into() => string("Yehuda Katz")}) - ]), - "name".into() => string("nu"), - "version".into() => string("0.4.0"), + let sample = Value::row(indexmap! { + "shell_policy".into() => row(indexmap! { + "releases".into() => table(&vec![ + row(indexmap! { + "nu.version.arepa".into() => row(indexmap! { + "code".into() => string("0.4.0"), "tag_line".into() => string("GitHub-era") + }) + }), + row(indexmap! { + "nu.version.taco".into() => row(indexmap! { + "code".into() => string("0.3.0"), "tag_line".into() => string("GitHub-era") + }) + }), + row(indexmap! { + "nu.version.stable".into() => row(indexmap! { + "code".into() => string("0.2.0"), "tag_line".into() => string("GitHub-era") + }) + }) + ]) }) }); - assert_eq!(name, **row.get_data_by_column_path(tag, &field).unwrap()) + let (replacement, tag) = row(indexmap! { + "code".into() => string("0.5.0"), + "tag_line".into() => string("CABALLEROS") + }) + .into_parts(); + + let actual = sample + .replace_data_at_column_path(tag.clone(), &field_path, replacement.clone()) + .unwrap(); + + assert_eq!( + actual, + Value::row(indexmap! { + "shell_policy".into() => row(indexmap! { + "releases".into() => table(&vec![ + row(indexmap! { + "nu.version.arepa".into() => replacement.tagged(&tag) + }), + row(indexmap! { + "nu.version.taco".into() => row(indexmap! { + "code".into() => string("0.3.0"), "tag_line".into() => string("GitHub-era") + }) + }), + row(indexmap! { + "nu.version.stable".into() => row(indexmap! { + "code".into() => string("0.2.0"), "tag_line".into() => string("GitHub-era") + }) + }) + ]) + }) + }).tagged(&tag) + ); } } diff --git a/src/data/dict.rs b/src/data/dict.rs index 8f9bb556ba..432170f361 100644 --- a/src/data/dict.rs +++ b/src/data/dict.rs @@ -89,6 +89,17 @@ impl Dictionary { } } + pub(crate) fn get_mut_data_by_key(&mut self, name: &str) -> Option<&mut Tagged> { + match self + .entries + .iter_mut() + .find(|(desc_name, _)| *desc_name == name) + { + Some((_, v)) => Some(v), + None => None, + } + } + pub(crate) fn debug(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut debug = f.debug_struct("Dictionary"); From 0611f56776e68254712185a8d25697e8a31c2aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Sun, 20 Oct 2019 18:42:07 -0500 Subject: [PATCH 317/342] Can group cells by given column name. --- README.md | 2 ++ src/cli.rs | 1 + src/commands.rs | 2 ++ src/commands/count.rs | 2 +- src/commands/group_by.rs | 59 ++++++++++++++++++++++++++++++++++++++++ tests/commands_test.rs | 28 +++++++++++++++++++ 6 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 src/commands/group_by.rs diff --git a/README.md b/README.md index cf36fd2cb1..c391b59903 100644 --- a/README.md +++ b/README.md @@ -249,10 +249,12 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | command | description | | ------------- | ------------- | | add column-or-column-path value | Add a new column to the table | +| count | Show the total number of cells | | edit column-or-column-path value | Edit an existing column to have a new value | | embed column | Creates a new table of one column with the given name, and places the current table inside of it | | first amount | Show only the first number of rows | | get column-or-column-path | Open column and get data from the corresponding cells | +| group-by column | Creates a new table with the data from the table rows grouped by the column given | | inc (column-or-column-path) | Increment a value or version. Optionally use the column of a table | | last amount | Show only the last number of rows | | nth row-number | Return only the selected row | diff --git a/src/cli.rs b/src/cli.rs index ad3eb8d39b..e88ee054fe 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -275,6 +275,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(ToURL), whole_stream_command(ToYAML), whole_stream_command(SortBy), + whole_stream_command(GroupBy), whole_stream_command(Tags), whole_stream_command(Count), whole_stream_command(First), diff --git a/src/commands.rs b/src/commands.rs index 0b155891cc..7f0fa0a25a 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -30,6 +30,7 @@ pub(crate) mod from_url; pub(crate) mod from_xml; pub(crate) mod from_yaml; pub(crate) mod get; +pub(crate) mod group_by; pub(crate) mod help; pub(crate) mod last; pub(crate) mod lines; @@ -103,6 +104,7 @@ pub(crate) use from_xml::FromXML; pub(crate) use from_yaml::FromYAML; pub(crate) use from_yaml::FromYML; pub(crate) use get::Get; +pub(crate) use group_by::GroupBy; pub(crate) use help::Help; pub(crate) use last::Last; pub(crate) use lines::Lines; diff --git a/src/commands/count.rs b/src/commands/count.rs index 5e44283737..6fe5a94633 100644 --- a/src/commands/count.rs +++ b/src/commands/count.rs @@ -20,7 +20,7 @@ impl WholeStreamCommand for Count { } fn usage(&self) -> &str { - "Show the total number of rows." + "Show the total number of cells." } fn run( diff --git a/src/commands/group_by.rs b/src/commands/group_by.rs new file mode 100644 index 0000000000..e08ebb2afb --- /dev/null +++ b/src/commands/group_by.rs @@ -0,0 +1,59 @@ +use crate::commands::WholeStreamCommand; +use crate::data::TaggedDictBuilder; +use crate::errors::ShellError; +use crate::prelude::*; + +pub struct GroupBy; + +#[derive(Deserialize)] +pub struct GroupByArgs { + column_name: Tagged, +} + +impl WholeStreamCommand for GroupBy { + fn name(&self) -> &str { + "group-by" + } + + fn signature(&self) -> Signature { + Signature::build("group-by").required("column_name", SyntaxShape::String) + } + + fn usage(&self) -> &str { + "Creates a new table with the data from the table rows grouped by the column given." + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, group_by)?.run() + } +} + +fn group_by( + GroupByArgs { column_name }: GroupByArgs, + RunnableContext { input, name, .. }: RunnableContext, +) -> Result { + let stream = async_stream! { + let values: Vec> = input.values.collect().await; + let mut groups = indexmap::IndexMap::new(); + + for row in values { + let key = row.get_data_by_key(&column_name.item).unwrap().as_string()?; + let mut group = groups.entry(key).or_insert(vec![]); + group.push(row); + } + + let mut out = TaggedDictBuilder::new(name.clone()); + + for (k,v) in groups.iter() { + out.insert(k, Value::table(v)); + } + + yield ReturnSuccess::value(out) + }; + + Ok(stream.to_output_stream()) +} diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 4d6fa84a65..7733942811 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -3,6 +3,34 @@ mod helpers; use helpers as h; use helpers::{Playground, Stub::*}; +#[test] +fn group_by() { + Playground::setup("group_by_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.csv", + r#" + first_name,last_name,rusty_luck,type + Andrés,Robalino,1,A + Jonathan,Turner,1,B + Yehuda,Katz,1,A + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), h::pipeline( + r#" + open los_tres_caballeros.csv + | group-by type + | get A + | count + | echo $it + "# + )); + + assert_eq!(actual, "2"); + }) +} + #[test] fn first_gets_first_rows_by_amount() { Playground::setup("first_test_1", |dirs, sandbox| { From 39fde52d8e1eb2f1607fda68758b9917c4514da3 Mon Sep 17 00:00:00 2001 From: Charles Schleich Date: Mon, 21 Oct 2019 17:59:20 +0200 Subject: [PATCH 318/342] added Docs for sort-by command --- docs/commands/sort-by.md | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 docs/commands/sort-by.md diff --git a/docs/commands/sort-by.md b/docs/commands/sort-by.md new file mode 100644 index 0000000000..1f0f3da9ed --- /dev/null +++ b/docs/commands/sort-by.md @@ -0,0 +1,56 @@ + +# env + +The `sort-by` command sorts the table being displayed in the terminal by a chosen column(s). + +`sort-by` takes multiple arguments (being the names of columns) sorting by each argument in order. + + +## Examples - + +```shell +/home/example> ls | sort-by size +━━━┯━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼──────┼──────┼──────────┼────────┼────────────────┼──────────────── + 0 │ az │ File │ │ 18 B │ 4 minutes ago │ 4 minutes ago + 1 │ a │ File │ │ 18 B │ 4 minutes ago │ 38 minutes ago + 2 │ ad │ File │ │ 18 B │ 4 minutes ago │ 4 minutes ago + 3 │ ac │ File │ │ 18 B │ 4 minutes ago │ 4 minutes ago + 4 │ ab │ File │ │ 18 B │ 4 minutes ago │ 4 minutes ago + 5 │ c │ File │ │ 102 B │ 35 minutes ago │ 35 minutes ago + 6 │ d │ File │ │ 189 B │ 35 minutes ago │ 34 minutes ago + 7 │ b │ File │ │ 349 B │ 35 minutes ago │ 35 minutes ago +━━━┷━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ +``` + +```shell +/home/example> ls | sort-by size name +━━━┯━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼──────┼──────┼──────────┼────────┼────────────────┼──────────────── + 0 │ a │ File │ │ 18 B │ 4 minutes ago │ 39 minutes ago + 1 │ ab │ File │ │ 18 B │ 4 minutes ago │ 4 minutes ago + 2 │ ac │ File │ │ 18 B │ 4 minutes ago │ 4 minutes ago + 3 │ ad │ File │ │ 18 B │ 4 minutes ago │ 4 minutes ago + 4 │ az │ File │ │ 18 B │ 4 minutes ago │ 4 minutes ago + 5 │ c │ File │ │ 102 B │ 36 minutes ago │ 35 minutes ago + 6 │ d │ File │ │ 189 B │ 35 minutes ago │ 35 minutes ago + 7 │ b │ File │ │ 349 B │ 36 minutes ago │ 36 minutes ago +``` + +``` +/home/example> ls | sort-by accessed +━━━┯━━━━━━┯━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━ + # │ name │ type │ readonly │ size │ accessed │ modified +───┼──────┼──────┼──────────┼────────┼────────────────┼──────────────── + 0 │ b │ File │ │ 349 B │ 37 minutes ago │ 37 minutes ago + 1 │ c │ File │ │ 102 B │ 37 minutes ago │ 37 minutes ago + 2 │ d │ File │ │ 189 B │ 37 minutes ago │ 36 minutes ago + 3 │ a │ File │ │ 18 B │ 6 minutes ago │ 40 minutes ago + 4 │ ab │ File │ │ 18 B │ 6 minutes ago │ 6 minutes ago + 5 │ ac │ File │ │ 18 B │ 6 minutes ago │ 6 minutes ago + 6 │ ad │ File │ │ 18 B │ 5 minutes ago │ 5 minutes ago + 7 │ az │ File │ │ 18 B │ 5 minutes ago │ 5 minutes ago +━━━┷━━━━━━┷━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━ +``` \ No newline at end of file From 4329629ee9222e1cd3ff5c108628f89e686b21d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 22 Oct 2019 03:43:39 -0500 Subject: [PATCH 319/342] baseline coverage for xml parsing. --- src/commands/from_xml.rs | 70 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/commands/from_xml.rs b/src/commands/from_xml.rs index 0425eb408b..e99e5664e5 100644 --- a/src/commands/from_xml.rs +++ b/src/commands/from_xml.rs @@ -134,3 +134,73 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result) -> Tagged { + Value::string(input.into()).tagged_unknown() + } + + fn row(entries: IndexMap>) -> Tagged { + Value::row(entries).tagged_unknown() + } + + fn table(list: &Vec>) -> Tagged { + Value::table(list).tagged_unknown() + } + + fn parse(xml: &str) -> Tagged { + from_xml::from_xml_string_to_value(xml.to_string(), Tag::unknown()).unwrap() + } + + #[test] + fn parses_empty_element() { + let source = ""; + + assert_eq!( + parse(source), + row(indexmap! { + "nu".into() => table(&vec![]) + }) + ); + } + + #[test] + fn parses_element_with_text() { + let source = "La era de los tres caballeros"; + + assert_eq!( + parse(source), + row(indexmap! { + "nu".into() => table(&vec![string("La era de los tres caballeros")]) + }) + ); + } + + #[test] + fn parses_element_with_elements() { + let source = "\ + + Andrés + Jonathan + Yehuda +"; + + assert_eq!( + parse(source), + row(indexmap! { + "nu".into() => table(&vec![ + row(indexmap! {"dev".into() => table(&vec![string("Andrés")])}), + row(indexmap! {"dev".into() => table(&vec![string("Jonathan")])}), + row(indexmap! {"dev".into() => table(&vec![string("Yehuda")])}) + ]) + }) + ); + } +} From 8f035616a0be57646231cd18deb475965e4778b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Ker=C3=A4nen?= Date: Tue, 22 Oct 2019 15:21:34 +0300 Subject: [PATCH 320/342] Fix `enter` crashing on nonexistent file Fixes #839 --- src/commands/enter.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/commands/enter.rs b/src/commands/enter.rs index 4a400241e8..efefd8394f 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -1,7 +1,6 @@ use crate::commands::command::CommandAction; use crate::commands::PerItemCommand; use crate::commands::UnevaluatedCallInfo; -use crate::data::meta::Span; use crate::errors::ShellError; use crate::parser::registry; use crate::prelude::*; @@ -34,10 +33,12 @@ impl PerItemCommand for Enter { match call_info.args.expect_nth(0)? { Tagged { item: Value::Primitive(Primitive::Path(location)), + tag, .. } => { let location_string = location.display().to_string(); let location_clone = location_string.clone(); + let tag_clone = tag.clone(); if location.starts_with("help") { let spec = location_string.split(":").collect::>(); @@ -71,9 +72,8 @@ impl PerItemCommand for Enter { crate::commands::open::fetch( &full_path, &location_clone, - Span::unknown(), - ) - .await.unwrap(); + tag_clone.span, + ).await?; match contents { Value::Primitive(Primitive::String(_)) => { From a317072e4e40d00632f2a66c9b2b408fc379e31e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 22 Oct 2019 08:08:24 -0500 Subject: [PATCH 321/342] Cover failure not found files cases. --- tests/command_enter_test.rs | 13 +++++++++++++ tests/command_open_tests.rs | 1 + 2 files changed, 14 insertions(+) diff --git a/tests/command_enter_test.rs b/tests/command_enter_test.rs index fe22b56dbe..fc4a437a23 100644 --- a/tests/command_enter_test.rs +++ b/tests/command_enter_test.rs @@ -73,3 +73,16 @@ fn knows_the_filesystems_entered() { )); }) } + +#[test] +fn errors_if_file_not_found() { + Playground::setup("enter_test_2", |dirs, _| { + let actual = nu_error!( + cwd: dirs.test(), + "enter i_dont_exist.csv" + ); + + assert!(actual.contains("File could not be opened")); + assert!(actual.contains("file not found")); + }) +} diff --git a/tests/command_open_tests.rs b/tests/command_open_tests.rs index 53e393eef4..48f438f3d6 100644 --- a/tests/command_open_tests.rs +++ b/tests/command_open_tests.rs @@ -226,4 +226,5 @@ fn errors_if_file_not_found() { ); assert!(actual.contains("File could not be opened")); + assert!(actual.contains("file not found")); } From 6a7c00eaefc412dd049149b5b2dbe4aff55051ce Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Mon, 21 Oct 2019 08:18:43 -0700 Subject: [PATCH 322/342] Finish the job of moving shapes into the stream This commit should finish the `coloring_in_tokens` feature, which moves the shape accumulator into the token stream. This allows rollbacks of the token stream to also roll back any shapes that were added. This commit also adds a much nicer syntax highlighter trace, which shows all of the paths the highlighter took to arrive at a particular coloring output. This change is fairly substantial, but really improves the understandability of the flow. I intend to update the normal parser with a similar tracing view. In general, this change also fleshes out the concept of "atomic" token stream operations. A good next step would be to try to make the parser more error-correcting, using the coloring infrastructure. A follow-up step would involve merging the parser and highlighter shapes themselves. --- Cargo.toml | 4 +- src/fuzzysearch.rs | 4 +- src/main.rs | 3 - src/parser.rs | 1 - src/parser/hir/expand_external_tokens.rs | 10 +- src/parser/hir/syntax_shape.rs | 321 +++++++--------- src/parser/hir/syntax_shape/block.rs | 27 +- src/parser/hir/syntax_shape/expression.rs | 18 +- .../hir/syntax_shape/expression/delimited.rs | 5 + .../hir/syntax_shape/expression/file_path.rs | 6 +- .../hir/syntax_shape/expression/list.rs | 24 +- .../hir/syntax_shape/expression/number.rs | 12 +- .../hir/syntax_shape/expression/pattern.rs | 4 + .../hir/syntax_shape/expression/string.rs | 6 +- .../syntax_shape/expression/variable_path.rs | 37 +- src/parser/hir/tokens_iterator.rs | 311 +++++++++++---- src/parser/hir/tokens_iterator/debug.rs | 359 +++++++++++++++++- src/parser/parse/parser.rs | 9 - src/parser/parse/pipeline.rs | 4 +- src/parser/parse_command.rs | 28 +- src/shell/helper.rs | 45 +-- tests/commands_test.rs | 11 +- 22 files changed, 888 insertions(+), 361 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index ce101b52df..29205d9af5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -84,7 +84,7 @@ heim = {version = "0.0.8", optional = true } battery = {version = "0.7.4", optional = true } rawkey = {version = "0.1.2", optional = true } clipboard = {version = "0.5", optional = true } -ptree = {version = "0.2", optional = true } +ptree = {version = "0.2" } image = { version = "0.22.2", default_features = false, features = ["png_codec", "jpeg"], optional = true } [features] @@ -95,7 +95,7 @@ binaryview = ["image", "crossterm"] sys = ["heim", "battery"] ps = ["heim"] # trace = ["nom-tracable/trace"] -all = ["raw-key", "textview", "binaryview", "sys", "ps", "clipboard", "ptree"] +all = ["raw-key", "textview", "binaryview", "sys", "ps", "clipboard"] [dependencies.rusqlite] version = "0.20.0" diff --git a/src/fuzzysearch.rs b/src/fuzzysearch.rs index 5cb08dd3f5..c7d58ed632 100644 --- a/src/fuzzysearch.rs +++ b/src/fuzzysearch.rs @@ -73,9 +73,7 @@ pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> Select searchinput.pop(); selected = 0; } - _ => { - // println!("OTHER InputEvent: {:?}", k); - } + _ => {} }, _ => {} } diff --git a/src/main.rs b/src/main.rs index 4b10944a2b..7f82808e74 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,9 +3,6 @@ use log::LevelFilter; use std::error::Error; fn main() -> Result<(), Box> { - #[cfg(feature1)] - println!("feature1 is enabled"); - let matches = App::new("nushell") .version(clap::crate_version!()) .arg( diff --git a/src/parser.rs b/src/parser.rs index 37c8c09c30..7acdf6e6bf 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -14,7 +14,6 @@ pub(crate) use parse::files::Files; pub(crate) use parse::flag::{Flag, FlagKind}; pub(crate) use parse::operator::Operator; pub(crate) use parse::parser::{nom_input, pipeline}; -pub(crate) use parse::pipeline::{Pipeline, PipelineElement}; pub(crate) use parse::text::Text; pub(crate) use parse::token_tree::{DelimitedNode, Delimiter, TokenNode}; pub(crate) use parse::tokens::{RawNumber, RawToken}; diff --git a/src/parser/hir/expand_external_tokens.rs b/src/parser/hir/expand_external_tokens.rs index e277efe2e8..5733a30c81 100644 --- a/src/parser/hir/expand_external_tokens.rs +++ b/src/parser/hir/expand_external_tokens.rs @@ -61,6 +61,10 @@ impl ColorSyntax for ExternalTokensShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "ExternalTokensShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -192,6 +196,10 @@ impl ColorSyntax for ExternalExpression { type Info = ExternalExpressionResult; type Input = (); + fn name(&self) -> &'static str { + "ExternalExpression" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -212,7 +220,7 @@ impl ColorSyntax for ExternalExpression { Ok(atom) => atom, }; - atom.color_tokens(token_nodes.mut_shapes()); + token_nodes.mutate_shapes(|shapes| atom.color_tokens(shapes)); return ExternalExpressionResult::Processed; } } diff --git a/src/parser/hir/syntax_shape.rs b/src/parser/hir/syntax_shape.rs index dc02e9373d..8a21fd79e6 100644 --- a/src/parser/hir/syntax_shape.rs +++ b/src/parser/hir/syntax_shape.rs @@ -11,16 +11,15 @@ use crate::parser::hir::expand_external_tokens::ExternalTokensShape; use crate::parser::hir::syntax_shape::block::AnyBlockShape; use crate::parser::hir::tokens_iterator::Peeked; use crate::parser::parse_command::{parse_command_tail, CommandTailShape}; -use crate::parser::PipelineElement; use crate::parser::{ hir, hir::{debug_tokens, TokensIterator}, - Operator, Pipeline, RawToken, TokenNode, + Operator, RawToken, TokenNode, }; use crate::prelude::*; use derive_new::new; use getset::Getters; -use log::{self, log_enabled, trace}; +use log::{self, trace}; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; @@ -41,6 +40,11 @@ pub(crate) use self::expression::variable_path::{ pub(crate) use self::expression::{continue_expression, AnyExpressionShape}; pub(crate) use self::flat_shape::FlatShape; +#[cfg(not(coloring_in_tokens))] +use crate::parser::parse::pipeline::Pipeline; +#[cfg(not(coloring_in_tokens))] +use log::log_enabled; + #[derive(Debug, Copy, Clone, Serialize, Deserialize)] pub enum SyntaxShape { Any, @@ -110,6 +114,10 @@ impl FallibleColorSyntax for SyntaxShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "SyntaxShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -241,6 +249,8 @@ pub trait FallibleColorSyntax: std::fmt::Debug + Copy { type Info; type Input; + fn name(&self) -> &'static str; + fn color_syntax<'a, 'b>( &self, input: &Self::Input, @@ -282,6 +292,8 @@ pub trait ColorSyntax: std::fmt::Debug + Copy { type Info; type Input; + fn name(&self) -> &'static str; + fn color_syntax<'a, 'b>( &self, input: &Self::Input, @@ -290,24 +302,6 @@ pub trait ColorSyntax: std::fmt::Debug + Copy { ) -> Self::Info; } -// impl ColorSyntax for T -// where -// T: FallibleColorSyntax, -// { -// type Info = Result; -// type Input = T::Input; - -// fn color_syntax<'a, 'b>( -// &self, -// input: &Self::Input, -// token_nodes: &'b mut TokensIterator<'a>, -// context: &ExpandContext, -// shapes: &mut Vec>, -// ) -> Result { -// FallibleColorSyntax::color_syntax(self, input, token_nodes, context, shapes) -// } -// } - pub(crate) trait ExpandSyntax: std::fmt::Debug + Copy { type Output: std::fmt::Debug; @@ -323,18 +317,18 @@ pub(crate) fn expand_syntax<'a, 'b, T: ExpandSyntax>( token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result { - trace!(target: "nu::expand_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + trace!(target: "nu::expand_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes.state(), context.source)); let result = shape.expand_syntax(token_nodes, context); match result { Err(err) => { - trace!(target: "nu::expand_syntax", "error :: {} :: {:?}", err, debug_tokens(token_nodes, context.source)); + trace!(target: "nu::expand_syntax", "error :: {} :: {:?}", err, debug_tokens(token_nodes.state(), context.source)); Err(err) } Ok(result) => { - trace!(target: "nu::expand_syntax", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes, context.source)); + trace!(target: "nu::expand_syntax", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes.state(), context.source)); Ok(result) } } @@ -347,12 +341,12 @@ pub fn color_syntax<'a, 'b, T: ColorSyntax, U>( context: &ExpandContext, shapes: &mut Vec>, ) -> ((), U) { - trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes.state(), context.source)); let len = shapes.len(); let result = shape.color_syntax(&(), token_nodes, context, shapes); - trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes.state(), context.source)); if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); @@ -375,26 +369,12 @@ pub fn color_syntax<'a, 'b, T: ColorSyntax, U>( token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> ((), U) { - trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); - - let len = token_nodes.shapes().len(); - let result = shape.color_syntax(&(), token_nodes, context); - - trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); - - if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { - trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); - - if len < token_nodes.shapes().len() { - for i in len..(token_nodes.shapes().len()) { - trace!(target: "nu::color_syntax", "new shape :: {:?}", token_nodes.shapes()[i]); - } - } else { - trace!(target: "nu::color_syntax", "no new shapes"); - } - } - - ((), result) + ( + (), + token_nodes.color_frame(shape.name(), |token_nodes| { + shape.color_syntax(&(), token_nodes, context) + }), + ) } #[cfg(not(coloring_in_tokens))] @@ -404,7 +384,7 @@ pub fn color_fallible_syntax<'a, 'b, T: FallibleColorSyntax>, ) -> Result { - trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes.state(), context.source)); if token_nodes.at_end() { trace!(target: "nu::color_syntax", "at eof"); @@ -414,7 +394,7 @@ pub fn color_fallible_syntax<'a, 'b, T: FallibleColorSyntax()); @@ -437,31 +417,9 @@ pub fn color_fallible_syntax<'a, 'b, T: FallibleColorSyntax, context: &ExpandContext, ) -> Result { - trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); - - if token_nodes.at_end() { - trace!(target: "nu::color_syntax", "at eof"); - return Err(ShellError::unexpected_eof("coloring", Tag::unknown())); - } - - let len = token_nodes.shapes().len(); - let result = shape.color_syntax(&(), token_nodes, context); - - trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); - - if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { - trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); - - if len < token_nodes.shapes().len() { - for i in len..(token_nodes.shapes().len()) { - trace!(target: "nu::color_syntax", "new shape :: {:?}", token_nodes.shapes()[i]); - } - } else { - trace!(target: "nu::color_syntax", "no new shapes"); - } - } - - result + token_nodes.color_fallible_frame(shape.name(), |token_nodes| { + shape.color_syntax(&(), token_nodes, context) + }) } #[cfg(not(coloring_in_tokens))] @@ -472,12 +430,12 @@ pub fn color_syntax_with<'a, 'b, T: ColorSyntax, U, I>( context: &ExpandContext, shapes: &mut Vec>, ) -> ((), U) { - trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes.state(), context.source)); let len = shapes.len(); let result = shape.color_syntax(input, token_nodes, context, shapes); - trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); + trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes.state(), context.source)); if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); @@ -501,26 +459,12 @@ pub fn color_syntax_with<'a, 'b, T: ColorSyntax, U, I>( token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> ((), U) { - trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); - - let len = token_nodes.shapes().len(); - let result = shape.color_syntax(input, token_nodes, context); - - trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); - - if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { - trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); - - if len < token_nodes.shapes().len() { - for i in len..(token_nodes.shapes().len()) { - trace!(target: "nu::color_syntax", "new shape :: {:?}", token_nodes.shapes()[i]); - } - } else { - trace!(target: "nu::color_syntax", "no new shapes"); - } - } - - ((), result) + ( + (), + token_nodes.color_frame(shape.name(), |token_nodes| { + shape.color_syntax(input, token_nodes, context) + }), + ) } #[cfg(not(coloring_in_tokens))] @@ -531,31 +475,9 @@ pub fn color_fallible_syntax_with<'a, 'b, T: FallibleColorSyntax>, ) -> Result { - trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); - - if token_nodes.at_end() { - trace!(target: "nu::color_syntax", "at eof"); - return Err(ShellError::unexpected_eof("coloring", Tag::unknown())); - } - - let len = shapes.len(); - let result = shape.color_syntax(input, token_nodes, context, shapes); - - trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); - - if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { - trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); - - if len < shapes.len() { - for i in len..(shapes.len()) { - trace!(target: "nu::color_syntax", "new shape :: {:?}", shapes[i]); - } - } else { - trace!(target: "nu::color_syntax", "no new shapes"); - } - } - - result + token_nodes.color_fallible_frame(std::any::type_name::(), |token_nodes| { + shape.color_syntax(input, token_nodes, context, shapes) + }) } #[cfg(coloring_in_tokens)] @@ -565,31 +487,9 @@ pub fn color_fallible_syntax_with<'a, 'b, T: FallibleColorSyntax, context: &ExpandContext, ) -> Result { - trace!(target: "nu::color_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); - - if token_nodes.at_end() { - trace!(target: "nu::color_syntax", "at eof"); - return Err(ShellError::unexpected_eof("coloring", Tag::unknown())); - } - - let len = token_nodes.shapes().len(); - let result = shape.color_syntax(input, token_nodes, context); - - trace!(target: "nu::color_syntax", "ok :: {:?}", debug_tokens(token_nodes, context.source)); - - if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { - trace!(target: "nu::color_syntax", "after {}", std::any::type_name::()); - - if len < token_nodes.shapes().len() { - for i in len..(token_nodes.shapes().len()) { - trace!(target: "nu::color_syntax", "new shape :: {:?}", token_nodes.shapes()[i]); - } - } else { - trace!(target: "nu::color_syntax", "no new shapes"); - } - } - - result + token_nodes.color_fallible_frame(shape.name(), |token_nodes| { + shape.color_syntax(input, token_nodes, context) + }) } pub(crate) fn expand_expr<'a, 'b, T: ExpandExpression>( @@ -597,18 +497,18 @@ pub(crate) fn expand_expr<'a, 'b, T: ExpandExpression>( token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result { - trace!(target: "nu::expand_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes, context.source)); + trace!(target: "nu::expand_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes.state(), context.source)); let result = shape.expand_syntax(token_nodes, context); match result { Err(err) => { - trace!(target: "nu::expand_syntax", "error :: {} :: {:?}", err, debug_tokens(token_nodes, context.source)); + trace!(target: "nu::expand_syntax", "error :: {} :: {:?}", err, debug_tokens(token_nodes.state(), context.source)); Err(err) } Ok(result) => { - trace!(target: "nu::expand_syntax", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes, context.source)); + trace!(target: "nu::expand_syntax", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes.state(), context.source)); Ok(result) } } @@ -738,7 +638,7 @@ impl FallibleColorSyntax for BareShape { _context: &ExpandContext, shapes: &mut Vec>, ) -> Result<(), ShellError> { - token_nodes.peek_any_token(|token| match token { + token_nodes.peek_any_token("word", |token| match token { // If it's a bare token, color it TokenNode::Token(Spanned { item: RawToken::Bare, @@ -759,21 +659,22 @@ impl FallibleColorSyntax for BareShape { type Info = (); type Input = FlatShape; + fn name(&self) -> &'static str { + "BareShape" + } + fn color_syntax<'a, 'b>( &self, input: &FlatShape, token_nodes: &'b mut TokensIterator<'a>, _context: &ExpandContext, ) -> Result<(), ShellError> { - let span = token_nodes.peek_any_token(|token| match token { + let span = token_nodes.peek_any_token("word", |token| match token { // If it's a bare token, color it TokenNode::Token(Spanned { item: RawToken::Bare, span, - }) => { - // token_nodes.color_shape((*input).spanned(*span)); - Ok(span) - } + }) => Ok(span), // otherwise, fail other => Err(ShellError::type_error("word", other.tagged_type_name())), @@ -872,7 +773,8 @@ impl FallibleColorSyntax for PipelineShape { shapes: &mut Vec>, ) -> Result<(), ShellError> { // Make sure we're looking at a pipeline - let Pipeline { parts, .. } = token_nodes.peek_any_token(|node| node.as_pipeline())?; + let Pipeline { parts, .. } = + token_nodes.peek_any_token("pipeline", |node| node.as_pipeline())?; // Enumerate the pipeline parts for part in parts { @@ -898,6 +800,10 @@ impl FallibleColorSyntax for PipelineShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "PipelineShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -905,7 +811,9 @@ impl FallibleColorSyntax for PipelineShape { context: &ExpandContext, ) -> Result<(), ShellError> { // Make sure we're looking at a pipeline - let Pipeline { parts, .. } = token_nodes.peek_any_token(|node| node.as_pipeline())?; + let pipeline = token_nodes.peek_any_token("pipeline", |node| node.as_pipeline())?; + + let parts = &pipeline.parts[..]; // Enumerate the pipeline parts for part in parts { @@ -914,40 +822,77 @@ impl FallibleColorSyntax for PipelineShape { token_nodes.color_shape(FlatShape::Pipe.spanned(pipe)) } - // Create a new iterator containing the tokens in the pipeline part to color - let mut token_nodes = TokensIterator::new(&part.tokens.item, part.span, false); + let tokens: Spanned<&[TokenNode]> = (&part.item.tokens[..]).spanned(part.span); - color_syntax(&MaybeSpaceShape, &mut token_nodes, context); - color_syntax(&CommandShape, &mut token_nodes, context); + token_nodes.child(tokens, move |token_nodes| { + color_syntax(&MaybeSpaceShape, token_nodes, context); + color_syntax(&CommandShape, token_nodes, context); + }); } Ok(()) } } +#[cfg(coloring_in_tokens)] impl ExpandSyntax for PipelineShape { type Output = ClassifiedPipeline; - fn expand_syntax<'a, 'b>( + fn expand_syntax<'content, 'me>( &self, - iterator: &'b mut TokensIterator<'a>, + iterator: &'me mut TokensIterator<'content>, context: &ExpandContext, ) -> Result { let source = context.source; let peeked = iterator.peek_any().not_eof("pipeline")?; - let pipeline = peeked.node.as_pipeline()?; - peeked.commit(); + let pipeline = peeked.commit().as_pipeline()?; - let Pipeline { parts, .. } = pipeline; + let parts = &pipeline.parts[..]; - let commands: Result, ShellError> = parts - .iter() - .map(|item| classify_command(item, context, &source)) - .collect(); + let mut out = vec![]; - Ok(ClassifiedPipeline { - commands: commands?, - }) + for part in parts { + let tokens: Spanned<&[TokenNode]> = (&part.item.tokens[..]).spanned(part.span); + + let classified = iterator.child(tokens, move |token_nodes| { + classify_command(token_nodes, context, &source) + })?; + + out.push(classified); + } + + Ok(ClassifiedPipeline { commands: out }) + } +} + +#[cfg(not(coloring_in_tokens))] +impl ExpandSyntax for PipelineShape { + type Output = ClassifiedPipeline; + fn expand_syntax<'content, 'me>( + &self, + iterator: &'me mut TokensIterator<'content>, + context: &ExpandContext, + ) -> Result { + let source = context.source; + + let peeked = iterator.peek_any().not_eof("pipeline")?; + let pipeline = peeked.commit().as_pipeline()?; + + let parts = &pipeline.parts[..]; + + let mut out = vec![]; + + for part in parts { + let tokens: Spanned<&[TokenNode]> = (&part.item.tokens[..]).spanned(part.span); + + let classified = iterator.child(tokens, move |token_nodes| { + classify_command(token_nodes, context, &source) + })?; + + out.push(classified); + } + + Ok(ClassifiedPipeline { commands: out }) } } @@ -1018,6 +963,10 @@ impl FallibleColorSyntax for CommandHeadShape { type Info = CommandHeadKind; type Input = (); + fn name(&self) -> &'static str { + "CommandHeadShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -1215,6 +1164,10 @@ impl FallibleColorSyntax for InternalCommandHeadShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "InternalCommandHeadShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -1299,7 +1252,7 @@ fn parse_single_node<'a, 'b, T>( expected: &'static str, callback: impl FnOnce(RawToken, Span, SingleError) -> Result, ) -> Result { - token_nodes.peek_any_token(|node| match node { + token_nodes.peek_any_token(expected, |node| match node { TokenNode::Token(token) => callback( token.item, token.span, @@ -1377,6 +1330,10 @@ impl FallibleColorSyntax for WhitespaceShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "WhitespaceShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -1502,6 +1459,10 @@ impl ColorSyntax for MaybeSpaceShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "MaybeSpaceShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -1559,6 +1520,10 @@ impl FallibleColorSyntax for SpaceShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "SpaceShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -1618,17 +1583,15 @@ fn expand_variable(span: Span, token_span: Span, source: &Text) -> hir::Expressi } fn classify_command( - command: &Spanned, + mut iterator: &mut TokensIterator, context: &ExpandContext, source: &Text, ) -> Result { - let mut iterator = TokensIterator::new(&command.tokens.item, command.span, true); - let head = CommandHeadShape.expand_syntax(&mut iterator, &context)?; match &head { CommandSignature::Expression(_) => Err(ShellError::syntax_error( - "Unexpected expression in command position".tagged(command.span), + "Unexpected expression in command position".tagged(iterator.whole_span()), )), // If the command starts with `^`, treat it as an external command no matter what @@ -1710,6 +1673,10 @@ impl ColorSyntax for CommandShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "CommandShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), diff --git a/src/parser/hir/syntax_shape/block.rs b/src/parser/hir/syntax_shape/block.rs index fdf2ecb3f8..0061c0fe8c 100644 --- a/src/parser/hir/syntax_shape/block.rs +++ b/src/parser/hir/syntax_shape/block.rs @@ -66,6 +66,10 @@ impl FallibleColorSyntax for AnyBlockShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "AnyBlockShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -85,13 +89,14 @@ impl FallibleColorSyntax for AnyBlockShape { match block { // If so, color it as a block Some((children, spans)) => { - let mut token_nodes = TokensIterator::new(children.item, context.span, false); - color_syntax_with( - &DelimitedShape, - &(Delimiter::Brace, spans.0, spans.1), - &mut token_nodes, - context, - ); + token_nodes.child(children, |token_nodes| { + color_syntax_with( + &DelimitedShape, + &(Delimiter::Brace, spans.0, spans.1), + token_nodes, + context, + ); + }); return Ok(()); } @@ -169,6 +174,10 @@ impl FallibleColorSyntax for ShorthandBlock { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "ShorthandBlock" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -264,6 +273,10 @@ impl FallibleColorSyntax for ShorthandPath { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "ShorthandPath" + } + fn color_syntax<'a, 'b>( &self, _input: &(), diff --git a/src/parser/hir/syntax_shape/expression.rs b/src/parser/hir/syntax_shape/expression.rs index eccebf7516..0681c9c403 100644 --- a/src/parser/hir/syntax_shape/expression.rs +++ b/src/parser/hir/syntax_shape/expression.rs @@ -69,6 +69,10 @@ impl FallibleColorSyntax for AnyExpressionShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "AnyExpressionShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -267,6 +271,10 @@ impl FallibleColorSyntax for AnyExpressionStartShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "AnyExpressionStartShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -315,7 +323,7 @@ impl FallibleColorSyntax for AnyExpressionStartShape { token_nodes.color_shape(FlatShape::Word.spanned(atom.span)); } - _ => atom.color_tokens(token_nodes.mut_shapes()), + _ => token_nodes.mutate_shapes(|shapes| atom.color_tokens(shapes)), } Ok(()) @@ -387,13 +395,17 @@ impl FallibleColorSyntax for BareTailShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "BareTailShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result<(), ShellError> { - let len = token_nodes.shapes().len(); + let len = token_nodes.state().shapes().len(); loop { let word = @@ -422,7 +434,7 @@ impl FallibleColorSyntax for BareTailShape { } } - if token_nodes.shapes().len() > len { + if token_nodes.state().shapes().len() > len { Ok(()) } else { Err(ShellError::syntax_error( diff --git a/src/parser/hir/syntax_shape/expression/delimited.rs b/src/parser/hir/syntax_shape/expression/delimited.rs index 5f8406c6cb..8cd1e9805a 100644 --- a/src/parser/hir/syntax_shape/expression/delimited.rs +++ b/src/parser/hir/syntax_shape/expression/delimited.rs @@ -66,6 +66,11 @@ impl ColorSyntax for DelimitedShape { impl ColorSyntax for DelimitedShape { type Info = (); type Input = (Delimiter, Span, Span); + + fn name(&self) -> &'static str { + "DelimitedShape" + } + fn color_syntax<'a, 'b>( &self, (delimiter, open, close): &(Delimiter, Span, Span), diff --git a/src/parser/hir/syntax_shape/expression/file_path.rs b/src/parser/hir/syntax_shape/expression/file_path.rs index acde8fba13..f0e5ee0079 100644 --- a/src/parser/hir/syntax_shape/expression/file_path.rs +++ b/src/parser/hir/syntax_shape/expression/file_path.rs @@ -52,6 +52,10 @@ impl FallibleColorSyntax for FilePathShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "FilePathShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -78,7 +82,7 @@ impl FallibleColorSyntax for FilePathShape { token_nodes.color_shape(FlatShape::Path.spanned(atom.span)); } - _ => atom.color_tokens(token_nodes.mut_shapes()), + _ => token_nodes.mutate_shapes(|shapes| atom.color_tokens(shapes)), } Ok(()) diff --git a/src/parser/hir/syntax_shape/expression/list.rs b/src/parser/hir/syntax_shape/expression/list.rs index 5a1ea8e383..51a6b852ca 100644 --- a/src/parser/hir/syntax_shape/expression/list.rs +++ b/src/parser/hir/syntax_shape/expression/list.rs @@ -121,6 +121,10 @@ impl ColorSyntax for ExpressionListShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "ExpressionListShape" + } + /// The intent of this method is to fully color an expression list shape infallibly. /// This means that if we can't expand a token into an expression, we fall back to /// a simpler coloring strategy. @@ -148,12 +152,12 @@ impl ColorSyntax for ExpressionListShape { } if backoff { - let len = token_nodes.shapes().len(); + let len = token_nodes.state().shapes().len(); // If we previously encountered a parsing error, use backoff coloring mode color_syntax(&SimplestExpression, token_nodes, context); - if len == token_nodes.shapes().len() && !token_nodes.at_end() { + if len == token_nodes.state().shapes().len() && !token_nodes.at_end() { // This should never happen, but if it does, a panic is better than an infinite loop panic!("Unexpected tokens left that couldn't be colored even with SimplestExpression") } @@ -222,6 +226,10 @@ impl ColorSyntax for BackoffColoringMode { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "BackoffColoringMode" + } + fn color_syntax<'a, 'b>( &self, _input: &Self::Input, @@ -233,12 +241,12 @@ impl ColorSyntax for BackoffColoringMode { break; } - let len = token_nodes.shapes().len(); + let len = token_nodes.state().shapes().len(); color_syntax(&SimplestExpression, token_nodes, context); - if len == token_nodes.shapes().len() && !token_nodes.at_end() { + if len == token_nodes.state().shapes().len() && !token_nodes.at_end() { // This shouldn't happen, but if it does, a panic is better than an infinite loop - panic!("SimplestExpression failed to consume any tokens, but it's not at the end. This is unexpected\n== token nodes==\n{:#?}\n\n== shapes ==\n{:#?}", token_nodes, token_nodes.shapes()); + panic!("SimplestExpression failed to consume any tokens, but it's not at the end. This is unexpected\n== token nodes==\n{:#?}\n\n== shapes ==\n{:#?}", token_nodes, token_nodes.state().shapes()); } } } @@ -281,6 +289,10 @@ impl ColorSyntax for SimplestExpression { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "SimplestExpression" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -296,7 +308,7 @@ impl ColorSyntax for SimplestExpression { match atom { Err(_) => {} - Ok(atom) => atom.color_tokens(token_nodes.mut_shapes()), + Ok(atom) => token_nodes.mutate_shapes(|shapes| atom.color_tokens(shapes)), } } } diff --git a/src/parser/hir/syntax_shape/expression/number.rs b/src/parser/hir/syntax_shape/expression/number.rs index d1475cbaf3..d4069478e9 100644 --- a/src/parser/hir/syntax_shape/expression/number.rs +++ b/src/parser/hir/syntax_shape/expression/number.rs @@ -79,6 +79,10 @@ impl FallibleColorSyntax for NumberShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "NumberShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -97,7 +101,7 @@ impl FallibleColorSyntax for NumberShape { Spanned { item: Ok(atom), .. } => atom, }; - atom.color_tokens(token_nodes.mut_shapes()); + token_nodes.mutate_shapes(|shapes| atom.color_tokens(shapes)); Ok(()) } @@ -171,6 +175,10 @@ impl FallibleColorSyntax for IntShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "IntShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -189,7 +197,7 @@ impl FallibleColorSyntax for IntShape { Spanned { item: Ok(atom), .. } => atom, }; - atom.color_tokens(token_nodes.mut_shapes()); + token_nodes.mutate_shapes(|shapes| atom.color_tokens(shapes)); Ok(()) } diff --git a/src/parser/hir/syntax_shape/expression/pattern.rs b/src/parser/hir/syntax_shape/expression/pattern.rs index 328e8f795e..eab0b6e5bb 100644 --- a/src/parser/hir/syntax_shape/expression/pattern.rs +++ b/src/parser/hir/syntax_shape/expression/pattern.rs @@ -41,6 +41,10 @@ impl FallibleColorSyntax for PatternShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "PatternShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), diff --git a/src/parser/hir/syntax_shape/expression/string.rs b/src/parser/hir/syntax_shape/expression/string.rs index e74fa0a6a7..116ed8fd0d 100644 --- a/src/parser/hir/syntax_shape/expression/string.rs +++ b/src/parser/hir/syntax_shape/expression/string.rs @@ -45,6 +45,10 @@ impl FallibleColorSyntax for StringShape { type Info = (); type Input = FlatShape; + fn name(&self) -> &'static str { + "StringShape" + } + fn color_syntax<'a, 'b>( &self, input: &FlatShape, @@ -63,7 +67,7 @@ impl FallibleColorSyntax for StringShape { item: AtomicToken::String { .. }, span, } => token_nodes.color_shape((*input).spanned(span)), - other => other.color_tokens(token_nodes.mut_shapes()), + atom => token_nodes.mutate_shapes(|shapes| atom.color_tokens(shapes)), } Ok(()) diff --git a/src/parser/hir/syntax_shape/expression/variable_path.rs b/src/parser/hir/syntax_shape/expression/variable_path.rs index 380b3f936c..e983630348 100644 --- a/src/parser/hir/syntax_shape/expression/variable_path.rs +++ b/src/parser/hir/syntax_shape/expression/variable_path.rs @@ -90,6 +90,10 @@ impl FallibleColorSyntax for VariablePathShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "VariablePathShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -166,6 +170,10 @@ impl FallibleColorSyntax for PathTailShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "PathTailShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -334,6 +342,10 @@ impl FallibleColorSyntax for ExpressionContinuationShape { type Info = ContinuationInfo; type Input = (); + fn name(&self) -> &'static str { + "ExpressionContinuationShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -446,6 +458,10 @@ impl FallibleColorSyntax for VariableShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "VariableShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -658,6 +674,10 @@ impl FallibleColorSyntax for ColumnPathShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "ColumnPathShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -758,6 +778,10 @@ impl FallibleColorSyntax for MemberShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "MemberShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -843,6 +867,10 @@ impl FallibleColorSyntax for ColorableDotShape { type Info = (); type Input = FlatShape; + fn name(&self) -> &'static str { + "ColorableDotShape" + } + fn color_syntax<'a, 'b>( &self, input: &FlatShape, @@ -953,6 +981,10 @@ impl FallibleColorSyntax for InfixShape { type Info = (); type Input = (); + fn name(&self) -> &'static str { + "InfixShape" + } + fn color_syntax<'a, 'b>( &self, _input: &(), @@ -971,10 +1003,7 @@ impl FallibleColorSyntax for InfixShape { |token, token_span, _| { match token { // If it's an operator (and not `.`), it's a match - RawToken::Operator(operator) if operator != Operator::Dot => { - // token_nodes.color_shape(FlatShape::Operator.spanned(token_span)); - Ok(token_span) - } + RawToken::Operator(operator) if operator != Operator::Dot => Ok(token_span), // Otherwise, it's not a match _ => Err(ShellError::type_error( diff --git a/src/parser/hir/tokens_iterator.rs b/src/parser/hir/tokens_iterator.rs index 094c5af8c6..b3069247c9 100644 --- a/src/parser/hir/tokens_iterator.rs +++ b/src/parser/hir/tokens_iterator.rs @@ -1,25 +1,37 @@ pub(crate) mod debug; +use self::debug::Tracer; use crate::errors::ShellError; #[cfg(coloring_in_tokens)] use crate::parser::hir::syntax_shape::FlatShape; use crate::parser::TokenNode; +use crate::prelude::*; use crate::{Span, Spanned, SpannedItem}; #[allow(unused)] -use getset::Getters; +use getset::{Getters, MutGetters}; #[derive(Getters, Debug)] -pub struct TokensIterator<'content> { +pub struct TokensIteratorState<'content> { tokens: &'content [TokenNode], span: Span, skip_ws: bool, index: usize, seen: indexmap::IndexSet, #[cfg(coloring_in_tokens)] - #[get = "pub"] + #[cfg_attr(coloring_in_tokens, get = "pub")] shapes: Vec>, } +#[derive(Getters, MutGetters, Debug)] +pub struct TokensIterator<'content> { + #[get = "pub"] + #[get_mut = "pub"] + state: TokensIteratorState<'content>, + #[get = "pub"] + #[get_mut = "pub"] + tracer: Tracer, +} + #[derive(Debug)] pub struct Checkpoint<'content, 'me> { pub(crate) iterator: &'me mut TokensIterator<'content>, @@ -39,10 +51,12 @@ impl<'content, 'me> Checkpoint<'content, 'me> { impl<'content, 'me> std::ops::Drop for Checkpoint<'content, 'me> { fn drop(&mut self) { if !self.committed { - self.iterator.index = self.index; - self.iterator.seen = self.seen.clone(); + let state = &mut self.iterator.state; + + state.index = self.index; + state.seen = self.seen.clone(); #[cfg(coloring_in_tokens)] - self.iterator.shapes.truncate(self.shape_start); + state.shapes.truncate(self.shape_start); } } } @@ -138,13 +152,16 @@ impl<'content> TokensIterator<'content> { skip_ws: bool, ) -> TokensIterator<'content> { TokensIterator { - tokens: items, - span, - skip_ws, - index: 0, - seen: indexmap::IndexSet::new(), - #[cfg(coloring_in_tokens)] - shapes: vec![], + state: TokensIteratorState { + tokens: items, + span, + skip_ws, + index: 0, + seen: indexmap::IndexSet::new(), + #[cfg(coloring_in_tokens)] + shapes: vec![], + }, + tracer: Tracer::new(), } } @@ -153,7 +170,7 @@ impl<'content> TokensIterator<'content> { } pub fn len(&self) -> usize { - self.tokens.len() + self.state.tokens.len() } pub fn spanned( @@ -171,35 +188,146 @@ impl<'content> TokensIterator<'content> { #[cfg(coloring_in_tokens)] pub fn color_shape(&mut self, shape: Spanned) { - self.shapes.push(shape); + self.with_tracer(|_, tracer| tracer.add_shape(shape)); + self.state.shapes.push(shape); } #[cfg(coloring_in_tokens)] - pub fn mut_shapes(&mut self) -> &mut Vec> { - &mut self.shapes + pub fn mutate_shapes(&mut self, block: impl FnOnce(&mut Vec>)) { + let new_shapes: Vec> = { + let shapes = &mut self.state.shapes; + let len = shapes.len(); + block(shapes); + (len..(shapes.len())).map(|i| shapes[i]).collect() + }; + + self.with_tracer(|_, tracer| { + for shape in new_shapes { + tracer.add_shape(shape) + } + }); } #[cfg(coloring_in_tokens)] - pub fn child( - &mut self, - tokens: Spanned<&'content [TokenNode]>, - block: impl FnOnce(&mut TokensIterator) -> T, + pub fn silently_mutate_shapes(&mut self, block: impl FnOnce(&mut Vec>)) { + let shapes = &mut self.state.shapes; + block(shapes); + } + + #[cfg(coloring_in_tokens)] + pub fn sort_shapes(&mut self) { + // This is pretty dubious, but it works. We should look into a better algorithm that doesn't end up requiring + // this solution. + + self.state + .shapes + .sort_by(|a, b| a.span.start().cmp(&b.span.start())); + } + + #[cfg(coloring_in_tokens)] + pub fn child<'me, T>( + &'me mut self, + tokens: Spanned<&'me [TokenNode]>, + block: impl FnOnce(&mut TokensIterator<'me>) -> T, ) -> T { let mut shapes = vec![]; - std::mem::swap(&mut shapes, &mut self.shapes); + std::mem::swap(&mut shapes, &mut self.state.shapes); + + let mut tracer = Tracer::new(); + std::mem::swap(&mut tracer, &mut self.tracer); let mut iterator = TokensIterator { - tokens: tokens.item, - span: tokens.span, - skip_ws: false, - index: 0, - seen: indexmap::IndexSet::new(), - shapes, + state: TokensIteratorState { + tokens: tokens.item, + span: tokens.span, + skip_ws: false, + index: 0, + seen: indexmap::IndexSet::new(), + shapes, + }, + tracer, }; let result = block(&mut iterator); - std::mem::swap(&mut iterator.shapes, &mut self.shapes); + std::mem::swap(&mut iterator.state.shapes, &mut self.state.shapes); + std::mem::swap(&mut iterator.tracer, &mut self.tracer); + + result + } + + #[cfg(not(coloring_in_tokens))] + pub fn child<'me, T>( + &'me mut self, + tokens: Spanned<&'me [TokenNode]>, + block: impl FnOnce(&mut TokensIterator<'me>) -> T, + ) -> T { + let mut tracer = Tracer::new(); + std::mem::swap(&mut tracer, &mut self.tracer); + + let mut iterator = TokensIterator { + state: TokensIteratorState { + tokens: tokens.item, + span: tokens.span, + skip_ws: false, + index: 0, + seen: indexmap::IndexSet::new(), + }, + tracer, + }; + + let result = block(&mut iterator); + + std::mem::swap(&mut iterator.tracer, &mut self.tracer); + + result + } + + pub fn with_tracer(&mut self, block: impl FnOnce(&mut TokensIteratorState, &mut Tracer)) { + let state = &mut self.state; + let tracer = &mut self.tracer; + + block(state, tracer) + } + + #[cfg(coloring_in_tokens)] + pub fn color_frame( + &mut self, + desc: &'static str, + block: impl FnOnce(&mut TokensIterator) -> T, + ) -> T { + self.with_tracer(|_, tracer| tracer.start(desc)); + + let result = block(self); + + self.with_tracer(|_, tracer| { + tracer.success(); + }); + + result + } + + pub fn color_fallible_frame( + &mut self, + desc: &'static str, + block: impl FnOnce(&mut TokensIterator) -> Result, + ) -> Result { + self.with_tracer(|_, tracer| tracer.start(desc)); + + if self.at_end() { + self.with_tracer(|_, tracer| tracer.eof_frame()); + return Err(ShellError::unexpected_eof("coloring", Tag::unknown())); + } + + let result = block(self); + + self.with_tracer(|_, tracer| match &result { + Ok(_) => { + tracer.success(); + } + + Err(err) => tracer.failed(err), + }); result } @@ -207,10 +335,12 @@ impl<'content> TokensIterator<'content> { /// Use a checkpoint when you need to peek more than one token ahead, but can't be sure /// that you'll succeed. pub fn checkpoint<'me>(&'me mut self) -> Checkpoint<'content, 'me> { - let index = self.index; + let state = &mut self.state; + + let index = state.index; #[cfg(coloring_in_tokens)] - let shape_start = self.shapes.len(); - let seen = self.seen.clone(); + let shape_start = state.shapes.len(); + let seen = state.seen.clone(); Checkpoint { iterator: self, @@ -228,10 +358,12 @@ impl<'content> TokensIterator<'content> { &'me mut self, block: impl FnOnce(&mut TokensIterator<'content>) -> Result, ) -> Result { - let index = self.index; + let state = &mut self.state; + + let index = state.index; #[cfg(coloring_in_tokens)] - let shape_start = self.shapes.len(); - let seen = self.seen.clone(); + let shape_start = state.shapes.len(); + let seen = state.seen.clone(); let checkpoint = Checkpoint { iterator: self, @@ -255,11 +387,11 @@ impl<'content> TokensIterator<'content> { &'me mut self, block: impl FnOnce(&mut TokensIterator<'content>) -> Result, ) -> (Result, Vec>) { - let index = self.index; + let index = self.state.index; let mut shapes = vec![]; - let seen = self.seen.clone(); - std::mem::swap(&mut self.shapes, &mut shapes); + let seen = self.state.seen.clone(); + std::mem::swap(&mut self.state.shapes, &mut shapes); let checkpoint = Checkpoint { iterator: self, @@ -274,7 +406,7 @@ impl<'content> TokensIterator<'content> { let value = match value { Err(err) => { drop(checkpoint); - std::mem::swap(&mut self.shapes, &mut shapes); + std::mem::swap(&mut self.state.shapes, &mut shapes); return (Err(err), vec![]); } @@ -282,12 +414,12 @@ impl<'content> TokensIterator<'content> { }; checkpoint.commit(); - std::mem::swap(&mut self.shapes, &mut shapes); + std::mem::swap(&mut self.state.shapes, &mut shapes); return (Ok(value), shapes); } fn eof_span(&self) -> Span { - Span::new(self.span.end(), self.span.end()) + Span::new(self.state.span.end(), self.state.span.end()) } pub fn typed_span_at_cursor(&mut self) -> Spanned<&'static str> { @@ -299,6 +431,10 @@ impl<'content> TokensIterator<'content> { } } + pub fn whole_span(&self) -> Span { + self.state.span + } + pub fn span_at_cursor(&mut self) -> Span { let next = self.peek_any(); @@ -309,11 +445,11 @@ impl<'content> TokensIterator<'content> { } pub fn remove(&mut self, position: usize) { - self.seen.insert(position); + self.state.seen.insert(position); } pub fn at_end(&self) -> bool { - peek(self, self.skip_ws).is_none() + peek(self, self.state.skip_ws).is_none() } pub fn at_end_possible_ws(&self) -> bool { @@ -321,13 +457,15 @@ impl<'content> TokensIterator<'content> { } pub fn advance(&mut self) { - self.seen.insert(self.index); - self.index += 1; + self.state.seen.insert(self.state.index); + self.state.index += 1; } pub fn extract(&mut self, f: impl Fn(&TokenNode) -> Option) -> Option<(usize, T)> { - for (i, item) in self.tokens.iter().enumerate() { - if self.seen.contains(&i) { + let state = &mut self.state; + + for (i, item) in state.tokens.iter().enumerate() { + if state.seen.contains(&i) { continue; } @@ -336,7 +474,7 @@ impl<'content> TokensIterator<'content> { continue; } Some(value) => { - self.seen.insert(i); + state.seen.insert(i); return Some((i, value)); } } @@ -346,22 +484,26 @@ impl<'content> TokensIterator<'content> { } pub fn move_to(&mut self, pos: usize) { - self.index = pos; + self.state.index = pos; } pub fn restart(&mut self) { - self.index = 0; + self.state.index = 0; } pub fn clone(&self) -> TokensIterator<'content> { + let state = &self.state; TokensIterator { - tokens: self.tokens, - span: self.span, - index: self.index, - seen: self.seen.clone(), - skip_ws: self.skip_ws, - #[cfg(coloring_in_tokens)] - shapes: self.shapes.clone(), + state: TokensIteratorState { + tokens: state.tokens, + span: state.span, + index: state.index, + seen: state.seen.clone(), + skip_ws: state.skip_ws, + #[cfg(coloring_in_tokens)] + shapes: state.shapes.clone(), + }, + tracer: self.tracer.clone(), } } @@ -384,10 +526,11 @@ impl<'content> TokensIterator<'content> { // Peek the next token, including whitespace, but not EOF pub fn peek_any_token<'me, T>( &'me mut self, + expected: &'static str, block: impl FnOnce(&'content TokenNode) -> Result, ) -> Result { let peeked = start_next(self, false); - let peeked = peeked.not_eof("invariant"); + let peeked = peeked.not_eof(expected); match peeked { Err(err) => return Err(err), @@ -403,10 +546,10 @@ impl<'content> TokensIterator<'content> { fn commit(&mut self, from: usize, to: usize) { for index in from..to { - self.seen.insert(index); + self.state.seen.insert(index); } - self.index = to; + self.state.index = to; } pub fn pos(&self, skip_ws: bool) -> Option { @@ -424,7 +567,7 @@ impl<'content> Iterator for TokensIterator<'content> { type Item = &'content TokenNode; fn next(&mut self) -> Option<&'content TokenNode> { - next(self, self.skip_ws) + next(self, self.state.skip_ws) } } @@ -432,23 +575,25 @@ fn peek<'content, 'me>( iterator: &'me TokensIterator<'content>, skip_ws: bool, ) -> Option<&'me TokenNode> { - let mut to = iterator.index; + let state = iterator.state(); + + let mut to = state.index; loop { - if to >= iterator.tokens.len() { + if to >= state.tokens.len() { return None; } - if iterator.seen.contains(&to) { + if state.seen.contains(&to) { to += 1; continue; } - if to >= iterator.tokens.len() { + if to >= state.tokens.len() { return None; } - let node = &iterator.tokens[to]; + let node = &state.tokens[to]; match node { TokenNode::Whitespace(_) if skip_ws => { @@ -465,23 +610,25 @@ fn peek_pos<'content, 'me>( iterator: &'me TokensIterator<'content>, skip_ws: bool, ) -> Option { - let mut to = iterator.index; + let state = iterator.state(); + + let mut to = state.index; loop { - if to >= iterator.tokens.len() { + if to >= state.tokens.len() { return None; } - if iterator.seen.contains(&to) { + if state.seen.contains(&to) { to += 1; continue; } - if to >= iterator.tokens.len() { + if to >= state.tokens.len() { return None; } - let node = &iterator.tokens[to]; + let node = &state.tokens[to]; match node { TokenNode::Whitespace(_) if skip_ws => { @@ -496,11 +643,13 @@ fn start_next<'content, 'me>( iterator: &'me mut TokensIterator<'content>, skip_ws: bool, ) -> Peeked<'content, 'me> { - let from = iterator.index; - let mut to = iterator.index; + let state = iterator.state(); + + let from = state.index; + let mut to = state.index; loop { - if to >= iterator.tokens.len() { + if to >= state.tokens.len() { return Peeked { node: None, iterator, @@ -509,12 +658,12 @@ fn start_next<'content, 'me>( }; } - if iterator.seen.contains(&to) { + if state.seen.contains(&to) { to += 1; continue; } - if to >= iterator.tokens.len() { + if to >= state.tokens.len() { return Peeked { node: None, iterator, @@ -523,7 +672,7 @@ fn start_next<'content, 'me>( }; } - let node = &iterator.tokens[to]; + let node = &state.tokens[to]; match node { TokenNode::Whitespace(_) if skip_ws => { @@ -547,20 +696,20 @@ fn next<'me, 'content>( skip_ws: bool, ) -> Option<&'content TokenNode> { loop { - if iterator.index >= iterator.tokens.len() { + if iterator.state().index >= iterator.state().tokens.len() { return None; } - if iterator.seen.contains(&iterator.index) { + if iterator.state().seen.contains(&iterator.state().index) { iterator.advance(); continue; } - if iterator.index >= iterator.tokens.len() { + if iterator.state().index >= iterator.state().tokens.len() { return None; } - match &iterator.tokens[iterator.index] { + match &iterator.state().tokens[iterator.state().index] { TokenNode::Whitespace(_) if skip_ws => { iterator.advance(); } diff --git a/src/parser/hir/tokens_iterator/debug.rs b/src/parser/hir/tokens_iterator/debug.rs index 2e26720154..332a74067c 100644 --- a/src/parser/hir/tokens_iterator/debug.rs +++ b/src/parser/hir/tokens_iterator/debug.rs @@ -1,5 +1,13 @@ -use crate::parser::hir::tokens_iterator::TokensIterator; +use crate::errors::ShellError; +use crate::parser::hir::syntax_shape::FlatShape; +use crate::parser::hir::tokens_iterator::TokensIteratorState; +use crate::prelude::*; use crate::traits::ToDebug; +use ansi_term::Color; +use log::trace; +use ptree::*; +use std::borrow::Cow; +use std::io; #[derive(Debug)] pub(crate) enum DebugIteratorToken { @@ -8,15 +16,15 @@ pub(crate) enum DebugIteratorToken { Cursor, } -pub(crate) fn debug_tokens(iterator: &TokensIterator, source: &str) -> Vec { +pub(crate) fn debug_tokens(state: &TokensIteratorState, source: &str) -> Vec { let mut out = vec![]; - for (i, token) in iterator.tokens.iter().enumerate() { - if iterator.index == i { + for (i, token) in state.tokens.iter().enumerate() { + if state.index == i { out.push(DebugIteratorToken::Cursor); } - if iterator.seen.contains(&i) { + if state.seen.contains(&i) { out.push(DebugIteratorToken::Seen(format!("{}", token.debug(source)))); } else { out.push(DebugIteratorToken::Unseen(format!( @@ -28,3 +36,344 @@ pub(crate) fn debug_tokens(iterator: &TokensIterator, source: &str) -> Vec), + Frame(ColorFrame), +} + +impl FrameChild { + fn colored_leaf_description(&self, text: &Text, f: &mut impl io::Write) -> io::Result<()> { + match self { + FrameChild::Shape(shape) => write!( + f, + "{} {:?}", + Color::White + .bold() + .on(Color::Green) + .paint(format!("{:?}", shape.item)), + shape.span.slice(text) + ), + + FrameChild::Frame(frame) => frame.colored_leaf_description(f), + } + } + + fn into_tree_child(self, text: &Text) -> TreeChild { + match self { + FrameChild::Shape(shape) => TreeChild::Shape(shape, text.clone()), + FrameChild::Frame(frame) => TreeChild::Frame(frame, text.clone()), + } + } +} + +#[derive(Debug, Clone)] +pub struct ColorFrame { + description: &'static str, + children: Vec, + error: Option, +} + +impl ColorFrame { + fn colored_leaf_description(&self, f: &mut impl io::Write) -> io::Result<()> { + if self.has_only_error_descendents() { + if self.children.len() == 0 { + write!( + f, + "{}", + Color::White.bold().on(Color::Red).paint(self.description) + ) + } else { + write!(f, "{}", Color::Red.normal().paint(self.description)) + } + } else if self.has_descendent_shapes() { + write!(f, "{}", Color::Green.normal().paint(self.description)) + } else { + write!(f, "{}", Color::Yellow.bold().paint(self.description)) + } + } + + fn colored_description(&self, text: &Text, f: &mut impl io::Write) -> io::Result<()> { + if self.children.len() == 1 { + let child = &self.children[0]; + + self.colored_leaf_description(f)?; + write!(f, " -> ")?; + child.colored_leaf_description(text, f) + } else { + self.colored_leaf_description(f) + } + } + + fn children_for_formatting(&self, text: &Text) -> Vec { + if self.children.len() == 1 { + let child = &self.children[0]; + + match child { + FrameChild::Shape(_) => vec![], + FrameChild::Frame(frame) => frame.tree_children(text), + } + } else { + self.tree_children(text) + } + } + + fn tree_children(&self, text: &Text) -> Vec { + self.children + .clone() + .into_iter() + .map(|c| c.into_tree_child(text)) + .collect() + } + + #[allow(unused)] + fn add_shape(&mut self, shape: Spanned) { + self.children.push(FrameChild::Shape(shape)) + } + + fn has_child_shapes(&self) -> bool { + self.any_child_shape(|_| true) + } + + fn any_child_shape(&self, predicate: impl Fn(Spanned) -> bool) -> bool { + for item in &self.children { + match item { + FrameChild::Shape(shape) => { + if predicate(*shape) { + return true; + } + } + + _ => {} + } + } + + false + } + + fn any_child_frame(&self, predicate: impl Fn(&ColorFrame) -> bool) -> bool { + for item in &self.children { + match item { + FrameChild::Frame(frame) => { + if predicate(frame) { + return true; + } + } + + _ => {} + } + } + + false + } + + fn has_descendent_shapes(&self) -> bool { + if self.has_child_shapes() { + true + } else { + self.any_child_frame(|frame| frame.has_descendent_shapes()) + } + } + + fn has_only_error_descendents(&self) -> bool { + if self.children.len() == 0 { + // if this frame has no children at all, it has only error descendents if this frame + // is an error + self.error.is_some() + } else { + // otherwise, it has only error descendents if all of its children terminate in an + // error (transitively) + + let mut seen_error = false; + + for child in &self.children { + match child { + // if this frame has at least one child shape, this frame has non-error descendents + FrameChild::Shape(_) => return false, + FrameChild::Frame(frame) => { + // if the chi + if frame.has_only_error_descendents() { + seen_error = true; + } else { + return false; + } + } + } + } + + seen_error + } + } +} + +#[derive(Debug, Clone)] +pub enum TreeChild { + Shape(Spanned, Text), + Frame(ColorFrame, Text), +} + +impl TreeChild { + fn colored_leaf_description(&self, f: &mut impl io::Write) -> io::Result<()> { + match self { + TreeChild::Shape(shape, text) => write!( + f, + "{} {:?}", + Color::White + .bold() + .on(Color::Green) + .paint(format!("{:?}", shape.item)), + shape.span.slice(text) + ), + + TreeChild::Frame(frame, _) => frame.colored_leaf_description(f), + } + } +} + +impl TreeItem for TreeChild { + type Child = TreeChild; + + fn write_self(&self, f: &mut W, _style: &Style) -> io::Result<()> { + match self { + shape @ TreeChild::Shape(..) => shape.colored_leaf_description(f), + + TreeChild::Frame(frame, text) => frame.colored_description(text, f), + } + } + + fn children(&self) -> Cow<[Self::Child]> { + match self { + TreeChild::Shape(..) => Cow::Borrowed(&[]), + TreeChild::Frame(frame, text) => Cow::Owned(frame.children_for_formatting(text)), + } + } +} + +#[derive(Debug, Clone)] +pub struct Tracer { + frame_stack: Vec, +} + +impl Tracer { + pub fn print(self, source: Text) -> PrintTracer { + PrintTracer { + tracer: self, + source, + } + } + + pub fn new() -> Tracer { + let root = ColorFrame { + description: "Trace", + children: vec![], + error: None, + }; + + Tracer { + frame_stack: vec![root], + } + } + + fn current_frame(&mut self) -> &mut ColorFrame { + let frames = &mut self.frame_stack; + let last = frames.len() - 1; + &mut frames[last] + } + + fn pop_frame(&mut self) -> ColorFrame { + let result = self.frame_stack.pop().expect("Can't pop root tracer frame"); + + if self.frame_stack.len() == 0 { + panic!("Can't pop root tracer frame"); + } + + self.debug(); + + result + } + + pub fn start(&mut self, description: &'static str) { + let frame = ColorFrame { + description, + children: vec![], + error: None, + }; + + self.frame_stack.push(frame); + self.debug(); + } + + pub fn eof_frame(&mut self) { + let current = self.pop_frame(); + self.current_frame() + .children + .push(FrameChild::Frame(current)); + } + + #[allow(unused)] + pub fn finish(&mut self) { + loop { + if self.frame_stack.len() == 1 { + break; + } + + let frame = self.pop_frame(); + self.current_frame().children.push(FrameChild::Frame(frame)); + } + } + + #[allow(unused)] + pub fn add_shape(&mut self, shape: Spanned) { + self.current_frame().add_shape(shape); + } + + pub fn success(&mut self) { + let current = self.pop_frame(); + self.current_frame() + .children + .push(FrameChild::Frame(current)); + } + + pub fn failed(&mut self, error: &ShellError) { + let mut current = self.pop_frame(); + current.error = Some(error.clone()); + self.current_frame() + .children + .push(FrameChild::Frame(current)); + } + + fn debug(&self) { + trace!(target: "nu::color_syntax", + "frames = {:?}", + self.frame_stack + .iter() + .map(|f| f.description) + .collect::>() + ); + + trace!(target: "nu::color_syntax", "{:#?}", self); + } +} + +#[derive(Debug, Clone)] +pub struct PrintTracer { + tracer: Tracer, + source: Text, +} + +impl TreeItem for PrintTracer { + type Child = TreeChild; + + fn write_self(&self, f: &mut W, style: &Style) -> io::Result<()> { + write!(f, "{}", style.paint("Color Trace")) + } + + fn children(&self) -> Cow<[Self::Child]> { + Cow::Owned(vec![TreeChild::Frame( + self.tracer.frame_stack[0].clone(), + self.source.clone(), + )]) + } +} diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index 793f7b6cef..b5aefabc26 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -310,15 +310,6 @@ pub fn bare(input: NomSpan) -> IResult { let next_char = &input.fragment.chars().nth(0); let prev_char = last.fragment.chars().nth(0); - // if let (Some(prev), Some(next)) = (prev_char, next_char) { - // if prev == '.' && is_member_start(*next) { - // return Err(nom::Err::Error(nom::error::make_error( - // input, - // nom::error::ErrorKind::TakeWhile1, - // ))); - // } - // } - if let Some(next_char) = next_char { if is_external_word_char(*next_char) || is_glob_specific_char(*next_char) { return Err(nom::Err::Error(nom::error::make_error( diff --git a/src/parser/parse/pipeline.rs b/src/parser/parse/pipeline.rs index 73db738078..4a8c72119c 100644 --- a/src/parser/parse/pipeline.rs +++ b/src/parser/parse/pipeline.rs @@ -5,8 +5,9 @@ use derive_new::new; use getset::Getters; use std::fmt; -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Getters, new)] pub struct Pipeline { + #[get = "pub"] pub(crate) parts: Vec>, // pub(crate) post_ws: Option, } @@ -24,6 +25,7 @@ impl ToDebug for Pipeline { #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] pub struct PipelineElement { pub pipe: Option, + #[get = "pub"] pub tokens: Spanned>, } diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index a4365db247..01ba60b491 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -90,11 +90,11 @@ pub fn parse_command_tail( let mut positional = vec![]; for arg in &config.positional { - trace!("Processing positional {:?}", arg); + trace!(target: "nu::parse", "Processing positional {:?}", arg); match arg { PositionalType::Mandatory(..) => { - if tail.at_end() { + if tail.at_end_possible_ws() { return Err(ShellError::argument_error( config.name.clone(), ArgumentError::MissingMandatoryPositional(arg.name().to_string()), @@ -107,7 +107,7 @@ pub fn parse_command_tail( } PositionalType::Optional(..) => { - if tail.at_end() { + if tail.at_end_possible_ws() { break; } } @@ -138,7 +138,7 @@ pub fn parse_command_tail( trace_remaining("after rest", tail.clone(), context.source()); - trace!("Constructed positional={:?} named={:?}", positional, named); + trace!(target: "nu::parse", "Constructed positional={:?} named={:?}", positional, named); let positional = if positional.len() == 0 { None @@ -154,7 +154,7 @@ pub fn parse_command_tail( Some(named) }; - trace!("Normalized positional={:?} named={:?}", positional, named); + trace!(target: "nu::parse", "Normalized positional={:?} named={:?}", positional, named); Ok(Some((positional, named))) } @@ -391,6 +391,10 @@ impl ColorSyntax for CommandTailShape { type Info = (); type Input = Signature; + fn name(&self) -> &'static str { + "CommandTailShape" + } + fn color_syntax<'a, 'b>( &self, signature: &Signature, @@ -427,10 +431,7 @@ impl ColorSyntax for CommandTailShape { token_nodes.move_to(pos); if token_nodes.at_end() { - // args.insert(pos, shapes); - // token_nodes.restart(); return Ok(()); - // continue; } // We still want to color the flag even if the following tokens don't match, so don't @@ -465,10 +466,7 @@ impl ColorSyntax for CommandTailShape { token_nodes.move_to(pos); if token_nodes.at_end() { - // args.insert(pos, shapes); - // token_nodes.restart(); return Ok(()); - // continue; } // We still want to color the flag even if the following tokens don't match, so don't @@ -573,16 +571,14 @@ impl ColorSyntax for CommandTailShape { } } - args.spread_shapes(token_nodes.mut_shapes()); + token_nodes.silently_mutate_shapes(|shapes| args.spread_shapes(shapes)); // Consume any remaining tokens with backoff coloring mode color_syntax(&BackoffColoringMode, token_nodes, context); // This is pretty dubious, but it works. We should look into a better algorithm that doesn't end up requiring // this solution. - token_nodes - .mut_shapes() - .sort_by(|a, b| a.span.start().cmp(&b.span.start())); + token_nodes.sort_shapes() } } @@ -633,7 +629,7 @@ fn extract_optional( pub fn trace_remaining(desc: &'static str, tail: hir::TokensIterator<'_>, source: &Text) { trace!( - target: "nu::expand_args", + target: "nu::parse", "{} = {:?}", desc, itertools::join( diff --git a/src/shell/helper.rs b/src/shell/helper.rs index 9b5446f5df..8f38a10002 100644 --- a/src/shell/helper.rs +++ b/src/shell/helper.rs @@ -5,7 +5,7 @@ use crate::parser::nom_input; use crate::parser::parse::token_tree::TokenNode; use crate::{Span, Spanned, SpannedItem, Tag, Tagged, Text}; use ansi_term::Color; -use log::trace; +use log::{log_enabled, trace}; use rustyline::completion::Completer; use rustyline::error::ReadlineError; use rustyline::highlight::Highlighter; @@ -34,23 +34,6 @@ impl Completer for Helper { } } -/* -impl Completer for Helper { - type Candidate = rustyline::completion::Pair; - - fn complete( - &self, - line: &str, - pos: usize, - ctx: &rustyline::Context<'_>, - ) -> Result<(usize, Vec), ReadlineError> { - let result = self.helper.complete(line, pos, ctx); - - result.map(|(x, y)| (x, y.iter().map(|z| z.into()).collect())) - } -} -*/ - impl Hinter for Helper { fn hint(&self, line: &str, pos: usize, ctx: &rustyline::Context<'_>) -> Option { self.context.shell_manager.hint(line, pos, ctx) @@ -103,14 +86,18 @@ impl Highlighter for Helper { let shapes = { // We just constructed a token list that only contains a pipeline, so it can't fail color_fallible_syntax(&PipelineShape, &mut tokens, &expand_context).unwrap(); + tokens.with_tracer(|_, tracer| tracer.finish()); - tokens.shapes() + tokens.state().shapes() }; - trace!(target: "nu::shapes", - "SHAPES :: {:?}", - shapes.iter().map(|shape| shape.item).collect::>() - ); + trace!(target: "nu::color_syntax", "{:#?}", tokens.tracer()); + + if log_enabled!(target: "nu::color_syntax", log::Level::Trace) { + println!(""); + ptree::print_tree(&tokens.tracer().clone().print(Text::from(line))).unwrap(); + println!(""); + } for shape in shapes { let styled = paint_flat_shape(&shape, line); @@ -118,18 +105,6 @@ impl Highlighter for Helper { } Cow::Owned(out) - - // loop { - // match iter.next() { - // None => { - // return Cow::Owned(out); - // } - // Some(token) => { - // let styled = paint_pipeline_element(&token, line); - // out.push_str(&styled.to_string()); - // } - // } - // } } } } diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 4d6fa84a65..1a3e63ab4f 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -246,13 +246,18 @@ fn it_arg_works_with_many_inputs_to_external_command() { let (stdout, stderr) = nu_combined!( cwd: dirs.test(), h::pipeline( r#" - echo file1 file2 + echo hello world | split-row " " - | cat $it + | ^echo $it "# )); - assert_eq!("text and more text", stdout); + #[cfg(windows)] + assert_eq!("hello world", stdout); + + #[cfg(not(windows))] + assert_eq!("helloworld", stdout); + assert!(!stderr.contains("No such file or directory")); }) } From 16751b5dee653ce9e401b04b101cf28e2574f8cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 22 Oct 2019 19:29:39 -0500 Subject: [PATCH 323/342] color escaped external command. --- src/parser/hir/syntax_shape.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/parser/hir/syntax_shape.rs b/src/parser/hir/syntax_shape.rs index 8a21fd79e6..2169467e43 100644 --- a/src/parser/hir/syntax_shape.rs +++ b/src/parser/hir/syntax_shape.rs @@ -985,8 +985,8 @@ impl FallibleColorSyntax for CommandHeadShape { match atom.item { // If the head is an explicit external command (^cmd), color it as an external command - AtomicToken::ExternalCommand { command } => { - token_nodes.color_shape(FlatShape::ExternalCommand.spanned(command)); + AtomicToken::ExternalCommand { .. } => { + token_nodes.color_shape(FlatShape::ExternalCommand.spanned(atom.span)); Ok(CommandHeadKind::External) } From f1630da2ccbcf38110bab7ff9e9c10956b3c7d06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Tue, 22 Oct 2019 00:00:06 -0500 Subject: [PATCH 324/342] Suggest a column name in case one unknown column is supplied. --- README.md | 2 +- src/commands/count.rs | 2 +- src/commands/group_by.rs | 39 +++++++++++++++++++++++++++++++++++---- tests/commands_test.rs | 25 +++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index c391b59903..64ff0e8015 100644 --- a/README.md +++ b/README.md @@ -249,7 +249,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | command | description | | ------------- | ------------- | | add column-or-column-path value | Add a new column to the table | -| count | Show the total number of cells | +| count | Show the total number of rows | | edit column-or-column-path value | Edit an existing column to have a new value | | embed column | Creates a new table of one column with the given name, and places the current table inside of it | | first amount | Show only the first number of rows | diff --git a/src/commands/count.rs b/src/commands/count.rs index 6fe5a94633..5e44283737 100644 --- a/src/commands/count.rs +++ b/src/commands/count.rs @@ -20,7 +20,7 @@ impl WholeStreamCommand for Count { } fn usage(&self) -> &str { - "Show the total number of cells." + "Show the total number of rows." } fn run( diff --git a/src/commands/group_by.rs b/src/commands/group_by.rs index e08ebb2afb..7f5f496408 100644 --- a/src/commands/group_by.rs +++ b/src/commands/group_by.rs @@ -40,10 +40,41 @@ fn group_by( let values: Vec> = input.values.collect().await; let mut groups = indexmap::IndexMap::new(); - for row in values { - let key = row.get_data_by_key(&column_name.item).unwrap().as_string()?; - let mut group = groups.entry(key).or_insert(vec![]); - group.push(row); + for value in values { + let group_key = value.get_data_by_key(&column_name.item); + + if group_key.is_none() { + + let possibilities = value.data_descriptors(); + + let mut possible_matches: Vec<_> = possibilities + .iter() + .map(|x| (natural::distance::levenshtein_distance(x, &column_name.item), x)) + .collect(); + + possible_matches.sort(); + + let err = { + if possible_matches.len() > 0 { + ShellError::labeled_error( + "Unknown column", + format!("did you mean '{}'?", possible_matches[0].1), + &column_name.tag,) + } else { + ShellError::labeled_error( + "Unknown column", + "row does not contain this column", + &column_name.tag, + ) + } + }; + + yield Err(err) + } else { + let group_key = group_key.unwrap().as_string()?; + let mut group = groups.entry(group_key).or_insert(vec![]); + group.push(value); + } } let mut out = TaggedDictBuilder::new(name.clone()); diff --git a/tests/commands_test.rs b/tests/commands_test.rs index 7733942811..45e4bcb228 100644 --- a/tests/commands_test.rs +++ b/tests/commands_test.rs @@ -31,6 +31,31 @@ fn group_by() { }) } +#[test] +fn group_by_errors_if_unknown_column_name() { + Playground::setup("group_by_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.csv", + r#" + first_name,last_name,rusty_luck,type + Andrés,Robalino,1,A + Jonathan,Turner,1,B + Yehuda,Katz,1,A + "#, + )]); + + let actual = nu_error!( + cwd: dirs.test(), h::pipeline( + r#" + open los_tres_caballeros.csv + | group-by ttype + "# + )); + + assert!(actual.contains("Unknown column")); + }) +} + #[test] fn first_gets_first_rows_by_amount() { Playground::setup("first_test_1", |dirs, sandbox| { From c34ebfe73918e01b9bbb9f4e2fab1d1fa12c818d Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 23 Oct 2019 20:57:04 +1300 Subject: [PATCH 325/342] Bump version Bump version so we can tell a difference between what has been released and what's in master. --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 29205d9af5..49e4920593 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nu" -version = "0.4.0" +version = "0.4.1" authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] description = "A shell for the GitHub era" license = "MIT" From d160e834eb48b542c711caa95f298937afe76046 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Sat, 26 Oct 2019 05:43:31 +1300 Subject: [PATCH 326/342] rustyline git and add plus for filenames --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- src/parser/parse/parser.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 510cc4d8ba..9f8ebfe787 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1487,7 +1487,7 @@ dependencies = [ [[package]] name = "nu" -version = "0.4.0" +version = "0.4.1" dependencies = [ "ansi_term 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1540,7 +1540,7 @@ dependencies = [ "regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "roxmltree 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "rustyline 5.0.3 (git+https://github.com/kkawakam/rustyline.git)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)", "serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2078,7 +2078,7 @@ dependencies = [ [[package]] name = "rustyline" version = "5.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" +source = "git+https://github.com/kkawakam/rustyline.git#449c811998f630102bb2d9fb0b59b890d9eabac5" dependencies = [ "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3056,7 +3056,7 @@ dependencies = [ "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -"checksum rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4795e277e6e57dec9df62b515cd4991371daa80e8dc8d80d596e58722b89c417" +"checksum rustyline 5.0.3 (git+https://github.com/kkawakam/rustyline.git)" = "" "checksum ryu 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "19d2271fa48eaf61e53cc88b4ad9adcbafa2d512c531e7fadb6dc11a4d3656c5" "checksum safemem 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d2b08423011dae9a5ca23f07cf57dac3857f5c885d352b76f6d95f4aea9434d0" "checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" diff --git a/Cargo.toml b/Cargo.toml index 49e4920593..e81e830e1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ documentation = "https://book.nushell.sh" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rustyline = "5.0.3" +rustyline = { git = "https://github.com/kkawakam/rustyline.git" } chrono = { version = "0.4.9", features = ["serde"] } derive-new = "0.5.8" prettytable-rs = "0.8.0" diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index b5aefabc26..a548102db9 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -688,7 +688,7 @@ fn is_start_bare_char(c: char) -> bool { fn is_bare_char(c: char) -> bool { match c { - '+' => false, + '+' => true, _ if c.is_alphanumeric() => true, '\\' => true, '/' => true, From 72fd1b047f63286f494cee027f8be7f660f481e2 Mon Sep 17 00:00:00 2001 From: Paul Delafosse Date: Fri, 25 Oct 2019 19:52:40 +0200 Subject: [PATCH 327/342] Create docs for from-csv command Partial fix of issue nushell#711 --- docs/commands/from-csv.md | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/commands/from-csv.md diff --git a/docs/commands/from-csv.md b/docs/commands/from-csv.md new file mode 100644 index 0000000000..86d309d86b --- /dev/null +++ b/docs/commands/from-csv.md @@ -0,0 +1,47 @@ +# from-csv + +Converts csv data into table. Use this when nushell cannot dertermine the input file extension. + +## Example +Let's say we have the following file : +```shell +> cat pets.txt +animal, name, age +cat, Tom, 7 +dog, Alfred, 10 +chameleon, Linda, 1 +``` + +`pets.txt` is actually a .csv file but it has the .txt extension, `open` is not able to convert it into a table : + +```shell +> open pets.txt +animal, name, age +cat, Tom, 7 +dog, Alfred, 10 +chameleon, Linda, 1 +``` + +To get a table from `pets.txt` we need to use the `from-csv` command : + +```shell +> open pets.txt | from-csv +━━━┯━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━ + # │ animal │ name │ age +───┼───────────┼─────────┼────── + 0 │ cat │ Tom │ 7 + 1 │ dog │ Alfred │ 10 + 2 │ chameleon │ Linda │ 1 +━━━┷━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━ +``` + +To ignore the csv headers use `--headerless` : +```shell +━━━┯━━━━━━━━━━━┯━━━━━━━━━┯━━━━━━━━━ + # │ Column1 │ Column2 │ Column3 +───┼───────────┼─────────┼───────── + 0 │ dog │ Alfred │ 10 + 1 │ chameleon │ Linda │ 1 +━━━┷━━━━━━━━━━━┷━━━━━━━━━┷━━━━━━━━━ +``` + From 07ceec3e0b4721dc04711277ba86d7085020ccfb Mon Sep 17 00:00:00 2001 From: Paul Delafosse Date: Fri, 25 Oct 2019 20:38:55 +0200 Subject: [PATCH 328/342] Create docs for from-toml command Partial fix of issue nushell#711 --- docs/commands/from-toml.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 docs/commands/from-toml.md diff --git a/docs/commands/from-toml.md b/docs/commands/from-toml.md new file mode 100644 index 0000000000..d3f3364c78 --- /dev/null +++ b/docs/commands/from-toml.md @@ -0,0 +1,23 @@ +# from-toml +Converts toml data into table. Use this when nushell cannot dertermine the input file extension. + +## Example +Let's say we have the following Rust .lock file : +```shell +> open Cargo.lock +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. [[package]] name = "adler32" version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" +... +``` + +The "Cargo.lock" file is actually a .toml file, but the file extension isn't .toml. That's okay, we can use the `from-toml` command : + + +```shell +> open Cargo.lock | from-toml +━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━ + metadata │ package +────────────────┼─────────────────── + [table: 1 row] │ [table: 154 rows] +━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━ +``` \ No newline at end of file From 2706ae076d8782800849908cdaf6d2e36f95e501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Fri, 25 Oct 2019 18:31:25 -0500 Subject: [PATCH 329/342] Move out tags when parsing and building tree nodes. --- src/parser/parse/parser.rs | 173 +++++++++++++++---------- src/parser/parse/token_tree.rs | 2 +- src/parser/parse/token_tree_builder.rs | 14 +- src/parser/parse/tokens.rs | 8 +- 4 files changed, 114 insertions(+), 83 deletions(-) diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index a548102db9..f7fce7c814 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -393,7 +393,7 @@ pub fn leaf(input: NomSpan) -> IResult { } #[tracable_parser] -pub fn token_list(input: NomSpan) -> IResult>> { +pub fn token_list(input: NomSpan) -> IResult>> { let start = input.offset; let (input, first) = node(input)?; @@ -403,7 +403,7 @@ pub fn token_list(input: NomSpan) -> IResult>> { Ok(( input, - make_token_list(first, list, None).tagged((start, end, None)), + make_token_list(first, list, None).spanned(Span::new(start, end)), )) } @@ -511,14 +511,14 @@ pub fn delimited_brace(input: NomSpan) -> IResult { } #[tracable_parser] -pub fn raw_call(input: NomSpan) -> IResult> { +pub fn raw_call(input: NomSpan) -> IResult> { let left = input.offset; let (input, items) = token_list(input)?; let right = input.offset; Ok(( input, - TokenTreeBuilder::tagged_call(items.item, (left, right, input.extra)), + TokenTreeBuilder::spanned_call(items.item, Span::new(left, right)), )) } @@ -598,7 +598,7 @@ pub fn nodes(input: NomSpan) -> IResult { Ok(( input, - TokenTreeBuilder::tagged_token_list(tokens.item, tokens.tag), + TokenTreeBuilder::spanned_token_list(tokens.item, tokens.span), )) } @@ -800,30 +800,30 @@ mod tests { ">" -> b::token_list(vec![b::op(">")]) } - // assert_leaf! { - // parsers [ operator ] - // ">=" -> 0..2 { Operator(Operator::GreaterThanOrEqual) } - // } + equal_tokens! { + + ">=" -> b::token_list(vec![b::op(">=")]) + } - // assert_leaf! { - // parsers [ operator ] - // "<" -> 0..1 { Operator(Operator::LessThan) } - // } + equal_tokens! { + + "<" -> b::token_list(vec![b::op("<")]) + } - // assert_leaf! { - // parsers [ operator ] - // "<=" -> 0..2 { Operator(Operator::LessThanOrEqual) } - // } + equal_tokens! { + + "<=" -> b::token_list(vec![b::op("<=")]) + } - // assert_leaf! { - // parsers [ operator ] - // "==" -> 0..2 { Operator(Operator::Equal) } - // } + equal_tokens! { + + "==" -> b::token_list(vec![b::op("==")]) + } - // assert_leaf! { - // parsers [ operator ] - // "!=" -> 0..2 { Operator(Operator::NotEqual) } - // } + equal_tokens! { + + "!=" -> b::token_list(vec![b::op("!=")]) + } } #[test] @@ -848,12 +848,14 @@ mod tests { } #[test] - fn test_simple_path() { + fn test_unit_sizes() { equal_tokens! { "450MB" -> b::token_list(vec![b::bare("450MB")]) } - + } + #[test] + fn test_simple_path() { equal_tokens! { "chrome.exe" -> b::token_list(vec![b::bare("chrome"), b::op(Operator::Dot), b::bare("exe")]) @@ -877,23 +879,23 @@ mod tests { #[test] fn test_flag() { - // assert_leaf! { - // parsers [ flag ] - // "--hello" -> 0..7 { Flag(Tagged::from_item(FlagKind::Longhand, span(2, 7))) } - // } + equal_tokens! { + + "--amigos" -> b::token_list(vec![b::flag("arepas")]) + } - // assert_leaf! { - // parsers [ flag ] - // "--hello-world" -> 0..13 { Flag(Tagged::from_item(FlagKind::Longhand, span(2, 13))) } - // } + equal_tokens! { + + "--all-amigos" -> b::token_list(vec![b::flag("all-amigos")]) + } } #[test] - fn test_shorthand() { - // assert_leaf! { - // parsers [ shorthand ] - // "-alt" -> 0..4 { Flag(Tagged::from_item(FlagKind::Shorthand, span(1, 4))) } - // } + fn test_shorthand_flag() { + equal_tokens! { + + "-katz" -> b::token_list(vec![b::shorthand("katz")]) + } } #[test] @@ -939,13 +941,13 @@ mod tests { equal_tokens! { - "( abc def )" -> b::token_list(vec![b::parens(vec![b::ws(" "), b::bare("abc"), b::ws(" "), b::bare("def"), b::ws(" ")])]) + "( abc def )" -> b::token_list(vec![b::parens(vec![b::ws(" "), b::bare("abc"), b::sp(), b::bare("def"), b::sp()])]) } equal_tokens! { "( abc def 123 456GB )" -> b::token_list(vec![b::parens(vec![ - b::ws(" "), b::bare("abc"), b::ws(" "), b::bare("def"), b::ws(" "), b::int(123), b::ws(" "), b::bare("456GB"), b::ws(" ") + b::ws(" "), b::bare("abc"), b::sp(), b::bare("def"), b::sp(), b::int(123), b::sp(), b::bare("456GB"), b::sp() ])]) } } @@ -964,13 +966,13 @@ mod tests { equal_tokens! { - "[ abc def ]" -> b::token_list(vec![b::square(vec![b::ws(" "), b::bare("abc"), b::ws(" "), b::bare("def"), b::ws(" ")])]) + "[ abc def ]" -> b::token_list(vec![b::square(vec![b::ws(" "), b::bare("abc"), b::sp(), b::bare("def"), b::sp()])]) } equal_tokens! { "[ abc def 123 456GB ]" -> b::token_list(vec![b::square(vec![ - b::ws(" "), b::bare("abc"), b::ws(" "), b::bare("def"), b::ws(" "), b::int(123), b::ws(" "), b::bare("456GB"), b::ws(" ") + b::ws(" "), b::bare("abc"), b::sp(), b::bare("def"), b::sp(), b::int(123), b::sp(), b::bare("456GB"), b::sp() ])]) } } @@ -984,6 +986,11 @@ mod tests { "$it.print" -> b::token_list(vec![b::var("it"), b::op("."), b::bare("print")]) } + equal_tokens! { + + "$it.0" -> b::token_list(vec![b::var("it"), b::op("."), b::int(0)]) + } + equal_tokens! { "$head.part1.part2" -> b::token_list(vec![b::var("head"), b::op("."), b::bare("part1"), b::op("."), b::bare("part2")]) @@ -1024,6 +1031,19 @@ mod tests { b::op("."), b::string("world")] ) } + + equal_tokens! { + + r#"$it."are PAS".0"# -> b::token_list( + vec![ + b::var("it"), + b::op("."), + b::string("are PAS"), + b::op("."), + b::int(0), + ] + ) + } } #[test] @@ -1062,6 +1082,19 @@ mod tests { "config --set tabs 2" -> b::token_list(vec![b::bare("config"), b::sp(), b::flag("set"), b::sp(), b::bare("tabs"), b::sp(), b::int(2)]) } + + equal_tokens! { + + "inc --patch package.version" -> b::token_list( + vec![ + b::bare("inc"), + b::sp(), + b::flag("patch"), + b::sp(), + b::bare("package"), b::op("."), b::bare("version") + ] + ) + } } #[test] @@ -1114,41 +1147,39 @@ mod tests { fn test_patterns() { equal_tokens! { - "cp ../formats/*" -> b::pipeline(vec![vec![b::bare("cp"), b::ws(" "), b::op("."), b::op("."), b::pattern("/formats/*")]]) + "cp ../formats/*" -> b::pipeline(vec![vec![b::bare("cp"), b::sp(), b::op("."), b::op("."), b::pattern("/formats/*")]]) } equal_tokens! { - "cp * /dev/null" -> b::pipeline(vec![vec![b::bare("cp"), b::ws(" "), b::pattern("*"), b::ws(" "), b::bare("/dev/null")]]) + "cp * /dev/null" -> b::pipeline(vec![vec![b::bare("cp"), b::sp(), b::pattern("*"), b::sp(), b::bare("/dev/null")]]) } } - // #[test] - // fn test_pseudo_paths() { - // let _ = pretty_env_logger::try_init(); + #[test] + fn test_pseudo_paths() { + let _ = pretty_env_logger::try_init(); - // equal_tokens!( - // r#"sys | where cpu."max ghz" > 1"# -> - // b::pipeline(vec![ - // (None, b::call(b::bare("sys"), vec![]), Some(" ")), - // ( - // Some(" "), - // b::call( - // b::bare("where"), - // vec![ - // b::sp(), - // b::path(b::bare("cpu"), vec![b::string("max ghz")]), - // b::sp(), - // b::op(">"), - // b::sp(), - // b::int(1) - // ] - // ), - // None - // ) - // ]) - // ); - // } + equal_tokens!( + + r#"sys | where cpu."max ghz" > 1"# -> b::pipeline(vec![ + vec![ + b::bare("sys"), b::sp() + ], + vec![ + b::sp(), + b::bare("where"), + b::sp(), + b::bare("cpu"), + b::op("."), + b::string("max ghz"), + b::sp(), + b::op(">"), + b::sp(), + b::int(1) + ]]) + ); + } // #[test] // fn test_smoke_pipeline() { diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index c3c1df652a..cb335e925e 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -274,7 +274,7 @@ impl TokenNode { item: RawToken::Bare, span, }) => *span, - other => panic!("Expected var, found {:?}", other), + other => panic!("Expected bare, found {:?}", other), } } } diff --git a/src/parser/parse/token_tree_builder.rs b/src/parser/parse/token_tree_builder.rs index 891e6b9e16..7146a3c201 100644 --- a/src/parser/parse/token_tree_builder.rs +++ b/src/parser/parse/token_tree_builder.rs @@ -18,7 +18,7 @@ pub struct TokenTreeBuilder { } pub type CurriedToken = Box TokenNode + 'static>; -pub type CurriedCall = Box Tagged + 'static>; +pub type CurriedCall = Box Spanned + 'static>; impl TokenTreeBuilder { pub fn build(block: impl FnOnce(&mut Self) -> TokenNode) -> (TokenNode, String) { @@ -89,12 +89,12 @@ impl TokenTreeBuilder { let tokens = input.into_iter().map(|i| i(b)).collect(); let end = b.pos; - TokenTreeBuilder::tagged_token_list(tokens, (start, end, None)) + TokenTreeBuilder::spanned_token_list(tokens, Span::new(start, end)) }) } - pub fn tagged_token_list(input: Vec, tag: impl Into) -> TokenNode { - TokenNode::Nodes(input.spanned(tag.into().span)) + pub fn spanned_token_list(input: Vec, span: impl Into) -> TokenNode { + TokenNode::Nodes(input.spanned(span.into())) } pub fn op(input: impl Into) -> CurriedToken { @@ -287,11 +287,11 @@ impl TokenTreeBuilder { let end = b.pos; - TokenTreeBuilder::tagged_call(nodes, (start, end, None)) + TokenTreeBuilder::spanned_call(nodes, Span::new(start, end)) }) } - pub fn tagged_call(input: Vec, tag: impl Into) -> Tagged { + pub fn spanned_call(input: Vec, span: impl Into) -> Spanned { if input.len() == 0 { panic!("BUG: spanned call (TODO)") } @@ -301,7 +301,7 @@ impl TokenTreeBuilder { let head = input.next().unwrap(); let tail = input.collect(); - CallNode::new(Box::new(head), tail).tagged(tag.into()) + CallNode::new(Box::new(head), tail).spanned(span.into()) } fn consume_delimiter( diff --git a/src/parser/parse/tokens.rs b/src/parser/parse/tokens.rs index 94955d84d9..29061ed7a2 100644 --- a/src/parser/parse/tokens.rs +++ b/src/parser/parse/tokens.rs @@ -19,14 +19,14 @@ pub enum RawToken { impl RawToken { pub fn type_name(&self) -> &'static str { match self { - RawToken::Number(_) => "Number", + RawToken::Number(_) => "number", RawToken::Operator(..) => "operator", - RawToken::String(_) => "String", + RawToken::String(_) => "string", RawToken::Variable(_) => "variable", RawToken::ExternalCommand(_) => "external command", RawToken::ExternalWord => "external word", RawToken::GlobPattern => "glob pattern", - RawToken::Bare => "String", + RawToken::Bare => "string", } } } @@ -72,7 +72,7 @@ impl Token { pub fn extract_number(&self) -> Option> { match self.item { - RawToken::Number(number) => Some((number).spanned(self.span)), + RawToken::Number(number) => Some(number.spanned(self.span)), _ => None, } } From 540cc4016ef403f7d1265bb79e2075e451559c3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20N=2E=20Robalino?= Date: Sat, 26 Oct 2019 20:01:58 -0500 Subject: [PATCH 330/342] Expand tilde in patterns. --- src/errors.rs | 8 +- src/evaluate/evaluator.rs | 2 +- src/parser/hir.rs | 12 +- src/parser/hir/baseline_parse/tests.rs | 32 +--- src/parser/hir/syntax_shape.rs | 14 +- .../hir/syntax_shape/expression/atom.rs | 5 +- .../hir/syntax_shape/expression/pattern.rs | 47 ++---- .../hir/syntax_shape/expression/string.rs | 8 +- .../syntax_shape/expression/variable_path.rs | 2 +- src/parser/hir/tokens_iterator.rs | 2 +- src/parser/parse/parser.rs | 4 +- src/parser/parse/token_tree.rs | 148 +++++++++++------- 12 files changed, 135 insertions(+), 149 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 11628dde4b..dfad5692a1 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -367,6 +367,10 @@ impl ShellError { // pub fn string(title: impl Into) -> ShellError { // ProximateShellError::String(StringError::new(title.into(), String::new())).start() // } + // + // pub(crate) fn unreachable(title: impl Into) -> ShellError { + // ShellError::untagged_runtime_error(&format!("BUG: Unreachable: {}", title.into())) + // } pub(crate) fn unimplemented(title: impl Into) -> ShellError { ShellError::untagged_runtime_error(&format!("Unimplemented: {}", title.into())) @@ -375,10 +379,6 @@ impl ShellError { pub(crate) fn unexpected(title: impl Into) -> ShellError { ShellError::untagged_runtime_error(&format!("Unexpected: {}", title.into())) } - - pub(crate) fn unreachable(title: impl Into) -> ShellError { - ShellError::untagged_runtime_error(&format!("BUG: Unreachable: {}", title.into())) - } } #[derive(Debug, Eq, PartialEq, Clone, Ord, PartialOrd, Serialize, Deserialize)] diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 75eb2f4667..f1caa21f1d 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -148,7 +148,7 @@ fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged int.into(), hir::Literal::Size(int, unit) => unit.compute(int), hir::Literal::String(tag) => Value::string(tag.slice(source)), - hir::Literal::GlobPattern => Value::pattern(literal.tag().slice(source)), + hir::Literal::GlobPattern(pattern) => Value::pattern(pattern), hir::Literal::Bare => Value::string(literal.tag().slice(source)), }; diff --git a/src/parser/hir.rs b/src/parser/hir.rs index ac6423943d..7108b0f7f9 100644 --- a/src/parser/hir.rs +++ b/src/parser/hir.rs @@ -227,8 +227,8 @@ impl Expression { RawExpression::Literal(Literal::Bare).spanned(span) } - pub(crate) fn pattern(span: impl Into) -> Expression { - RawExpression::Literal(Literal::GlobPattern).spanned(span.into()) + pub(crate) fn pattern(inner: impl Into, outer: impl Into) -> Expression { + RawExpression::Literal(Literal::GlobPattern(inner.into())).spanned(outer.into()) } pub(crate) fn variable(inner: impl Into, outer: impl Into) -> Expression { @@ -297,7 +297,7 @@ pub enum Literal { Number(Number), Size(Number, Unit), String(Span), - GlobPattern, + GlobPattern(String), Bare, } @@ -315,7 +315,7 @@ impl std::fmt::Display for Tagged<&Literal> { Literal::Number(number) => write!(f, "{}", number), Literal::Size(number, unit) => write!(f, "{}{}", number, unit.as_str()), Literal::String(_) => write!(f, "String{{ {}..{} }}", span.start(), span.end()), - Literal::GlobPattern => write!(f, "Glob{{ {}..{} }}", span.start(), span.end()), + Literal::GlobPattern(_) => write!(f, "Glob{{ {}..{} }}", span.start(), span.end()), Literal::Bare => write!(f, "Bare{{ {}..{} }}", span.start(), span.end()), } } @@ -327,7 +327,7 @@ impl ToDebug for Spanned<&Literal> { Literal::Number(number) => write!(f, "{:?}", number), Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), Literal::String(tag) => write!(f, "{}", tag.slice(source)), - Literal::GlobPattern => write!(f, "{}", self.span.slice(source)), + Literal::GlobPattern(_) => write!(f, "{}", self.span.slice(source)), Literal::Bare => write!(f, "{}", self.span.slice(source)), } } @@ -340,7 +340,7 @@ impl Literal { Literal::Size(..) => "size", Literal::String(..) => "string", Literal::Bare => "string", - Literal::GlobPattern => "pattern", + Literal::GlobPattern(_) => "pattern", } } } diff --git a/src/parser/hir/baseline_parse/tests.rs b/src/parser/hir/baseline_parse/tests.rs index d3b9248496..ddd4af4930 100644 --- a/src/parser/hir/baseline_parse/tests.rs +++ b/src/parser/hir/baseline_parse/tests.rs @@ -6,7 +6,7 @@ use crate::parser::hir::syntax_shape::*; use crate::parser::hir::TokensIterator; use crate::parser::parse::token_tree_builder::{CurriedToken, TokenTreeBuilder as b}; use crate::parser::TokenNode; -use crate::{Span, SpannedItem, Tag, Tagged, Text}; +use crate::{Span, SpannedItem, Tag, Text}; use pretty_assertions::assert_eq; use std::fmt::Debug; @@ -63,7 +63,7 @@ fn test_parse_command() { vec![b::bare("ls"), b::sp(), b::pattern("*.txt")], |tokens| { let bare = tokens[0].expect_bare(); - let pat = tokens[2].span(); + let pattern = tokens[2].expect_pattern(); ClassifiedCommand::Internal(InternalCommand::new( "ls".to_string(), @@ -73,7 +73,7 @@ fn test_parse_command() { }, hir::Call { head: Box::new(hir::RawExpression::Command(bare).spanned(bare)), - positional: Some(vec![hir::Expression::pattern(pat)]), + positional: Some(vec![hir::Expression::pattern("*.txt", pattern)]), named: None, }, )) @@ -84,41 +84,19 @@ fn test_parse_command() { // ) }, ); - - parse_tokens( - VariablePathShape, - vec![ - b::var("cpu"), - b::op("."), - b::bare("amount"), - b::op("."), - b::string("max ghz"), - ], - |tokens| { - let (outer_var, inner_var) = tokens[0].expect_var(); - let amount = tokens[2].expect_bare(); - let (outer_max_ghz, _) = tokens[4].expect_string(); - - hir::Expression::path( - hir::Expression::variable(inner_var, outer_var), - vec!["amount".spanned(amount), "max ghz".spanned(outer_max_ghz)], - outer_var.until(outer_max_ghz), - ) - }, - ); } fn parse_tokens( shape: impl ExpandSyntax, tokens: Vec, - expected: impl FnOnce(Tagged<&[TokenNode]>) -> T, + expected: impl FnOnce(&[TokenNode]) -> T, ) { let tokens = b::token_list(tokens); let (tokens, source) = b::build(tokens); ExpandContext::with_empty(&Text::from(source), |context| { let tokens = tokens.expect_list(); - let mut iterator = TokensIterator::all(tokens.item, *context.span()); + let mut iterator = TokensIterator::all(tokens, *context.span()); let expr = expand_syntax(&shape, &mut iterator, &context); diff --git a/src/parser/hir/syntax_shape.rs b/src/parser/hir/syntax_shape.rs index 2169467e43..a38a77500b 100644 --- a/src/parser/hir/syntax_shape.rs +++ b/src/parser/hir/syntax_shape.rs @@ -497,18 +497,18 @@ pub(crate) fn expand_expr<'a, 'b, T: ExpandExpression>( token_nodes: &'b mut TokensIterator<'a>, context: &ExpandContext, ) -> Result { - trace!(target: "nu::expand_syntax", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes.state(), context.source)); + trace!(target: "nu::expand_expression", "before {} :: {:?}", std::any::type_name::(), debug_tokens(token_nodes.state(), context.source)); - let result = shape.expand_syntax(token_nodes, context); + let result = shape.expand_expr(token_nodes, context); match result { Err(err) => { - trace!(target: "nu::expand_syntax", "error :: {} :: {:?}", err, debug_tokens(token_nodes.state(), context.source)); + trace!(target: "nu::expand_expression", "error :: {} :: {:?}", err, debug_tokens(token_nodes.state(), context.source)); Err(err) } Ok(result) => { - trace!(target: "nu::expand_syntax", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes.state(), context.source)); + trace!(target: "nu::expand_expression", "ok :: {:?} :: {:?}", result, debug_tokens(token_nodes.state(), context.source)); Ok(result) } } @@ -719,11 +719,7 @@ impl TestSyntax for BareShape { let peeked = token_nodes.peek_any(); match peeked.node { - Some(TokenNode::Token(token)) => match token.item { - RawToken::Bare => Some(peeked), - _ => None, - }, - + Some(token) if token.is_bare() => Some(peeked), _ => None, } } diff --git a/src/parser/hir/syntax_shape/expression/atom.rs b/src/parser/hir/syntax_shape/expression/atom.rs index bb1b8065ec..888d9430e6 100644 --- a/src/parser/hir/syntax_shape/expression/atom.rs +++ b/src/parser/hir/syntax_shape/expression/atom.rs @@ -142,7 +142,10 @@ impl<'tokens> SpannedAtomicToken<'tokens> { Expression::external_command(*command, self.span) } AtomicToken::ExternalWord { text } => Expression::string(*text, self.span), - AtomicToken::GlobPattern { pattern } => Expression::pattern(*pattern), + AtomicToken::GlobPattern { pattern } => Expression::pattern( + expand_file_path(pattern.slice(context.source), context).to_string_lossy(), + self.span, + ), AtomicToken::Word { text } => Expression::string(*text, *text), AtomicToken::SquareDelimited { .. } => unimplemented!("into_hir"), AtomicToken::ParenDelimited { .. } => unimplemented!("into_hir"), diff --git a/src/parser/hir/syntax_shape/expression/pattern.rs b/src/parser/hir/syntax_shape/expression/pattern.rs index eab0b6e5bb..ed3bd610cd 100644 --- a/src/parser/hir/syntax_shape/expression/pattern.rs +++ b/src/parser/hir/syntax_shape/expression/pattern.rs @@ -1,7 +1,6 @@ use crate::parser::hir::syntax_shape::{ - expand_atom, expand_bare, expand_syntax, expression::expand_file_path, parse_single_node, - AtomicToken, ExpandContext, ExpandExpression, ExpandSyntax, ExpansionRule, FallibleColorSyntax, - FlatShape, + expand_atom, expand_bare, expression::expand_file_path, AtomicToken, ExpandContext, + ExpandExpression, ExpandSyntax, ExpansionRule, FallibleColorSyntax, FlatShape, }; use crate::parser::{hir, hir::TokensIterator, Operator, RawToken, TokenNode}; use crate::prelude::*; @@ -72,43 +71,17 @@ impl ExpandExpression for PatternShape { token_nodes: &mut TokensIterator<'_>, context: &ExpandContext, ) -> Result { - let pattern = expand_syntax(&BarePatternShape, token_nodes, context); + let atom = expand_atom(token_nodes, "pattern", context, ExpansionRule::new())?; - match pattern { - Ok(tag) => { - return Ok(hir::Expression::pattern(tag)); + match atom.item { + AtomicToken::Word { text: body } + | AtomicToken::String { body } + | AtomicToken::GlobPattern { pattern: body } => { + let path = expand_file_path(body.slice(context.source), context); + return Ok(hir::Expression::pattern(path.to_string_lossy(), atom.span)); } - Err(_) => {} + _ => return atom.into_hir(context, "pattern"), } - - parse_single_node(token_nodes, "Pattern", |token, token_tag, _| { - Ok(match token { - RawToken::GlobPattern => { - return Err(ShellError::unreachable( - "glob pattern after glob already returned", - )) - } - RawToken::Operator(..) => { - return Err(ShellError::unreachable("dot after glob already returned")) - } - RawToken::Bare => { - return Err(ShellError::unreachable("bare after glob already returned")) - } - - RawToken::Variable(tag) if tag.slice(context.source) == "it" => { - hir::Expression::it_variable(tag, token_tag) - } - RawToken::Variable(tag) => hir::Expression::variable(tag, token_tag), - RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token_tag), - RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token_tag)), - RawToken::Number(_) => hir::Expression::bare(token_tag), - - RawToken::String(tag) => hir::Expression::file_path( - expand_file_path(tag.slice(context.source), context), - token_tag, - ), - }) - }) } } diff --git a/src/parser/hir/syntax_shape/expression/string.rs b/src/parser/hir/syntax_shape/expression/string.rs index 116ed8fd0d..46015376e8 100644 --- a/src/parser/hir/syntax_shape/expression/string.rs +++ b/src/parser/hir/syntax_shape/expression/string.rs @@ -3,7 +3,7 @@ use crate::parser::hir::syntax_shape::{ ExpansionRule, FallibleColorSyntax, FlatShape, TestSyntax, }; use crate::parser::hir::tokens_iterator::Peeked; -use crate::parser::{hir, hir::TokensIterator, RawToken, TokenNode}; +use crate::parser::{hir, hir::TokensIterator, RawToken}; use crate::prelude::*; #[derive(Debug, Copy, Clone)] @@ -118,11 +118,7 @@ impl TestSyntax for StringShape { let peeked = token_nodes.peek_any(); match peeked.node { - Some(TokenNode::Token(token)) => match token.item { - RawToken::String(_) => Some(peeked), - _ => None, - }, - + Some(token) if token.is_string() => Some(peeked), _ => None, } } diff --git a/src/parser/hir/syntax_shape/expression/variable_path.rs b/src/parser/hir/syntax_shape/expression/variable_path.rs index e983630348..5ed615a9e8 100644 --- a/src/parser/hir/syntax_shape/expression/variable_path.rs +++ b/src/parser/hir/syntax_shape/expression/variable_path.rs @@ -821,7 +821,7 @@ impl ExpandSyntax for MemberShape { if let Some(peeked) = string { let node = peeked.not_eof("column")?.commit(); - let (outer, inner) = node.expect_string(); + let (outer, inner) = node.as_string().unwrap(); return Ok(Member::String(outer, inner)); } diff --git a/src/parser/hir/tokens_iterator.rs b/src/parser/hir/tokens_iterator.rs index b3069247c9..8e2f4a8f88 100644 --- a/src/parser/hir/tokens_iterator.rs +++ b/src/parser/hir/tokens_iterator.rs @@ -566,7 +566,7 @@ impl<'content> TokensIterator<'content> { impl<'content> Iterator for TokensIterator<'content> { type Item = &'content TokenNode; - fn next(&mut self) -> Option<&'content TokenNode> { + fn next(&mut self) -> Option { next(self, self.state.skip_ws) } } diff --git a/src/parser/parse/parser.rs b/src/parser/parse/parser.rs index f7fce7c814..0dd1bc8566 100644 --- a/src/parser/parse/parser.rs +++ b/src/parser/parse/parser.rs @@ -666,7 +666,7 @@ fn is_glob_specific_char(c: char) -> bool { } fn is_start_glob_char(c: char) -> bool { - is_start_bare_char(c) || is_glob_specific_char(c) + is_start_bare_char(c) || is_glob_specific_char(c) || c == '.' } fn is_glob_char(c: char) -> bool { @@ -1147,7 +1147,7 @@ mod tests { fn test_patterns() { equal_tokens! { - "cp ../formats/*" -> b::pipeline(vec![vec![b::bare("cp"), b::sp(), b::op("."), b::op("."), b::pattern("/formats/*")]]) + "cp ../formats/*" -> b::pipeline(vec![vec![b::bare("cp"), b::sp(), b::pattern("../formats/*")]]) } equal_tokens! { diff --git a/src/parser/parse/token_tree.rs b/src/parser/parse/token_tree.rs index cb335e925e..0d00dcff0d 100644 --- a/src/parser/parse/token_tree.rs +++ b/src/parser/parse/token_tree.rs @@ -155,6 +155,26 @@ impl TokenNode { } } + pub fn is_string(&self) -> bool { + match self { + TokenNode::Token(Spanned { + item: RawToken::String(_), + .. + }) => true, + _ => false, + } + } + + pub fn as_string(&self) -> Option<(Span, Span)> { + match self { + TokenNode::Token(Spanned { + item: RawToken::String(inner_span), + span: outer_span, + }) => Some((*outer_span, *inner_span)), + _ => None, + } + } + pub fn is_pattern(&self) -> bool { match self { TokenNode::Token(Spanned { @@ -200,16 +220,6 @@ impl TokenNode { } } - pub fn expect_external(&self) -> Span { - match self { - TokenNode::Token(Spanned { - item: RawToken::ExternalCommand(span), - .. - }) => *span, - _ => panic!("Only call expect_external if you checked is_external first"), - } - } - pub(crate) fn as_flag(&self, value: &str, source: &Text) -> Option> { match self { TokenNode::Flag( @@ -224,7 +234,7 @@ impl TokenNode { pub fn as_pipeline(&self) -> Result { match self { TokenNode::Pipeline(Spanned { item, .. }) => Ok(item.clone()), - _ => Err(ShellError::unimplemented("unimplemented")), + _ => Err(ShellError::type_error("pipeline", self.tagged_type_name())), } } @@ -234,49 +244,6 @@ impl TokenNode { _ => false, } } - - pub fn expect_string(&self) -> (Span, Span) { - match self { - TokenNode::Token(Spanned { - item: RawToken::String(inner_span), - span: outer_span, - }) => (*outer_span, *inner_span), - other => panic!("Expected string, found {:?}", other), - } - } -} - -#[cfg(test)] -impl TokenNode { - pub fn expect_list(&self) -> Tagged<&[TokenNode]> { - match self { - TokenNode::Nodes(Spanned { item, span }) => (&item[..]).tagged(Tag { - span: *span, - anchor: None, - }), - other => panic!("Expected list, found {:?}", other), - } - } - - pub fn expect_var(&self) -> (Span, Span) { - match self { - TokenNode::Token(Spanned { - item: RawToken::Variable(inner_span), - span: outer_span, - }) => (*outer_span, *inner_span), - other => panic!("Expected var, found {:?}", other), - } - } - - pub fn expect_bare(&self) -> Span { - match self { - TokenNode::Token(Spanned { - item: RawToken::Bare, - span, - }) => *span, - other => panic!("Expected bare, found {:?}", other), - } - } } #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] @@ -328,3 +295,76 @@ pub struct PathNode { head: Box, tail: Vec, } + +#[cfg(test)] +impl TokenNode { + pub fn expect_external(&self) -> Span { + match self { + TokenNode::Token(Spanned { + item: RawToken::ExternalCommand(span), + .. + }) => *span, + other => panic!( + "Only call expect_external if you checked is_external first, found {:?}", + other + ), + } + } + + pub fn expect_string(&self) -> (Span, Span) { + match self { + TokenNode::Token(Spanned { + item: RawToken::String(inner_span), + span: outer_span, + }) => (*outer_span, *inner_span), + other => panic!("Expected string, found {:?}", other), + } + } + + pub fn expect_list(&self) -> &[TokenNode] { + match self { + TokenNode::Nodes(token_nodes) => &token_nodes[..], + other => panic!("Expected list, found {:?}", other), + } + } + + pub fn expect_pattern(&self) -> Span { + match self { + TokenNode::Token(Spanned { + item: RawToken::GlobPattern, + span: outer_span, + }) => *outer_span, + other => panic!("Expected pattern, found {:?}", other), + } + } + + pub fn expect_var(&self) -> (Span, Span) { + match self { + TokenNode::Token(Spanned { + item: RawToken::Variable(inner_span), + span: outer_span, + }) => (*outer_span, *inner_span), + other => panic!("Expected var, found {:?}", other), + } + } + + pub fn expect_dot(&self) -> Span { + match self { + TokenNode::Token(Spanned { + item: RawToken::Operator(Operator::Dot), + span, + }) => *span, + other => panic!("Expected dot, found {:?}", other), + } + } + + pub fn expect_bare(&self) -> Span { + match self { + TokenNode::Token(Spanned { + item: RawToken::Bare, + span, + }) => *span, + other => panic!("Expected bare, found {:?}", other), + } + } +} From aed386b3cd515dc3b57abf62161606567bb36788 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 28 Oct 2019 05:58:39 +1300 Subject: [PATCH 331/342] Always save history, add history command --- src/cli.rs | 3 +++ src/commands.rs | 2 ++ src/commands/history.rs | 49 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/commands/history.rs diff --git a/src/cli.rs b/src/cli.rs index e88ee054fe..d52a55e267 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -306,6 +306,7 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(SkipWhile), per_item_command(Enter), per_item_command(Help), + per_item_command(History), whole_stream_command(Exit), whole_stream_command(Autoview), whole_stream_command(Pivot), @@ -413,6 +414,7 @@ pub async fn cli() -> Result<(), Box> { match process_line(readline, &mut context).await { LineResult::Success(line) => { rl.add_history_entry(line.clone()); + let _ = rl.save_history(&History::path()); } LineResult::CtrlC => { @@ -440,6 +442,7 @@ pub async fn cli() -> Result<(), Box> { LineResult::Error(line, err) => { rl.add_history_entry(line.clone()); + let _ = rl.save_history(&History::path()); context.with_host(|host| { print_err(err, host, &Text::from(line)); diff --git a/src/commands.rs b/src/commands.rs index 7f0fa0a25a..c75ca81192 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -32,6 +32,7 @@ pub(crate) mod from_yaml; pub(crate) mod get; pub(crate) mod group_by; pub(crate) mod help; +pub(crate) mod history; pub(crate) mod last; pub(crate) mod lines; pub(crate) mod ls; @@ -106,6 +107,7 @@ pub(crate) use from_yaml::FromYML; pub(crate) use get::Get; pub(crate) use group_by::GroupBy; pub(crate) use help::Help; +pub(crate) use history::History; pub(crate) use last::Last; pub(crate) use lines::Lines; pub(crate) use ls::LS; diff --git a/src/commands/history.rs b/src/commands/history.rs new file mode 100644 index 0000000000..fdc6d655a2 --- /dev/null +++ b/src/commands/history.rs @@ -0,0 +1,49 @@ +use crate::cli::History as HistoryFile; +use crate::commands::PerItemCommand; +use crate::errors::ShellError; +use crate::parser::registry::{self}; +use crate::prelude::*; +use std::fs::File; +use std::io::{BufRead, BufReader}; + +pub struct History; + +impl PerItemCommand for History { + fn name(&self) -> &str { + "history" + } + + fn signature(&self) -> registry::Signature { + Signature::build("history") + } + + fn usage(&self) -> &str { + "Display command history." + } + + fn run( + &self, + call_info: &CallInfo, + _registry: &CommandRegistry, + _raw_args: &RawCommandArgs, + _input: Tagged, + ) -> Result { + let tag = call_info.name_tag.clone(); + + let stream = async_stream! { + let history_path = HistoryFile::path(); + let file = File::open(history_path); + if let Ok(file) = file { + let reader = BufReader::new(file); + for line in reader.lines() { + if let Ok(line) = line { + yield ReturnSuccess::value(Value::string(line).tagged(tag.clone())); + } + } + } else { + yield Err(ShellError::labeled_error("Could not open history", "history file could not be opened", tag.clone())); + } + }; + Ok(stream.to_output_stream()) + } +} From fbd980f8b03b4555b1eadb3f077c6e94d1a395a8 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 28 Oct 2019 18:15:35 +1300 Subject: [PATCH 332/342] Add descriptions to arguments --- src/commands/cd.rs | 6 +++- src/commands/config.rs | 16 ++++++---- src/commands/cp.rs | 7 ++--- src/commands/date.rs | 4 ++- src/commands/echo.rs | 2 +- src/commands/enter.rs | 6 +++- src/commands/exit.rs | 2 +- src/commands/fetch.rs | 8 +++-- src/commands/first.rs | 6 +++- src/commands/from_csv.rs | 3 +- src/commands/from_json.rs | 2 +- src/commands/from_ssv.rs | 8 +++-- src/commands/from_tsv.rs | 3 +- src/commands/get.rs | 11 +++++-- src/commands/group_by.rs | 6 +++- src/commands/help.rs | 59 ++++++++++++++++++++++++++++++------ src/commands/last.rs | 6 +++- src/commands/ls.rs | 6 +++- src/commands/mkdir.rs | 2 +- src/commands/mv.rs | 13 ++++++-- src/commands/nth.rs | 6 +++- src/commands/open.rs | 8 +++-- src/commands/pick.rs | 2 +- src/commands/pivot.rs | 9 ++++-- src/commands/post.rs | 26 +++++++++++----- src/commands/reject.rs | 2 +- src/commands/rm.rs | 11 ++++--- src/commands/save.rs | 7 +++-- src/commands/skip_while.rs | 6 +++- src/commands/sort_by.rs | 2 +- src/commands/split_column.rs | 10 ++++-- src/commands/split_row.rs | 6 +++- src/commands/table.rs | 6 +++- src/commands/to_csv.rs | 5 ++- src/commands/to_tsv.rs | 5 ++- src/commands/where_.rs | 6 +++- src/commands/which_.rs | 6 +++- src/data/command.rs | 6 ++-- src/parser/parse_command.rs | 18 +++++------ src/parser/registry.rs | 55 +++++++++++++++++++++++---------- src/plugins/add.rs | 11 ++++--- src/plugins/binaryview.rs | 2 +- src/plugins/edit.rs | 12 ++++++-- src/plugins/embed.rs | 2 +- src/plugins/inc.rs | 8 ++--- src/plugins/match.rs | 4 +-- src/plugins/skip.rs | 2 +- src/plugins/str.rs | 10 +++--- 48 files changed, 308 insertions(+), 121 deletions(-) diff --git a/src/commands/cd.rs b/src/commands/cd.rs index a3f5a8d89b..65cc45231d 100644 --- a/src/commands/cd.rs +++ b/src/commands/cd.rs @@ -10,7 +10,11 @@ impl WholeStreamCommand for CD { } fn signature(&self) -> Signature { - Signature::build("cd").optional("directory", SyntaxShape::Path) + Signature::build("cd").optional( + "directory", + SyntaxShape::Path, + "the directory to change to", + ) } fn usage(&self) -> &str { diff --git a/src/commands/config.rs b/src/commands/config.rs index 9cde5213de..a85920e455 100644 --- a/src/commands/config.rs +++ b/src/commands/config.rs @@ -26,12 +26,16 @@ impl WholeStreamCommand for Config { fn signature(&self) -> Signature { Signature::build("config") - .named("load", SyntaxShape::Path) - .named("set", SyntaxShape::Any) - .named("get", SyntaxShape::Any) - .named("remove", SyntaxShape::Any) - .switch("clear") - .switch("path") + .named( + "load", + SyntaxShape::Path, + "load the config from the path give", + ) + .named("set", SyntaxShape::Any, "set a value in the config") + .named("get", SyntaxShape::Any, "get a value from the config") + .named("remove", SyntaxShape::Any, "remove a value from the config") + .switch("clear", "clear the config") + .switch("path", "return the path to the config file") } fn usage(&self) -> &str { diff --git a/src/commands/cp.rs b/src/commands/cp.rs index bf20c74ce9..5ca21adb1e 100644 --- a/src/commands/cp.rs +++ b/src/commands/cp.rs @@ -21,10 +21,9 @@ impl PerItemCommand for Cpy { fn signature(&self) -> Signature { Signature::build("cp") - .required("src", SyntaxShape::Pattern) - .required("dst", SyntaxShape::Path) - .named("file", SyntaxShape::Any) - .switch("recursive") + .required("src", SyntaxShape::Pattern, "the place to copy from") + .required("dst", SyntaxShape::Path, "the place to copy to") + .switch("recursive", "copy recursively through subdirectories") } fn usage(&self) -> &str { diff --git a/src/commands/date.rs b/src/commands/date.rs index bff6b550f7..24ebc876e4 100644 --- a/src/commands/date.rs +++ b/src/commands/date.rs @@ -17,7 +17,9 @@ impl WholeStreamCommand for Date { } fn signature(&self) -> Signature { - Signature::build("date").switch("utc").switch("local") + Signature::build("date") + .switch("utc", "use universal time (UTC)") + .switch("local", "use the local time") } fn usage(&self) -> &str { diff --git a/src/commands/echo.rs b/src/commands/echo.rs index 4483f91371..db4993d017 100644 --- a/src/commands/echo.rs +++ b/src/commands/echo.rs @@ -12,7 +12,7 @@ impl PerItemCommand for Echo { } fn signature(&self) -> Signature { - Signature::build("echo").rest(SyntaxShape::Any) + Signature::build("echo").rest(SyntaxShape::Any, "the values to echo") } fn usage(&self) -> &str { diff --git a/src/commands/enter.rs b/src/commands/enter.rs index efefd8394f..59f7ca0f21 100644 --- a/src/commands/enter.rs +++ b/src/commands/enter.rs @@ -14,7 +14,11 @@ impl PerItemCommand for Enter { } fn signature(&self) -> registry::Signature { - Signature::build("enter").required("location", SyntaxShape::Path) + Signature::build("enter").required( + "location", + SyntaxShape::Path, + "the location to create a new shell from", + ) } fn usage(&self) -> &str { diff --git a/src/commands/exit.rs b/src/commands/exit.rs index 8a382d8b7d..b7db7cc340 100644 --- a/src/commands/exit.rs +++ b/src/commands/exit.rs @@ -11,7 +11,7 @@ impl WholeStreamCommand for Exit { } fn signature(&self) -> Signature { - Signature::build("exit").switch("now") + Signature::build("exit").switch("now", "exit out of the shell immediately") } fn usage(&self) -> &str { diff --git a/src/commands/fetch.rs b/src/commands/fetch.rs index e66536729f..703c3279c5 100644 --- a/src/commands/fetch.rs +++ b/src/commands/fetch.rs @@ -19,8 +19,12 @@ impl PerItemCommand for Fetch { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxShape::Path) - .switch("raw") + .required( + "path", + SyntaxShape::Path, + "the URL to fetch the contents from", + ) + .switch("raw", "fetch contents as text rather than a table") } fn usage(&self) -> &str { diff --git a/src/commands/first.rs b/src/commands/first.rs index 4c1c3b8c35..a9a287978a 100644 --- a/src/commands/first.rs +++ b/src/commands/first.rs @@ -16,7 +16,11 @@ impl WholeStreamCommand for First { } fn signature(&self) -> Signature { - Signature::build("first").optional("rows", SyntaxShape::Int) + Signature::build("first").optional( + "rows", + SyntaxShape::Int, + "starting from the front, the number of rows to return", + ) } fn usage(&self) -> &str { diff --git a/src/commands/from_csv.rs b/src/commands/from_csv.rs index 877c8dc166..7442a07fc9 100644 --- a/src/commands/from_csv.rs +++ b/src/commands/from_csv.rs @@ -16,7 +16,8 @@ impl WholeStreamCommand for FromCSV { } fn signature(&self) -> Signature { - Signature::build("from-csv").switch("headerless") + Signature::build("from-csv") + .switch("headerless", "don't treat the first row as column names") } fn usage(&self) -> &str { diff --git a/src/commands/from_json.rs b/src/commands/from_json.rs index 0687e348ac..01883818c6 100644 --- a/src/commands/from_json.rs +++ b/src/commands/from_json.rs @@ -15,7 +15,7 @@ impl WholeStreamCommand for FromJSON { } fn signature(&self) -> Signature { - Signature::build("from-json").switch("objects") + Signature::build("from-json").switch("objects", "treat each line as a separate value") } fn usage(&self) -> &str { diff --git a/src/commands/from_ssv.rs b/src/commands/from_ssv.rs index f14d89356a..aaf6018fb7 100644 --- a/src/commands/from_ssv.rs +++ b/src/commands/from_ssv.rs @@ -21,8 +21,12 @@ impl WholeStreamCommand for FromSSV { fn signature(&self) -> Signature { Signature::build(STRING_REPRESENTATION) - .switch("headerless") - .named("minimum-spaces", SyntaxShape::Int) + .switch("headerless", "don't treat the first row as column names") + .named( + "minimum-spaces", + SyntaxShape::Int, + "the mininum spaces to separate columns", + ) } fn usage(&self) -> &str { diff --git a/src/commands/from_tsv.rs b/src/commands/from_tsv.rs index 80951b71aa..2284e95573 100644 --- a/src/commands/from_tsv.rs +++ b/src/commands/from_tsv.rs @@ -16,7 +16,8 @@ impl WholeStreamCommand for FromTSV { } fn signature(&self) -> Signature { - Signature::build("from-tsv").switch("headerless") + Signature::build("from-tsv") + .switch("headerless", "don't treat the first row as column names") } fn usage(&self) -> &str { diff --git a/src/commands/get.rs b/src/commands/get.rs index 21dbe6b0a7..70508bdb7a 100644 --- a/src/commands/get.rs +++ b/src/commands/get.rs @@ -20,8 +20,15 @@ impl WholeStreamCommand for Get { fn signature(&self) -> Signature { Signature::build("get") - .required("member", SyntaxShape::ColumnPath) - .rest(SyntaxShape::ColumnPath) + .required( + "member", + SyntaxShape::ColumnPath, + "the path to the data to get", + ) + .rest( + SyntaxShape::ColumnPath, + "optionally return additional data by path", + ) } fn usage(&self) -> &str { diff --git a/src/commands/group_by.rs b/src/commands/group_by.rs index 7f5f496408..f36d3f57dd 100644 --- a/src/commands/group_by.rs +++ b/src/commands/group_by.rs @@ -16,7 +16,11 @@ impl WholeStreamCommand for GroupBy { } fn signature(&self) -> Signature { - Signature::build("group-by").required("column_name", SyntaxShape::String) + Signature::build("group-by").required( + "column_name", + SyntaxShape::String, + "the name of the column to group by", + ) } fn usage(&self) -> &str { diff --git a/src/commands/help.rs b/src/commands/help.rs index 04e03fb10d..d5f755f67d 100644 --- a/src/commands/help.rs +++ b/src/commands/help.rs @@ -12,7 +12,7 @@ impl PerItemCommand for Help { } fn signature(&self) -> registry::Signature { - Signature::build("help").rest(SyntaxShape::Any) + Signature::build("help").rest(SyntaxShape::Any, "the name of command(s) to get help on") } fn usage(&self) -> &str { @@ -65,8 +65,8 @@ impl PerItemCommand for Help { one_liner.push_str("{flags} "); } - for positional in signature.positional { - match positional { + for positional in &signature.positional { + match &positional.0 { PositionalType::Mandatory(name, _m) => { one_liner.push_str(&format!("<{}> ", name)); } @@ -77,25 +77,66 @@ impl PerItemCommand for Help { } if signature.rest_positional.is_some() { - one_liner.push_str(" ...args"); + one_liner.push_str(&format!(" ...args",)); } + long_desc.push_str(&format!("\nUsage:\n > {}\n", one_liner)); + if signature.positional.len() > 0 || signature.rest_positional.is_some() { + long_desc.push_str("\nparameters:\n"); + for positional in signature.positional { + match positional.0 { + PositionalType::Mandatory(name, _m) => { + long_desc + .push_str(&format!(" <{}> {}\n", name, positional.1)); + } + PositionalType::Optional(name, _o) => { + long_desc + .push_str(&format!(" ({}) {}\n", name, positional.1)); + } + } + } + if signature.rest_positional.is_some() { + long_desc.push_str(&format!( + " ...args{} {}\n", + if signature.rest_positional.is_some() { + ":" + } else { + "" + }, + signature.rest_positional.unwrap().1 + )); + } + } if signature.named.len() > 0 { long_desc.push_str("\nflags:\n"); for (flag, ty) in signature.named { - match ty { + match ty.0 { NamedType::Switch => { - long_desc.push_str(&format!(" --{}\n", flag)); + long_desc.push_str(&format!( + " --{}{} {}\n", + flag, + if ty.1.len() > 0 { ":" } else { "" }, + ty.1 + )); } NamedType::Mandatory(m) => { long_desc.push_str(&format!( - " --{} <{}> (required parameter)\n", - flag, m + " --{} <{}> (required parameter){} {}\n", + flag, + m, + if ty.1.len() > 0 { ":" } else { "" }, + ty.1 )); } NamedType::Optional(o) => { - long_desc.push_str(&format!(" --{} <{}>\n", flag, o)); + long_desc.push_str(&format!( + " --{} <{}>{} {}\n", + flag, + o, + if ty.1.len() > 0 { ":" } else { "" }, + ty.1 + )); } } } diff --git a/src/commands/last.rs b/src/commands/last.rs index 04db0f4c48..abb10f5fce 100644 --- a/src/commands/last.rs +++ b/src/commands/last.rs @@ -16,7 +16,11 @@ impl WholeStreamCommand for Last { } fn signature(&self) -> Signature { - Signature::build("last").optional("rows", SyntaxShape::Number) + Signature::build("last").optional( + "rows", + SyntaxShape::Number, + "starting from the back, the number of rows to return", + ) } fn usage(&self) -> &str { diff --git a/src/commands/ls.rs b/src/commands/ls.rs index db229ecd0c..b108a53c0c 100644 --- a/src/commands/ls.rs +++ b/src/commands/ls.rs @@ -16,7 +16,11 @@ impl WholeStreamCommand for LS { } fn signature(&self) -> Signature { - Signature::build("ls").optional("path", SyntaxShape::Pattern) + Signature::build("ls").optional( + "path", + SyntaxShape::Pattern, + "a path to get the directory contents from", + ) } fn usage(&self) -> &str { diff --git a/src/commands/mkdir.rs b/src/commands/mkdir.rs index 8bf8a97d4a..e801a27530 100644 --- a/src/commands/mkdir.rs +++ b/src/commands/mkdir.rs @@ -17,7 +17,7 @@ impl PerItemCommand for Mkdir { } fn signature(&self) -> Signature { - Signature::build("mkdir").rest(SyntaxShape::Path) + Signature::build("mkdir").rest(SyntaxShape::Path, "the name(s) of the path(s) to create") } fn usage(&self) -> &str { diff --git a/src/commands/mv.rs b/src/commands/mv.rs index 2ace1fa05f..a9a11f5064 100644 --- a/src/commands/mv.rs +++ b/src/commands/mv.rs @@ -20,9 +20,16 @@ impl PerItemCommand for Move { fn signature(&self) -> Signature { Signature::build("mv") - .required("source", SyntaxShape::Pattern) - .required("destination", SyntaxShape::Path) - .named("file", SyntaxShape::Any) + .required( + "source", + SyntaxShape::Pattern, + "the location to move files/directories from", + ) + .required( + "destination", + SyntaxShape::Path, + "the location to move files/directories to", + ) } fn usage(&self) -> &str { diff --git a/src/commands/nth.rs b/src/commands/nth.rs index 18bb6f23af..bcd3057879 100644 --- a/src/commands/nth.rs +++ b/src/commands/nth.rs @@ -16,7 +16,11 @@ impl WholeStreamCommand for Nth { } fn signature(&self) -> Signature { - Signature::build("nth").required("row number", SyntaxShape::Any) + Signature::build("nth").required( + "row number", + SyntaxShape::Any, + "the number of the row to return", + ) } fn usage(&self) -> &str { diff --git a/src/commands/open.rs b/src/commands/open.rs index 2972144bcd..19c7d539ed 100644 --- a/src/commands/open.rs +++ b/src/commands/open.rs @@ -16,8 +16,12 @@ impl PerItemCommand for Open { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxShape::Path) - .switch("raw") + .required( + "path", + SyntaxShape::Path, + "the file path to load values from", + ) + .switch("raw", "load content as a string insead of a table") } fn usage(&self) -> &str { diff --git a/src/commands/pick.rs b/src/commands/pick.rs index 605b7f8890..b9c4e53bcc 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(SyntaxShape::Any) + Signature::build("pick").rest(SyntaxShape::Any, "the columns to select from the table") } fn usage(&self) -> &str { diff --git a/src/commands/pivot.rs b/src/commands/pivot.rs index e52ab90924..0556999f2d 100644 --- a/src/commands/pivot.rs +++ b/src/commands/pivot.rs @@ -21,9 +21,12 @@ impl WholeStreamCommand for Pivot { fn signature(&self) -> Signature { Signature::build("pivot") - .switch("header-row") - .switch("ignore-titles") - .rest(SyntaxShape::String) + .switch("header-row", "treat the first row as column names") + .switch("ignore-titles", "don't pivot the column names into values") + .rest( + SyntaxShape::String, + "the names to give columns once pivoted", + ) } fn usage(&self) -> &str { diff --git a/src/commands/post.rs b/src/commands/post.rs index 374616d2e5..eb06cdbae5 100644 --- a/src/commands/post.rs +++ b/src/commands/post.rs @@ -25,13 +25,25 @@ impl PerItemCommand for Post { fn signature(&self) -> Signature { Signature::build(self.name()) - .required("path", SyntaxShape::Any) - .required("body", SyntaxShape::Any) - .named("user", SyntaxShape::Any) - .named("password", SyntaxShape::Any) - .named("content-type", SyntaxShape::Any) - .named("content-length", SyntaxShape::Any) - .switch("raw") + .required("path", SyntaxShape::Any, "the URL to post to") + .required("body", SyntaxShape::Any, "the contents of the post body") + .named("user", SyntaxShape::Any, "the username when authenticating") + .named( + "password", + SyntaxShape::Any, + "the password when authenticating", + ) + .named( + "content-type", + SyntaxShape::Any, + "the MIME type of content to post", + ) + .named( + "content-length", + SyntaxShape::Any, + "the length of the content being posted", + ) + .switch("raw", "return values as a string instead of a table") } fn usage(&self) -> &str { diff --git a/src/commands/reject.rs b/src/commands/reject.rs index 3521635233..f02a72aa4c 100644 --- a/src/commands/reject.rs +++ b/src/commands/reject.rs @@ -16,7 +16,7 @@ impl WholeStreamCommand for Reject { } fn signature(&self) -> Signature { - Signature::build("reject").rest(SyntaxShape::Member) + Signature::build("reject").rest(SyntaxShape::Member, "the names of columns to remove") } fn usage(&self) -> &str { diff --git a/src/commands/rm.rs b/src/commands/rm.rs index c1e671f4b0..76222d2c28 100644 --- a/src/commands/rm.rs +++ b/src/commands/rm.rs @@ -21,13 +21,16 @@ impl PerItemCommand for Remove { fn signature(&self) -> Signature { Signature::build("rm") - .required("path", SyntaxShape::Pattern) - .switch("trash") - .switch("recursive") + .required("path", SyntaxShape::Pattern, "the file path to remove") + .switch( + "trash", + "use the platform's recycle bin instead of permanently deleting", + ) + .switch("recursive", "delete subdirectories recursively") } fn usage(&self) -> &str { - "Remove a file. Append '--recursive' to remove directories and '--trash' for seding it to system recycle bin" + "Remove a file" } fn run( diff --git a/src/commands/save.rs b/src/commands/save.rs index ac48fe280f..45063dca4e 100644 --- a/src/commands/save.rs +++ b/src/commands/save.rs @@ -93,8 +93,11 @@ impl WholeStreamCommand for Save { fn signature(&self) -> Signature { Signature::build("save") - .optional("path", SyntaxShape::Path) - .switch("raw") + .optional("path", SyntaxShape::Path, "the path to save contents to") + .switch( + "raw", + "treat values as-is rather than auto-converting based on file extension", + ) } fn usage(&self) -> &str { diff --git a/src/commands/skip_while.rs b/src/commands/skip_while.rs index a768ae6133..e8bec7dac2 100644 --- a/src/commands/skip_while.rs +++ b/src/commands/skip_while.rs @@ -17,7 +17,11 @@ impl WholeStreamCommand for SkipWhile { fn signature(&self) -> Signature { Signature::build("skip-while") - .required("condition", SyntaxShape::Block) + .required( + "condition", + SyntaxShape::Block, + "the condition that must be met to continue skipping", + ) .filter() } diff --git a/src/commands/sort_by.rs b/src/commands/sort_by.rs index 1e6b87491a..d384207c92 100644 --- a/src/commands/sort_by.rs +++ b/src/commands/sort_by.rs @@ -15,7 +15,7 @@ impl WholeStreamCommand for SortBy { } fn signature(&self) -> Signature { - Signature::build("sort-by").rest(SyntaxShape::String) + Signature::build("sort-by").rest(SyntaxShape::String, "the column(s) to sort by") } fn usage(&self) -> &str { diff --git a/src/commands/split_column.rs b/src/commands/split_column.rs index d174283023..fd872d452d 100644 --- a/src/commands/split_column.rs +++ b/src/commands/split_column.rs @@ -21,9 +21,13 @@ impl WholeStreamCommand for SplitColumn { fn signature(&self) -> Signature { Signature::build("split-column") - .required("separator", SyntaxShape::Any) - .switch("collapse-empty") - .rest(SyntaxShape::Member) + .required( + "separator", + SyntaxShape::Any, + "the character that denotes what separates columns", + ) + .switch("collapse-empty", "remove empty columns") + .rest(SyntaxShape::Member, "column names to give the new columns") } fn usage(&self) -> &str { diff --git a/src/commands/split_row.rs b/src/commands/split_row.rs index 94f7564b40..6c848c325a 100644 --- a/src/commands/split_row.rs +++ b/src/commands/split_row.rs @@ -17,7 +17,11 @@ impl WholeStreamCommand for SplitRow { } fn signature(&self) -> Signature { - Signature::build("split-row").required("separator", SyntaxShape::Any) + Signature::build("split-row").required( + "separator", + SyntaxShape::Any, + "the character that denotes what separates rows", + ) } fn usage(&self) -> &str { diff --git a/src/commands/table.rs b/src/commands/table.rs index 8ad2c246db..f8cdcd13c7 100644 --- a/src/commands/table.rs +++ b/src/commands/table.rs @@ -11,7 +11,11 @@ impl WholeStreamCommand for Table { } fn signature(&self) -> Signature { - Signature::build("table").named("start_number", SyntaxShape::Number) + Signature::build("table").named( + "start_number", + SyntaxShape::Number, + "row number to start viewing from", + ) } fn usage(&self) -> &str { diff --git a/src/commands/to_csv.rs b/src/commands/to_csv.rs index 90f4837453..d2b46d9f88 100644 --- a/src/commands/to_csv.rs +++ b/src/commands/to_csv.rs @@ -16,7 +16,10 @@ impl WholeStreamCommand for ToCSV { } fn signature(&self) -> Signature { - Signature::build("to-csv").switch("headerless") + Signature::build("to-csv").switch( + "headerless", + "do not output the columns names as the first row", + ) } fn usage(&self) -> &str { diff --git a/src/commands/to_tsv.rs b/src/commands/to_tsv.rs index 83cb4a07f1..7857d1eeec 100644 --- a/src/commands/to_tsv.rs +++ b/src/commands/to_tsv.rs @@ -16,7 +16,10 @@ impl WholeStreamCommand for ToTSV { } fn signature(&self) -> Signature { - Signature::build("to-tsv").switch("headerless") + Signature::build("to-tsv").switch( + "headerless", + "do not output the column names as the first row", + ) } fn usage(&self) -> &str { diff --git a/src/commands/where_.rs b/src/commands/where_.rs index 9e3c4d2c07..ce7367b1a6 100644 --- a/src/commands/where_.rs +++ b/src/commands/where_.rs @@ -12,7 +12,11 @@ impl PerItemCommand for Where { } fn signature(&self) -> registry::Signature { - Signature::build("where").required("condition", SyntaxShape::Block) + Signature::build("where").required( + "condition", + SyntaxShape::Block, + "the condition that must match", + ) } fn usage(&self) -> &str { diff --git a/src/commands/which_.rs b/src/commands/which_.rs index e3b6d1c96c..405efe7dca 100644 --- a/src/commands/which_.rs +++ b/src/commands/which_.rs @@ -13,7 +13,11 @@ impl WholeStreamCommand for Which { } fn signature(&self) -> Signature { - Signature::build("which").required("name", SyntaxShape::Any) + Signature::build("which").required( + "name", + SyntaxShape::Any, + "the name of the command to find the path to", + ) } fn usage(&self) -> &str { diff --git a/src/data/command.rs b/src/data/command.rs index 25301e6fa1..5993dda6f5 100644 --- a/src/data/command.rs +++ b/src/data/command.rs @@ -45,12 +45,12 @@ fn signature_dict(signature: Signature, tag: impl Into) -> Tagged { let mut sig = TaggedListBuilder::new(&tag); for arg in signature.positional.iter() { - let is_required = match arg { + let is_required = match arg.0 { PositionalType::Mandatory(_, _) => true, PositionalType::Optional(_, _) => false, }; - sig.insert_tagged(for_spec(arg.name(), "argument", is_required, &tag)); + sig.insert_tagged(for_spec(arg.0.name(), "argument", is_required, &tag)); } if let Some(_) = signature.rest_positional { @@ -59,7 +59,7 @@ fn signature_dict(signature: Signature, tag: impl Into) -> Tagged { } for (name, ty) in signature.named.iter() { - match ty { + match ty.0 { NamedType::Mandatory(_) => sig.insert_tagged(for_spec(name, "flag", true, &tag)), NamedType::Optional(_) => sig.insert_tagged(for_spec(name, "flag", false, &tag)), NamedType::Switch => sig.insert_tagged(for_spec(name, "switch", false, &tag)), diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index 01ba60b491..23c4d27036 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -25,7 +25,7 @@ pub fn parse_command_tail( for (name, kind) in &config.named { trace!(target: "nu::parse", "looking for {} : {:?}", name, kind); - match kind { + match &kind.0 { NamedType::Switch => { let flag = extract_switch(name, tail, context.source()); @@ -92,12 +92,12 @@ pub fn parse_command_tail( for arg in &config.positional { trace!(target: "nu::parse", "Processing positional {:?}", arg); - match arg { + match &arg.0 { PositionalType::Mandatory(..) => { if tail.at_end_possible_ws() { return Err(ShellError::argument_error( config.name.clone(), - ArgumentError::MissingMandatoryPositional(arg.name().to_string()), + ArgumentError::MissingMandatoryPositional(arg.0.name().to_string()), Tag { span: command_span, anchor: None, @@ -113,14 +113,14 @@ pub fn parse_command_tail( } } - let result = expand_expr(&spaced(arg.syntax_type()), tail, context)?; + let result = expand_expr(&spaced(arg.0.syntax_type()), tail, context)?; positional.push(result); } trace_remaining("after positional", tail.clone(), context.source()); - if let Some(syntax_type) = config.rest_positional { + if let Some((syntax_type, _)) = config.rest_positional { let mut out = vec![]; loop { @@ -207,7 +207,7 @@ impl ColorSyntax for CommandTailShape { for (name, kind) in &signature.named { trace!(target: "nu::color_syntax", "looking for {} : {:?}", name, kind); - match kind { + match &kind.0 { NamedType::Switch => { match token_nodes.extract(|t| t.as_flag(name, context.source())) { Some((pos, flag)) => args.insert(pos, vec![flag.color()]), @@ -300,7 +300,7 @@ impl ColorSyntax for CommandTailShape { for arg in &signature.positional { trace!("Processing positional {:?}", arg); - match arg { + match arg.0 { PositionalType::Mandatory(..) => { if token_nodes.at_end() { break; @@ -327,7 +327,7 @@ impl ColorSyntax for CommandTailShape { // If no match, we should roll back any whitespace we chomped color_fallible_syntax( - &arg.syntax_type(), + &arg.0.syntax_type(), token_nodes, context, &mut shapes, @@ -343,7 +343,7 @@ impl ColorSyntax for CommandTailShape { trace_remaining("after positional", token_nodes.clone(), context.source()); - if let Some(syntax_type) = signature.rest_positional { + if let Some((syntax_type, _)) = signature.rest_positional { loop { if token_nodes.at_end_possible_ws() { break; diff --git a/src/parser/registry.rs b/src/parser/registry.rs index 790925e800..ff0a98ae85 100644 --- a/src/parser/registry.rs +++ b/src/parser/registry.rs @@ -58,17 +58,19 @@ impl PositionalType { } } +type Description = String; + #[derive(Debug, Serialize, Deserialize, Clone, new)] pub struct Signature { pub name: String, #[new(default)] pub usage: String, #[new(default)] - pub positional: Vec, + pub positional: Vec<(PositionalType, Description)>, #[new(value = "None")] - pub rest_positional: Option, + pub rest_positional: Option<(SyntaxShape, Description)>, #[new(default)] - pub named: IndexMap, + pub named: IndexMap, #[new(value = "false")] pub is_filter: bool, } @@ -83,23 +85,42 @@ impl Signature { self } - pub fn required(mut self, name: impl Into, ty: impl Into) -> Signature { - self.positional - .push(PositionalType::Mandatory(name.into(), ty.into())); + pub fn required( + mut self, + name: impl Into, + ty: impl Into, + desc: impl Into, + ) -> Signature { + self.positional.push(( + PositionalType::Mandatory(name.into(), ty.into()), + desc.into(), + )); self } - pub fn optional(mut self, name: impl Into, ty: impl Into) -> Signature { - self.positional - .push(PositionalType::Optional(name.into(), ty.into())); + pub fn optional( + mut self, + name: impl Into, + ty: impl Into, + desc: impl Into, + ) -> Signature { + self.positional.push(( + PositionalType::Optional(name.into(), ty.into()), + desc.into(), + )); self } - pub fn named(mut self, name: impl Into, ty: impl Into) -> Signature { + pub fn named( + mut self, + name: impl Into, + ty: impl Into, + desc: impl Into, + ) -> Signature { self.named - .insert(name.into(), NamedType::Optional(ty.into())); + .insert(name.into(), (NamedType::Optional(ty.into()), desc.into())); self } @@ -108,15 +129,17 @@ impl Signature { mut self, name: impl Into, ty: impl Into, + desc: impl Into, ) -> Signature { self.named - .insert(name.into(), NamedType::Mandatory(ty.into())); + .insert(name.into(), (NamedType::Mandatory(ty.into()), desc.into())); self } - pub fn switch(mut self, name: impl Into) -> Signature { - self.named.insert(name.into(), NamedType::Switch); + pub fn switch(mut self, name: impl Into, desc: impl Into) -> Signature { + self.named + .insert(name.into(), (NamedType::Switch, desc.into())); self } @@ -126,8 +149,8 @@ impl Signature { self } - pub fn rest(mut self, ty: SyntaxShape) -> Signature { - self.rest_positional = Some(ty); + pub fn rest(mut self, ty: SyntaxShape, desc: impl Into) -> Signature { + self.rest_positional = Some((ty, desc.into())); self } } diff --git a/src/plugins/add.rs b/src/plugins/add.rs index 98cf3819b3..5bda9d0593 100644 --- a/src/plugins/add.rs +++ b/src/plugins/add.rs @@ -53,10 +53,13 @@ impl Add { impl Plugin for Add { fn config(&mut self) -> Result { Ok(Signature::build("add") - .desc("Add a new field to the table.") - .required("Field", SyntaxShape::ColumnPath) - .required("Value", SyntaxShape::String) - .rest(SyntaxShape::String) + .desc("Add a new column to the table.") + .required("column", SyntaxShape::ColumnPath, "the column name to add") + .required( + "value", + SyntaxShape::String, + "the value to give the cell(s)", + ) .filter()) } diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index b834f440e2..0072df5b4d 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -16,7 +16,7 @@ impl Plugin for BinaryView { fn config(&mut self) -> Result { Ok(Signature::build("binaryview") .desc("Autoview of binary data.") - .switch("lores")) + .switch("lores", "use low resolution output mode")) } fn sink(&mut self, call_info: CallInfo, input: Vec>) { diff --git a/src/plugins/edit.rs b/src/plugins/edit.rs index 34653bd66d..78cb32cef3 100644 --- a/src/plugins/edit.rs +++ b/src/plugins/edit.rs @@ -48,8 +48,16 @@ impl Plugin for Edit { fn config(&mut self) -> Result { Ok(Signature::build("edit") .desc("Edit an existing column to have a new value.") - .required("Field", SyntaxShape::ColumnPath) - .required("Value", SyntaxShape::String) + .required( + "Field", + SyntaxShape::ColumnPath, + "the name of the column to edit", + ) + .required( + "Value", + SyntaxShape::String, + "the new value to give the cell(s)", + ) .filter()) } diff --git a/src/plugins/embed.rs b/src/plugins/embed.rs index e659bfeb3b..6dc539d107 100644 --- a/src/plugins/embed.rs +++ b/src/plugins/embed.rs @@ -28,7 +28,7 @@ impl Plugin for Embed { fn config(&mut self) -> Result { Ok(Signature::build("embed") .desc("Embeds a new field to the table.") - .optional("field", SyntaxShape::String) + .optional("field", SyntaxShape::String, "the name of the new column") .filter()) } diff --git a/src/plugins/inc.rs b/src/plugins/inc.rs index 1cb6cb2b97..ed0416ce43 100644 --- a/src/plugins/inc.rs +++ b/src/plugins/inc.rs @@ -137,10 +137,10 @@ impl Plugin for Inc { fn config(&mut self) -> Result { Ok(Signature::build("inc") .desc("Increment a value or version. Optionally use the column of a table.") - .switch("major") - .switch("minor") - .switch("patch") - .rest(SyntaxShape::ColumnPath) + .switch("major", "increment the major version (eg 1.2.1 -> 2.0.0)") + .switch("minor", "increment the minor version (eg 1.2.1 -> 1.3.0)") + .switch("patch", "increment the patch version (eg 1.2.1 -> 1.2.2)") + .rest(SyntaxShape::ColumnPath, "the column(s) to update") .filter()) } diff --git a/src/plugins/match.rs b/src/plugins/match.rs index 7133524050..eefbf10632 100644 --- a/src/plugins/match.rs +++ b/src/plugins/match.rs @@ -22,8 +22,8 @@ impl Plugin for Match { fn config(&mut self) -> Result { Ok(Signature::build("match") .desc("filter rows by regex") - .required("member", SyntaxShape::Member) - .required("regex", SyntaxShape::String) + .required("member", SyntaxShape::Member, "the column name to match") + .required("regex", SyntaxShape::String, "the regex to match with") .filter()) } fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { diff --git a/src/plugins/skip.rs b/src/plugins/skip.rs index efd3231525..5ec290fe04 100644 --- a/src/plugins/skip.rs +++ b/src/plugins/skip.rs @@ -17,7 +17,7 @@ impl Plugin for Skip { fn config(&mut self) -> Result { Ok(Signature::build("skip") .desc("Skip a number of rows") - .rest(SyntaxShape::Number) + .rest(SyntaxShape::Number, "the number of rows to skip") .filter()) } fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { diff --git a/src/plugins/str.rs b/src/plugins/str.rs index 60625e7f17..8260bdac2c 100644 --- a/src/plugins/str.rs +++ b/src/plugins/str.rs @@ -128,11 +128,11 @@ impl Str { impl Plugin for Str { fn config(&mut self) -> Result { Ok(Signature::build("str") - .desc("Apply string function. Optional use the field of a table") - .switch("downcase") - .switch("upcase") - .switch("to-int") - .rest(SyntaxShape::ColumnPath) + .desc("Apply string function. Optional use the column of a table") + .switch("downcase", "convert string to lowercase") + .switch("upcase", "convert string to uppercase") + .switch("to-int", "convert string to integer") + .rest(SyntaxShape::ColumnPath, "the column(s) to convert") .filter()) } From 3f600c5b82db9989cc0026ab80a0bd1739d76535 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Mon, 28 Oct 2019 18:30:14 +1300 Subject: [PATCH 333/342] Fix build issues --- src/parser/parse_command.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/parser/parse_command.rs b/src/parser/parse_command.rs index 23c4d27036..d531da62ac 100644 --- a/src/parser/parse_command.rs +++ b/src/parser/parse_command.rs @@ -407,7 +407,7 @@ impl ColorSyntax for CommandTailShape { for (name, kind) in &signature.named { trace!(target: "nu::color_syntax", "looking for {} : {:?}", name, kind); - match kind { + match &kind.0 { NamedType::Switch => { match token_nodes.extract(|t| t.as_flag(name, context.source())) { Some((pos, flag)) => args.insert(pos, vec![flag.color()]), @@ -502,7 +502,7 @@ impl ColorSyntax for CommandTailShape { for arg in &signature.positional { trace!("Processing positional {:?}", arg); - match arg { + match &arg.0 { PositionalType::Mandatory(..) => { if token_nodes.at_end() { break; @@ -527,7 +527,7 @@ impl ColorSyntax for CommandTailShape { color_syntax(&MaybeSpaceShape, token_nodes, context); // If no match, we should roll back any whitespace we chomped - color_fallible_syntax(&arg.syntax_type(), token_nodes, context)?; + color_fallible_syntax(&arg.0.syntax_type(), token_nodes, context)?; Ok(()) }); @@ -539,7 +539,7 @@ impl ColorSyntax for CommandTailShape { trace_remaining("after positional", token_nodes.clone(), context.source()); - if let Some(syntax_type) = signature.rest_positional { + if let Some((syntax_type, _)) = signature.rest_positional { loop { if token_nodes.at_end_possible_ws() { break; From 1de80aeac3334c82eef603a11153b965a78a6b83 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 29 Oct 2019 06:51:08 +1300 Subject: [PATCH 334/342] Add support for :config and :env --- src/evaluate/evaluator.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index 75eb2f4667..e3bc9a7730 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -5,6 +5,7 @@ use crate::parser::{ CommandRegistry, Text, }; use crate::prelude::*; +use crate::TaggedDictBuilder; use derive_new::new; use indexmap::IndexMap; use log::trace; @@ -164,11 +165,24 @@ fn evaluate_reference( trace!("Evaluating {} with Scope {}", name, scope); match name { hir::Variable::It(_) => Ok(scope.it.item.clone().tagged(tag)), - hir::Variable::Other(inner) => Ok(scope - .vars - .get(inner.slice(source)) - .map(|v| v.clone()) - .unwrap_or_else(|| Value::nothing().tagged(tag))), + hir::Variable::Other(inner) => match inner.slice(source) { + x if x == "nu:env" => { + let mut dict = TaggedDictBuilder::new(&tag); + for v in std::env::vars() { + dict.insert(v.0, Value::string(v.1)); + } + Ok(dict.into_tagged_value()) + } + x if x == "nu:config" => { + let config = crate::data::config::read(tag.clone(), &None)?; + Ok(Value::row(config).tagged(tag)) + } + x => Ok(scope + .vars + .get(x) + .map(|v| v.clone()) + .unwrap_or_else(|| Value::nothing().tagged(tag))), + }, } } From 53911ebecd4a4edff69b059423d3ec48f7eceb65 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 29 Oct 2019 07:40:34 +1300 Subject: [PATCH 335/342] Add support for :path --- src/commands/command.rs | 19 ++++++++++++------- src/evaluate/evaluator.rs | 12 ++++++++++++ 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/commands/command.rs b/src/commands/command.rs index 5f3f4809bd..6677dfbd7e 100644 --- a/src/commands/command.rs +++ b/src/commands/command.rs @@ -589,17 +589,22 @@ impl Command { out.to_output_stream() } else { let nothing = Value::nothing().tagged(Tag::unknown()); + let call_info = raw_args .clone() .call_info - .evaluate(®istry, &Scope::it_value(nothing.clone())) - .unwrap(); + .evaluate(®istry, &Scope::it_value(nothing.clone())); - match command - .run(&call_info, ®istry, &raw_args, nothing) - .into() - { - Ok(o) => o, + match call_info { + Ok(call_info) => { + match command + .run(&call_info, ®istry, &raw_args, nothing) + .into() + { + Ok(o) => o, + Err(e) => OutputStream::one(Err(e)), + } + } Err(e) => OutputStream::one(Err(e)), } } diff --git a/src/evaluate/evaluator.rs b/src/evaluate/evaluator.rs index df3186808f..9313d0fe5c 100644 --- a/src/evaluate/evaluator.rs +++ b/src/evaluate/evaluator.rs @@ -177,6 +177,18 @@ fn evaluate_reference( let config = crate::data::config::read(tag.clone(), &None)?; Ok(Value::row(config).tagged(tag)) } + x if x == "nu:path" => { + let mut table = vec![]; + match std::env::var_os("PATH") { + Some(paths) => { + for path in std::env::split_paths(&paths) { + table.push(Value::path(path).tagged(&tag)); + } + } + _ => {} + } + Ok(Value::table(&table).tagged(tag)) + } x => Ok(scope .vars .get(x) From 30b6eac03dfd9c119b0ee4c95d34f0a1758e2e5d Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Tue, 29 Oct 2019 10:22:31 +1300 Subject: [PATCH 336/342] Allow updating path in config --- src/cli.rs | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/src/cli.rs b/src/cli.rs index d52a55e267..9661cb3202 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -520,6 +520,43 @@ async fn process_line(readline: Result, ctx: &mut Context let mut iter = pipeline.commands.into_iter().peekable(); let mut is_first_command = true; + // Check the config to see if we need to update the path + // TODO: make sure config is cached so we don't path this load every call + let config = crate::data::config::read(Tag::unknown(), &None).unwrap(); + if config.contains_key("path") { + // Override the path with what they give us from config + let value = config.get("path"); + + match value { + Some(value) => match value { + Tagged { + item: Value::Table(table), + .. + } => { + let mut paths = vec![]; + for val in table { + let path_str = val.as_string(); + match path_str { + Err(_) => {} + Ok(path_str) => { + paths.push(PathBuf::from(path_str)); + } + } + } + let path_os_string = std::env::join_paths(&paths); + match path_os_string { + Ok(path_os_string) => { + std::env::set_var("PATH", path_os_string); + } + Err(_) => {} + } + } + _ => {} + }, + None => {} + } + } + loop { let item: Option = iter.next(); let next: Option<&ClassifiedCommand> = iter.peek(); From e09160e80d11e1b096a8f3c0eb3e2c42db837053 Mon Sep 17 00:00:00 2001 From: Ryan Blecher Date: Mon, 28 Oct 2019 20:22:51 -0400 Subject: [PATCH 337/342] add ability to create PathBuf from string to avoid type mismatch --- src/data/base.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/data/base.rs b/src/data/base.rs index 2cf1f2cedb..bc567f0dfe 100644 --- a/src/data/base.rs +++ b/src/data/base.rs @@ -817,6 +817,7 @@ impl Tagged { pub(crate) fn as_path(&self) -> Result { match self.item() { Value::Primitive(Primitive::Path(path)) => Ok(path.clone()), + Value::Primitive(Primitive::String(path_str)) => Ok(PathBuf::from(&path_str).clone()), other => Err(ShellError::type_error( "Path", other.type_name().tagged(self.tag()), From 3820fef801641b13a49c4106a8f5823c1eb5ef4b Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 30 Oct 2019 11:33:36 +1300 Subject: [PATCH 338/342] Add a simple read/parse plugin to better handle text data --- Cargo.toml | 6 +- src/plugins/read.rs | 156 +++++++++++++++++++++++++++++++ src/utils.rs | 4 + tests/fixtures/formats/fileA.txt | 3 + tests/tests.rs | 16 ++++ 5 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 src/plugins/read.rs create mode 100644 tests/fixtures/formats/fileA.txt diff --git a/Cargo.toml b/Cargo.toml index e81e830e1a..52589cb733 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,8 +74,8 @@ natural = "0.3.0" serde_urlencoded = "0.6.1" sublime_fuzzy = "0.5" trash = "1.0.0" +regex = "1" -regex = {version = "1", optional = true } neso = { version = "0.5.0", optional = true } crossterm = { version = "0.10.2", optional = true } syntect = {version = "3.2.0", optional = true } @@ -136,6 +136,10 @@ path = "src/plugins/add.rs" name = "nu_plugin_edit" path = "src/plugins/edit.rs" +[[bin]] +name = "nu_plugin_read" +path = "src/plugins/read.rs" + [[bin]] name = "nu_plugin_str" path = "src/plugins/str.rs" diff --git a/src/plugins/read.rs b/src/plugins/read.rs new file mode 100644 index 0000000000..de88946e91 --- /dev/null +++ b/src/plugins/read.rs @@ -0,0 +1,156 @@ +use nu::{ + serve_plugin, CallInfo, Plugin, Primitive, ReturnSuccess, ReturnValue, ShellError, Signature, + SyntaxShape, Tagged, TaggedDictBuilder, Value, +}; + +use nom::{ + bytes::complete::{tag, take_while}, + IResult, +}; +use regex::Regex; + +#[derive(Debug)] +enum ReadCommand { + Text(String), + Column(String), +} + +fn read(input: &str) -> IResult<&str, Vec> { + let mut output = vec![]; + + let mut loop_input = input; + loop { + let (input, before) = take_while(|c| c != '{')(loop_input)?; + if before.len() > 0 { + output.push(ReadCommand::Text(before.to_string())); + } + if input != "" { + // Look for column as we're now at one + let (input, _) = tag("{")(input)?; + let (input, column) = take_while(|c| c != '}')(input)?; + let (input, _) = tag("}")(input)?; + + output.push(ReadCommand::Column(column.to_string())); + loop_input = input; + } else { + loop_input = input; + } + if loop_input == "" { + break; + } + } + + Ok((loop_input, output)) +} + +fn column_names(commands: &[ReadCommand]) -> Vec { + let mut output = vec![]; + + for command in commands { + match command { + ReadCommand::Column(c) => { + output.push(c.clone()); + } + _ => {} + } + } + + output +} + +fn build_regex(commands: &[ReadCommand]) -> String { + let mut output = String::new(); + + for command in commands { + match command { + ReadCommand::Text(s) => { + output.push_str(&s.replace("(", "\\(")); + } + ReadCommand::Column(_) => { + output.push_str("(.*)"); + } + } + } + + return output; +} +struct Read { + regex: Regex, + column_names: Vec, +} + +impl Read { + fn new() -> Self { + Read { + regex: Regex::new("").unwrap(), + column_names: vec![], + } + } +} + +impl Plugin for Read { + fn config(&mut self) -> Result { + Ok(Signature::build("read") + .desc("Parse columns from string data using a simple pattern") + .required( + "pattern", + SyntaxShape::Any, + "the pattern to match. Eg) \"{foo}: {bar}\"", + ) + .filter()) + } + fn begin_filter(&mut self, call_info: CallInfo) -> Result, ShellError> { + if let Some(args) = call_info.args.positional { + match &args[0] { + Tagged { + item: Value::Primitive(Primitive::String(pattern)), + .. + } => { + //self.pattern = s.clone(); + let read_pattern = read(&pattern).unwrap(); + let read_regex = build_regex(&read_pattern.1); + + self.column_names = column_names(&read_pattern.1); + + self.regex = Regex::new(&read_regex).unwrap(); + } + Tagged { tag, .. } => { + return Err(ShellError::labeled_error( + "Unrecognized type in params", + "value", + tag, + )); + } + } + } + Ok(vec![]) + } + + fn filter(&mut self, input: Tagged) -> Result, ShellError> { + let mut results = vec![]; + match &input { + Tagged { + tag, + item: Value::Primitive(Primitive::String(s)), + } => { + //self.full_input.push_str(&s); + + for cap in self.regex.captures_iter(&s) { + let mut dict = TaggedDictBuilder::new(tag); + + for (idx, column_name) in self.column_names.iter().enumerate() { + dict.insert(column_name, Value::string(&cap[idx + 1].to_string())); + } + + results.push(ReturnSuccess::value(dict.into_tagged_value())); + } + } + _ => {} + } + Ok(results) + } +} + +fn main() { + serve_plugin(&mut Read::new()); +} diff --git a/src/utils.rs b/src/utils.rs index 6b1318f9e8..56fee491b6 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -448,6 +448,10 @@ mod tests { loc: fixtures().join("cargo_sample.toml"), at: 0 }, + Res { + loc: fixtures().join("fileA.txt"), + at: 0 + }, Res { loc: fixtures().join("jonathan.xml"), at: 0 diff --git a/tests/fixtures/formats/fileA.txt b/tests/fixtures/formats/fileA.txt new file mode 100644 index 0000000000..0ce9fb3fa2 --- /dev/null +++ b/tests/fixtures/formats/fileA.txt @@ -0,0 +1,3 @@ +VAR1=Chill +VAR2=StupidLongName +VAR3=AlsoChill diff --git a/tests/tests.rs b/tests/tests.rs index 25337edb09..1a739f1982 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -56,6 +56,22 @@ fn add_plugin() { assert_eq!(actual, "1"); } +#[test] +fn read_plugin() { + let actual = nu!( + cwd: "tests/fixtures/formats", h::pipeline( + r#" + open fileA.txt + | read "{Name}={Value}" + | nth 1 + | get Value + | echo $it + "# + )); + + assert_eq!(actual, "StupidLongName"); +} + #[test] fn edit_plugin() { let actual = nu!( From 81691e07c6c47644f2ff6d33c45bd6822521c421 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 30 Oct 2019 19:54:06 +1300 Subject: [PATCH 339/342] Add prepend and append commands --- src/cli.rs | 2 ++ src/commands.rs | 4 ++++ src/commands/append.rs | 49 +++++++++++++++++++++++++++++++++++++++++ src/commands/prepend.rs | 49 +++++++++++++++++++++++++++++++++++++++++ tests/tests.rs | 32 +++++++++++++++++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 src/commands/append.rs create mode 100644 src/commands/prepend.rs diff --git a/src/cli.rs b/src/cli.rs index 9661cb3202..f46db10529 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -264,6 +264,8 @@ pub async fn cli() -> Result<(), Box> { whole_stream_command(Lines), whole_stream_command(Reject), whole_stream_command(Reverse), + whole_stream_command(Append), + whole_stream_command(Prepend), whole_stream_command(Trim), whole_stream_command(ToBSON), whole_stream_command(ToCSV), diff --git a/src/commands.rs b/src/commands.rs index c75ca81192..ba69d1e822 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,6 +1,7 @@ #[macro_use] pub(crate) mod macros; +pub(crate) mod append; pub(crate) mod args; pub(crate) mod autoview; pub(crate) mod cd; @@ -45,6 +46,7 @@ pub(crate) mod pick; pub(crate) mod pivot; pub(crate) mod plugin; pub(crate) mod post; +pub(crate) mod prepend; pub(crate) mod prev; pub(crate) mod pwd; pub(crate) mod reject; @@ -79,6 +81,7 @@ pub(crate) use command::{ UnevaluatedCallInfo, WholeStreamCommand, }; +pub(crate) use append::Append; pub(crate) use classified::ClassifiedCommand; pub(crate) use config::Config; pub(crate) use count::Count; @@ -119,6 +122,7 @@ pub(crate) use open::Open; pub(crate) use pick::Pick; pub(crate) use pivot::Pivot; pub(crate) use post::Post; +pub(crate) use prepend::Prepend; pub(crate) use prev::Previous; pub(crate) use pwd::PWD; pub(crate) use reject::Reject; diff --git a/src/commands/append.rs b/src/commands/append.rs new file mode 100644 index 0000000000..b8ca7b6e92 --- /dev/null +++ b/src/commands/append.rs @@ -0,0 +1,49 @@ +use crate::commands::WholeStreamCommand; +use crate::errors::ShellError; +use crate::parser::CommandRegistry; +use crate::prelude::*; + +#[derive(Deserialize)] +struct AppendArgs { + row: Tagged, +} + +pub struct Append; + +impl WholeStreamCommand for Append { + fn name(&self) -> &str { + "append" + } + + fn signature(&self) -> Signature { + Signature::build("append").required( + "row value", + SyntaxShape::Any, + "the value of the row to append to the table", + ) + } + + fn usage(&self) -> &str { + "Append the given row to the table" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, append)?.run() + } +} + +fn append( + AppendArgs { row }: AppendArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { + let mut after: VecDeque> = VecDeque::new(); + after.push_back(row); + + Ok(OutputStream::from_input( + input.values.chain(after), + )) +} diff --git a/src/commands/prepend.rs b/src/commands/prepend.rs new file mode 100644 index 0000000000..4d9c037f43 --- /dev/null +++ b/src/commands/prepend.rs @@ -0,0 +1,49 @@ +use crate::commands::WholeStreamCommand; +use crate::errors::ShellError; +use crate::parser::CommandRegistry; +use crate::prelude::*; + +#[derive(Deserialize)] +struct PrependArgs { + row: Tagged, +} + +pub struct Prepend; + +impl WholeStreamCommand for Prepend { + fn name(&self) -> &str { + "prepend" + } + + fn signature(&self) -> Signature { + Signature::build("prepend").required( + "row value", + SyntaxShape::Any, + "the value of the row to prepend to the table", + ) + } + + fn usage(&self) -> &str { + "Prepend the given row to the front of the table" + } + + fn run( + &self, + args: CommandArgs, + registry: &CommandRegistry, + ) -> Result { + args.process(registry, prepend)?.run() + } +} + +fn prepend( + PrependArgs { row }: PrependArgs, + RunnableContext { input, .. }: RunnableContext, +) -> Result { + let mut prepend: VecDeque> = VecDeque::new(); + prepend.push_back(row); + + Ok(OutputStream::from_input( + prepend.chain(input.values), + )) +} diff --git a/tests/tests.rs b/tests/tests.rs index 1a739f1982..14552a41ee 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -72,6 +72,38 @@ fn read_plugin() { assert_eq!(actual, "StupidLongName"); } +#[test] +fn prepend_plugin() { + let actual = nu!( + cwd: "tests/fixtures/formats", h::pipeline( + r#" + open fileA.txt + | lines + | prepend "testme" + | nth 0 + | echo $it + "# + )); + + assert_eq!(actual, "testme"); +} + +#[test] +fn append_plugin() { + let actual = nu!( + cwd: "tests/fixtures/formats", h::pipeline( + r#" + open fileA.txt + | lines + | append "testme" + | nth 3 + | echo $it + "# + )); + + assert_eq!(actual, "testme"); +} + #[test] fn edit_plugin() { let actual = nu!( From a9cd6b4f7afd8617936fd357c408dc4412857821 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 30 Oct 2019 20:04:39 +1300 Subject: [PATCH 340/342] Format files --- src/commands/append.rs | 4 +--- src/commands/prepend.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/commands/append.rs b/src/commands/append.rs index b8ca7b6e92..fe22c9065e 100644 --- a/src/commands/append.rs +++ b/src/commands/append.rs @@ -43,7 +43,5 @@ fn append( let mut after: VecDeque> = VecDeque::new(); after.push_back(row); - Ok(OutputStream::from_input( - input.values.chain(after), - )) + Ok(OutputStream::from_input(input.values.chain(after))) } diff --git a/src/commands/prepend.rs b/src/commands/prepend.rs index 4d9c037f43..b6fa935b0b 100644 --- a/src/commands/prepend.rs +++ b/src/commands/prepend.rs @@ -43,7 +43,5 @@ fn prepend( let mut prepend: VecDeque> = VecDeque::new(); prepend.push_back(row); - Ok(OutputStream::from_input( - prepend.chain(input.values), - )) + Ok(OutputStream::from_input(prepend.chain(input.values))) } From 2d44b7d296d24e8a875d350dc995e1de165b5475 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Wed, 30 Oct 2019 20:22:01 +1300 Subject: [PATCH 341/342] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 64ff0e8015..5e482bc29e 100644 --- a/README.md +++ b/README.md @@ -249,6 +249,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | command | description | | ------------- | ------------- | | add column-or-column-path value | Add a new column to the table | +| append row-data | Append a row to the end of the table | | count | Show the total number of rows | | edit column-or-column-path value | Edit an existing column to have a new value | | embed column | Creates a new table of one column with the given name, and places the current table inside of it | @@ -260,6 +261,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | nth row-number | Return only the selected row | | pick ...columns | Down-select table to only these columns | | pivot --header-row | Pivot the tables, making columns into rows and vice versa | +| prepend row-data | Prepend a row to the beginning of the table | | reject ...columns | Remove the given columns from the table | | reverse | Reverses the table. | | skip amount | Skip a number of rows | @@ -293,6 +295,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat | from-xml | Parse text as .xml and create a table | | from-yaml | Parse text as a .yaml/.yml and create a table | | lines | Split single string into rows, one per line | +| read pattern | Convert text to a table by matching the given pattern | | size | Gather word count statistics on the text | | split-column sep ...column-names | Split row contents across multiple columns via the separator, optionally give the columns names | | split-row sep | Split row contents over multiple rows via the separator | From fd922718847638805a6afa56009a7ba17bb305e4 Mon Sep 17 00:00:00 2001 From: Jonathan Turner Date: Thu, 31 Oct 2019 09:14:47 +1300 Subject: [PATCH 342/342] Move rustyline dep back to crates --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 52589cb733..97b02b450c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ documentation = "https://book.nushell.sh" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -rustyline = { git = "https://github.com/kkawakam/rustyline.git" } +rustyline = "5.0.4" chrono = { version = "0.4.9", features = ["serde"] } derive-new = "0.5.8" prettytable-rs = "0.8.0"

RU^3_3YY^M|1SmwL+Xy?& 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

*4 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 z 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