From a215997dcdc9d9123c6cff086b1dfb34bfea4a45 Mon Sep 17 00:00:00 2001 From: Jan Koprowski Date: Tue, 10 Sep 2019 18:40:14 +0200 Subject: [PATCH 01/33] 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 ab915f1c4439dd63fa6c7e2113b58e93b9e218c6 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Sat, 14 Sep 2019 11:30:24 -0500 Subject: [PATCH 02/33] 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 03/33] 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 04/33] 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 05/33] 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 06/33] 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 07/33] 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 08/33] 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 09/33] 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 10/33] 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 11/33] 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 12/33] 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 13/33] 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 14/33] 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 15/33] 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 16/33] 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 17/33] 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 18/33] 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 19/33] 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 20/33] 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 21/33] 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 22/33] 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 23/33] 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 24/33] 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 25/33] 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 26/33] 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 27/33] 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 28/33] 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 29/33] 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 30/33] 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 31/33] 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 32/33] 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 33/33] 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