Merge branch 'master' of github.com:jonnywalker81/nushell into initial-docker-command-impl

This commit is contained in:
Jonathan Rothberg 2019-09-22 18:53:31 -07:00
commit 707af3f3ca
127 changed files with 2217 additions and 1438 deletions

7
.gitignore vendored
View file

@ -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/

24
Cargo.lock generated
View file

@ -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)",
@ -1556,15 +1556,17 @@ 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)",
"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)",
"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)",
@ -2195,8 +2197,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)",
@ -2442,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"
@ -3142,7 +3149,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"
@ -3216,7 +3223,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)" = "<none>"
"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"
@ -3246,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"

View file

@ -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"
@ -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"
@ -72,6 +72,8 @@ 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"
sublime_fuzzy = "0.5"
neso = { version = "0.5.0", optional = true }
crossterm = { version = "0.10.2", optional = true }
@ -85,12 +87,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"

View file

@ -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 <headers> | 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.

5
debian/changelog vendored Normal file
View file

@ -0,0 +1,5 @@
nu (0.2.0-1) unstable; urgency=low
* Initial release
-- Jan Koprowski <jan.koprowski@gmail.com> Wed, 04 Sep 2019 21:38:44 +0200

1
debian/compat vendored Normal file
View file

@ -0,0 +1 @@
10

18
debian/control vendored Normal file
View file

@ -0,0 +1,18 @@
Source: nu
Section: shells
Priority: optional
Maintainer: Jan Koprowski <jan.koprowski@gmail.com>
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.

32
debian/copyright vendored Normal file
View file

@ -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.

10
debian/install vendored Normal file
View file

@ -0,0 +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

8
debian/postinst vendored Normal file
View file

@ -0,0 +1,8 @@
#! /bin/bash
if [ "$1" = configure ] && which add-shell >/dev/null
then
add-shell /usr/bin/nu
fi
exit 0

17
debian/postrm vendored Normal file
View file

@ -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

25
debian/rules vendored Executable file
View file

@ -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)

1
debian/source/format vendored Normal file
View file

@ -0,0 +1 @@
3.0 (quilt)

View file

@ -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

View file

@ -0,0 +1,55 @@
# Packaging
This directory contains docker images used for creating packages for different distribution.
## How to use this docker files?
Start with:
```bash
$ docker build -f docker/packaging/Dockerfile.ubuntu-bionic -t nushell/package:ubuntu-bionic .
```
after building the image please run container:
```bash
$ docker run -td --rm --name nushell_package_ubuntu_bionic nushell/package:ubuntu-bionic
```
and copy deb package from inside:
```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
* 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.
https://github.com/nushell/nushell/issues/682
* Building package and nu version should be parametrized.
https://github.com/nushell/nushell/issues/683

View file

@ -7,8 +7,10 @@ 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};
use crate::git::current_branch;
use crate::parser::registry::Signature;
use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode};
@ -21,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)]
@ -59,6 +62,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::<JsonRpc<Result<Signature, ShellError>>>(&input);
match response {
@ -208,6 +212,20 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
Ok(())
}
pub 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<dyn Error>> {
let mut context = Context::basic()?;
@ -239,11 +257,13 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
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),
whole_stream_command(First),
whole_stream_command(Last),
whole_stream_command(Env),
whole_stream_command(FromCSV),
whole_stream_command(FromTSV),
whole_stream_command(FromINI),
@ -252,6 +272,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
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),
@ -269,13 +290,13 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
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),
]);
@ -298,7 +319,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
}
// 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();
@ -319,7 +340,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
context.shell_manager.clone(),
)));
let edit_mode = crate::data::config::config(Span::unknown())?
let edit_mode = config::config(Tag::unknown())?
.get("edit_mode")
.map(|s| match s.as_string().unwrap().as_ref() {
"vi" => EditMode::Vi,
@ -330,14 +351,47 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
rl.set_edit_mode(edit_mode);
let readline = rl.readline(&format!(
// 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);
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 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 {
SelectionResult::Selected(line) => {
println!("{}{}", &prompt, &line); // TODO: colorize prompt
readline = Ok(line.clone());
initial_command = None;
}
SelectionResult::Edit(line) => {
initial_command = Some(line);
}
SelectionResult::NoSelection => {
readline = Ok("".to_string());
initial_command = None;
}
}
} else {
initial_command = None;
}
}
match process_line(readline, &mut context).await {
LineResult::Success(line) => {
@ -346,6 +400,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
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)"));
@ -380,7 +435,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
}
// we are ok if we can not save history
let _ = rl.save_history("history.txt");
let _ = rl.save_history(&History::path());
Ok(())
}
@ -397,7 +452,7 @@ async fn process_line(readline: Result<String, ReadlineError>, 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);
}
@ -419,7 +474,7 @@ async fn process_line(readline: Result<String, ReadlineError>, 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,
@ -431,6 +486,7 @@ async fn process_line(readline: Result<String, ReadlineError>, 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<ClassifiedCommand> = iter.next();
@ -456,20 +512,29 @@ async fn process_line(readline: Result<String, ReadlineError>, 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),
}
@ -496,7 +561,9 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
Err(err) => return LineResult::Error(line.clone(), err),
}
}
}
};
is_first_command = false;
}
LineResult::Success(line.clone())
@ -539,10 +606,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
@ -564,19 +631,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())),
}
}
@ -593,10 +660,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![],
@ -606,7 +670,7 @@ fn external_command(
ClassifiedCommand::External(ExternalCommand {
name: name.to_string(),
name_span: tag.span,
name_tag: tag,
args: arg_list_strings,
})
}

View file

@ -14,6 +14,7 @@ pub(crate) mod debug;
pub(crate) mod docker;
pub(crate) mod echo;
pub(crate) mod enter;
pub(crate) mod env;
pub(crate) mod exit;
pub(crate) mod fetch;
pub(crate) mod first;
@ -24,6 +25,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;
@ -37,6 +39,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;
@ -59,10 +62,10 @@ 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;
pub(crate) mod vtable;
pub(crate) mod where_;
pub(crate) mod which_;
@ -80,6 +83,7 @@ pub(crate) use debug::Debug;
pub(crate) use docker::Docker;
pub(crate) use echo::Echo;
pub(crate) use enter::Enter;
pub(crate) use env::Env;
pub(crate) use exit::Exit;
pub(crate) use fetch::Fetch;
pub(crate) use first::First;
@ -91,6 +95,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;
@ -105,6 +110,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;
@ -127,9 +133,9 @@ 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;
pub(crate) use vtable::VTable;
pub(crate) use where_::Where;
pub(crate) use which_::Which;

View file

@ -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::<Vec<_>>().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::<Vec<_>>().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::<Vec<_>>().await;
}
}
@ -110,14 +110,13 @@ fn is_single_origined_text_value(input: &Vec<Tagged<Value>>) -> bool {
if input.len() != 1 {
return false;
}
if let Tagged {
item: Value::Primitive(Primitive::String(_)),
tag: Tag {
origin: Some(_), ..
},
tag: Tag { origin, .. },
} = input[0]
{
true
origin != uuid::Uuid::nil()
} else {
false
}

View file

@ -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 {

View file

@ -86,7 +86,7 @@ pub(crate) enum ClassifiedCommand {
pub(crate) struct InternalCommand {
pub(crate) command: Arc<Command>,
pub(crate) name_span: Span,
pub(crate) name_tag: Tag,
pub(crate) args: hir::Call,
}
@ -96,6 +96,7 @@ impl InternalCommand {
context: &mut Context,
input: ClassifiedInputStream,
source: Text,
is_first_command: bool,
) -> Result<InputStream, ShellError> {
if log_enabled!(log::Level::Trace) {
trace!(target: "nu::run::internal", "->");
@ -108,11 +109,12 @@ impl InternalCommand {
let result = context.run_command(
self.command,
self.name_span.clone(),
self.name_tag.clone(),
context.source_map.clone(),
self.args,
&source,
objects,
is_first_command,
);
let result = trace_out_stream!(target: "nu::trace_stream::internal", source: &source, "output" = result);
@ -128,26 +130,23 @@ 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 {
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())?,
));
}
}
@ -171,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
}
}
},
@ -189,7 +188,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<Tagged<String>>,
}
@ -208,7 +207,7 @@ impl ExternalCommand {
) -> Result<ClassifiedInputStream, ShellError> {
let stdin = input.stdin;
let inputs: Vec<Tagged<Value>> = 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 +226,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 +315,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<Value>>
))

View file

@ -51,7 +51,7 @@ pub mod clipboard {
Ok(OutputStream::from(stream))
}
async fn inner_clip(input: Vec<Tagged<Value>>, name: Span) -> OutputStream {
async fn inner_clip(input: Vec<Tagged<Value>>, name: Tag) -> OutputStream {
let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap();
let mut new_copy_data = String::new();

View file

@ -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,43 +38,16 @@ impl UnevaluatedCallInfo {
Ok(CallInfo {
args,
source_map: self.source_map,
name_span: self.name_span,
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)]
pub struct CallInfo {
pub args: registry::EvaluatedArgs,
pub source_map: SourceMap,
pub name_span: Span,
pub name_tag: Tag,
}
impl CallInfo {
@ -89,7 +62,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 +131,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 +140,7 @@ impl CommandArgs {
input,
commands: registry.clone(),
shell_manager,
name: name_span,
name: name_tag,
source_map,
host,
},
@ -191,7 +164,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 +173,7 @@ impl CommandArgs {
input,
commands: registry.clone(),
shell_manager,
name: name_span,
name: name_tag,
source_map,
host,
},
@ -212,7 +185,7 @@ impl CommandArgs {
pub struct RunnablePerItemContext {
pub shell_manager: ShellManager,
pub name: Span,
pub name: Tag,
}
impl RunnablePerItemContext {
@ -227,7 +200,7 @@ pub struct RunnableContext {
pub host: Arc<Mutex<dyn Host>>,
pub commands: CommandRegistry,
pub source_map: SourceMap,
pub name: Span,
pub name: Tag,
}
impl RunnableContext {
@ -311,8 +284,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 +444,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 {
@ -562,13 +529,20 @@ impl Command {
}
}
pub fn run(&self, args: CommandArgs, registry: &registry::CommandRegistry) -> OutputStream {
pub fn run(
&self,
args: CommandArgs,
registry: &registry::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)
}
}
}
@ -577,6 +551,7 @@ impl Command {
command: Arc<dyn PerItemCommand>,
args: CommandArgs,
registry: CommandRegistry,
is_first_command: bool,
) -> OutputStream {
let raw_args = RawCommandArgs {
host: args.host,
@ -584,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
@ -609,7 +584,6 @@ impl Command {
.call_info
.evaluate(&registry, &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, &registry, &raw_args, nothing)

View file

@ -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());
}

View file

@ -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")
}

View file

@ -33,58 +33,40 @@ impl WholeStreamCommand for Date {
}
}
pub fn date_to_value<T: TimeZone>(dt: DateTime<T>, span: Span) -> Tagged<Value>
pub fn date_to_value<T: TimeZone>(dt: DateTime<T>, tag: Tag) -> Tagged<Value>
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<OutputStream, ShellError> {
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> = Utc::now();
date_to_value(utc, span)
date_to_value(utc, tag)
} else {
let local: DateTime<Local> = Local::now();
date_to_value(local, span)
date_to_value(local, tag)
};
date_out.push_back(value);

View file

@ -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<OutputStream, ShellError> {
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())

View file

@ -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::*;
@ -14,7 +15,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 {
@ -74,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,
));
}
@ -103,12 +104,13 @@ 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(
new_args.with_input(vec![tagged_contents]),
&registry,
false
);
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await;

76
src/commands/env.rs Normal file
View file

@ -0,0 +1,76 @@
use crate::cli::History;
use crate::data::config;
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<OutputStream, ShellError> {
env(args, registry)
}
}
pub fn get_environment(tag: Tag) -> Result<Tagged<Value>, Box<dyn std::error::Error>> {
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 config = config::default_path()?;
indexmap.insert("config".to_string(), Value::path(config).tagged(tag));
let history = History::path();
indexmap.insert("history".to_string(), Value::path(history).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<OutputStream, ShellError> {
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())
}

View file

@ -1,8 +1,9 @@
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::SyntaxType;
use crate::parser::hir::SyntaxShape;
use crate::parser::registry::Signature;
use crate::prelude::*;
use mime::Mime;
@ -19,7 +20,7 @@ impl PerItemCommand for Fetch {
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("path", SyntaxType::Path)
.required("path", SyntaxShape::Path)
.switch("raw")
}
@ -75,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,
));
}
@ -99,10 +100,10 @@ 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]), &registry);
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), &registry, false);
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
for res in result_vec {
match res {
@ -157,7 +158,7 @@ pub async fn fetch(
})?),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::Url(location.to_string()),
)),
@ -172,7 +173,7 @@ pub async fn fetch(
})?),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::Url(location.to_string()),
)),
@ -186,10 +187,10 @@ pub async fn fetch(
})?;
Ok((
None,
Value::Primitive(Primitive::Binary(buf)),
Value::binary(buf),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::Url(location.to_string()),
))
@ -205,7 +206,7 @@ pub async fn fetch(
})?),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::Url(location.to_string()),
)),
@ -219,10 +220,10 @@ pub async fn fetch(
})?;
Ok((
Some(image_ty.to_string()),
Value::Primitive(Primitive::Binary(buf)),
Value::binary(buf),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::Url(location.to_string()),
))
@ -238,7 +239,7 @@ pub async fn fetch(
})?),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::Url(location.to_string()),
)),
@ -265,7 +266,7 @@ pub async fn fetch(
})?),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::Url(location.to_string()),
))
@ -275,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()),
)),
@ -286,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()),
)),

View file

@ -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 {

View file

@ -198,7 +198,7 @@ pub fn from_bson_bytes_to_value(
fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<OutputStre
let value_tag = value.tag();
match value.item {
Value::Primitive(Primitive::Binary(vb)) =>
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,
)),
}

View file

@ -86,7 +86,7 @@ fn from_csv(
}: FromCSVArgs,
RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let name_span = name;
let name_tag = name;
let stream = async_stream_block! {
let values: Vec<Tagged<Value>> = 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,
))
} ,
}

View file

@ -64,7 +64,7 @@ pub fn from_ini_string_to_value(
fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<OutputStrea
_ => 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<OutputStrea
yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as INI",
"input cannot be parsed as INI",
span,
tag,
"value originates from here",
last_tag.span,
last_tag,
))
} ,
}

View file

@ -72,7 +72,7 @@ fn from_json(
FromJSONArgs { objects }: FromJSONArgs,
RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let name_span = name;
let name_tag = name;
let stream = async_stream_block! {
let values: Vec<Tagged<Value>> = 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))
}
}
}

View file

@ -128,7 +128,7 @@ pub fn from_sqlite_bytes_to_value(
fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<OutputSt
let value_tag = value.tag();
match value.item {
Value::Primitive(Primitive::Binary(vb)) =>
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<OutputSt
yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as SQLite",
"input cannot be parsed as SQLite",
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,
)),
}

View file

@ -68,7 +68,7 @@ pub fn from_toml(
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
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,
))
} ,
}

View file

@ -87,7 +87,7 @@ fn from_tsv(
}: FromTSVArgs,
RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let name_span = name;
let name_tag = name;
let stream = async_stream_block! {
let values: Vec<Tagged<Value>> = 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,
))
} ,
}

85
src/commands/from_url.rs Normal file
View file

@ -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<OutputStream, ShellError> {
from_url(args, registry)
}
}
fn from_url(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?;
let tag = args.name_tag();
let input = args.input;
let stream = async_stream_block! {
let values: Vec<Tagged<Value>> = input.values.collect().await;
let mut concat_string = String::new();
let mut latest_tag: Option<Tag> = 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::<Vec<(String, String)>>(&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())
}

View file

@ -83,7 +83,7 @@ pub fn from_xml_string_to_value(
fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<OutputStrea
_ => 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<OutputStrea
yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as XML",
"input cannot be parsed as XML",
span,
tag,
"value originates from here",
last_tag.span,
last_tag,
))
} ,
}

View file

@ -97,7 +97,7 @@ pub fn from_yaml_string_to_value(
fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<OutputStre
_ => 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<OutputStre
yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as YAML",
"input cannot be parsed as YAML",
span,
tag,
"value originates from here",
last_tag.span,
last_tag,
))
} ,
}

View file

@ -18,8 +18,8 @@ impl WholeStreamCommand for Get {
fn signature(&self) -> Signature {
Signature::build("get")
.required("member", SyntaxType::Member)
.rest(SyntaxType::Member)
.required("member", SyntaxShape::Member)
.rest(SyntaxShape::Member)
}
fn usage(&self) -> &str {
@ -61,7 +61,7 @@ fn get_member(path: &Tagged<String>, obj: &Tagged<Value>) -> Result<Tagged<Value
return Err(ShellError::labeled_error(
"Unknown column",
format!("did you mean '{}'?", possible_matches[0].1),
path.span(),
path.tag(),
));
}
}

View file

@ -12,7 +12,7 @@ impl PerItemCommand for Help {
}
fn signature(&self) -> 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<Value>,
) -> Result<OutputStream, ShellError> {
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())
}

View file

@ -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 {

View file

@ -32,7 +32,7 @@ impl WholeStreamCommand for Lines {
fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<OutputStream,
result.push_back(Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline",
"requires string input",
span,
tag,
"value originates from here",
v.span(),
v.tag(),
)));
result
}

View file

@ -1,16 +1,22 @@
use crate::commands::WholeStreamCommand;
use crate::errors::ShellError;
use crate::prelude::*;
use std::path::PathBuf;
pub struct LS;
#[derive(Deserialize)]
pub struct LsArgs {
path: Option<Tagged<PathBuf>>,
}
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<OutputStream, ShellError> {
ls(args, registry)
args.process(registry, ls)?.run()
// ls(args, registry)
}
}
fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<OutputStream, ShellError> {
context.shell_manager.ls(path, context.name)
}

View file

@ -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 {

View file

@ -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 {

View file

@ -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 {

View file

@ -1,8 +1,9 @@
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::SyntaxType;
use crate::parser::hir::SyntaxShape;
use crate::parser::registry::Signature;
use crate::prelude::*;
use std::path::{Path, PathBuf};
@ -16,7 +17,7 @@ impl PerItemCommand for Open {
fn signature(&self) -> Signature {
Signature::build(self.name())
.required("path", SyntaxType::Path)
.required("path", SyntaxShape::Path)
.switch("raw")
}
@ -76,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,
));
}
@ -100,10 +101,10 @@ 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]), &registry);
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), &registry, false);
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
for res in result_vec {
match res {
@ -146,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()),
)),
@ -165,16 +166,16 @@ 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()),
)),
Err(_) => Ok((
None,
Value::Primitive(Primitive::Binary(bytes)),
Value::binary(bytes),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::File(cwd.to_string_lossy().to_string()),
)),
@ -182,10 +183,10 @@ pub async fn fetch(
} else {
Ok((
None,
Value::Primitive(Primitive::Binary(bytes)),
Value::binary(bytes),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::File(cwd.to_string_lossy().to_string()),
))
@ -203,16 +204,16 @@ 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()),
)),
Err(_) => Ok((
None,
Value::Primitive(Primitive::Binary(bytes)),
Value::binary(bytes),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::File(cwd.to_string_lossy().to_string()),
)),
@ -220,10 +221,10 @@ pub async fn fetch(
} else {
Ok((
None,
Value::Primitive(Primitive::Binary(bytes)),
Value::binary(bytes),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::File(cwd.to_string_lossy().to_string()),
))
@ -231,10 +232,10 @@ pub async fn fetch(
}
_ => Ok((
None,
Value::Primitive(Primitive::Binary(bytes)),
Value::binary(bytes),
Tag {
span,
origin: Some(Uuid::new_v4()),
origin: Uuid::new_v4(),
},
SpanSource::File(cwd.to_string_lossy().to_string()),
)),

View file

@ -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 {

133
src/commands/pivot.rs Normal file
View file

@ -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<Tagged<String>>,
#[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<OutputStream, ShellError> {
args.process(registry, pivot)?.run()
}
}
fn merge_descriptors(values: &[Tagged<Value>]) -> Vec<String> {
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<OutputStream, ShellError> {
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))
}

View file

@ -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
@ -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,
));
}
@ -109,10 +109,10 @@ 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]), &registry);
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), &registry, false);
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
for res in result_vec {
match res {
@ -143,7 +143,7 @@ pub async fn post(
body: &Tagged<Value>,
user: Option<String>,
password: Option<String>,
span: Span,
tag: Tag,
registry: &CommandRegistry,
raw_args: &RawCommandArgs,
) -> Result<(Option<String>, Value, Tag, SpanSource), ShellError> {
@ -189,12 +189,13 @@ 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(
new_args.with_input(vec![item.clone().tagged(tag.clone())]),
&registry,
false,
);
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await;
@ -211,7 +212,7 @@ pub async fn post(
return Err(ShellError::labeled_error(
"Save could not successfully save",
"unexpected data during save",
span,
*tag,
));
}
}
@ -227,7 +228,7 @@ pub async fn post(
return Err(ShellError::labeled_error(
"Could not automatically convert table",
"needs manual conversion",
tag.span,
*tag,
));
}
}
@ -243,13 +244,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 +256,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 +267,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 +282,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 +298,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 +322,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 +335,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 +343,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 +351,7 @@ pub async fn post(
return Err(ShellError::labeled_error(
"URL could not be opened",
"url not found",
span,
tag,
));
}
}
@ -382,7 +359,7 @@ pub async fn post(
Err(ShellError::labeled_error(
"Expected a url",
"needs a url",
span,
tag,
))
}
}

View file

@ -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 {

View file

@ -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")
}

View file

@ -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<u8> = 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<OutputStream, ShellError> {
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! {
@ -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));
@ -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), &registry);
let mut result = converter.run(new_args.with_input(input), &registry, false);
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = 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())

View file

@ -29,10 +29,10 @@ impl WholeStreamCommand for Shells {
fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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());

View file

@ -29,7 +29,7 @@ impl WholeStreamCommand for Size {
fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<OutputStream,
_ => 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<Tag>) -> Tagged<Value> {
}
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));

View file

@ -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()
}

View file

@ -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 {

View file

@ -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())

View file

@ -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
}

View file

@ -36,13 +36,13 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream,
let mut tags = TaggedDictBuilder::new(v.tag());
{
let origin = v.origin();
let span = v.span();
let span = v.tag().span;
let mut dict = TaggedDictBuilder::new(v.tag());
dict.insert("start", Value::int(span.start as i64));
dict.insert("end", Value::int(span.end as i64));
tags.insert_tagged("span", dict.into_tagged_value());
match origin.and_then(|x| source_map.get(&x)) {
match source_map.get(&origin) {
Some(SpanSource::File(source)) => {
tags.insert("origin", Value::string(source));
}

View file

@ -190,44 +190,40 @@ fn generic_object_value_to_bson(o: &Dictionary) -> Result<Bson, ShellError> {
Ok(Bson::Document(doc))
}
fn shell_encode_document(
writer: &mut Vec<u8>,
doc: Document,
span: Span,
) -> Result<(), ShellError> {
fn shell_encode_document(writer: &mut Vec<u8>, 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<Vec<u8>, ShellError> {
fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result<Vec<u8>, 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<Vec<u8>, ShellError> {
fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<Tagged<Value>> = args.input.values.collect().await;
@ -252,23 +248,23 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
for value in to_process_input {
match value_to_bson_value(&value) {
Ok(bson_value) => {
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))
}
}
};

View file

@ -134,7 +134,7 @@ fn to_csv(
ToCSVArgs { headerless }: ToCSVArgs,
RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let name_span = name;
let name_tag = name;
let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = 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(),
))
}
}

View file

@ -80,7 +80,7 @@ fn json_list(input: &Vec<Tagged<Value>>) -> Result<Vec<serde_json::Value>, Shell
fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<Tagged<Value>> = args.input.values.collect().await;
@ -98,21 +98,21 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
Ok(json_value) => {
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))
}
}
};

View file

@ -200,7 +200,7 @@ fn sqlite_input_stream_to_bytes(
fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<Tagged<Value>> = args.input.values.collect().await;
@ -208,9 +208,9 @@ fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
Ok(out) => 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,
))
},
}

View file

@ -75,7 +75,7 @@ fn collect_values(input: &Vec<Tagged<Value>>) -> Result<Vec<toml::Value>, ShellE
fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<Tagged<Value>> = args.input.values.collect().await;
@ -93,21 +93,21 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
Ok(toml_value) => {
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))
}
}
};

View file

@ -133,7 +133,7 @@ fn to_tsv(
ToTSVArgs { headerless }: ToTSVArgs,
RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> {
let name_span = name;
let name_tag = name;
let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = 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(),
))
}
}

85
src/commands/to_url.rs Normal file
View file

@ -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<OutputStream, ShellError> {
to_url(args, registry)
}
}
fn to_url(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?;
let tag = args.name_tag();
let input = args.input;
let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = 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())
}

View file

@ -76,7 +76,7 @@ pub fn value_to_yaml_value(v: &Tagged<Value>) -> Result<serde_yaml::Value, Shell
fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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<Tagged<Value>> = args.input.values.collect().await;
@ -94,21 +94,21 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
Ok(yaml_value) => {
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))
}
}
};

View file

@ -34,7 +34,7 @@ fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream,
.values
.map(move |v| {
let string = String::extract(&v)?;
ReturnSuccess::value(Value::string(string.trim()).simple_spanned(v.span()))
ReturnSuccess::value(Value::string(string.trim()).tagged(v.tag()))
})
.to_output_stream())
}

View file

@ -31,14 +31,14 @@ impl WholeStreamCommand for Version {
pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
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))
}

View file

@ -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<OutputStream, ShellError> {
args.process(registry, vtable)?.run()
}
}
pub fn vtable(_args: VTableArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
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))
}

View file

@ -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,
))
}
};

View file

@ -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<OutputStre
let args = args.evaluate_once(registry)?;
let mut which_out = VecDeque::new();
let span = args.call_info.name_span;
let tag = args.call_info.name_tag;
if let Some(v) = &args.call_info.args.positional {
if v.len() > 0 {
@ -52,7 +52,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
return Err(ShellError::labeled_error(
"Expected a filename to find",
"needs a filename",
tag.span,
*tag,
));
}
}
@ -60,14 +60,14 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
return Err(ShellError::labeled_error(
"Expected a binary to find",
"needs application name",
span,
tag,
));
}
} else {
return Err(ShellError::labeled_error(
"Expected a binary to find",
"needs application name",
span,
tag,
));
}

View file

@ -120,14 +120,15 @@ impl Context {
pub(crate) fn run_command<'a>(
&mut self,
command: Arc<Command>,
name_span: Span,
name_tag: Tag,
source_map: SourceMap,
args: hir::Call,
source: &Text,
input: InputStream,
is_first_command: bool,
) -> OutputStream {
let command_args = self.command_args(args, input, source, source_map, name_span);
command.run(command_args, self.registry())
let command_args = self.command_args(args, input, source, source_map, name_tag);
command.run(command_args, self.registry(), is_first_command)
}
fn call_info(
@ -135,13 +136,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 +152,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,
}
}

View file

@ -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<S>(big_int: &super::BigInt, serializer: S) -> Result<S::Ok, S::Error>
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<super::BigInt, D::Error>
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<S>(big_decimal: &super::BigDecimal, serializer: S) -> Result<S::Ok, S::Error>
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<super::BigDecimal, D::Error>
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),
@ -150,7 +204,7 @@ pub struct Operation {
pub struct Block {
pub(crate) expressions: Vec<hir::Expression>,
pub(crate) source: Text,
pub(crate) span: Span,
pub(crate) tag: Tag,
}
impl Block {
@ -158,7 +212,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 +303,7 @@ impl std::convert::TryFrom<&Tagged<Value>> 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 +319,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for i64 {
}
v => Err(ShellError::type_error(
"Integer",
value.copy_span(v.type_name()),
value.copy_tag(v.type_name()),
)),
}
}
@ -279,7 +333,7 @@ impl std::convert::TryFrom<&Tagged<Value>> 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 +347,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for Vec<u8> {
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 +361,7 @@ impl<'a> std::convert::TryFrom<&'a Tagged<Value>> 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 +383,7 @@ impl std::convert::TryFrom<Option<&Tagged<Value>>> 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 +695,7 @@ impl Tagged<Value> {
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()),
)),
}
}

View file

@ -23,10 +23,7 @@ pub const APP_INFO: AppInfo = AppInfo {
};
pub fn config_path() -> Result<PathBuf, ShellError> {
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<PathBuf, ShellError> {
@ -49,8 +46,19 @@ pub fn default_path_for(file: &Option<PathBuf>) -> Result<PathBuf, ShellError> {
Ok(filename.clone())
}
pub fn user_data() -> Result<PathBuf, ShellError> {
app_path(AppDataType::UserData, "user data")
}
pub fn app_path(app_data_type: AppDataType, display: &str) -> Result<PathBuf, ShellError> {
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(
span: impl Into<Span>,
tag: impl Into<Tag>,
at: &Option<PathBuf>,
) -> Result<IndexMap<String, Tagged<Value>>, ShellError> {
let filename = default_path()?;
@ -64,15 +72,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 +91,8 @@ pub fn read(
}
}
pub(crate) fn config(span: impl Into<Span>) -> Result<IndexMap<String, Tagged<Value>>, ShellError> {
read(span, &None)
pub(crate) fn config(tag: impl Into<Tag>) -> Result<IndexMap<String, Tagged<Value>>, ShellError> {
read(tag, &None)
}
pub fn write(

View file

@ -169,6 +169,10 @@ impl TaggedDictBuilder {
pub fn into_tagged_dict(self) -> Tagged<Dictionary> {
Dictionary { entries: self.dict }.tagged(self.tag)
}
pub fn is_empty(&self) -> bool {
self.dict.is_empty()
}
}
impl From<TaggedDictBuilder> for Tagged<Value> {

View file

@ -15,8 +15,8 @@ impl From<String> for Value {
impl<T: Into<Value>> Tagged<T> {
pub fn into_tagged_value(self) -> Tagged<Value> {
let value_span = self.span();
let value_tag = self.tag();
let value = self.item.into();
value.simple_spanned(value_span)
value.tagged(value_tag)
}
}

View file

@ -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<T> {
pub item: T,
}
impl<T> HasSpan for Tagged<T> {
fn span(&self) -> Span {
self.tag.span
impl<T> HasTag for Tagged<T> {
fn tag(&self) -> Tag {
self.tag
}
}
impl AsRef<Path> for Tagged<PathBuf> {
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<Span>) -> Tagged<Self> {
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.
@ -36,7 +39,7 @@ pub trait TaggedItem: Sized {
self,
Tag {
span: Span::unknown(),
origin: None,
origin: uuid::Uuid::nil(),
},
)
}
@ -53,14 +56,8 @@ impl<T> std::ops::Deref for Tagged<T> {
}
impl<T> Tagged<T> {
pub fn spanned(self, span: impl Into<Span>) -> Tagged<T> {
Tagged::from_item(
self.item,
Tag {
span: span.into(),
origin: None,
},
)
pub fn with_tag(self, tag: impl Into<Tag>) -> Tagged<T> {
Tagged::from_item(self.item, tag)
}
pub fn from_item(item: T, tag: impl Into<Tag>) -> Tagged<T> {
@ -70,49 +67,37 @@ impl<T> Tagged<T> {
}
}
pub fn from_simple_spanned_item(item: T, span: impl Into<Span>) -> Tagged<T> {
Tagged::from_item(
item,
Tag {
span: span.into(),
origin: None,
},
)
}
pub fn map<U>(self, input: impl FnOnce(T) -> U) -> Tagged<U> {
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<U>(&self, output: U) -> Tagged<U> {
let span = self.span();
Tagged::from_simple_spanned_item(output, span)
pub(crate) fn copy_tag<U>(&self, output: U) -> Tagged<U> {
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
}
pub fn origin(&self) -> Option<uuid::Uuid> {
pub fn span(&self) -> Span {
self.tag.span
}
pub fn origin(&self) -> uuid::Uuid {
self.tag.origin
}
pub fn origin_name(&self, source_map: &SourceMap) -> Option<String> {
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,
}
}
@ -126,20 +111,14 @@ impl<T> Tagged<T> {
}
}
impl<T> From<&Tagged<T>> for Span {
fn from(input: &Tagged<T>) -> 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<nom5_locate::LocatedSpan<&str>> for Span {
fn from(input: nom5_locate::LocatedSpan<&str>) -> Span {
impl From<nom_locate::LocatedSpanEx<&str, Uuid>> for Span {
fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Span {
Span {
start: input.offset,
end: input.offset + input.fragment.len(),
@ -147,8 +126,18 @@ impl From<nom5_locate::LocatedSpan<&str>> for Span {
}
}
impl<T> From<(nom5_locate::LocatedSpan<T>, nom5_locate::LocatedSpan<T>)> for Span {
fn from(input: (nom5_locate::LocatedSpan<T>, nom5_locate::LocatedSpan<T>)) -> Span {
impl<T>
From<(
nom_locate::LocatedSpanEx<T, Uuid>,
nom_locate::LocatedSpanEx<T, Uuid>,
)> for Span
{
fn from(
input: (
nom_locate::LocatedSpanEx<T, Uuid>,
nom_locate::LocatedSpanEx<T, Uuid>,
),
) -> Span {
Span {
start: input.0.offset,
end: input.1.offset,
@ -178,25 +167,62 @@ impl From<&std::ops::Range<usize>> for Span {
Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters,
)]
pub struct Tag {
pub origin: Option<Uuid>,
pub origin: Uuid,
pub span: Span,
}
impl From<Span> 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,
}
}
}
impl From<(usize, usize, Uuid)> for Tag {
fn from((start, end, origin): (usize, usize, Uuid)) -> Self {
Tag {
origin,
span: Span { start, end },
}
}
}
impl From<(usize, usize, Option<Uuid>)> for Tag {
fn from((start, end, origin): (usize, usize, Option<Uuid>)) -> Self {
Tag {
origin: if let Some(uuid) = origin {
uuid
} else {
uuid::Uuid::nil()
},
span: Span { start, end },
}
}
}
impl From<nom_locate::LocatedSpanEx<&str, Uuid>> for Tag {
fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Tag {
Tag {
origin: input.extra,
span: Span {
start: input.offset,
end: input.offset + input.fragment.len(),
},
}
}
}
impl From<Tag> for Span {
fn from(tag: Tag) -> Self {
tag.span
@ -211,15 +237,45 @@ 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,
span: Span::unknown(),
}
}
pub fn unknown() -> Tag {
Tag {
origin: None,
origin: uuid::Uuid::nil(),
span: Span::unknown(),
}
}
pub fn until(&self, other: impl Into<Tag>) -> 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 +340,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
}
}

View file

@ -14,20 +14,22 @@ pub enum Description {
impl Description {
pub fn from(value: Tagged<impl Into<String>>) -> 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<Label<Span>, String> {
fn into_label(self) -> Result<Label<Tag>, 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<Span>, reason: impl Into<String>) -> ShellError {
pub(crate) fn missing_value(tag: Option<Tag>, reason: impl Into<String>) -> ShellError {
ProximateShellError::MissingValue {
span,
tag,
reason: reason.into(),
}
.start()
@ -127,28 +129,31 @@ impl ShellError {
pub(crate) fn argument_error(
command: impl Into<String>,
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<Span>) -> ShellError {
pub(crate) fn diagnostic(diagnostic: Diagnostic<Tag>) -> ShellError {
ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start()
}
pub(crate) fn to_diagnostic(self) -> Diagnostic<Span> {
pub(crate) fn to_diagnostic(self) -> Diagnostic<Tag> {
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<String>,
label: impl Into<String>,
span: impl Into<Span>,
tag: impl Into<Tag>,
) -> 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<String>,
primary_label: impl Into<String>,
primary_span: Span,
primary_span: impl Into<Tag>,
secondary_label: impl Into<String>,
secondary_span: Span,
secondary_span: impl Into<Tag>,
) -> 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<Span>,
tag: Option<Tag>,
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<Span>,
pub(crate) diagnostic: Diagnostic<Tag>,
}
impl PartialEq for ShellDiagnostic {

View file

@ -38,13 +38,13 @@ pub(crate) fn evaluate_baseline_expr(
source: &Text,
) -> Result<Tagged<Value>, 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<Val
let result = match literal.item {
hir::Literal::Number(int) => 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<Tagged<Value>, 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<Tagged<Value>, ShellError> {
Err(ShellError::syntax_error(
"Unexpected external command".tagged(external.name()),
"Unexpected external command".tagged(*external.name()),
))
}

View file

@ -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>;

View file

@ -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,

View file

@ -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<Vec<String>>,
}
impl VTableView {
pub fn from_list(values: &[Tagged<Value>]) -> Option<VTableView> {
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(())
}
}

184
src/fuzzysearch.rs Normal file
View file

@ -0,0 +1,184 @@
use ansi_term::{ANSIString, ANSIStrings, Colour, Style};
use crossterm::{cursor, terminal, ClearType, InputEvent, KeyEvent, RawScreen};
use std::io::Write;
use sublime_fuzzy::best_match;
pub enum SelectionResult {
Selected(String),
Edit(String),
NoSelection,
}
pub fn interactive_fuzzy_search(lines: &Vec<&str>, max_results: usize) -> SelectionResult {
#[derive(PartialEq)]
enum State {
Selecting,
Quit,
Selected(String),
Edit(String),
}
let mut state = State::Selecting;
if let Ok(_raw) = RawScreen::into_raw_mode() {
// User input for search
let mut searchinput = String::new();
let mut selected = 0;
let mut cursor = cursor();
let _ = cursor.hide();
let input = crossterm::input();
let mut sync_stdin = input.read_sync();
while state == State::Selecting {
let mut selected_lines = fuzzy_search(&searchinput, &lines, max_results);
let num_lines = selected_lines.len();
paint_selection_list(&selected_lines, selected);
if let Some(ev) = sync_stdin.next() {
match ev {
InputEvent::Keyboard(k) => match k {
KeyEvent::Esc | KeyEvent::Ctrl('c') => {
state = State::Quit;
}
KeyEvent::Up => {
if selected > 0 {
selected -= 1;
}
}
KeyEvent::Down => {
if selected + 1 < selected_lines.len() {
selected += 1;
}
}
KeyEvent::Char('\n') => {
state = if selected_lines.len() > 0 {
State::Selected(selected_lines.remove(selected).text)
} else {
State::Edit("".to_string())
};
}
KeyEvent::Char('\t') | KeyEvent::Right => {
state = if selected_lines.len() > 0 {
State::Edit(selected_lines.remove(selected).text)
} else {
State::Edit("".to_string())
};
}
KeyEvent::Char(ch) => {
searchinput.push(ch);
selected = 0;
}
KeyEvent::Backspace => {
searchinput.pop();
selected = 0;
}
_ => {
// println!("OTHER InputEvent: {:?}", k);
}
},
_ => {}
}
}
if num_lines > 0 {
cursor.move_up(num_lines as u16);
}
}
let (_x, y) = cursor.pos();
let _ = cursor.goto(0, y - 1);
let _ = cursor.show();
let _ = RawScreen::disable_raw_mode();
}
terminal().clear(ClearType::FromCursorDown).unwrap();
match state {
State::Selected(line) => SelectionResult::Selected(line),
State::Edit(line) => SelectionResult::Edit(line),
_ => SelectionResult::NoSelection,
}
}
pub struct Match {
text: String,
char_matches: Vec<(usize, usize)>,
}
pub fn fuzzy_search(searchstr: &str, lines: &Vec<&str>, max_results: usize) -> Vec<Match> {
if searchstr.is_empty() {
return lines
.iter()
.take(max_results)
.map(|line| Match {
text: line.to_string(),
char_matches: Vec::new(),
})
.collect();
}
let mut matches = lines
.iter()
.enumerate()
.map(|(idx, line)| (idx, best_match(&searchstr, line)))
.filter(|(_i, m)| m.is_some())
.map(|(i, m)| (i, m.unwrap()))
.collect::<Vec<_>>();
matches.sort_by(|a, b| b.1.score().cmp(&a.1.score()));
let results: Vec<Match> = matches
.iter()
.take(max_results)
.map(|(i, m)| Match {
text: lines[*i].to_string(),
char_matches: m.continuous_matches(),
})
.collect();
results
}
fn highlight(textmatch: &Match, normal: Style, highlighted: Style) -> Vec<ANSIString> {
let text = &textmatch.text;
let mut ansi_strings = vec![];
let mut idx = 0;
for (match_idx, len) in &textmatch.char_matches {
ansi_strings.push(normal.paint(&text[idx..*match_idx]));
idx = match_idx + len;
ansi_strings.push(highlighted.paint(&text[*match_idx..idx]));
}
if idx < text.len() {
ansi_strings.push(normal.paint(&text[idx..text.len()]));
}
ansi_strings
}
fn paint_selection_list(lines: &Vec<Match>, 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));
let (style, highlighted) = if selected == i {
(Colour::White.normal(), Colour::Cyan.normal())
} else {
(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!(
"{}",
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();
}
#[test]
fn fuzzy_match() {
let matches = fuzzy_search("cb", &vec!["abc", "cargo build"], 1);
assert_eq!(matches[0].text, "cargo build");
}

View file

@ -12,6 +12,7 @@ mod env;
mod errors;
mod evaluate;
mod format;
mod fuzzysearch;
mod git;
mod parser;
mod plugin;
@ -23,7 +24,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 +32,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;

View file

@ -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<TokenNode, ShellError> {
pub fn parse(input: &str, origin: uuid::Uuid) -> Result<TokenNode, ShellError> {
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)),
}

View file

@ -37,7 +37,7 @@ impl<'de> ConfigDeserializer<'de> {
let value: Option<Tagged<Value>> = 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(())

View file

@ -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<Expression>, tail: Vec<Tagged<impl Into<String>>>) -> Path {
Path::new(
@ -131,72 +131,57 @@ impl RawExpression {
pub type Expression = Tagged<RawExpression>;
impl Expression {
pub(crate) fn number(i: impl Into<Number>, span: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Number(i.into())), span)
pub(crate) fn number(i: impl Into<Number>, tag: impl Into<Tag>) -> Expression {
RawExpression::Literal(Literal::Number(i.into())).tagged(tag.into())
}
pub(crate) fn size(
i: impl Into<Number>,
unit: impl Into<Unit>,
span: impl Into<Span>,
tag: impl Into<Tag>,
) -> 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<String>) -> Expression {
RawExpression::Synthetic(Synthetic::String(s.into())).tagged_unknown()
}
pub(crate) fn string(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(
RawExpression::Literal(Literal::String(inner.into())),
outer.into(),
)
pub(crate) fn string(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
RawExpression::Literal(Literal::String(inner.into())).tagged(outer.into())
}
pub(crate) fn file_path(path: impl Into<PathBuf>, outer: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::FilePath(path.into()), outer.into())
pub(crate) fn file_path(path: impl Into<PathBuf>, outer: impl Into<Tag>) -> Expression {
RawExpression::FilePath(path.into()).tagged(outer)
}
pub(crate) fn bare(span: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into())
pub(crate) fn bare(tag: impl Into<Tag>) -> Expression {
RawExpression::Literal(Literal::Bare).tagged(tag)
}
pub(crate) fn pattern(tag: impl Into<Tag>) -> Expression {
RawExpression::Literal(Literal::GlobPattern).tagged(tag.into())
}
pub(crate) fn variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(
RawExpression::Variable(Variable::Other(inner.into())),
outer.into(),
)
pub(crate) fn variable(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
RawExpression::Variable(Variable::Other(inner.into())).tagged(outer)
}
pub(crate) fn external_command(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(
RawExpression::ExternalCommand(ExternalCommand::new(inner.into())),
outer.into(),
)
pub(crate) fn external_command(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
RawExpression::ExternalCommand(ExternalCommand::new(inner.into())).tagged(outer)
}
pub(crate) fn it_variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
Tagged::from_simple_spanned_item(
RawExpression::Variable(Variable::It(inner.into())),
outer.into(),
)
pub(crate) fn it_variable(inner: impl Into<Tag>, outer: impl Into<Tag>) -> 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<Tagged<Path>> 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),
}

View file

@ -10,19 +10,19 @@ pub fn baseline_parse_single_token(
source: &Text,
) -> Result<hir::Expression, ShellError> {
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<hir::Expression, ShellError> {
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<hir::Expression, ShellError> {
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<hir::Expression, ShellError> {
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<hir::Expression, ShellError> {
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())
}
})
}

View file

@ -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<Vec<hir::Expression>, ShellError> {
let mut exprs: Vec<hir::Expression> = 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<hir::Expression, ShellError> {
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<hir::RawExpression> = 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)]

View file

@ -9,7 +9,7 @@ use std::fmt;
)]
#[get = "pub(crate)"]
pub struct ExternalCommand {
name: Span,
name: Tag,
}
impl ToDebug for ExternalCommand {

View file

@ -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))?,
}

View file

@ -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<Self::Span> {
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
}
fn file_name(&self, _file: Self::FileId) -> FileName {
FileName::Verbatim(format!("shell"))
}
fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option<usize> {
unimplemented!("byte_index")
}
fn location(&self, _file: Self::FileId, byte_index: usize) -> Option<Location> {
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<Self::Span> {
fn line_span(&self, file: Self::FileId, lineno: usize) -> Option<Self::Span> {
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<String> {
if span.start > span.end {
fn source(&self, tag: Self::Span) -> Option<String> {
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())
}
}

View file

@ -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,
}

View file

@ -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<NomSpan, Tagged<RawNumber>> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, Tagged<Unit>> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<NomSpan, Tagged<CallNode>> {
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<NomSpan, TokenNode> {
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<NomSpan, TokenNode> {
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<Delimiter>,
children: Vec<TokenNode>,
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<Token>, 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<T>(block: CurriedNode<T>) -> 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()
}
}

View file

@ -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<PipelineElement>,
pub(crate) post_ws: Option<Span>,
pub(crate) post_ws: Option<Tag>,
}
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<Span>,
pub pre_ws: Option<Span>,
pub pipe: Option<Tag>,
pub pre_ws: Option<Tag>,
#[get = "pub(crate)"]
call: Tagged<CallNode>,
pub post_ws: Option<Span>,
pub post_ws: Option<Tag>,
}
impl ToDebug for PipelineElement {

View file

@ -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<Pipeline>),
Operator(Tagged<Operator>),
Flag(Tagged<Flag>),
Member(Span),
Whitespace(Span),
Member(Tag),
Whitespace(Tag),
Error(Tagged<Box<ShellError>>),
Path(Tagged<PathNode>),
@ -78,31 +78,31 @@ impl fmt::Debug for DebugTokenNode<'_> {
)
}
TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)),
TokenNode::Error(s) => write!(f, "<error> for {:?}", s.span().slice(self.source)),
rest => write!(f, "{}", rest.span().slice(self.source)),
TokenNode::Error(s) => write!(f, "<error> 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"),
}
}

Some files were not shown because too many files have changed in this diff Show more