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 **/*.rs.bk
history.txt history.txt
tests/fixtures/nuplayground 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]] [[package]]
name = "nom5_locate" name = "nom_locate"
version = "0.1.1" version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "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)", "neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "nom 5.0.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-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)", "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)", "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)", "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)", "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)", "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)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.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-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_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_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.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)", "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)", "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)", "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)", "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)", "syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2195,8 +2197,8 @@ dependencies = [
[[package]] [[package]]
name = "rustyline" name = "rustyline"
version = "5.0.2" version = "5.0.3"
source = "git+https://github.com/kkawakam/rustyline#5e68e972810133a7343b75db30addc98aea63ba0" source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [ dependencies = [
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
@ -2442,6 +2444,11 @@ name = "strsim"
version = "0.8.0" version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" 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]] [[package]]
name = "subprocess" name = "subprocess"
version = "0.1.18" 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 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 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
"checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b" "checksum nom 5.0.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 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-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a"
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-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-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-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 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 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 safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e133ccc4f4d1cd4f89cc8a7ff618287d56dc7f638b8e38fc32c5fdcadc339dd5"
"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421" "checksum 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 stackvector 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1c4725650978235083241fab0fdc8e694c3de37821524e7534a1a9061d1068af"
"checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241" "checksum static_assertions 0.3.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 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 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 surf 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "018eed64aede455beb88505d50c5c64882bebbe0996d4b660c272e3d8bb6f883"
"checksum syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ee06ea4b620ab59a2267c6b48be16244a3389f8bfa0986bdd15c35b890b00af3" "checksum syn 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 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
rustyline = { git = "https://github.com/kkawakam/rustyline" } rustyline = "5.0.3"
chrono = { version = "0.4.9", features = ["serde"] } chrono = { version = "0.4.9", features = ["serde"] }
derive-new = "0.5.8" derive-new = "0.5.8"
prettytable-rs = "0.8.0" prettytable-rs = "0.8.0"
@ -53,7 +53,7 @@ ctrlc = "3.1.3"
surf = "1.0.2" surf = "1.0.2"
url = "2.1.0" url = "2.1.0"
roxmltree = "0.7.0" roxmltree = "0.7.0"
nom5_locate = "0.1.1" nom_locate = "1.0.0"
enum-utils = "0.1.1" enum-utils = "0.1.1"
unicode-xid = "0.2.0" unicode-xid = "0.2.0"
serde_ini = "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"] } num-bigint = { version = "0.2.3", features = ["serde"] }
bigdecimal = { version = "0.1.0", features = ["serde"] } bigdecimal = { version = "0.1.0", features = ["serde"] }
natural = "0.3.0" natural = "0.3.0"
serde_urlencoded = "0.6.1"
sublime_fuzzy = "0.5"
neso = { version = "0.5.0", optional = true } neso = { version = "0.5.0", optional = true }
crossterm = { version = "0.10.2", 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 } image = { version = "0.22.2", default_features = false, features = ["png_codec", "jpeg"], optional = true }
[features] [features]
default = ["textview", "sys", "ps"]
raw-key = ["rawkey", "neso"] raw-key = ["rawkey", "neso"]
textview = ["syntect", "onig_sys", "crossterm"] textview = ["syntect", "onig_sys", "crossterm"]
binaryview = ["image", "crossterm"] binaryview = ["image", "crossterm"]
sys = ["heim", "battery"] sys = ["heim", "battery"]
ps = ["heim"] ps = ["heim"]
all = ["raw-key", "textview", "binaryview", "sys", "ps", "clipboard", "ptree"]
[dependencies.rusqlite] [dependencies.rusqlite]
version = "0.20.0" 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 | | first amount | Show only the first number of rows |
| last amount | Show only the last number of rows | | last amount | Show only the last number of rows |
| nth row-number | Return only the selected row | | 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 | | str (column) | Apply string function. Optionally use the column of a table |
| tags | Read the tags (metadata) for values | | tags | Read the tags (metadata) for values |
| to-json | Convert table into .json text | | 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 | | table | View the contents of the pipeline as a table |
| textview | Autoview of text data | | textview | Autoview of text data |
| tree | View the contents of the pipeline as a tree | | tree | View the contents of the pipeline as a tree |
| vtable | View the contents of the pipeline as a vertical (rotated) table |
# License # License
The project is made available under the MIT license. See "LICENSE" for more information. 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::plugin::{PluginCommand, PluginSink};
use crate::commands::whole_stream_command; use crate::commands::whole_stream_command;
use crate::context::Context; use crate::context::Context;
use crate::data::config;
use crate::data::Value; use crate::data::Value;
pub(crate) use crate::errors::ShellError; pub(crate) use crate::errors::ShellError;
use crate::fuzzysearch::{interactive_fuzzy_search, SelectionResult};
use crate::git::current_branch; use crate::git::current_branch;
use crate::parser::registry::Signature; use crate::parser::registry::Signature;
use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode}; use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode};
@ -21,6 +23,7 @@ use std::env;
use std::error::Error; use std::error::Error;
use std::io::{BufRead, BufReader, Write}; use std::io::{BufRead, BufReader, Write};
use std::iter::Iterator; use std::iter::Iterator;
use std::path::PathBuf;
use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::atomic::{AtomicBool, Ordering};
#[derive(Debug)] #[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) { let result = match reader.read_line(&mut input) {
Ok(count) => { Ok(count) => {
trace!("processing response ({} bytes)", count); trace!("processing response ({} bytes)", count);
trace!("response: {}", input);
let response = serde_json::from_str::<JsonRpc<Result<Signature, ShellError>>>(&input); let response = serde_json::from_str::<JsonRpc<Result<Signature, ShellError>>>(&input);
match response { match response {
@ -208,6 +212,20 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
Ok(()) 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>> { pub async fn cli() -> Result<(), Box<dyn Error>> {
let mut context = Context::basic()?; let mut context = Context::basic()?;
@ -239,11 +257,13 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
whole_stream_command(ToDB), whole_stream_command(ToDB),
whole_stream_command(ToTOML), whole_stream_command(ToTOML),
whole_stream_command(ToTSV), whole_stream_command(ToTSV),
whole_stream_command(ToURL),
whole_stream_command(ToYAML), whole_stream_command(ToYAML),
whole_stream_command(SortBy), whole_stream_command(SortBy),
whole_stream_command(Tags), whole_stream_command(Tags),
whole_stream_command(First), whole_stream_command(First),
whole_stream_command(Last), whole_stream_command(Last),
whole_stream_command(Env),
whole_stream_command(FromCSV), whole_stream_command(FromCSV),
whole_stream_command(FromTSV), whole_stream_command(FromTSV),
whole_stream_command(FromINI), whole_stream_command(FromINI),
@ -252,6 +272,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
whole_stream_command(FromDB), whole_stream_command(FromDB),
whole_stream_command(FromSQLite), whole_stream_command(FromSQLite),
whole_stream_command(FromTOML), whole_stream_command(FromTOML),
whole_stream_command(FromURL),
whole_stream_command(FromXML), whole_stream_command(FromXML),
whole_stream_command(FromYAML), whole_stream_command(FromYAML),
whole_stream_command(FromYML), whole_stream_command(FromYML),
@ -269,13 +290,13 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
per_item_command(Help), per_item_command(Help),
whole_stream_command(Exit), whole_stream_command(Exit),
whole_stream_command(Autoview), whole_stream_command(Autoview),
whole_stream_command(Pivot),
per_item_command(Cpy), per_item_command(Cpy),
whole_stream_command(Date), whole_stream_command(Date),
per_item_command(Mkdir), per_item_command(Mkdir),
per_item_command(Move), per_item_command(Move),
whole_stream_command(Save), whole_stream_command(Save),
whole_stream_command(Table), whole_stream_command(Table),
whole_stream_command(VTable),
whole_stream_command(Version), whole_stream_command(Version),
whole_stream_command(Which), whole_stream_command(Which),
]); ]);
@ -298,7 +319,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
} }
// we are ok if history does not exist // 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 ctrl_c = Arc::new(AtomicBool::new(false));
let cc = ctrl_c.clone(); let cc = ctrl_c.clone();
@ -319,7 +340,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
context.shell_manager.clone(), context.shell_manager.clone(),
))); )));
let edit_mode = crate::data::config::config(Span::unknown())? let edit_mode = config::config(Tag::unknown())?
.get("edit_mode") .get("edit_mode")
.map(|s| match s.as_string().unwrap().as_ref() { .map(|s| match s.as_string().unwrap().as_ref() {
"vi" => EditMode::Vi, "vi" => EditMode::Vi,
@ -330,14 +351,47 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
rl.set_edit_mode(edit_mode); 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, cwd,
match current_branch() { match current_branch() {
Some(s) => format!("({})", s), Some(s) => format!("({})", s),
None => "".to_string(), 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 { match process_line(readline, &mut context).await {
LineResult::Success(line) => { LineResult::Success(line) => {
@ -346,6 +400,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
LineResult::CtrlC => { LineResult::CtrlC => {
if ctrlcbreak { if ctrlcbreak {
let _ = rl.save_history(&History::path());
std::process::exit(0); std::process::exit(0);
} else { } else {
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)")); 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 // we are ok if we can not save history
let _ = rl.save_history("history.txt"); let _ = rl.save_history(&History::path());
Ok(()) 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) if line.trim() == "" => LineResult::Success(line.clone()),
Ok(line) => { Ok(line) => {
let result = match crate::parser::parse(&line) { let result = match crate::parser::parse(&line, uuid::Uuid::nil()) {
Err(err) => { Err(err) => {
return LineResult::Error(line.clone(), err); return LineResult::Error(line.clone(), err);
} }
@ -419,7 +474,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
.commands .commands
.push(ClassifiedCommand::Internal(InternalCommand { .push(ClassifiedCommand::Internal(InternalCommand {
command: whole_stream_command(autoview::Autoview), command: whole_stream_command(autoview::Autoview),
name_span: Span::unknown(), name_tag: Tag::unknown(),
args: hir::Call::new( args: hir::Call::new(
Box::new(hir::Expression::synthetic_string("autoview")), Box::new(hir::Expression::synthetic_string("autoview")),
None, None,
@ -431,6 +486,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
let mut input = ClassifiedInputStream::new(); let mut input = ClassifiedInputStream::new();
let mut iter = pipeline.commands.into_iter().peekable(); let mut iter = pipeline.commands.into_iter().peekable();
let mut is_first_command = true;
loop { loop {
let item: Option<ClassifiedCommand> = iter.next(); 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::Internal(left)),
Some(ClassifiedCommand::External(_)), 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), Ok(val) => ClassifiedInputStream::from_input_stream(val),
Err(err) => return LineResult::Error(line.clone(), err), Err(err) => return LineResult::Error(line.clone(), err),
}, },
(Some(ClassifiedCommand::Internal(left)), Some(_)) => { (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), Ok(val) => ClassifiedInputStream::from_input_stream(val),
Err(err) => return LineResult::Error(line.clone(), err), Err(err) => return LineResult::Error(line.clone(), err),
} }
} }
(Some(ClassifiedCommand::Internal(left)), None) => { (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), Ok(val) => ClassifiedInputStream::from_input_stream(val),
Err(err) => return LineResult::Error(line.clone(), err), 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), Err(err) => return LineResult::Error(line.clone(), err),
} }
} }
} };
is_first_command = false;
} }
LineResult::Success(line.clone()) LineResult::Success(line.clone())
@ -539,10 +606,10 @@ fn classify_command(
match call { match call {
// If the command starts with `^`, treat it as an external command no matter what // If the command starts with `^`, treat it as an external command no matter what
call if call.head().is_external() => { call if call.head().is_external() => {
let name_span = call.head().expect_external(); let name_tag = call.head().expect_external();
let name = name_span.slice(source); 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 // 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 { Ok(ClassifiedCommand::Internal(InternalCommand {
command, command,
name_span: head.span().clone(), name_tag: head.tag(),
args, args,
})) }))
} }
// otherwise, it's an external command // 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. // 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. // 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() .iter()
.filter_map(|i| match i { .filter_map(|i| match i {
TokenNode::Whitespace(_) => None, TokenNode::Whitespace(_) => None,
other => Some(Tagged::from_simple_spanned_item( other => Some(other.as_external_arg(source).tagged(other.tag())),
other.as_external_arg(source),
other.span(),
)),
}) })
.collect(), .collect(),
None => vec![], None => vec![],
@ -606,7 +670,7 @@ fn external_command(
ClassifiedCommand::External(ExternalCommand { ClassifiedCommand::External(ExternalCommand {
name: name.to_string(), name: name.to_string(),
name_span: tag.span, name_tag: tag,
args: arg_list_strings, args: arg_list_strings,
}) })
} }

View file

@ -14,6 +14,7 @@ pub(crate) mod debug;
pub(crate) mod docker; pub(crate) mod docker;
pub(crate) mod echo; pub(crate) mod echo;
pub(crate) mod enter; pub(crate) mod enter;
pub(crate) mod env;
pub(crate) mod exit; pub(crate) mod exit;
pub(crate) mod fetch; pub(crate) mod fetch;
pub(crate) mod first; pub(crate) mod first;
@ -24,6 +25,7 @@ pub(crate) mod from_json;
pub(crate) mod from_sqlite; pub(crate) mod from_sqlite;
pub(crate) mod from_toml; pub(crate) mod from_toml;
pub(crate) mod from_tsv; pub(crate) mod from_tsv;
pub(crate) mod from_url;
pub(crate) mod from_xml; pub(crate) mod from_xml;
pub(crate) mod from_yaml; pub(crate) mod from_yaml;
pub(crate) mod get; pub(crate) mod get;
@ -37,6 +39,7 @@ pub(crate) mod next;
pub(crate) mod nth; pub(crate) mod nth;
pub(crate) mod open; pub(crate) mod open;
pub(crate) mod pick; pub(crate) mod pick;
pub(crate) mod pivot;
pub(crate) mod plugin; pub(crate) mod plugin;
pub(crate) mod post; pub(crate) mod post;
pub(crate) mod prev; pub(crate) mod prev;
@ -59,10 +62,10 @@ pub(crate) mod to_json;
pub(crate) mod to_sqlite; pub(crate) mod to_sqlite;
pub(crate) mod to_toml; pub(crate) mod to_toml;
pub(crate) mod to_tsv; pub(crate) mod to_tsv;
pub(crate) mod to_url;
pub(crate) mod to_yaml; pub(crate) mod to_yaml;
pub(crate) mod trim; pub(crate) mod trim;
pub(crate) mod version; pub(crate) mod version;
pub(crate) mod vtable;
pub(crate) mod where_; pub(crate) mod where_;
pub(crate) mod which_; pub(crate) mod which_;
@ -80,6 +83,7 @@ pub(crate) use debug::Debug;
pub(crate) use docker::Docker; pub(crate) use docker::Docker;
pub(crate) use echo::Echo; pub(crate) use echo::Echo;
pub(crate) use enter::Enter; pub(crate) use enter::Enter;
pub(crate) use env::Env;
pub(crate) use exit::Exit; pub(crate) use exit::Exit;
pub(crate) use fetch::Fetch; pub(crate) use fetch::Fetch;
pub(crate) use first::First; 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_sqlite::FromSQLite;
pub(crate) use from_toml::FromTOML; pub(crate) use from_toml::FromTOML;
pub(crate) use from_tsv::FromTSV; pub(crate) use from_tsv::FromTSV;
pub(crate) use from_url::FromURL;
pub(crate) use from_xml::FromXML; pub(crate) use from_xml::FromXML;
pub(crate) use from_yaml::FromYAML; pub(crate) use from_yaml::FromYAML;
pub(crate) use from_yaml::FromYML; pub(crate) use from_yaml::FromYML;
@ -105,6 +110,7 @@ pub(crate) use next::Next;
pub(crate) use nth::Nth; pub(crate) use nth::Nth;
pub(crate) use open::Open; pub(crate) use open::Open;
pub(crate) use pick::Pick; pub(crate) use pick::Pick;
pub(crate) use pivot::Pivot;
pub(crate) use post::Post; pub(crate) use post::Post;
pub(crate) use prev::Previous; pub(crate) use prev::Previous;
pub(crate) use pwd::PWD; 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_sqlite::ToSQLite;
pub(crate) use to_toml::ToTOML; pub(crate) use to_toml::ToTOML;
pub(crate) use to_tsv::ToTSV; pub(crate) use to_tsv::ToTSV;
pub(crate) use to_url::ToURL;
pub(crate) use to_yaml::ToYAML; pub(crate) use to_yaml::ToYAML;
pub(crate) use trim::Trim; pub(crate) use trim::Trim;
pub(crate) use version::Version; pub(crate) use version::Version;
pub(crate) use vtable::VTable;
pub(crate) use where_::Where; pub(crate) use where_::Where;
pub(crate) use which_::Which; pub(crate) use which_::Which;

View file

@ -45,7 +45,7 @@ pub fn autoview(
{ {
let binary = context.get_command("binaryview"); let binary = context.get_command("binaryview");
if let Some(binary) = binary { 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; result.collect::<Vec<_>>().await;
} else { } else {
for i in input { for i in input {
@ -61,7 +61,7 @@ pub fn autoview(
} else if is_single_origined_text_value(&input) { } else if is_single_origined_text_value(&input) {
let text = context.get_command("textview"); let text = context.get_command("textview");
if let Some(text) = text { 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; result.collect::<Vec<_>>().await;
} else { } else {
for i in input { for i in input {
@ -84,7 +84,7 @@ pub fn autoview(
} }
} else { } else {
let table = context.expect_command("table"); 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; result.collect::<Vec<_>>().await;
} }
} }
@ -110,14 +110,13 @@ fn is_single_origined_text_value(input: &Vec<Tagged<Value>>) -> bool {
if input.len() != 1 { if input.len() != 1 {
return false; return false;
} }
if let Tagged { if let Tagged {
item: Value::Primitive(Primitive::String(_)), item: Value::Primitive(Primitive::String(_)),
tag: Tag { tag: Tag { origin, .. },
origin: Some(_), ..
},
} = input[0] } = input[0]
{ {
true origin != uuid::Uuid::nil()
} else { } else {
false false
} }

View file

@ -10,7 +10,7 @@ impl WholeStreamCommand for CD {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("cd").optional("directory", SyntaxType::Path) Signature::build("cd").optional("directory", SyntaxShape::Path)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {

View file

@ -86,7 +86,7 @@ pub(crate) enum ClassifiedCommand {
pub(crate) struct InternalCommand { pub(crate) struct InternalCommand {
pub(crate) command: Arc<Command>, pub(crate) command: Arc<Command>,
pub(crate) name_span: Span, pub(crate) name_tag: Tag,
pub(crate) args: hir::Call, pub(crate) args: hir::Call,
} }
@ -96,6 +96,7 @@ impl InternalCommand {
context: &mut Context, context: &mut Context,
input: ClassifiedInputStream, input: ClassifiedInputStream,
source: Text, source: Text,
is_first_command: bool,
) -> Result<InputStream, ShellError> { ) -> Result<InputStream, ShellError> {
if log_enabled!(log::Level::Trace) { if log_enabled!(log::Level::Trace) {
trace!(target: "nu::run::internal", "->"); trace!(target: "nu::run::internal", "->");
@ -108,11 +109,12 @@ impl InternalCommand {
let result = context.run_command( let result = context.run_command(
self.command, self.command,
self.name_span.clone(), self.name_tag.clone(),
context.source_map.clone(), context.source_map.clone(),
self.args, self.args,
&source, &source,
objects, objects,
is_first_command,
); );
let result = trace_out_stream!(target: "nu::trace_stream::internal", source: &source, "output" = result); 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) => { CommandAction::AddSpanSource(uuid, span_source) => {
context.add_span_source(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) => { CommandAction::EnterHelpShell(value) => {
match value { match value {
Tagged { Tagged {
item: Value::Primitive(Primitive::String(cmd)), item: Value::Primitive(Primitive::String(cmd)),
.. tag,
} => { } => {
context.shell_manager.insert_at_current(Box::new( context.shell_manager.insert_at_current(Box::new(
HelpShell::for_command( HelpShell::for_command(
Tagged::from_simple_spanned_item( Value::string(cmd).tagged(tag),
Value::string(cmd), &context.registry(),
Span::unknown(),
),
&context.registry().clone(),
)?, )?,
)); ));
} }
_ => { _ => {
context.shell_manager.insert_at_current(Box::new( 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 => { CommandAction::LeaveShell => {
context.shell_manager.remove_at_current(); context.shell_manager.remove_at_current();
if context.shell_manager.is_empty() { 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) struct ExternalCommand {
pub(crate) name: String, pub(crate) name: String,
pub(crate) name_span: Span, pub(crate) name_tag: Tag,
pub(crate) args: Vec<Tagged<String>>, pub(crate) args: Vec<Tagged<String>>,
} }
@ -208,7 +207,7 @@ impl ExternalCommand {
) -> Result<ClassifiedInputStream, ShellError> { ) -> Result<ClassifiedInputStream, ShellError> {
let stdin = input.stdin; let stdin = input.stdin;
let inputs: Vec<Tagged<Value>> = input.objects.into_vec().await; 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", "-> {}", self.name);
trace!(target: "nu::run::external", "inputs = {:?}", inputs); trace!(target: "nu::run::external", "inputs = {:?}", inputs);
@ -227,17 +226,17 @@ impl ExternalCommand {
for i in &inputs { for i in &inputs {
if i.as_string().is_err() { if i.as_string().is_err() {
let mut span = None; let mut tag = None;
for arg in &self.args { for arg in &self.args {
if arg.item.contains("$it") { 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( return Err(ShellError::labeled_error(
"External $it needs string data", "External $it needs string data",
"given row instead of string data", "given row instead of string data",
span, tag,
)); ));
} else { } else {
return Err(ShellError::string("Error: $it needs string data")); return Err(ShellError::string("Error: $it needs string data"));
@ -316,9 +315,7 @@ impl ExternalCommand {
let stdout = popen.stdout.take().unwrap(); let stdout = popen.stdout.take().unwrap();
let file = futures::io::AllowStdIo::new(stdout); let file = futures::io::AllowStdIo::new(stdout);
let stream = Framed::new(file, LinesCodec {}); let stream = Framed::new(file, LinesCodec {});
let stream = stream.map(move |line| { let stream = stream.map(move |line| Value::string(line.unwrap()).tagged(name_tag));
Tagged::from_simple_spanned_item(Value::string(line.unwrap()), name_span)
});
Ok(ClassifiedInputStream::from_input_stream( Ok(ClassifiedInputStream::from_input_stream(
stream.boxed() as BoxStream<'static, Tagged<Value>> stream.boxed() as BoxStream<'static, Tagged<Value>>
)) ))

View file

@ -51,7 +51,7 @@ pub mod clipboard {
Ok(OutputStream::from(stream)) 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 clip_context: ClipboardContext = ClipboardProvider::new().unwrap();
let mut new_copy_data = String::new(); let mut new_copy_data = String::new();

View file

@ -18,7 +18,7 @@ pub struct UnevaluatedCallInfo {
pub args: hir::Call, pub args: hir::Call,
pub source: Text, pub source: Text,
pub source_map: SourceMap, pub source_map: SourceMap,
pub name_span: Span, pub name_tag: Tag,
} }
impl ToDebug for UnevaluatedCallInfo { impl ToDebug for UnevaluatedCallInfo {
@ -38,43 +38,16 @@ impl UnevaluatedCallInfo {
Ok(CallInfo { Ok(CallInfo {
args, args,
source_map: self.source_map, 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)] #[derive(Deserialize, Serialize, Debug, Clone)]
pub struct CallInfo { pub struct CallInfo {
pub args: registry::EvaluatedArgs, pub args: registry::EvaluatedArgs,
pub source_map: SourceMap, pub source_map: SourceMap,
pub name_span: Span, pub name_tag: Tag,
} }
impl CallInfo { impl CallInfo {
@ -89,7 +62,7 @@ impl CallInfo {
args: T::deserialize(&mut deserializer)?, args: T::deserialize(&mut deserializer)?,
context: RunnablePerItemContext { context: RunnablePerItemContext {
shell_manager: shell_manager.clone(), shell_manager: shell_manager.clone(),
name: self.name_span, name: self.name_tag,
}, },
callback, callback,
}) })
@ -158,7 +131,7 @@ impl CommandArgs {
let host = self.host.clone(); let host = self.host.clone();
let args = self.evaluate_once(registry)?; let args = self.evaluate_once(registry)?;
let (input, args) = args.split(); 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); let mut deserializer = ConfigDeserializer::from_call_info(args.call_info);
Ok(RunnableArgs { Ok(RunnableArgs {
@ -167,7 +140,7 @@ impl CommandArgs {
input, input,
commands: registry.clone(), commands: registry.clone(),
shell_manager, shell_manager,
name: name_span, name: name_tag,
source_map, source_map,
host, host,
}, },
@ -191,7 +164,7 @@ impl CommandArgs {
let host = self.host.clone(); let host = self.host.clone();
let args = self.evaluate_once(registry)?; let args = self.evaluate_once(registry)?;
let (input, args) = args.split(); 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); let mut deserializer = ConfigDeserializer::from_call_info(args.call_info);
Ok(RunnableRawArgs { Ok(RunnableRawArgs {
@ -200,7 +173,7 @@ impl CommandArgs {
input, input,
commands: registry.clone(), commands: registry.clone(),
shell_manager, shell_manager,
name: name_span, name: name_tag,
source_map, source_map,
host, host,
}, },
@ -212,7 +185,7 @@ impl CommandArgs {
pub struct RunnablePerItemContext { pub struct RunnablePerItemContext {
pub shell_manager: ShellManager, pub shell_manager: ShellManager,
pub name: Span, pub name: Tag,
} }
impl RunnablePerItemContext { impl RunnablePerItemContext {
@ -227,7 +200,7 @@ pub struct RunnableContext {
pub host: Arc<Mutex<dyn Host>>, pub host: Arc<Mutex<dyn Host>>,
pub commands: CommandRegistry, pub commands: CommandRegistry,
pub source_map: SourceMap, pub source_map: SourceMap,
pub name: Span, pub name: Tag,
} }
impl RunnableContext { impl RunnableContext {
@ -311,8 +284,8 @@ impl EvaluatedWholeStreamCommandArgs {
} }
} }
pub fn name_span(&self) -> Span { pub fn name_tag(&self) -> Tag {
self.args.call_info.name_span self.args.call_info.name_tag
} }
pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) { pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) {
@ -471,12 +444,6 @@ impl ReturnSuccess {
pub fn action(input: CommandAction) -> ReturnValue { pub fn action(input: CommandAction) -> ReturnValue {
Ok(ReturnSuccess::Action(input)) 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 { 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 { match self {
Command::WholeStream(command) => match command.run(args, registry) { Command::WholeStream(command) => match command.run(args, registry) {
Ok(stream) => stream, Ok(stream) => stream,
Err(err) => OutputStream::one(Err(err)), 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>, command: Arc<dyn PerItemCommand>,
args: CommandArgs, args: CommandArgs,
registry: CommandRegistry, registry: CommandRegistry,
is_first_command: bool,
) -> OutputStream { ) -> OutputStream {
let raw_args = RawCommandArgs { let raw_args = RawCommandArgs {
host: args.host, host: args.host,
@ -584,7 +559,7 @@ impl Command {
call_info: args.call_info, call_info: args.call_info,
}; };
if raw_args.call_info.has_it_or_block() { if !is_first_command {
let out = args let out = args
.input .input
.values .values
@ -609,7 +584,6 @@ impl Command {
.call_info .call_info
.evaluate(&registry, &Scope::it_value(nothing.clone())) .evaluate(&registry, &Scope::it_value(nothing.clone()))
.unwrap(); .unwrap();
// We don't have an $it or block, so just execute what we have
match command match command
.run(&call_info, &registry, &raw_args, nothing) .run(&call_info, &registry, &raw_args, nothing)

View file

@ -1,7 +1,7 @@
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::data::{config, Value}; use crate::data::{config, Value};
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxShape;
use crate::parser::registry::{self}; use crate::parser::registry::{self};
use crate::prelude::*; use crate::prelude::*;
use std::iter::FromIterator; use std::iter::FromIterator;
@ -26,10 +26,10 @@ impl WholeStreamCommand for Config {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("config") Signature::build("config")
.named("load", SyntaxType::Path) .named("load", SyntaxShape::Path)
.named("set", SyntaxType::Any) .named("set", SyntaxShape::Any)
.named("get", SyntaxType::Any) .named("get", SyntaxShape::Any)
.named("remove", SyntaxType::Any) .named("remove", SyntaxShape::Any)
.switch("clear") .switch("clear")
.switch("path") .switch("path")
} }
@ -96,41 +96,21 @@ pub fn config(
config::write(&result, &configuration)?; config::write(&result, &configuration)?;
return Ok(stream![Tagged::from_simple_spanned_item( return Ok(stream![Value::Row(result.into()).tagged(value.tag())].from_input_stream());
Value::Row(result.into()),
value.span()
)]
.from_input_stream());
} }
if let Tagged { if let Tagged { item: true, tag } = clear {
item: true,
tag: Tag { span, .. },
} = clear
{
result.clear(); result.clear();
config::write(&result, &configuration)?; config::write(&result, &configuration)?;
return Ok(stream![Tagged::from_simple_spanned_item( return Ok(stream![Value::Row(result.into()).tagged(tag)].from_input_stream());
Value::Row(result.into()),
span
)]
.from_input_stream());
} }
if let Tagged { if let Tagged { item: true, tag } = path {
item: true,
tag: Tag { span, .. },
} = path
{
let path = config::default_path_for(&configuration)?; let path = config::default_path_for(&configuration)?;
return Ok(stream![Tagged::from_simple_spanned_item( return Ok(stream![Value::Primitive(Primitive::Path(path)).tagged(tag)].from_input_stream());
Value::Primitive(Primitive::Path(path)),
span
)]
.from_input_stream());
} }
if let Some(v) = remove { 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(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::commands::command::RunnablePerItemContext;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxShape;
use crate::parser::registry::{CommandRegistry, Signature}; use crate::parser::registry::{CommandRegistry, Signature};
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
@ -21,9 +21,9 @@ impl PerItemCommand for Cpy {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("cp") Signature::build("cp")
.required("src", SyntaxType::Pattern) .required("src", SyntaxShape::Pattern)
.required("dst", SyntaxType::Path) .required("dst", SyntaxShape::Path)
.named("file", SyntaxType::Any) .named("file", SyntaxShape::Any)
.switch("recursive") .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 where
T::Offset: Display, T::Offset: Display,
{ {
let mut indexmap = IndexMap::new(); let mut indexmap = IndexMap::new();
indexmap.insert( indexmap.insert("year".to_string(), Value::int(dt.year()).tagged(tag));
"year".to_string(), indexmap.insert("month".to_string(), Value::int(dt.month()).tagged(tag));
Tagged::from_simple_spanned_item(Value::int(dt.year()), span), indexmap.insert("day".to_string(), Value::int(dt.day()).tagged(tag));
); indexmap.insert("hour".to_string(), Value::int(dt.hour()).tagged(tag));
indexmap.insert( indexmap.insert("minute".to_string(), Value::int(dt.minute()).tagged(tag));
"month".to_string(), indexmap.insert("second".to_string(), Value::int(dt.second()).tagged(tag));
Tagged::from_simple_spanned_item(Value::int(dt.month()), span),
);
indexmap.insert(
"day".to_string(),
Tagged::from_simple_spanned_item(Value::int(dt.day()), span),
);
indexmap.insert(
"hour".to_string(),
Tagged::from_simple_spanned_item(Value::int(dt.hour()), span),
);
indexmap.insert(
"minute".to_string(),
Tagged::from_simple_spanned_item(Value::int(dt.minute()), span),
);
indexmap.insert(
"second".to_string(),
Tagged::from_simple_spanned_item(Value::int(dt.second()), span),
);
let tz = dt.offset(); let tz = dt.offset();
indexmap.insert( indexmap.insert(
"timezone".to_string(), "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> { pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let mut date_out = VecDeque::new(); 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 value = if args.has("utc") {
let utc: DateTime<Utc> = Utc::now(); let utc: DateTime<Utc> = Utc::now();
date_to_value(utc, span) date_to_value(utc, tag)
} else { } else {
let local: DateTime<Local> = Local::now(); let local: DateTime<Local> = Local::now();
date_to_value(local, span) date_to_value(local, tag)
}; };
date_out.push_back(value); date_out.push_back(value);

View file

@ -12,7 +12,7 @@ impl PerItemCommand for Echo {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("echo").rest(SyntaxType::Any) Signature::build("echo").rest(SyntaxShape::Any)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -35,7 +35,7 @@ fn run(
_registry: &CommandRegistry, _registry: &CommandRegistry,
_raw_args: &RawCommandArgs, _raw_args: &RawCommandArgs,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let name = call_info.name_span; let name = call_info.name_tag;
let mut output = String::new(); let mut output = String::new();
@ -57,7 +57,7 @@ fn run(
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expect a string from pipeline", "Expect a string from pipeline",
"not a string-compatible value", "not a string-compatible value",
i.span(), i.tag(),
)); ));
} }
} }
@ -65,7 +65,7 @@ fn run(
} }
let stream = VecDeque::from(vec![Ok(ReturnSuccess::Value( let stream = VecDeque::from(vec![Ok(ReturnSuccess::Value(
Value::string(output).simple_spanned(name), Value::string(output).tagged(name),
))]); ))]);
Ok(stream.to_output_stream()) Ok(stream.to_output_stream())

View file

@ -1,6 +1,7 @@
use crate::commands::command::CommandAction; use crate::commands::command::CommandAction;
use crate::commands::PerItemCommand; use crate::commands::PerItemCommand;
use crate::commands::UnevaluatedCallInfo; use crate::commands::UnevaluatedCallInfo;
use crate::data::meta::Span;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::registry; use crate::parser::registry;
use crate::prelude::*; use crate::prelude::*;
@ -14,7 +15,7 @@ impl PerItemCommand for Enter {
} }
fn signature(&self) -> registry::Signature { fn signature(&self) -> registry::Signature {
Signature::build("enter").required("location", SyntaxType::Block) Signature::build("enter").required("location", SyntaxShape::Block)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -74,10 +75,10 @@ impl PerItemCommand for Enter {
) )
.await.unwrap(); .await.unwrap();
if let Some(uuid) = contents_tag.origin { if contents_tag.origin != uuid::Uuid::nil() {
// If we have loaded something, track its source // If we have loaded something, track its source
yield ReturnSuccess::action(CommandAction::AddSpanSource( yield ReturnSuccess::action(CommandAction::AddSpanSource(
uuid, contents_tag.origin,
span_source, span_source,
)); ));
} }
@ -103,12 +104,13 @@ impl PerItemCommand for Enter {
}, },
source: raw_args.call_info.source, source: raw_args.call_info.source,
source_map: raw_args.call_info.source_map, 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( let mut result = converter.run(
new_args.with_input(vec![tagged_contents]), new_args.with_input(vec![tagged_contents]),
&registry, &registry,
false
); );
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await; 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::commands::UnevaluatedCallInfo;
use crate::context::SpanSource; use crate::context::SpanSource;
use crate::data::meta::Span;
use crate::data::Value; use crate::data::Value;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxShape;
use crate::parser::registry::Signature; use crate::parser::registry::Signature;
use crate::prelude::*; use crate::prelude::*;
use mime::Mime; use mime::Mime;
@ -19,7 +20,7 @@ impl PerItemCommand for Fetch {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build(self.name()) Signature::build(self.name())
.required("path", SyntaxType::Path) .required("path", SyntaxShape::Path)
.switch("raw") .switch("raw")
} }
@ -75,10 +76,10 @@ fn run(
file_extension.or(path_str.split('.').last().map(String::from)) 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 // If we have loaded something, track its source
yield ReturnSuccess::action(CommandAction::AddSpanSource( yield ReturnSuccess::action(CommandAction::AddSpanSource(
uuid, contents_tag.origin,
span_source, span_source,
)); ));
} }
@ -99,10 +100,10 @@ fn run(
}, },
source: raw_args.call_info.source, source: raw_args.call_info.source,
source_map: raw_args.call_info.source_map, 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; let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
for res in result_vec { for res in result_vec {
match res { match res {
@ -157,7 +158,7 @@ pub async fn fetch(
})?), })?),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
@ -172,7 +173,7 @@ pub async fn fetch(
})?), })?),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
@ -186,10 +187,10 @@ pub async fn fetch(
})?; })?;
Ok(( Ok((
None, None,
Value::Primitive(Primitive::Binary(buf)), Value::binary(buf),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)) ))
@ -205,7 +206,7 @@ pub async fn fetch(
})?), })?),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
@ -219,10 +220,10 @@ pub async fn fetch(
})?; })?;
Ok(( Ok((
Some(image_ty.to_string()), Some(image_ty.to_string()),
Value::Primitive(Primitive::Binary(buf)), Value::binary(buf),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)) ))
@ -238,7 +239,7 @@ pub async fn fetch(
})?), })?),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
@ -265,7 +266,7 @@ pub async fn fetch(
})?), })?),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)) ))
@ -275,7 +276,7 @@ pub async fn fetch(
Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)), Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
@ -286,7 +287,7 @@ pub async fn fetch(
Value::string(format!("No content type found")), Value::string(format!("No content type found")),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),

View file

@ -16,7 +16,7 @@ impl WholeStreamCommand for First {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("first").required("amount", SyntaxType::Literal) Signature::build("first").required("amount", SyntaxShape::Literal)
} }
fn usage(&self) -> &str { 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> { fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let span = args.name_span(); let tag = args.name_tag();
let input = args.input; let input = args.input;
let stream = async_stream_block! { let stream = async_stream_block! {
@ -208,24 +208,24 @@ fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
let value_tag = value.tag(); let value_tag = value.tag();
match value.item { match value.item {
Value::Primitive(Primitive::Binary(vb)) => 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), Ok(x) => yield ReturnSuccess::value(x),
Err(_) => { Err(_) => {
yield Err(ShellError::labeled_error_with_secondary( yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as BSON", "Could not parse as BSON",
"input cannot be parsed as BSON", "input cannot be parsed as BSON",
span, tag,
"value originates from here", "value originates from here",
value_tag.span, value_tag,
)) ))
} }
} }
_ => yield Err(ShellError::labeled_error_with_secondary( _ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
span, tag,
"value originates from here", "value originates from here",
value_tag.span, value_tag,
)), )),
} }

View file

@ -86,7 +86,7 @@ fn from_csv(
}: FromCSVArgs, }: FromCSVArgs,
RunnableContext { input, name, .. }: RunnableContext, RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let name_span = name; let name_tag = name;
let stream = async_stream_block! { let stream = async_stream_block! {
let values: Vec<Tagged<Value>> = input.values.collect().await; let values: Vec<Tagged<Value>> = input.values.collect().await;
@ -105,15 +105,15 @@ fn from_csv(
_ => yield Err(ShellError::labeled_error_with_secondary( _ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_span, name_tag,
"value originates from here", "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 { Ok(x) => match x {
Tagged { item: Value::Table(list), .. } => { Tagged { item: Value::Table(list), .. } => {
for l in list { for l in list {
@ -126,9 +126,9 @@ fn from_csv(
yield Err(ShellError::labeled_error_with_secondary( yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as CSV", "Could not parse as CSV",
"input cannot be parsed as CSV", "input cannot be parsed as CSV",
name_span, name_tag,
"value originates from here", "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> { fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let span = args.name_span(); let tag = args.name_tag();
let input = args.input; let input = args.input;
let stream = async_stream_block! { 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( _ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
span, tag,
"value originates from here", "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 { Ok(x) => match x {
Tagged { item: Value::Table(list), .. } => { Tagged { item: Value::Table(list), .. } => {
for l in 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( yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as INI", "Could not parse as INI",
"input cannot be parsed as INI", "input cannot be parsed as INI",
span, tag,
"value originates from here", "value originates from here",
last_tag.span, last_tag,
)) ))
} , } ,
} }

View file

@ -72,7 +72,7 @@ fn from_json(
FromJSONArgs { objects }: FromJSONArgs, FromJSONArgs { objects }: FromJSONArgs,
RunnableContext { input, name, .. }: RunnableContext, RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let name_span = name; let name_tag = name;
let stream = async_stream_block! { let stream = async_stream_block! {
let values: Vec<Tagged<Value>> = input.values.collect().await; let values: Vec<Tagged<Value>> = input.values.collect().await;
@ -91,9 +91,9 @@ fn from_json(
_ => yield Err(ShellError::labeled_error_with_secondary( _ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_span, name_tag,
"value originates from here", "value originates from here",
value_tag.span, value_tag,
)), )),
} }
@ -106,7 +106,7 @@ fn from_json(
continue; 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) => Ok(x) =>
yield ReturnSuccess::value(x), yield ReturnSuccess::value(x),
Err(_) => { Err(_) => {
@ -114,15 +114,15 @@ fn from_json(
yield Err(ShellError::labeled_error_with_secondary( yield Err(ShellError::labeled_error_with_secondary(
"Could nnot parse as JSON", "Could nnot parse as JSON",
"input cannot be parsed as JSON", "input cannot be parsed as JSON",
name_span, name_tag,
"value originates from here", "value originates from here",
last_tag.span)) last_tag))
} }
} }
} }
} }
} else { } else {
match from_json_string_to_value(concat_string, name_span) { match from_json_string_to_value(concat_string, name_tag) {
Ok(x) => Ok(x) =>
match x { match x {
Tagged { item: Value::Table(list), .. } => { Tagged { item: Value::Table(list), .. } => {
@ -137,9 +137,9 @@ fn from_json(
yield Err(ShellError::labeled_error_with_secondary( yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as JSON", "Could not parse as JSON",
"input cannot be parsed as JSON", "input cannot be parsed as JSON",
name_span, name_tag,
"value originates from here", "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> { fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let span = args.name_span(); let tag = args.name_tag();
let input = args.input; let input = args.input;
let stream = async_stream_block! { let stream = async_stream_block! {
@ -138,7 +138,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
let value_tag = value.tag(); let value_tag = value.tag();
match value.item { match value.item {
Value::Primitive(Primitive::Binary(vb)) => 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 { Ok(x) => match x {
Tagged { item: Value::Table(list), .. } => { Tagged { item: Value::Table(list), .. } => {
for l in 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( yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as SQLite", "Could not parse as SQLite",
"input cannot be parsed as SQLite", "input cannot be parsed as SQLite",
span, tag,
"value originates from here", "value originates from here",
value_tag.span, value_tag,
)) ))
} }
} }
_ => yield Err(ShellError::labeled_error_with_secondary( _ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
span, tag,
"value originates from here", "value originates from here",
value_tag.span, value_tag,
)), )),
} }

View file

@ -68,7 +68,7 @@ pub fn from_toml(
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let span = args.name_span(); let tag = args.name_tag();
let input = args.input; let input = args.input;
let stream = async_stream_block! { let stream = async_stream_block! {
@ -88,15 +88,15 @@ pub fn from_toml(
_ => yield Err(ShellError::labeled_error_with_secondary( _ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
span, tag,
"value originates from here", "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 { Ok(x) => match x {
Tagged { item: Value::Table(list), .. } => { Tagged { item: Value::Table(list), .. } => {
for l in list { for l in list {
@ -109,9 +109,9 @@ pub fn from_toml(
yield Err(ShellError::labeled_error_with_secondary( yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as TOML", "Could not parse as TOML",
"input cannot be parsed as TOML", "input cannot be parsed as TOML",
span, tag,
"value originates from here", "value originates from here",
last_tag.span, last_tag,
)) ))
} , } ,
} }

View file

@ -87,7 +87,7 @@ fn from_tsv(
}: FromTSVArgs, }: FromTSVArgs,
RunnableContext { input, name, .. }: RunnableContext, RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let name_span = name; let name_tag = name;
let stream = async_stream_block! { let stream = async_stream_block! {
let values: Vec<Tagged<Value>> = input.values.collect().await; let values: Vec<Tagged<Value>> = input.values.collect().await;
@ -106,15 +106,15 @@ fn from_tsv(
_ => yield Err(ShellError::labeled_error_with_secondary( _ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
name_span, name_tag,
"value originates from here", "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 { Ok(x) => match x {
Tagged { item: Value::Table(list), .. } => { Tagged { item: Value::Table(list), .. } => {
for l in list { for l in list {
@ -127,9 +127,9 @@ fn from_tsv(
yield Err(ShellError::labeled_error_with_secondary( yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as TSV", "Could not parse as TSV",
"input cannot be parsed as TSV", "input cannot be parsed as TSV",
name_span, name_tag,
"value originates from here", "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> { fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let span = args.name_span(); let tag = args.name_tag();
let input = args.input; let input = args.input;
let stream = async_stream_block! { 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( _ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
span, tag,
"value originates from here", "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 { Ok(x) => match x {
Tagged { item: Value::Table(list), .. } => { Tagged { item: Value::Table(list), .. } => {
for l in 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( yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as XML", "Could not parse as XML",
"input cannot be parsed as XML", "input cannot be parsed as XML",
span, tag,
"value originates from here", "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> { fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let span = args.name_span(); let tag = args.name_tag();
let input = args.input; let input = args.input;
let stream = async_stream_block! { 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( _ => yield Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
span, tag,
"value originates from here", "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 { Ok(x) => match x {
Tagged { item: Value::Table(list), .. } => { Tagged { item: Value::Table(list), .. } => {
for l in 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( yield Err(ShellError::labeled_error_with_secondary(
"Could not parse as YAML", "Could not parse as YAML",
"input cannot be parsed as YAML", "input cannot be parsed as YAML",
span, tag,
"value originates from here", "value originates from here",
last_tag.span, last_tag,
)) ))
} , } ,
} }

View file

@ -18,8 +18,8 @@ impl WholeStreamCommand for Get {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("get") Signature::build("get")
.required("member", SyntaxType::Member) .required("member", SyntaxShape::Member)
.rest(SyntaxType::Member) .rest(SyntaxShape::Member)
} }
fn usage(&self) -> &str { 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( return Err(ShellError::labeled_error(
"Unknown column", "Unknown column",
format!("did you mean '{}'?", possible_matches[0].1), 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 { fn signature(&self) -> registry::Signature {
Signature::build("help").rest(SyntaxType::Any) Signature::build("help").rest(SyntaxShape::Any)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -26,13 +26,20 @@ impl PerItemCommand for Help {
_raw_args: &RawCommandArgs, _raw_args: &RawCommandArgs,
_input: Tagged<Value>, _input: Tagged<Value>,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let span = call_info.name_span; let tag = call_info.name_tag;
match call_info.args.nth(0) { if call_info.args.len() == 0 {
Some(Tagged { 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)), item: Value::Primitive(Primitive::String(document)),
tag, tag,
}) => { } => {
let mut help = VecDeque::new(); let mut help = VecDeque::new();
if document == "commands" { if document == "commands" {
let mut sorted_names = registry.names(); 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(); let mut output_stream = VecDeque::new();
output_stream.push_back(ReturnSuccess::value( output_stream.push_back(ReturnSuccess::value(Value::string(msg).tagged(tag)));
Value::string(msg).simple_spanned(span),
));
Ok(output_stream.to_output_stream()) Ok(output_stream.to_output_stream())
} }

View file

@ -16,7 +16,7 @@ impl WholeStreamCommand for Last {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("last").required("amount", SyntaxType::Number) Signature::build("last").required("amount", SyntaxShape::Number)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {

View file

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

View file

@ -1,16 +1,22 @@
use crate::commands::WholeStreamCommand; use crate::commands::WholeStreamCommand;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf;
pub struct LS; pub struct LS;
#[derive(Deserialize)]
pub struct LsArgs {
path: Option<Tagged<PathBuf>>,
}
impl WholeStreamCommand for LS { impl WholeStreamCommand for LS {
fn name(&self) -> &str { fn name(&self) -> &str {
"ls" "ls"
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("ls").optional("path", SyntaxType::Pattern) Signature::build("ls").optional("path", SyntaxShape::Pattern)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -22,12 +28,11 @@ impl WholeStreamCommand for LS {
args: CommandArgs, args: CommandArgs,
registry: &CommandRegistry, registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
ls(args, registry) args.process(registry, ls)?.run()
// ls(args, registry)
} }
} }
fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { fn ls(LsArgs { path }: LsArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
let shell_manager = args.shell_manager.clone(); context.shell_manager.ls(path, context.name)
let args = args.evaluate_once(registry)?;
shell_manager.ls(args)
} }

View file

@ -17,7 +17,7 @@ impl PerItemCommand for Mkdir {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("mkdir").rest(SyntaxType::Path) Signature::build("mkdir").rest(SyntaxShape::Path)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {

View file

@ -1,6 +1,6 @@
use crate::commands::command::RunnablePerItemContext; use crate::commands::command::RunnablePerItemContext;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxShape;
use crate::parser::registry::{CommandRegistry, Signature}; use crate::parser::registry::{CommandRegistry, Signature};
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
@ -20,9 +20,9 @@ impl PerItemCommand for Move {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("mv") Signature::build("mv")
.required("source", SyntaxType::Path) .required("source", SyntaxShape::Pattern)
.required("destination", SyntaxType::Path) .required("destination", SyntaxShape::Path)
.named("file", SyntaxType::Any) .named("file", SyntaxShape::Any)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {

View file

@ -16,7 +16,7 @@ impl WholeStreamCommand for Nth {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("nth").required("row number", SyntaxType::Any) Signature::build("nth").required("amount", SyntaxShape::Any)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {

View file

@ -1,8 +1,9 @@
use crate::commands::UnevaluatedCallInfo; use crate::commands::UnevaluatedCallInfo;
use crate::context::SpanSource; use crate::context::SpanSource;
use crate::data::meta::Span;
use crate::data::Value; use crate::data::Value;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxShape;
use crate::parser::registry::Signature; use crate::parser::registry::Signature;
use crate::prelude::*; use crate::prelude::*;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -16,7 +17,7 @@ impl PerItemCommand for Open {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build(self.name()) Signature::build(self.name())
.required("path", SyntaxType::Path) .required("path", SyntaxShape::Path)
.switch("raw") .switch("raw")
} }
@ -76,10 +77,10 @@ fn run(
file_extension.or(path_str.split('.').last().map(String::from)) 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 // If we have loaded something, track its source
yield ReturnSuccess::action(CommandAction::AddSpanSource( yield ReturnSuccess::action(CommandAction::AddSpanSource(
uuid, contents_tag.origin,
span_source, span_source,
)); ));
} }
@ -100,10 +101,10 @@ fn run(
}, },
source: raw_args.call_info.source, source: raw_args.call_info.source,
source_map: raw_args.call_info.source_map, 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; let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
for res in result_vec { for res in result_vec {
match res { match res {
@ -146,7 +147,7 @@ pub async fn fetch(
Value::string(s), Value::string(s),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::File(cwd.to_string_lossy().to_string()), SpanSource::File(cwd.to_string_lossy().to_string()),
)), )),
@ -165,16 +166,16 @@ pub async fn fetch(
Value::string(s), Value::string(s),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::File(cwd.to_string_lossy().to_string()), SpanSource::File(cwd.to_string_lossy().to_string()),
)), )),
Err(_) => Ok(( Err(_) => Ok((
None, None,
Value::Primitive(Primitive::Binary(bytes)), Value::binary(bytes),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::File(cwd.to_string_lossy().to_string()), SpanSource::File(cwd.to_string_lossy().to_string()),
)), )),
@ -182,10 +183,10 @@ pub async fn fetch(
} else { } else {
Ok(( Ok((
None, None,
Value::Primitive(Primitive::Binary(bytes)), Value::binary(bytes),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::File(cwd.to_string_lossy().to_string()), SpanSource::File(cwd.to_string_lossy().to_string()),
)) ))
@ -203,16 +204,16 @@ pub async fn fetch(
Value::string(s), Value::string(s),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::File(cwd.to_string_lossy().to_string()), SpanSource::File(cwd.to_string_lossy().to_string()),
)), )),
Err(_) => Ok(( Err(_) => Ok((
None, None,
Value::Primitive(Primitive::Binary(bytes)), Value::binary(bytes),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::File(cwd.to_string_lossy().to_string()), SpanSource::File(cwd.to_string_lossy().to_string()),
)), )),
@ -220,10 +221,10 @@ pub async fn fetch(
} else { } else {
Ok(( Ok((
None, None,
Value::Primitive(Primitive::Binary(bytes)), Value::binary(bytes),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::File(cwd.to_string_lossy().to_string()), SpanSource::File(cwd.to_string_lossy().to_string()),
)) ))
@ -231,10 +232,10 @@ pub async fn fetch(
} }
_ => Ok(( _ => Ok((
None, None,
Value::Primitive(Primitive::Binary(bytes)), Value::binary(bytes),
Tag { Tag {
span, span,
origin: Some(Uuid::new_v4()), origin: Uuid::new_v4(),
}, },
SpanSource::File(cwd.to_string_lossy().to_string()), SpanSource::File(cwd.to_string_lossy().to_string()),
)), )),

View file

@ -17,7 +17,7 @@ impl WholeStreamCommand for Pick {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("pick").rest(SyntaxType::Any) Signature::build("pick").rest(SyntaxShape::Any)
} }
fn usage(&self) -> &str { 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::context::SpanSource;
use crate::data::Value; use crate::data::Value;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxShape;
use crate::parser::registry::Signature; use crate::parser::registry::Signature;
use crate::prelude::*; use crate::prelude::*;
use base64::encode; use base64::encode;
@ -10,7 +10,7 @@ use mime::Mime;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use surf::mime; use surf::mime;
use uuid::Uuid;
pub struct Post; pub struct Post;
impl PerItemCommand for Post { impl PerItemCommand for Post {
@ -20,10 +20,10 @@ impl PerItemCommand for Post {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build(self.name()) Signature::build(self.name())
.required("path", SyntaxType::Any) .required("path", SyntaxShape::Any)
.required("body", SyntaxType::Any) .required("body", SyntaxShape::Any)
.named("user", SyntaxType::Any) .named("user", SyntaxShape::Any)
.named("password", SyntaxType::Any) .named("password", SyntaxShape::Any)
.switch("raw") .switch("raw")
} }
@ -63,7 +63,7 @@ fn run(
file => file.clone(), file => file.clone(),
}; };
let path_str = path.as_string()?; 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 has_raw = call_info.args.has("raw");
let user = call_info.args.get("user").map(|x| x.as_string().unwrap()); let user = call_info.args.get("user").map(|x| x.as_string().unwrap());
let password = call_info let password = call_info
@ -85,10 +85,10 @@ fn run(
file_extension.or(path_str.split('.').last().map(String::from)) 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 // If we have loaded something, track its source
yield ReturnSuccess::action(CommandAction::AddSpanSource( yield ReturnSuccess::action(CommandAction::AddSpanSource(
uuid, contents_tag.origin,
span_source, span_source,
)); ));
} }
@ -109,10 +109,10 @@ fn run(
}, },
source: raw_args.call_info.source, source: raw_args.call_info.source,
source_map: raw_args.call_info.source_map, 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; let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
for res in result_vec { for res in result_vec {
match res { match res {
@ -143,7 +143,7 @@ pub async fn post(
body: &Tagged<Value>, body: &Tagged<Value>,
user: Option<String>, user: Option<String>,
password: Option<String>, password: Option<String>,
span: Span, tag: Tag,
registry: &CommandRegistry, registry: &CommandRegistry,
raw_args: &RawCommandArgs, raw_args: &RawCommandArgs,
) -> Result<(Option<String>, Value, Tag, SpanSource), ShellError> { ) -> Result<(Option<String>, Value, Tag, SpanSource), ShellError> {
@ -189,12 +189,13 @@ pub async fn post(
}, },
source: raw_args.call_info.source, source: raw_args.call_info.source,
source_map: raw_args.call_info.source_map, 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( let mut result = converter.run(
new_args.with_input(vec![item.clone().tagged(tag.clone())]), new_args.with_input(vec![item.clone().tagged(tag.clone())]),
&registry, &registry,
false,
); );
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
result.drain_vec().await; result.drain_vec().await;
@ -211,7 +212,7 @@ pub async fn post(
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Save could not successfully save", "Save could not successfully save",
"unexpected data during save", "unexpected data during save",
span, *tag,
)); ));
} }
} }
@ -227,7 +228,7 @@ pub async fn post(
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Could not automatically convert table", "Could not automatically convert table",
"needs manual conversion", "needs manual conversion",
tag.span, *tag,
)); ));
} }
} }
@ -243,13 +244,10 @@ pub async fn post(
ShellError::labeled_error( ShellError::labeled_error(
"Could not load text from remote url", "Could not load text from remote url",
"could not load", "could not load",
span, tag,
) )
})?), })?),
Tag { tag,
span,
origin: Some(Uuid::new_v4()),
},
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
(mime::APPLICATION, mime::JSON) => Ok(( (mime::APPLICATION, mime::JSON) => Ok((
@ -258,13 +256,10 @@ pub async fn post(
ShellError::labeled_error( ShellError::labeled_error(
"Could not load text from remote url", "Could not load text from remote url",
"could not load", "could not load",
span, tag,
) )
})?), })?),
Tag { tag,
span,
origin: Some(Uuid::new_v4()),
},
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
(mime::APPLICATION, mime::OCTET_STREAM) => { (mime::APPLICATION, mime::OCTET_STREAM) => {
@ -272,16 +267,13 @@ pub async fn post(
ShellError::labeled_error( ShellError::labeled_error(
"Could not load binary file", "Could not load binary file",
"could not load", "could not load",
span, tag,
) )
})?; })?;
Ok(( Ok((
None, None,
Value::Primitive(Primitive::Binary(buf)), Value::binary(buf),
Tag { tag,
span,
origin: Some(Uuid::new_v4()),
},
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)) ))
} }
@ -290,16 +282,13 @@ pub async fn post(
ShellError::labeled_error( ShellError::labeled_error(
"Could not load image file", "Could not load image file",
"could not load", "could not load",
span, tag,
) )
})?; })?;
Ok(( Ok((
Some(image_ty.to_string()), Some(image_ty.to_string()),
Value::Primitive(Primitive::Binary(buf)), Value::binary(buf),
Tag { tag,
span,
origin: Some(Uuid::new_v4()),
},
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)) ))
} }
@ -309,13 +298,10 @@ pub async fn post(
ShellError::labeled_error( ShellError::labeled_error(
"Could not load text from remote url", "Could not load text from remote url",
"could not load", "could not load",
span, tag,
) )
})?), })?),
Tag { tag,
span,
origin: Some(Uuid::new_v4()),
},
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
(mime::TEXT, mime::PLAIN) => { (mime::TEXT, mime::PLAIN) => {
@ -336,13 +322,10 @@ pub async fn post(
ShellError::labeled_error( ShellError::labeled_error(
"Could not load text from remote url", "Could not load text from remote url",
"could not load", "could not load",
span, tag,
) )
})?), })?),
Tag { tag,
span,
origin: Some(Uuid::new_v4()),
},
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)) ))
} }
@ -352,10 +335,7 @@ pub async fn post(
"Not yet supported MIME type: {} {}", "Not yet supported MIME type: {} {}",
ty, sub_ty ty, sub_ty
)), )),
Tag { tag,
span,
origin: Some(Uuid::new_v4()),
},
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
} }
@ -363,10 +343,7 @@ pub async fn post(
None => Ok(( None => Ok((
None, None,
Value::string(format!("No content type found")), Value::string(format!("No content type found")),
Tag { tag,
span,
origin: Some(Uuid::new_v4()),
},
SpanSource::Url(location.to_string()), SpanSource::Url(location.to_string()),
)), )),
}, },
@ -374,7 +351,7 @@ pub async fn post(
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"URL could not be opened", "URL could not be opened",
"url not found", "url not found",
span, tag,
)); ));
} }
} }
@ -382,7 +359,7 @@ pub async fn post(
Err(ShellError::labeled_error( Err(ShellError::labeled_error(
"Expected a url", "Expected a url",
"needs a url", "needs a url",
span, tag,
)) ))
} }
} }

View file

@ -16,7 +16,7 @@ impl WholeStreamCommand for Reject {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("reject").rest(SyntaxType::Member) Signature::build("reject").rest(SyntaxShape::Member)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {

View file

@ -1,6 +1,6 @@
use crate::commands::command::RunnablePerItemContext; use crate::commands::command::RunnablePerItemContext;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxShape;
use crate::parser::registry::{CommandRegistry, Signature}; use crate::parser::registry::{CommandRegistry, Signature};
use crate::prelude::*; use crate::prelude::*;
use std::path::PathBuf; use std::path::PathBuf;
@ -20,7 +20,7 @@ impl PerItemCommand for Remove {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("rm") Signature::build("rm")
.required("path", SyntaxType::Path) .required("path", SyntaxShape::Pattern)
.switch("recursive") .switch("recursive")
} }

View file

@ -7,7 +7,7 @@ use std::path::{Path, PathBuf};
pub struct Save; pub struct Save;
macro_rules! process_string { macro_rules! process_string {
($input:ident, $name_span:ident) => {{ ($input:ident, $name_tag:ident) => {{
let mut result_string = String::new(); let mut result_string = String::new();
for res in $input { for res in $input {
match res { match res {
@ -21,7 +21,7 @@ macro_rules! process_string {
yield core::task::Poll::Ready(Err(ShellError::labeled_error( yield core::task::Poll::Ready(Err(ShellError::labeled_error(
"Save could not successfully save", "Save could not successfully save",
"unexpected data during save", "unexpected data during save",
$name_span, $name_tag,
))); )));
} }
} }
@ -31,7 +31,7 @@ macro_rules! process_string {
} }
macro_rules! process_string_return_success { macro_rules! process_string_return_success {
($result_vec:ident, $name_span:ident) => {{ ($result_vec:ident, $name_tag:ident) => {{
let mut result_string = String::new(); let mut result_string = String::new();
for res in $result_vec { for res in $result_vec {
match res { match res {
@ -45,7 +45,7 @@ macro_rules! process_string_return_success {
yield core::task::Poll::Ready(Err(ShellError::labeled_error( yield core::task::Poll::Ready(Err(ShellError::labeled_error(
"Save could not successfully save", "Save could not successfully save",
"unexpected data during text 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 { 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(); let mut result_binary: Vec<u8> = Vec::new();
for res in $result_vec { for res in $result_vec {
match res { match res {
@ -71,7 +71,7 @@ macro_rules! process_binary_return_success {
yield core::task::Poll::Ready(Err(ShellError::labeled_error( yield core::task::Poll::Ready(Err(ShellError::labeled_error(
"Save could not successfully save", "Save could not successfully save",
"unexpected data during binary save", "unexpected data during binary save",
$name_span, $name_tag,
))); )));
} }
} }
@ -93,7 +93,7 @@ impl WholeStreamCommand for Save {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("save") Signature::build("save")
.optional("path", SyntaxType::Path) .optional("path", SyntaxShape::Path)
.switch("raw") .switch("raw")
} }
@ -127,7 +127,7 @@ fn save(
raw_args: RawCommandArgs, raw_args: RawCommandArgs,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let mut full_path = PathBuf::from(shell_manager.path()); let mut full_path = PathBuf::from(shell_manager.path());
let name_span = name; let name_tag = name;
let source_map = source_map.clone(); let source_map = source_map.clone();
let stream = async_stream_block! { let stream = async_stream_block! {
@ -136,7 +136,7 @@ fn save(
// If there is no filename, check the metadata for the origin filename // If there is no filename, check the metadata for the origin filename
if input.len() > 0 { if input.len() > 0 {
let origin = input[0].origin(); let origin = input[0].origin();
match origin.and_then(|x| source_map.get(&x)) { match source_map.get(&origin) {
Some(path) => match path { Some(path) => match path {
SpanSource::File(file) => { SpanSource::File(file) => {
full_path.push(Path::new(file)); full_path.push(Path::new(file));
@ -145,7 +145,7 @@ fn save(
yield Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Save requires a filepath", "Save requires a filepath",
"needs path", "needs path",
name_span, name_tag,
)); ));
} }
}, },
@ -153,7 +153,7 @@ fn save(
yield Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Save requires a filepath", "Save requires a filepath",
"needs path", "needs path",
name_span, name_tag,
)); ));
} }
} }
@ -161,7 +161,7 @@ fn save(
yield Err(ShellError::labeled_error( yield Err(ShellError::labeled_error(
"Save requires a filepath", "Save requires a filepath",
"needs path", "needs path",
name_span, name_tag,
)); ));
} }
} else { } else {
@ -185,21 +185,21 @@ fn save(
}, },
source: raw_args.call_info.source, source: raw_args.call_info.source,
source_map: raw_args.call_info.source_map, 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; let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
if converter.is_binary() { if converter.is_binary() {
process_binary_return_success!(result_vec, name_span) process_binary_return_success!(result_vec, name_tag)
} else { } else {
process_string_return_success!(result_vec, name_span) process_string_return_success!(result_vec, name_tag)
} }
} else { } else {
process_string!(input, name_span) process_string!(input, name_tag)
} }
} else { } else {
process_string!(input, name_span) process_string!(input, name_tag)
} }
} else { } else {
Ok(string_from(&input).into_bytes()) 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> { fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let mut shells_out = VecDeque::new(); 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() { 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 { if index == args.shell_manager.current_shell {
dict.insert(" ", "X".to_string()); dict.insert(" ", "X".to_string());

View file

@ -29,7 +29,7 @@ impl WholeStreamCommand for Size {
fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> { fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let input = args.input; let input = args.input;
let span = args.call_info.name_span; let tag = args.call_info.name_tag;
Ok(input Ok(input
.values .values
.map(move |v| match v.item { .map(move |v| match v.item {
@ -37,9 +37,9 @@ fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream,
_ => Err(ShellError::labeled_error_with_secondary( _ => Err(ShellError::labeled_error_with_secondary(
"Expected a string from pipeline", "Expected a string from pipeline",
"requires string input", "requires string input",
span, tag,
"value originates from here", "value originates from here",
v.span(), v.tag(),
)), )),
}) })
.to_output_stream()) .to_output_stream())
@ -71,7 +71,7 @@ fn count(contents: &str, tag: impl Into<Tag>) -> Tagged<Value> {
} }
let mut dict = TaggedDictBuilder::new(tag); 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("name", Value::string(name));
dict.insert("lines", Value::int(lines)); dict.insert("lines", Value::int(lines));
dict.insert("words", Value::int(words)); dict.insert("words", Value::int(words));

View file

@ -16,7 +16,7 @@ impl WholeStreamCommand for SkipWhile {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("skip-while") Signature::build("skip-while")
.required("condition", SyntaxType::Block) .required("condition", SyntaxShape::Block)
.filter() .filter()
} }

View file

@ -15,7 +15,7 @@ impl WholeStreamCommand for SortBy {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("sort-by").rest(SyntaxType::String) Signature::build("sort-by").rest(SyntaxShape::String)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {

View file

@ -21,9 +21,9 @@ impl WholeStreamCommand for SplitColumn {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("split-column") Signature::build("split-column")
.required("separator", SyntaxType::Any) .required("separator", SyntaxShape::Any)
.switch("collapse-empty") .switch("collapse-empty")
.rest(SyntaxType::Member) .rest(SyntaxShape::Member)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -96,7 +96,7 @@ fn split_column(
"requires string input", "requires string input",
name, name,
"value originates from here", "value originates from here",
v.span(), v.tag(),
)), )),
}) })
.to_output_stream()) .to_output_stream())

View file

@ -17,7 +17,7 @@ impl WholeStreamCommand for SplitRow {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("split-row").required("separator", SyntaxType::Any) Signature::build("split-row").required("separator", SyntaxShape::Any)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -62,7 +62,7 @@ fn split_row(
"requires string input", "requires string input",
name, name,
"value originates from here", "value originates from here",
v.span(), v.tag(),
))); )));
result result
} }

View file

@ -36,13 +36,13 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream,
let mut tags = TaggedDictBuilder::new(v.tag()); let mut tags = TaggedDictBuilder::new(v.tag());
{ {
let origin = v.origin(); let origin = v.origin();
let span = v.span(); let span = v.tag().span;
let mut dict = TaggedDictBuilder::new(v.tag()); let mut dict = TaggedDictBuilder::new(v.tag());
dict.insert("start", Value::int(span.start as i64)); dict.insert("start", Value::int(span.start as i64));
dict.insert("end", Value::int(span.end as i64)); dict.insert("end", Value::int(span.end as i64));
tags.insert_tagged("span", dict.into_tagged_value()); 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)) => { Some(SpanSource::File(source)) => {
tags.insert("origin", Value::string(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)) Ok(Bson::Document(doc))
} }
fn shell_encode_document( fn shell_encode_document(writer: &mut Vec<u8>, doc: Document, tag: Tag) -> Result<(), ShellError> {
writer: &mut Vec<u8>,
doc: Document,
span: Span,
) -> Result<(), ShellError> {
match encode_document(writer, &doc) { match encode_document(writer, &doc) {
Err(e) => Err(ShellError::labeled_error( Err(e) => Err(ShellError::labeled_error(
format!("Failed to encode document due to: {:?}", e), format!("Failed to encode document due to: {:?}", e),
"requires BSON-compatible document", "requires BSON-compatible document",
span, tag,
)), )),
_ => Ok(()), _ => 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(); let mut out = Vec::new();
match bson { match bson {
Bson::Array(a) => { Bson::Array(a) => {
for v in a.into_iter() { for v in a.into_iter() {
match v { 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( return Err(ShellError::labeled_error(
format!("All top level values must be Documents, got {:?}", v), format!("All top level values must be Documents, got {:?}", v),
"requires BSON-compatible document", "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( return Err(ShellError::labeled_error(
format!("All top level values must be Documents, got {:?}", bson), format!("All top level values must be Documents, got {:?}", bson),
"requires BSON-compatible document", "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> { fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let name_span = args.name_span(); let name_tag = args.name_tag();
let stream = async_stream_block! { let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = args.input.values.collect().await; 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 { for value in to_process_input {
match value_to_bson_value(&value) { match value_to_bson_value(&value) {
Ok(bson_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( 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( _ => 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", "requires BSON-compatible input",
name_span, name_tag,
"originates from here".to_string(), "originates from here".to_string(),
value.span(), value.tag(),
)), )),
} }
} }
_ => yield Err(ShellError::labeled_error( _ => yield Err(ShellError::labeled_error(
"Expected a table with BSON-compatible structure from pipeline", "Expected a table with BSON-compatible structure from pipeline",
"requires BSON-compatible input", "requires BSON-compatible input",
name_span)) name_tag))
} }
} }
}; };

View file

@ -134,7 +134,7 @@ fn to_csv(
ToCSVArgs { headerless }: ToCSVArgs, ToCSVArgs { headerless }: ToCSVArgs,
RunnableContext { input, name, .. }: RunnableContext, RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let name_span = name; let name_tag = name;
let stream = async_stream_block! { let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = input.values.collect().await; let input: Vec<Tagged<Value>> = input.values.collect().await;
@ -155,15 +155,15 @@ fn to_csv(
} else { } else {
x 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( 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", "requires CSV-compatible input",
name_span, name_tag,
"originates from here".to_string(), "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> { fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let name_span = args.name_span(); let name_tag = args.name_tag();
let stream = async_stream_block! { let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = args.input.values.collect().await; 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) => { Ok(json_value) => {
match serde_json::to_string(&json_value) { match serde_json::to_string(&json_value) {
Ok(x) => yield ReturnSuccess::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( _ => 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", "requires JSON-compatible input",
name_span, name_tag,
"originates from here".to_string(), "originates from here".to_string(),
value.span(), value.tag(),
)), )),
} }
} }
_ => yield Err(ShellError::labeled_error( _ => yield Err(ShellError::labeled_error(
"Expected a table with JSON-compatible structure from pipeline", "Expected a table with JSON-compatible structure from pipeline",
"requires JSON-compatible input", "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> { fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let name_span = args.name_span(); let name_tag = args.name_tag();
let stream = async_stream_block! { let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = args.input.values.collect().await; 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), Ok(out) => yield ReturnSuccess::value(out),
_ => { _ => {
yield Err(ShellError::labeled_error( 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", "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> { fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let name_span = args.name_span(); let name_tag = args.name_tag();
let stream = async_stream_block! { let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = args.input.values.collect().await; 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) => { Ok(toml_value) => {
match toml::to_string(&toml_value) { match toml::to_string(&toml_value) {
Ok(x) => yield ReturnSuccess::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( _ => 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", "requires TOML-compatible input",
name_span, name_tag,
"originates from here".to_string(), "originates from here".to_string(),
value.span(), value.tag(),
)), )),
} }
} }
_ => yield Err(ShellError::labeled_error( _ => yield Err(ShellError::labeled_error(
"Expected a table with TOML-compatible structure from pipeline", "Expected a table with TOML-compatible structure from pipeline",
"requires TOML-compatible input", "requires TOML-compatible input",
name_span)) name_tag))
} }
} }
}; };

View file

@ -133,7 +133,7 @@ fn to_tsv(
ToTSVArgs { headerless }: ToTSVArgs, ToTSVArgs { headerless }: ToTSVArgs,
RunnableContext { input, name, .. }: RunnableContext, RunnableContext { input, name, .. }: RunnableContext,
) -> Result<OutputStream, ShellError> { ) -> Result<OutputStream, ShellError> {
let name_span = name; let name_tag = name;
let stream = async_stream_block! { let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = input.values.collect().await; let input: Vec<Tagged<Value>> = input.values.collect().await;
@ -154,15 +154,15 @@ fn to_tsv(
} else { } else {
x 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( 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", "requires TSV-compatible input",
name_span, name_tag,
"originates from here".to_string(), "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> { fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let name_span = args.name_span(); let name_tag = args.name_tag();
let stream = async_stream_block! { let stream = async_stream_block! {
let input: Vec<Tagged<Value>> = args.input.values.collect().await; 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) => { Ok(yaml_value) => {
match serde_yaml::to_string(&yaml_value) { match serde_yaml::to_string(&yaml_value) {
Ok(x) => yield ReturnSuccess::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( _ => 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", "requires YAML-compatible input",
name_span, name_tag,
"originates from here".to_string(), "originates from here".to_string(),
value.span(), value.tag(),
)), )),
} }
} }
_ => yield Err(ShellError::labeled_error( _ => yield Err(ShellError::labeled_error(
"Expected a table with YAML-compatible structure from pipeline", "Expected a table with YAML-compatible structure from pipeline",
"requires YAML-compatible input", "requires YAML-compatible input",
name_span)) name_tag))
} }
} }
}; };

View file

@ -34,7 +34,7 @@ fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream,
.values .values
.map(move |v| { .map(move |v| {
let string = String::extract(&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()) .to_output_stream())
} }

View file

@ -31,14 +31,14 @@ impl WholeStreamCommand for Version {
pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> { pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once(registry)?; 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(); let mut indexmap = IndexMap::new();
indexmap.insert( indexmap.insert(
"version".to_string(), "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)) 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::commands::PerItemCommand;
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::hir::SyntaxType; use crate::parser::hir::SyntaxShape;
use crate::parser::registry; use crate::parser::registry;
use crate::prelude::*; use crate::prelude::*;
@ -12,7 +12,7 @@ impl PerItemCommand for Where {
} }
fn signature(&self) -> registry::Signature { fn signature(&self) -> registry::Signature {
Signature::build("where").required("condition", SyntaxType::Block) Signature::build("where").required("condition", SyntaxShape::Block)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -49,7 +49,7 @@ impl PerItemCommand for Where {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a condition", "Expected a condition",
"where needs a condition", "where needs a condition",
tag.span, *tag,
)) ))
} }
}; };

View file

@ -13,7 +13,7 @@ impl WholeStreamCommand for Which {
} }
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("which").required("name", SyntaxType::Any) Signature::build("which").required("name", SyntaxShape::Any)
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
@ -33,7 +33,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
let args = args.evaluate_once(registry)?; let args = args.evaluate_once(registry)?;
let mut which_out = VecDeque::new(); 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 let Some(v) = &args.call_info.args.positional {
if v.len() > 0 { if v.len() > 0 {
@ -52,7 +52,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a filename to find", "Expected a filename to find",
"needs a filename", "needs a filename",
tag.span, *tag,
)); ));
} }
} }
@ -60,14 +60,14 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a binary to find", "Expected a binary to find",
"needs application name", "needs application name",
span, tag,
)); ));
} }
} else { } else {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected a binary to find", "Expected a binary to find",
"needs application name", "needs application name",
span, tag,
)); ));
} }

View file

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

View file

@ -13,10 +13,64 @@ use std::fmt;
use std::path::PathBuf; use std::path::PathBuf;
use std::time::SystemTime; 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)] #[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize, Serialize)]
pub enum Primitive { pub enum Primitive {
Nothing, Nothing,
#[serde(with = "serde_bigint")]
Int(BigInt), Int(BigInt),
#[serde(with = "serde_bigdecimal")]
Decimal(BigDecimal), Decimal(BigDecimal),
Bytes(u64), Bytes(u64),
String(String), String(String),
@ -150,7 +204,7 @@ pub struct Operation {
pub struct Block { pub struct Block {
pub(crate) expressions: Vec<hir::Expression>, pub(crate) expressions: Vec<hir::Expression>,
pub(crate) source: Text, pub(crate) source: Text,
pub(crate) span: Span, pub(crate) tag: Tag,
} }
impl Block { impl Block {
@ -158,7 +212,7 @@ impl Block {
let scope = Scope::new(value.clone()); let scope = Scope::new(value.clone());
if self.expressions.len() == 0 { if self.expressions.len() == 0 {
return Ok(Value::nothing().simple_spanned(self.span)); return Ok(Value::nothing().tagged(self.tag));
} }
let mut last = None; let mut last = None;
@ -249,7 +303,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for Block {
Value::Block(block) => Ok(block.clone()), Value::Block(block) => Ok(block.clone()),
v => Err(ShellError::type_error( v => Err(ShellError::type_error(
"Block", "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( v => Err(ShellError::type_error(
"Integer", "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()), Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
v => Err(ShellError::type_error( v => Err(ShellError::type_error(
"String", "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()), Value::Primitive(Primitive::Binary(b)) => Ok(b.clone()),
v => Err(ShellError::type_error( v => Err(ShellError::type_error(
"Binary", "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), Value::Row(d) => Ok(d),
v => Err(ShellError::type_error( v => Err(ShellError::type_error(
"Dictionary", "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), Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present),
v => Err(ShellError::type_error( v => Err(ShellError::type_error(
"Boolean", "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()), Value::Primitive(Primitive::Path(path)) => Ok(path.clone()),
other => Err(ShellError::type_error( other => Err(ShellError::type_error(
"Path", "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> { pub fn config_path() -> Result<PathBuf, ShellError> {
let path = app_root(AppDataType::UserConfig, &APP_INFO) app_path(AppDataType::UserConfig, "config")
.map_err(|err| ShellError::string(&format!("Couldn't open config path:\n{}", err)))?;
Ok(path)
} }
pub fn default_path() -> Result<PathBuf, ShellError> { 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()) 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( pub fn read(
span: impl Into<Span>, tag: impl Into<Tag>,
at: &Option<PathBuf>, at: &Option<PathBuf>,
) -> Result<IndexMap<String, Tagged<Value>>, ShellError> { ) -> Result<IndexMap<String, Tagged<Value>>, ShellError> {
let filename = default_path()?; let filename = default_path()?;
@ -64,15 +72,15 @@ pub fn read(
trace!("config file = {}", filename.display()); trace!("config file = {}", filename.display());
let span = span.into(); let tag = tag.into();
let contents = fs::read_to_string(filename) 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)))?; .map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?;
let parsed: toml::Value = toml::from_str(&contents) let parsed: toml::Value = toml::from_str(&contents)
.map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?; .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(); let tag = value.tag();
match value.item { match value.item {
Value::Row(Dictionary { entries }) => Ok(entries), 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> { pub(crate) fn config(tag: impl Into<Tag>) -> Result<IndexMap<String, Tagged<Value>>, ShellError> {
read(span, &None) read(tag, &None)
} }
pub fn write( pub fn write(

View file

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

View file

@ -15,8 +15,8 @@ impl From<String> for Value {
impl<T: Into<Value>> Tagged<T> { impl<T: Into<Value>> Tagged<T> {
pub fn into_tagged_value(self) -> Tagged<Value> { pub fn into_tagged_value(self) -> Tagged<Value> {
let value_span = self.span(); let value_tag = self.tag();
let value = self.item.into(); 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 getset::Getters;
use serde::Deserialize; use serde::Deserialize;
use serde::Serialize; use serde::Serialize;
use std::path::{Path, PathBuf};
use uuid::Uuid; use uuid::Uuid;
#[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)] #[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
@ -13,9 +14,15 @@ pub struct Tagged<T> {
pub item: T, pub item: T,
} }
impl<T> HasSpan for Tagged<T> { impl<T> HasTag for Tagged<T> {
fn span(&self) -> Span { fn tag(&self) -> Tag {
self.tag.span 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()) 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 // 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 // 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. // have the infrastructure to make that work.
@ -36,7 +39,7 @@ pub trait TaggedItem: Sized {
self, self,
Tag { Tag {
span: Span::unknown(), 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> { impl<T> Tagged<T> {
pub fn spanned(self, span: impl Into<Span>) -> Tagged<T> { pub fn with_tag(self, tag: impl Into<Tag>) -> Tagged<T> {
Tagged::from_item( Tagged::from_item(self.item, tag)
self.item,
Tag {
span: span.into(),
origin: None,
},
)
} }
pub fn from_item(item: T, tag: impl Into<Tag>) -> Tagged<T> { 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> { pub fn map<U>(self, input: impl FnOnce(T) -> U) -> Tagged<U> {
let tag = self.tag(); let tag = self.tag();
let mapped = input(self.item); 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> { pub(crate) fn copy_tag<U>(&self, output: U) -> Tagged<U> {
let span = self.span(); Tagged::from_item(output, self.tag())
Tagged::from_simple_spanned_item(output, span)
} }
pub fn source(&self, source: &Text) -> Text { pub fn source(&self, source: &Text) -> Text {
Text::from(self.span().slice(source)) Text::from(self.tag().slice(source))
}
pub fn span(&self) -> Span {
self.tag.span
} }
pub fn tag(&self) -> Tag { pub fn tag(&self) -> 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 self.tag.origin
} }
pub fn origin_name(&self, source_map: &SourceMap) -> Option<String> { pub fn origin_name(&self, source_map: &SourceMap) -> Option<String> {
match self.tag.origin.map(|x| source_map.get(&x)) { match source_map.get(&self.tag.origin) {
Some(Some(SpanSource::File(file))) => Some(file.clone()), Some(SpanSource::File(file)) => Some(file.clone()),
Some(Some(SpanSource::Url(url))) => Some(url.clone()), Some(SpanSource::Url(url)) => Some(url.clone()),
_ => None, _ => None,
} }
} }
@ -126,20 +111,14 @@ impl<T> Tagged<T> {
} }
} }
impl<T> From<&Tagged<T>> for Span { impl From<&Tag> for Tag {
fn from(input: &Tagged<T>) -> Span { fn from(input: &Tag) -> Tag {
input.span()
}
}
impl From<&Span> for Span {
fn from(input: &Span) -> Span {
*input *input
} }
} }
impl From<nom5_locate::LocatedSpan<&str>> for Span { impl From<nom_locate::LocatedSpanEx<&str, Uuid>> for Span {
fn from(input: nom5_locate::LocatedSpan<&str>) -> Span { fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Span {
Span { Span {
start: input.offset, start: input.offset,
end: input.offset + input.fragment.len(), 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 { impl<T>
fn from(input: (nom5_locate::LocatedSpan<T>, nom5_locate::LocatedSpan<T>)) -> Span { 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 { Span {
start: input.0.offset, start: input.0.offset,
end: input.1.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, Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters,
)] )]
pub struct Tag { pub struct Tag {
pub origin: Option<Uuid>, pub origin: Uuid,
pub span: Span, pub span: Span,
} }
impl From<Span> for Tag { impl From<Span> for Tag {
fn from(span: Span) -> Self { fn from(span: Span) -> Self {
Tag { origin: None, span } Tag {
origin: uuid::Uuid::nil(),
span,
}
} }
} }
impl From<&Span> for Tag { impl From<&Span> for Tag {
fn from(span: &Span) -> Self { fn from(span: &Span) -> Self {
Tag { Tag {
origin: None, origin: uuid::Uuid::nil(),
span: *span, 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 { impl From<Tag> for Span {
fn from(tag: Tag) -> Self { fn from(tag: Tag) -> Self {
tag.span tag.span
@ -211,15 +237,45 @@ impl From<&Tag> for Span {
impl Tag { impl Tag {
pub fn unknown_origin(span: Span) -> 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 { pub fn unknown() -> Tag {
Tag { Tag {
origin: None, origin: uuid::Uuid::nil(),
span: Span::unknown(), 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)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
@ -284,3 +340,33 @@ impl language_reporting::ReportingSpan for Span {
self.end 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 { impl Description {
pub fn from(value: Tagged<impl Into<String>>) -> Description { pub fn from(value: Tagged<impl Into<String>>) -> Description {
let value_span = value.span();
let value_tag = value.tag(); let value_tag = value.tag();
match value_span { match value_tag {
Span { start: 0, end: 0 } => Description::Synthetic(value.item.into()), 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)), _ => Description::Source(Tagged::from_item(value.item.into(), value_tag)),
} }
} }
} }
impl Description { impl Description {
fn into_label(self) -> Result<Label<Span>, String> { fn into_label(self) -> Result<Label<Tag>, String> {
match self { 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), Description::Synthetic(s) => Err(s),
} }
} }
@ -81,7 +83,7 @@ impl ShellError {
) -> ShellError { ) -> ShellError {
ProximateShellError::RangeError { ProximateShellError::RangeError {
kind: expected.into(), kind: expected.into(),
actual_kind: actual.copy_span(format!("{:?}", actual.item)), actual_kind: actual.copy_tag(format!("{:?}", actual.item)),
operation, operation,
} }
.start() .start()
@ -116,9 +118,9 @@ impl ShellError {
ProximateShellError::MissingProperty { subpath, expr }.start() 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 { ProximateShellError::MissingValue {
span, tag,
reason: reason.into(), reason: reason.into(),
} }
.start() .start()
@ -127,28 +129,31 @@ impl ShellError {
pub(crate) fn argument_error( pub(crate) fn argument_error(
command: impl Into<String>, command: impl Into<String>,
kind: ArgumentError, kind: ArgumentError,
span: Span, tag: Tag,
) -> ShellError { ) -> ShellError {
ProximateShellError::ArgumentError { ProximateShellError::ArgumentError {
command: command.into(), command: command.into(),
error: kind, error: kind,
span, tag,
} }
.start() .start()
} }
pub(crate) fn invalid_external_word(span: Span) -> ShellError { pub(crate) fn invalid_external_word(tag: Tag) -> ShellError {
ProximateShellError::ArgumentError { ProximateShellError::ArgumentError {
command: "Invalid argument to Nu command (did you mean to call an external command?)" command: "Invalid argument to Nu command (did you mean to call an external command?)"
.into(), .into(),
error: ArgumentError::InvalidExternalWord, error: ArgumentError::InvalidExternalWord,
span, tag,
} }
.start() .start()
} }
pub(crate) fn parse_error( 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 { ) -> ShellError {
use language_reporting::*; use language_reporting::*;
@ -164,34 +169,34 @@ impl ShellError {
} }
nom::Err::Failure(span) | nom::Err::Error(span) => { nom::Err::Failure(span) | nom::Err::Error(span) => {
let diagnostic = Diagnostic::new(Severity::Error, format!("Parse Error")) 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) ShellError::diagnostic(diagnostic)
} }
} }
} }
pub(crate) fn diagnostic(diagnostic: Diagnostic<Span>) -> ShellError { pub(crate) fn diagnostic(diagnostic: Diagnostic<Tag>) -> ShellError {
ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start() ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start()
} }
pub(crate) fn to_diagnostic(self) -> Diagnostic<Span> { pub(crate) fn to_diagnostic(self) -> Diagnostic<Tag> {
match self.error { match self.error {
ProximateShellError::String(StringError { title, .. }) => { ProximateShellError::String(StringError { title, .. }) => {
Diagnostic::new(Severity::Error, title) Diagnostic::new(Severity::Error, title)
} }
ProximateShellError::InvalidCommand { command } => { ProximateShellError::InvalidCommand { command } => {
Diagnostic::new(Severity::Error, "Invalid 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( let mut d = Diagnostic::new(
Severity::Bug, Severity::Bug,
format!("Internal Error (missing value) :: {}", reason), format!("Internal Error (missing value) :: {}", reason),
); );
if let Some(span) = span { if let Some(tag) = tag {
d = d.with_label(Label::new_primary(span)); d = d.with_label(Label::new_primary(tag));
} }
d d
@ -199,12 +204,12 @@ impl ShellError {
ProximateShellError::ArgumentError { ProximateShellError::ArgumentError {
command, command,
error, error,
span, tag,
} => match error { } => match error {
ArgumentError::InvalidExternalWord => Diagnostic::new( ArgumentError::InvalidExternalWord => Diagnostic::new(
Severity::Error, Severity::Error,
format!("Invalid bare word for Nu command (did you intend to invoke an external command?)")) 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( ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new(
Severity::Error, Severity::Error,
format!( format!(
@ -214,7 +219,7 @@ impl ShellError {
Color::Black.bold().paint(name) Color::Black.bold().paint(name)
), ),
) )
.with_label(Label::new_primary(span)), .with_label(Label::new_primary(tag)),
ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new( ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new(
Severity::Error, Severity::Error,
format!( format!(
@ -224,7 +229,7 @@ impl ShellError {
), ),
) )
.with_label( .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( ArgumentError::MissingValueForName(name) => Diagnostic::new(
Severity::Error, Severity::Error,
@ -235,17 +240,17 @@ impl ShellError {
Color::Black.bold().paint(name) Color::Black.bold().paint(name)
), ),
) )
.with_label(Label::new_primary(span)), .with_label(Label::new_primary(tag)),
}, },
ProximateShellError::TypeError { ProximateShellError::TypeError {
expected, expected,
actual: actual:
Tagged { Tagged {
item: Some(actual), item: Some(actual),
tag: Tag { span, .. }, tag,
}, },
} => Diagnostic::new(Severity::Error, "Type Error").with_label( } => Diagnostic::new(Severity::Error, "Type Error").with_label(
Label::new_primary(span) Label::new_primary(tag)
.with_message(format!("Expected {}, found {}", expected, actual)), .with_message(format!("Expected {}, found {}", expected, actual)),
), ),
@ -254,10 +259,10 @@ impl ShellError {
actual: actual:
Tagged { Tagged {
item: None, item: None,
tag: Tag { span, .. }, tag
}, },
} => Diagnostic::new(Severity::Error, "Type Error") } => 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 { ProximateShellError::RangeError {
kind, kind,
@ -265,10 +270,10 @@ impl ShellError {
actual_kind: actual_kind:
Tagged { Tagged {
item, item,
tag: Tag { span, .. }, tag
}, },
} => Diagnostic::new(Severity::Error, "Range Error").with_label( } => 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", "Expected to convert {} to {} while {}, but it was out of range",
item, item,
kind.desc(), kind.desc(),
@ -279,11 +284,11 @@ impl ShellError {
ProximateShellError::SyntaxError { ProximateShellError::SyntaxError {
problem: problem:
Tagged { Tagged {
tag: Tag { span, .. }, tag,
.. ..
}, },
} => Diagnostic::new(Severity::Error, "Syntax Error") } => 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 } => { ProximateShellError::MissingProperty { subpath, expr } => {
let subpath = subpath.into_label(); let subpath = subpath.into_label();
@ -306,8 +311,8 @@ impl ShellError {
ProximateShellError::Diagnostic(diag) => diag.diagnostic, ProximateShellError::Diagnostic(diag) => diag.diagnostic,
ProximateShellError::CoerceError { left, right } => { ProximateShellError::CoerceError { left, right } => {
Diagnostic::new(Severity::Error, "Coercion error") Diagnostic::new(Severity::Error, "Coercion error")
.with_label(Label::new_primary(left.span()).with_message(left.item)) .with_label(Label::new_primary(left.tag()).with_message(left.item))
.with_label(Label::new_secondary(right.span()).with_message(right.item)) .with_label(Label::new_secondary(right.tag()).with_message(right.item))
} }
} }
} }
@ -315,26 +320,29 @@ impl ShellError {
pub fn labeled_error( pub fn labeled_error(
msg: impl Into<String>, msg: impl Into<String>,
label: impl Into<String>, label: impl Into<String>,
span: impl Into<Span>, tag: impl Into<Tag>,
) -> ShellError { ) -> ShellError {
ShellError::diagnostic( ShellError::diagnostic(
Diagnostic::new(Severity::Error, msg.into()) 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( pub fn labeled_error_with_secondary(
msg: impl Into<String>, msg: impl Into<String>,
primary_label: impl Into<String>, primary_label: impl Into<String>,
primary_span: Span, primary_span: impl Into<Tag>,
secondary_label: impl Into<String>, secondary_label: impl Into<String>,
secondary_span: Span, secondary_span: impl Into<Tag>,
) -> ShellError { ) -> ShellError {
ShellError::diagnostic( ShellError::diagnostic(
Diagnostic::new_error(msg.into()) Diagnostic::new_error(msg.into())
.with_label(Label::new_primary(primary_span).with_message(primary_label.into()))
.with_label( .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, expr: Description,
}, },
MissingValue { MissingValue {
span: Option<Span>, tag: Option<Tag>,
reason: String, reason: String,
}, },
ArgumentError { ArgumentError {
command: String, command: String,
error: ArgumentError, error: ArgumentError,
span: Span, tag: Tag,
}, },
RangeError { RangeError {
kind: ExpectedRange, kind: ExpectedRange,
@ -447,7 +455,7 @@ impl ToDebug for ProximateShellError {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ShellDiagnostic { pub struct ShellDiagnostic {
pub(crate) diagnostic: Diagnostic<Span>, pub(crate) diagnostic: Diagnostic<Tag>,
} }
impl PartialEq for ShellDiagnostic { impl PartialEq for ShellDiagnostic {

View file

@ -38,13 +38,13 @@ pub(crate) fn evaluate_baseline_expr(
source: &Text, source: &Text,
) -> Result<Tagged<Value>, ShellError> { ) -> Result<Tagged<Value>, ShellError> {
match &expr.item { 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( RawExpression::ExternalWord => Err(ShellError::argument_error(
"Invalid external word", "Invalid external word",
ArgumentError::InvalidExternalWord, 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)) => { RawExpression::Synthetic(hir::Synthetic::String(s)) => {
Ok(Value::string(s).tagged_unknown()) 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)?; let right = evaluate_baseline_expr(binary.right(), registry, scope, source)?;
match left.compare(binary.op(), &*right) { match left.compare(binary.op(), &*right) {
Ok(result) => Ok(Tagged::from_simple_spanned_item( Ok(result) => Ok(Value::boolean(result).tagged(expr.tag())),
Value::boolean(result),
expr.span(),
)),
Err((left_type, right_type)) => Err(ShellError::coerce_error( Err((left_type, right_type)) => Err(ShellError::coerce_error(
binary.left().copy_span(left_type), binary.left().copy_tag(left_type),
binary.right().copy_span(right_type), binary.right().copy_tag(right_type),
)), )),
} }
} }
@ -73,12 +70,14 @@ pub(crate) fn evaluate_baseline_expr(
exprs.push(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) => { RawExpression::Path(path) => {
let value = evaluate_baseline_expr(path.head(), registry, scope, source)?; let value = evaluate_baseline_expr(path.head(), registry, scope, source)?;
let mut item = value; let mut item = value;
@ -94,18 +93,12 @@ pub(crate) fn evaluate_baseline_expr(
)) ))
} }
Some(next) => { Some(next) => {
item = Tagged::from_simple_spanned_item( item = next.clone().item.tagged(expr.tag());
next.clone().item,
(expr.span().start, name.span().end),
)
} }
}; };
} }
Ok(Tagged::from_simple_spanned_item( Ok(item.item().clone().tagged(expr.tag()))
item.item().clone(),
expr.span(),
))
} }
RawExpression::Boolean(_boolean) => unimplemented!(), RawExpression::Boolean(_boolean) => unimplemented!(),
} }
@ -115,9 +108,9 @@ fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged<Val
let result = match literal.item { let result = match literal.item {
hir::Literal::Number(int) => int.into(), hir::Literal::Number(int) => int.into(),
hir::Literal::Size(int, unit) => unit.compute(int), hir::Literal::Size(int, unit) => unit.compute(int),
hir::Literal::String(span) => Value::string(span.slice(source)), hir::Literal::String(tag) => Value::string(tag.slice(source)),
hir::Literal::GlobPattern => Value::pattern(literal.span().slice(source)), hir::Literal::GlobPattern => Value::pattern(literal.tag().slice(source)),
hir::Literal::Bare => Value::string(literal.span().slice(source)), hir::Literal::Bare => Value::string(literal.tag().slice(source)),
}; };
literal.map(|_| result) literal.map(|_| result)
@ -129,12 +122,12 @@ fn evaluate_reference(
source: &Text, source: &Text,
) -> Result<Tagged<Value>, ShellError> { ) -> Result<Tagged<Value>, ShellError> {
match name { match name {
hir::Variable::It(span) => Ok(scope.it.item.clone().simple_spanned(span)), hir::Variable::It(tag) => Ok(scope.it.item.clone().tagged(*tag)),
hir::Variable::Other(span) => Ok(scope hir::Variable::Other(tag) => Ok(scope
.vars .vars
.get(span.slice(source)) .get(tag.slice(source))
.map(|v| v.clone()) .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, _source: &Text,
) -> Result<Tagged<Value>, ShellError> { ) -> Result<Tagged<Value>, ShellError> {
Err(ShellError::syntax_error( 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 generic;
pub(crate) mod list; pub(crate) mod list;
pub(crate) mod table; pub(crate) mod table;
pub(crate) mod vtable;
use crate::prelude::*; use crate::prelude::*;
pub(crate) use entries::EntriesView; pub(crate) use entries::EntriesView;
pub(crate) use table::TableView; pub(crate) use table::TableView;
pub(crate) use vtable::VTableView;
pub(crate) trait RenderView { pub(crate) trait RenderView {
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError>; 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 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") .get("table_mode")
.map(|s| match s.as_string().unwrap().as_ref() { .map(|s| match s.as_string().unwrap().as_ref() {
"light" => TableMode::Light, "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 errors;
mod evaluate; mod evaluate;
mod format; mod format;
mod fuzzysearch;
mod git; mod git;
mod parser; mod parser;
mod plugin; mod plugin;
@ -23,7 +24,7 @@ mod utils;
pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue}; pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue};
pub use crate::context::{SourceMap, SpanSource}; pub use crate::context::{SourceMap, SpanSource};
pub use crate::env::host::BasicHost; 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::parser::parse::token_tree_builder::TokenTreeBuilder;
pub use crate::plugin::{serve_plugin, Plugin}; pub use crate::plugin::{serve_plugin, Plugin};
pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath}; pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath};
@ -31,7 +32,7 @@ pub use cli::cli;
pub use data::base::{Primitive, Value}; pub use data::base::{Primitive, Value};
pub use data::config::{config_path, APP_INFO}; pub use data::config::{config_path, APP_INFO};
pub use data::dict::{Dictionary, TaggedDictBuilder}; 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 errors::{CoerceInto, ShellError};
pub use num_traits::cast::ToPrimitive; pub use num_traits::cast::ToPrimitive;
pub use parser::parse::text::Text; 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 parse_command::parse_command;
pub(crate) use registry::CommandRegistry; 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(); let _ = pretty_env_logger::try_init();
match pipeline(nom_input(input)) { match pipeline(nom_input(input, origin)) {
Ok((_rest, val)) => Ok(val), Ok((_rest, val)) => Ok(val),
Err(err) => Err(ShellError::parse_error(err)), 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 value: Option<Tagged<Value>> = if name == "rest" {
let positional = self.call.args.slice_from(self.position); let positional = self.call.args.slice_from(self.position);
self.position += positional.len(); self.position += positional.len();
Some(Value::Table(positional).tagged_unknown()) // TODO: correct span Some(Value::Table(positional).tagged_unknown()) // TODO: correct tag
} else { } else {
if self.call.args.has(name) { if self.call.args.has(name) {
self.call.args.get(name).map(|x| x.clone()) self.call.args.get(name).map(|x| x.clone())
@ -52,9 +52,7 @@ impl<'de> ConfigDeserializer<'de> {
self.stack.push(DeserializerItem { self.stack.push(DeserializerItem {
key_struct_field: Some((name.to_string(), name)), key_struct_field: Some((name.to_string(), name)),
val: value.unwrap_or_else(|| { val: value.unwrap_or_else(|| Value::nothing().tagged(self.call.name_tag)),
Value::nothing().tagged(Tag::unknown_origin(self.call.name_span))
}),
}); });
Ok(()) Ok(())

View file

@ -25,7 +25,7 @@ pub(crate) use self::external_command::ExternalCommand;
pub(crate) use self::named::NamedArguments; pub(crate) use self::named::NamedArguments;
pub(crate) use self::path::Path; 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 { pub fn path(head: impl Into<Expression>, tail: Vec<Tagged<impl Into<String>>>) -> Path {
Path::new( Path::new(
@ -131,72 +131,57 @@ impl RawExpression {
pub type Expression = Tagged<RawExpression>; pub type Expression = Tagged<RawExpression>;
impl Expression { impl Expression {
pub(crate) fn number(i: impl Into<Number>, span: impl Into<Span>) -> Expression { pub(crate) fn number(i: impl Into<Number>, tag: impl Into<Tag>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Number(i.into())), span) RawExpression::Literal(Literal::Number(i.into())).tagged(tag.into())
} }
pub(crate) fn size( pub(crate) fn size(
i: impl Into<Number>, i: impl Into<Number>,
unit: impl Into<Unit>, unit: impl Into<Unit>,
span: impl Into<Span>, tag: impl Into<Tag>,
) -> Expression { ) -> Expression {
Tagged::from_simple_spanned_item( RawExpression::Literal(Literal::Size(i.into(), unit.into())).tagged(tag.into())
RawExpression::Literal(Literal::Size(i.into(), unit.into())),
span,
)
} }
pub(crate) fn synthetic_string(s: impl Into<String>) -> Expression { pub(crate) fn synthetic_string(s: impl Into<String>) -> Expression {
RawExpression::Synthetic(Synthetic::String(s.into())).tagged_unknown() RawExpression::Synthetic(Synthetic::String(s.into())).tagged_unknown()
} }
pub(crate) fn string(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression { pub(crate) fn string(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
Tagged::from_simple_spanned_item( RawExpression::Literal(Literal::String(inner.into())).tagged(outer.into())
RawExpression::Literal(Literal::String(inner.into())),
outer.into(),
)
} }
pub(crate) fn file_path(path: impl Into<PathBuf>, outer: impl Into<Span>) -> Expression { pub(crate) fn file_path(path: impl Into<PathBuf>, outer: impl Into<Tag>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::FilePath(path.into()), outer.into()) RawExpression::FilePath(path.into()).tagged(outer)
} }
pub(crate) fn bare(span: impl Into<Span>) -> Expression { pub(crate) fn bare(tag: impl Into<Tag>) -> Expression {
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into()) RawExpression::Literal(Literal::Bare).tagged(tag)
} }
pub(crate) fn pattern(tag: impl Into<Tag>) -> Expression { pub(crate) fn pattern(tag: impl Into<Tag>) -> Expression {
RawExpression::Literal(Literal::GlobPattern).tagged(tag.into()) RawExpression::Literal(Literal::GlobPattern).tagged(tag.into())
} }
pub(crate) fn variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression { pub(crate) fn variable(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
Tagged::from_simple_spanned_item( RawExpression::Variable(Variable::Other(inner.into())).tagged(outer)
RawExpression::Variable(Variable::Other(inner.into())),
outer.into(),
)
} }
pub(crate) fn external_command(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression { pub(crate) fn external_command(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
Tagged::from_simple_spanned_item( RawExpression::ExternalCommand(ExternalCommand::new(inner.into())).tagged(outer)
RawExpression::ExternalCommand(ExternalCommand::new(inner.into())),
outer.into(),
)
} }
pub(crate) fn it_variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression { pub(crate) fn it_variable(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
Tagged::from_simple_spanned_item( RawExpression::Variable(Variable::It(inner.into())).tagged(outer)
RawExpression::Variable(Variable::It(inner.into())),
outer.into(),
)
} }
} }
impl ToDebug for Expression { impl ToDebug for Expression {
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result { fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result {
match self.item() { 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::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::Synthetic(Synthetic::String(s)) => write!(f, "{:?}", s),
RawExpression::Variable(Variable::It(_)) => write!(f, "$it"), RawExpression::Variable(Variable::It(_)) => write!(f, "$it"),
RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)), RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)),
@ -242,7 +227,7 @@ impl From<Tagged<Path>> for Expression {
pub enum Literal { pub enum Literal {
Number(Number), Number(Number),
Size(Number, Unit), Size(Number, Unit),
String(Span), String(Tag),
GlobPattern, GlobPattern,
Bare, Bare,
} }
@ -252,9 +237,9 @@ impl ToDebug for Tagged<&Literal> {
match self.item() { match self.item() {
Literal::Number(number) => write!(f, "{:?}", *number), Literal::Number(number) => write!(f, "{:?}", *number),
Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit), Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit),
Literal::String(span) => write!(f, "{}", span.slice(source)), Literal::String(tag) => write!(f, "{}", tag.slice(source)),
Literal::GlobPattern => write!(f, "{}", self.span().slice(source)), Literal::GlobPattern => write!(f, "{}", self.tag().slice(source)),
Literal::Bare => write!(f, "{}", self.span().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)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
pub enum Variable { pub enum Variable {
It(Span), It(Tag),
Other(Span), Other(Tag),
} }

View file

@ -10,19 +10,19 @@ pub fn baseline_parse_single_token(
source: &Text, source: &Text,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
Ok(match *token.item() { 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) => { 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::String(tag) => hir::Expression::string(tag, token.tag()),
RawToken::Variable(span) if span.slice(source) == "it" => { RawToken::Variable(tag) if tag.slice(source) == "it" => {
hir::Expression::it_variable(span, token.span()) hir::Expression::it_variable(tag, token.tag())
} }
RawToken::Variable(span) => hir::Expression::variable(span, token.span()), RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()),
RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()),
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())),
RawToken::GlobPattern => hir::Expression::pattern(token.span()), RawToken::GlobPattern => hir::Expression::pattern(token.tag()),
RawToken::Bare => hir::Expression::bare(token.span()), RawToken::Bare => hir::Expression::bare(token.tag()),
}) })
} }
@ -31,24 +31,24 @@ pub fn baseline_parse_token_as_number(
source: &Text, source: &Text,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
Ok(match *token.item() { Ok(match *token.item() {
RawToken::Variable(span) if span.slice(source) == "it" => { RawToken::Variable(tag) if tag.slice(source) == "it" => {
hir::Expression::it_variable(span, token.span()) hir::Expression::it_variable(tag, token.tag())
} }
RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()),
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())),
RawToken::Variable(span) => hir::Expression::variable(span, token.span()), RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()),
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(number, unit) => { 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 => { RawToken::GlobPattern => {
return Err(ShellError::type_error( return Err(ShellError::type_error(
"Number", "Number",
"glob pattern".to_string().tagged(token.tag()), "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, source: &Text,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
Ok(match *token.item() { Ok(match *token.item() {
RawToken::Variable(span) if span.slice(source) == "it" => { RawToken::Variable(tag) if tag.slice(source) == "it" => {
hir::Expression::it_variable(span, token.span()) hir::Expression::it_variable(tag, token.tag())
} }
RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()), RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()),
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())),
RawToken::Variable(span) => hir::Expression::variable(span, token.span()), RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()),
RawToken::Number(_) => hir::Expression::bare(token.span()), RawToken::Number(_) => hir::Expression::bare(token.tag()),
RawToken::Size(_, _) => hir::Expression::bare(token.span()), RawToken::Size(_, _) => hir::Expression::bare(token.tag()),
RawToken::Bare => hir::Expression::bare(token.span()), RawToken::Bare => hir::Expression::bare(token.tag()),
RawToken::GlobPattern => { RawToken::GlobPattern => {
return Err(ShellError::type_error( return Err(ShellError::type_error(
"String", "String",
"glob pattern".tagged(token.tag()), "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, source: &Text,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
Ok(match *token.item() { Ok(match *token.item() {
RawToken::Variable(span) if span.slice(source) == "it" => { RawToken::Variable(tag) if tag.slice(source) == "it" => {
hir::Expression::it_variable(span, token.span()) 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 => { RawToken::GlobPattern => {
return Err(ShellError::type_error( return Err(ShellError::type_error(
"Path", "Path",
"glob pattern".tagged(token.tag()), "glob pattern".tagged(token.tag()),
)) ))
} }
RawToken::String(span) => { RawToken::String(tag) => {
hir::Expression::file_path(expand_path(span.slice(source), context), token.span()) 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, source: &Text,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
Ok(match *token.item() { Ok(match *token.item() {
RawToken::Variable(span) if span.slice(source) == "it" => { RawToken::Variable(tag) if tag.slice(source) == "it" => {
hir::Expression::it_variable(span, token.span()) hir::Expression::it_variable(tag, token.tag())
} }
RawToken::ExternalCommand(_) => { RawToken::ExternalCommand(_) => {
return Err(ShellError::syntax_error( return Err(ShellError::syntax_error(
"Invalid external command".to_string().tagged(token.tag()), "Invalid external command".to_string().tagged(token.tag()),
)) ))
} }
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())), RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())),
RawToken::Variable(span) => hir::Expression::variable(span, token.span()), RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()),
RawToken::Number(_) => hir::Expression::bare(token.span()), RawToken::Number(_) => hir::Expression::bare(token.tag()),
RawToken::Size(_, _) => hir::Expression::bare(token.span()), RawToken::Size(_, _) => hir::Expression::bare(token.tag()),
RawToken::GlobPattern => hir::Expression::pattern(token.span()), RawToken::GlobPattern => hir::Expression::pattern(token.tag()),
RawToken::Bare => hir::Expression::file_path( RawToken::Bare => {
expand_path(token.span().slice(source), context), hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag())
token.span(), }
), RawToken::String(tag) => {
RawToken::String(span) => { hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag())
hir::Expression::file_path(expand_path(span.slice(source), context), token.span())
} }
}) })
} }

View file

@ -8,7 +8,7 @@ use crate::parser::{
}, },
DelimitedNode, Delimiter, PathNode, RawToken, TokenNode, DelimitedNode, Delimiter, PathNode, RawToken, TokenNode,
}; };
use crate::{Span, Tag, Tagged, TaggedItem, Text}; use crate::{Tag, Tagged, TaggedItem, Text};
use derive_new::new; use derive_new::new;
use log::trace; use log::trace;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -17,7 +17,7 @@ pub fn baseline_parse_tokens(
token_nodes: &mut TokensIterator<'_>, token_nodes: &mut TokensIterator<'_>,
context: &Context, context: &Context,
source: &Text, source: &Text,
syntax_type: SyntaxType, syntax_type: SyntaxShape,
) -> Result<Vec<hir::Expression>, ShellError> { ) -> Result<Vec<hir::Expression>, ShellError> {
let mut exprs: Vec<hir::Expression> = vec![]; let mut exprs: Vec<hir::Expression> = vec![];
@ -34,7 +34,7 @@ pub fn baseline_parse_tokens(
} }
#[derive(Debug, Copy, Clone, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum SyntaxType { pub enum SyntaxShape {
Any, Any,
List, List,
Literal, Literal,
@ -49,21 +49,21 @@ pub enum SyntaxType {
Boolean, Boolean,
} }
impl std::fmt::Display for SyntaxType { impl std::fmt::Display for SyntaxShape {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self { match self {
SyntaxType::Any => write!(f, "Any"), SyntaxShape::Any => write!(f, "Any"),
SyntaxType::List => write!(f, "List"), SyntaxShape::List => write!(f, "List"),
SyntaxType::Literal => write!(f, "Literal"), SyntaxShape::Literal => write!(f, "Literal"),
SyntaxType::String => write!(f, "String"), SyntaxShape::String => write!(f, "String"),
SyntaxType::Member => write!(f, "Member"), SyntaxShape::Member => write!(f, "Member"),
SyntaxType::Variable => write!(f, "Variable"), SyntaxShape::Variable => write!(f, "Variable"),
SyntaxType::Number => write!(f, "Number"), SyntaxShape::Number => write!(f, "Number"),
SyntaxType::Path => write!(f, "Path"), SyntaxShape::Path => write!(f, "Path"),
SyntaxType::Pattern => write!(f, "Pattern"), SyntaxShape::Pattern => write!(f, "Pattern"),
SyntaxType::Binary => write!(f, "Binary"), SyntaxShape::Binary => write!(f, "Binary"),
SyntaxType::Block => write!(f, "Block"), SyntaxShape::Block => write!(f, "Block"),
SyntaxType::Boolean => write!(f, "Boolean"), SyntaxShape::Boolean => write!(f, "Boolean"),
} }
} }
} }
@ -72,7 +72,7 @@ pub fn baseline_parse_next_expr(
tokens: &mut TokensIterator, tokens: &mut TokensIterator,
context: &Context, context: &Context,
source: &Text, source: &Text,
syntax_type: SyntaxType, syntax_type: SyntaxShape,
) -> Result<hir::Expression, ShellError> { ) -> Result<hir::Expression, ShellError> {
let next = tokens let next = tokens
.next() .next()
@ -81,69 +81,69 @@ pub fn baseline_parse_next_expr(
trace!(target: "nu::parser::parse_one_expr", "syntax_type={:?}, token={:?}", syntax_type, next); trace!(target: "nu::parser::parse_one_expr", "syntax_type={:?}, token={:?}", syntax_type, next);
match (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) return baseline_parse_token_as_path(token, context, source)
} }
(SyntaxType::Path, token) => { (SyntaxShape::Path, token) => {
return Err(ShellError::type_error( return Err(ShellError::type_error(
"Path", "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) return baseline_parse_token_as_pattern(token, context, source)
} }
(SyntaxType::Pattern, token) => { (SyntaxShape::Pattern, token) => {
return Err(ShellError::type_error( return Err(ShellError::type_error(
"Path", "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); return baseline_parse_token_as_string(token, source);
} }
(SyntaxType::String, token) => { (SyntaxShape::String, token) => {
return Err(ShellError::type_error( return Err(ShellError::type_error(
"String", "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)?); return Ok(baseline_parse_token_as_number(token, source)?);
} }
(SyntaxType::Number, token) => { (SyntaxShape::Number, token) => {
return Err(ShellError::type_error( return Err(ShellError::type_error(
"Numeric", "Numeric",
token.type_name().simple_spanned(token.span()), token.type_name().tagged(token.tag()),
)) ))
} }
// TODO: More legit member processing // TODO: More legit member processing
(SyntaxType::Member, TokenNode::Token(token)) => { (SyntaxShape::Member, TokenNode::Token(token)) => {
return baseline_parse_token_as_string(token, source); return baseline_parse_token_as_string(token, source);
} }
(SyntaxType::Member, token) => { (SyntaxShape::Member, token) => {
return Err(ShellError::type_error( return Err(ShellError::type_error(
"member", "member",
token.type_name().simple_spanned(token.span()), token.type_name().tagged(token.tag()),
)) ))
} }
(SyntaxType::Any, _) => {} (SyntaxShape::Any, _) => {}
(SyntaxType::List, _) => {} (SyntaxShape::List, _) => {}
(SyntaxType::Literal, _) => {} (SyntaxShape::Literal, _) => {}
(SyntaxType::Variable, _) => {} (SyntaxShape::Variable, _) => {}
(SyntaxType::Binary, _) => {} (SyntaxShape::Binary, _) => {}
(SyntaxType::Block, _) => {} (SyntaxShape::Block, _) => {}
(SyntaxType::Boolean, _) => {} (SyntaxShape::Boolean, _) => {}
}; };
let first = baseline_parse_semantic_token(next, context, source)?; let first = baseline_parse_semantic_token(next, context, source)?;
@ -162,7 +162,7 @@ pub fn baseline_parse_next_expr(
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"Expected something after an operator", "Expected something after an operator",
"operator", "operator",
op.span(), op.tag(),
)) ))
} }
Some(token) => baseline_parse_semantic_token(token, context, source)?, 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 // We definitely have a binary expression here -- let's see if we should coerce it into a block
match syntax_type { match syntax_type {
SyntaxType::Any => { SyntaxShape::Any => {
let span = (first.span().start, second.span().end); let tag = first.tag().until(second.tag());
let binary = hir::Binary::new(first, op, second); let binary = hir::Binary::new(first, op, second);
let binary = hir::RawExpression::Binary(Box::new(binary)); let binary = hir::RawExpression::Binary(Box::new(binary));
let binary = Tagged::from_simple_spanned_item(binary, span); let binary = binary.tagged(tag);
Ok(binary) Ok(binary)
} }
SyntaxType::Block => { SyntaxShape::Block => {
let span = (first.span().start, second.span().end); let tag = first.tag().until(second.tag());
let path: Tagged<hir::RawExpression> = match first { let path: Tagged<hir::RawExpression> = match first {
Tagged { Tagged {
item: hir::RawExpression::Literal(hir::Literal::Bare), item: hir::RawExpression::Literal(hir::Literal::Bare),
tag: Tag { span, .. }, tag,
} => { } => {
let string = let string = tag.slice(source).to_string().tagged(tag);
Tagged::from_simple_spanned_item(span.slice(source).to_string(), span);
let path = hir::Path::new( let path = hir::Path::new(
Tagged::from_simple_spanned_item( // TODO: Deal with synthetic nodes that have no representation at all in source
// TODO: Deal with synthetic nodes that have no representation at all in source hir::RawExpression::Variable(hir::Variable::It(Tag::unknown()))
hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), .tagged(Tag::unknown()),
(0, 0),
),
vec![string], vec![string],
); );
let path = hir::RawExpression::Path(Box::new(path)); let path = hir::RawExpression::Path(Box::new(path));
Tagged::from_simple_spanned_item(path, first.span()) path.tagged(first.tag())
} }
Tagged { Tagged {
item: hir::RawExpression::Literal(hir::Literal::String(inner)), item: hir::RawExpression::Literal(hir::Literal::String(inner)),
tag: Tag { span, .. }, tag,
} => { } => {
let string = let string = inner.slice(source).to_string().tagged(tag);
Tagged::from_simple_spanned_item(inner.slice(source).to_string(), span);
let path = hir::Path::new( let path = hir::Path::new(
Tagged::from_simple_spanned_item( // TODO: Deal with synthetic nodes that have no representation at all in source
// TODO: Deal with synthetic nodes that have no representation at all in source hir::RawExpression::Variable(hir::Variable::It(Tag::unknown()))
hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))), .tagged_unknown(),
(0, 0),
),
vec![string], vec![string],
); );
let path = hir::RawExpression::Path(Box::new(path)); let path = hir::RawExpression::Path(Box::new(path));
Tagged::from_simple_spanned_item(path, first.span()) path.tagged(first.tag())
} }
Tagged { Tagged {
item: hir::RawExpression::Variable(..), item: hir::RawExpression::Variable(..),
.. ..
} => first, } => first,
Tagged { Tagged { tag, item } => {
tag: Tag { span, .. },
item,
} => {
return Err(ShellError::labeled_error( return Err(ShellError::labeled_error(
"The first part of an un-braced block must be a column name", "The first part of an un-braced block must be a column name",
item.type_name(), item.type_name(),
span, tag,
)) ))
} }
}; };
let binary = hir::Binary::new(path, op, second); let binary = hir::Binary::new(path, op, second);
let binary = hir::RawExpression::Binary(Box::new(binary)); 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 = hir::RawExpression::Block(vec![binary]);
let block = Tagged::from_simple_spanned_item(block, span); let block = block.tagged(tag);
Ok(block) Ok(block)
} }
@ -265,11 +256,11 @@ pub fn baseline_parse_semantic_token(
"Unexpected operator".tagged(op.tag), "Unexpected operator".tagged(op.tag),
)), )),
TokenNode::Flag(flag) => Err(ShellError::syntax_error("Unexpected flag".tagged(flag.tag))), TokenNode::Flag(flag) => Err(ShellError::syntax_error("Unexpected flag".tagged(flag.tag))),
TokenNode::Member(span) => Err(ShellError::syntax_error( TokenNode::Member(tag) => Err(ShellError::syntax_error(
"BUG: Top-level member".tagged(span), "BUG: Top-level member".tagged(*tag),
)), )),
TokenNode::Whitespace(span) => Err(ShellError::syntax_error( TokenNode::Whitespace(tag) => Err(ShellError::syntax_error(
"BUG: Whitespace found during parse".tagged(span), "BUG: Whitespace found during parse".tagged(*tag),
)), )),
TokenNode::Error(error) => Err(*error.item.clone()), TokenNode::Error(error) => Err(*error.item.clone()),
TokenNode::Path(path) => baseline_parse_path(path, context, source), TokenNode::Path(path) => baseline_parse_path(path, context, source),
@ -288,11 +279,11 @@ pub fn baseline_parse_delimited(
&mut TokensIterator::new(children), &mut TokensIterator::new(children),
context, context,
source, source,
SyntaxType::Any, SyntaxShape::Any,
)?; )?;
let expr = hir::RawExpression::Block(exprs); let expr = hir::RawExpression::Block(exprs);
Ok(Tagged::from_simple_spanned_item(expr, token.span())) Ok(expr.tagged(token.tag()))
} }
Delimiter::Paren => unimplemented!(), Delimiter::Paren => unimplemented!(),
Delimiter::Square => { Delimiter::Square => {
@ -301,11 +292,11 @@ pub fn baseline_parse_delimited(
&mut TokensIterator::new(children), &mut TokensIterator::new(children),
context, context,
source, source,
SyntaxType::Any, SyntaxShape::Any,
)?; )?;
let expr = hir::RawExpression::List(exprs); 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() { for part in token.tail() {
let string = match part { let string = match part {
TokenNode::Token(token) => match token.item() { TokenNode::Token(token) => match token.item() {
RawToken::Bare => token.span().slice(source), RawToken::Bare => token.tag().slice(source),
RawToken::String(span) => span.slice(source), RawToken::String(tag) => tag.slice(source),
RawToken::Number(_) RawToken::Number(_)
| RawToken::Size(..) | RawToken::Size(..)
| RawToken::Variable(_) | RawToken::Variable(_)
@ -332,26 +323,26 @@ pub fn baseline_parse_path(
| RawToken::ExternalWord => { | RawToken::ExternalWord => {
return Err(ShellError::type_error( return Err(ShellError::type_error(
"String", "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 // TODO: Make this impossible
other => { other => {
return Err(ShellError::syntax_error( 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(); .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)] #[derive(Debug, new)]

View file

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

View file

@ -1,7 +1,6 @@
use crate::parser::hir::Expression; use crate::parser::hir::Expression;
use crate::parser::Flag; use crate::parser::Flag;
use crate::prelude::*; use crate::prelude::*;
use crate::Span;
use derive_new::new; use derive_new::new;
use indexmap::IndexMap; use indexmap::IndexMap;
use log::trace; use log::trace;
@ -11,7 +10,7 @@ use std::fmt;
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum NamedValue { pub enum NamedValue {
AbsentSwitch, AbsentSwitch,
PresentSwitch(Span), PresentSwitch(Tag),
AbsentValue, AbsentValue,
Value(Expression), Value(Expression),
} }
@ -27,7 +26,7 @@ impl ToDebug for NamedArguments {
for (name, value) in &self.named { for (name, value) in &self.named {
match value { match value {
NamedValue::AbsentSwitch => continue, NamedValue::AbsentSwitch => continue,
NamedValue::PresentSwitch(span) => write!(f, " --{}", span.slice(source))?, NamedValue::PresentSwitch(tag) => write!(f, " --{}", tag.slice(source))?,
NamedValue::AbsentValue => continue, NamedValue::AbsentValue => continue,
NamedValue::Value(expr) => write!(f, " --{} {}", name, expr.debug(source))?, 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 derive_new::new;
use language_reporting::{FileName, Location}; use language_reporting::{FileName, Location};
use uuid::Uuid;
#[derive(new, Debug, Clone)] #[derive(new, Debug, Clone)]
pub struct Files { pub struct Files {
@ -8,26 +9,30 @@ pub struct Files {
} }
impl language_reporting::ReportingFiles for Files { impl language_reporting::ReportingFiles for Files {
type Span = Span; type Span = Tag;
type FileId = usize; type FileId = Uuid;
fn byte_span( fn byte_span(
&self, &self,
_file: Self::FileId, file: Self::FileId,
from_index: usize, from_index: usize,
to_index: usize, to_index: usize,
) -> Option<Self::Span> { ) -> 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 { fn file_name(&self, _file: Self::FileId) -> FileName {
FileName::Verbatim(format!("shell")) FileName::Verbatim(format!("shell"))
} }
fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option<usize> { fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option<usize> {
unimplemented!("byte_index") unimplemented!("byte_index")
} }
fn location(&self, _file: Self::FileId, byte_index: usize) -> Option<Location> { fn location(&self, _file: Self::FileId, byte_index: usize) -> Option<Location> {
let source = &self.snippet; let source = &self.snippet;
let mut seen_lines = 0; let mut seen_lines = 0;
@ -51,14 +56,15 @@ impl language_reporting::ReportingFiles for Files {
None 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 source = &self.snippet;
let mut seen_lines = 0; let mut seen_lines = 0;
let mut seen_bytes = 0; let mut seen_bytes = 0;
for (pos, _) in source.match_indices('\n') { for (pos, _) in source.match_indices('\n') {
if seen_lines == lineno { if seen_lines == lineno {
return Some(Span::from((seen_bytes, pos))); return Some(Tag::from((seen_bytes, pos, file)));
} else { } else {
seen_lines += 1; seen_lines += 1;
seen_bytes = pos + 1; seen_bytes = pos + 1;
@ -66,17 +72,18 @@ impl language_reporting::ReportingFiles for Files {
} }
if seen_lines == 0 { if seen_lines == 0 {
Some(Span::from((0, self.snippet.len() - 1))) Some(Tag::from((0, self.snippet.len() - 1, file)))
} else { } else {
None 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; return None;
} else if span.end >= self.snippet.len() { } else if tag.span.end >= self.snippet.len() {
return None; 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 derive_new::new;
use getset::Getters; use getset::Getters;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -13,5 +13,5 @@ pub enum FlagKind {
#[get = "pub(crate)"] #[get = "pub(crate)"]
pub struct Flag { pub struct Flag {
kind: FlagKind, kind: FlagKind,
name: Span, name: Tag,
} }

View file

@ -5,7 +5,7 @@ use crate::parser::parse::{
tokens::*, unit::*, tokens::*, unit::*,
}; };
use crate::prelude::*; use crate::prelude::*;
use crate::{Span, Tagged}; use crate::{Tag, Tagged};
use nom; use nom;
use nom::branch::*; use nom::branch::*;
use nom::bytes::complete::*; use nom::bytes::complete::*;
@ -18,15 +18,16 @@ use log::trace;
use nom::dbg; use nom::dbg;
use nom::*; use nom::*;
use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice}; use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice};
use nom5_locate::{position, LocatedSpan}; use nom_locate::{position, LocatedSpanEx};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt::Debug; use std::fmt::Debug;
use std::str::FromStr; 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<'_> { pub fn nom_input(s: &str, origin: Uuid) -> NomSpan<'_> {
LocatedSpan::new(s) LocatedSpanEx::new_extra(s, origin)
} }
macro_rules! operator { macro_rules! operator {
@ -38,7 +39,7 @@ macro_rules! operator {
Ok(( Ok((
input, 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, Ok((input, dot)) => input,
// it's just an integer // 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 (input, tail) = digit1(input)?;
let end = input.offset; 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; let end = input.offset;
Ok(( Ok((
input, 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(( Ok((
input, 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(( Ok((
input, 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; 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); let next_char = &input.fragment.chars().nth(0);
if let Some(next_char) = next_char { 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( return Err(nom::Err::Error(nom::error::make_error(
input, input,
nom::error::ErrorKind::TakeWhile1, nom::error::ErrorKind::TakeWhile1,
@ -273,7 +277,10 @@ pub fn bare(input: NomSpan) -> IResult<NomSpan, TokenNode> {
let end = input.offset; 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 (input, _) = take_while1(is_external_word_char)(input)?;
let end = input.offset; 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(( Ok((
input, 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; 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(( Ok((
input, 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(( Ok((
input, 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(( Ok((
input, 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(( Ok((
input, input,
TokenTreeBuilder::spanned_size((number.item, *size), (start, end)), TokenTreeBuilder::tagged_size((number.item, *size), (start, end, input.extra)),
)) ))
} else { } else {
let end = input.offset; let end = input.offset;
@ -401,7 +414,7 @@ pub fn size(input: NomSpan) -> IResult<NomSpan, TokenNode> {
Ok(( Ok((
input, 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![]; let mut nodes = vec![];
if let Some(sp_left) = sp_left { 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); nodes.push(first);
for (ws, token) in list { for (ws, token) in list {
nodes.push(TokenNode::Whitespace(Span::from(ws))); nodes.push(TokenNode::Whitespace(Tag::from(ws)));
nodes.push(token); nodes.push(token);
} }
if let Some(sp_right) = sp_right { if let Some(sp_right) = sp_right {
nodes.push(TokenNode::Whitespace(Span::from(sp_right))); nodes.push(TokenNode::Whitespace(Tag::from(sp_right)));
} }
nodes nodes
@ -478,7 +491,10 @@ pub fn whitespace(input: NomSpan) -> IResult<NomSpan, TokenNode> {
let (input, ws1) = space1(input)?; let (input, ws1) = space1(input)?;
let right = input.offset; 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(( Ok((
input, 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(( Ok((
input, 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(( Ok((
input, 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 (input, items) = token_list(input)?;
let right = input.offset; 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(( Ok((
input, 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(( Ok((
input, input,
TokenTreeBuilder::spanned_pipeline( TokenTreeBuilder::tagged_pipeline(
(make_call_list(head, items), tail.map(Span::from)), (make_call_list(head, items), tail.map(Tag::from)),
(start, end), (start, end, input.extra),
), ),
)) ))
}) })
@ -643,17 +665,17 @@ fn make_call_list(
let mut out = vec![]; let mut out = vec![];
if let Some(head) = head { 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); out.push(el);
} }
for (pipe, ws1, call, ws2) in items { for (pipe, ws1, call, ws2) in items {
let el = PipelineElement::new( let el = PipelineElement::new(
Some(pipe).map(Span::from), Some(pipe).map(Tag::from),
ws1.map(Span::from), ws1.map(Tag::from),
call, call,
ws2.map(Span::from), ws2.map(Tag::from),
); );
out.push(el); 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 { 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 { 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 { fn is_start_bare_char(c: char) -> bool {
@ -779,7 +806,7 @@ mod tests {
macro_rules! equal_tokens { macro_rules! equal_tokens {
($source:tt -> $tokens:expr) => { ($source:tt -> $tokens:expr) => {
let result = apply(pipeline, "pipeline", $source); 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 { if result != expected_tree {
let debug_result = format!("{}", result.debug($source)); let debug_result = format!("{}", result.debug($source));
@ -818,12 +845,12 @@ mod tests {
fn test_integer() { fn test_integer() {
assert_leaf! { assert_leaf! {
parsers [ size ] parsers [ size ]
"123" -> 0..3 { Number(RawNumber::int((0, 3)).item) } "123" -> 0..3 { Number(RawNumber::int((0, 3, test_uuid())).item) }
} }
assert_leaf! { assert_leaf! {
parsers [ size ] 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() { fn test_size() {
assert_leaf! { assert_leaf! {
parsers [ size ] 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! { assert_leaf! {
parsers [ size ] 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() { fn test_string() {
assert_leaf! { assert_leaf! {
parsers [ string dq_string ] parsers [ string dq_string ]
r#""hello world""# -> 0..13 { String(span(1, 12)) } r#""hello world""# -> 0..13 { String(tag(1, 12)) }
} }
assert_leaf! { assert_leaf! {
parsers [ string sq_string ] 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() { fn test_variable() {
assert_leaf! { assert_leaf! {
parsers [ var ] parsers [ var ]
"$it" -> 0..3 { Variable(span(1, 3)) } "$it" -> 0..3 { Variable(tag(1, 3)) }
} }
assert_leaf! { assert_leaf! {
parsers [ var ] 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() { fn test_external() {
assert_leaf! { assert_leaf! {
parsers [ external ] parsers [ external ]
"^ls" -> 0..3 { ExternalCommand(span(1, 3)) } "^ls" -> 0..3 { ExternalCommand(tag(1, 3)) }
} }
} }
@ -1260,7 +1287,7 @@ mod tests {
desc: &str, desc: &str,
string: &str, string: &str,
) -> T { ) -> T {
match f(NomSpan::new(string)) { match f(NomSpan::new_extra(string, uuid::Uuid::nil())) {
Ok(v) => v.1, Ok(v) => v.1,
Err(other) => { Err(other) => {
println!("{:?}", other); println!("{:?}", other);
@ -1270,44 +1297,46 @@ mod tests {
} }
} }
fn span(left: usize, right: usize) -> Span { fn tag(left: usize, right: usize) -> Tag {
Span::from((left, right)) Tag::from((left, right, uuid::Uuid::nil()))
} }
fn delimited( fn delimited(
delimiter: Delimiter, delimiter: Tagged<Delimiter>,
children: Vec<TokenNode>, children: Vec<TokenNode>,
left: usize, left: usize,
right: usize, right: usize,
) -> TokenNode { ) -> TokenNode {
let node = DelimitedNode::new(delimiter, children); let node = DelimitedNode::new(*delimiter, children);
let spanned = Tagged::from_simple_spanned_item(node, (left, right)); let spanned = node.tagged((left, right, delimiter.tag.origin));
TokenNode::Delimited(spanned) TokenNode::Delimited(spanned)
} }
fn path(head: TokenNode, tail: Vec<Token>, left: usize, right: usize) -> TokenNode { fn path(head: TokenNode, tail: Vec<Token>, left: usize, right: usize) -> TokenNode {
let tag = head.tag();
let node = PathNode::new( let node = PathNode::new(
Box::new(head), Box::new(head),
tail.into_iter().map(TokenNode::Token).collect(), 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) 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 { 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 { fn build<T>(block: CurriedNode<T>) -> T {
let mut builder = TokenTreeBuilder::new(); let mut builder = TokenTreeBuilder::new(uuid::Uuid::nil());
block(&mut builder) block(&mut builder)
} }
fn build_token(block: CurriedToken) -> TokenNode { 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::parser::CallNode;
use crate::traits::ToDebug; use crate::traits::ToDebug;
use crate::{Span, Tagged}; use crate::{Tag, Tagged};
use derive_new::new; use derive_new::new;
use getset::Getters; use getset::Getters;
use std::fmt; use std::fmt;
@ -8,7 +8,7 @@ use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)]
pub struct Pipeline { pub struct Pipeline {
pub(crate) parts: Vec<PipelineElement>, pub(crate) parts: Vec<PipelineElement>,
pub(crate) post_ws: Option<Span>, pub(crate) post_ws: Option<Tag>,
} }
impl ToDebug for Pipeline { impl ToDebug for Pipeline {
@ -27,11 +27,11 @@ impl ToDebug for Pipeline {
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)] #[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)]
pub struct PipelineElement { pub struct PipelineElement {
pub pipe: Option<Span>, pub pipe: Option<Tag>,
pub pre_ws: Option<Span>, pub pre_ws: Option<Tag>,
#[get = "pub(crate)"] #[get = "pub(crate)"]
call: Tagged<CallNode>, call: Tagged<CallNode>,
pub post_ws: Option<Span>, pub post_ws: Option<Tag>,
} }
impl ToDebug for PipelineElement { impl ToDebug for PipelineElement {

View file

@ -1,7 +1,7 @@
use crate::errors::ShellError; use crate::errors::ShellError;
use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*}; use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*};
use crate::traits::ToDebug; use crate::traits::ToDebug;
use crate::{Span, Tagged, Text}; use crate::{Tag, Tagged, Text};
use derive_new::new; use derive_new::new;
use enum_utils::FromStr; use enum_utils::FromStr;
use getset::Getters; use getset::Getters;
@ -16,8 +16,8 @@ pub enum TokenNode {
Pipeline(Tagged<Pipeline>), Pipeline(Tagged<Pipeline>),
Operator(Tagged<Operator>), Operator(Tagged<Operator>),
Flag(Tagged<Flag>), Flag(Tagged<Flag>),
Member(Span), Member(Tag),
Whitespace(Span), Whitespace(Tag),
Error(Tagged<Box<ShellError>>), Error(Tagged<Box<ShellError>>),
Path(Tagged<PathNode>), Path(Tagged<PathNode>),
@ -78,31 +78,31 @@ impl fmt::Debug for DebugTokenNode<'_> {
) )
} }
TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)), TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)),
TokenNode::Error(s) => write!(f, "<error> for {:?}", s.span().slice(self.source)), TokenNode::Error(s) => write!(f, "<error> for {:?}", s.tag().slice(self.source)),
rest => write!(f, "{}", rest.span().slice(self.source)), rest => write!(f, "{}", rest.tag().slice(self.source)),
} }
} }
} }
impl From<&TokenNode> for Span { impl From<&TokenNode> for Tag {
fn from(token: &TokenNode) -> Span { fn from(token: &TokenNode) -> Tag {
token.span() token.tag()
} }
} }
impl TokenNode { impl TokenNode {
pub fn span(&self) -> Span { pub fn tag(&self) -> Tag {
match self { match self {
TokenNode::Token(t) => t.span(), TokenNode::Token(t) => t.tag(),
TokenNode::Call(s) => s.span(), TokenNode::Call(s) => s.tag(),
TokenNode::Delimited(s) => s.span(), TokenNode::Delimited(s) => s.tag(),
TokenNode::Pipeline(s) => s.span(), TokenNode::Pipeline(s) => s.tag(),
TokenNode::Operator(s) => s.span(), TokenNode::Operator(s) => s.tag(),
TokenNode::Flag(s) => s.span(), TokenNode::Flag(s) => s.tag(),
TokenNode::Member(s) => *s, TokenNode::Member(s) => *s,
TokenNode::Whitespace(s) => *s, TokenNode::Whitespace(s) => *s,
TokenNode::Error(s) => s.span(), TokenNode::Error(s) => s.tag(),
TokenNode::Path(s) => s.span(), TokenNode::Path(s) => s.tag(),
} }
} }
@ -127,11 +127,11 @@ impl TokenNode {
} }
pub fn as_external_arg(&self, source: &Text) -> String { 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 { pub fn source<'a>(&self, source: &'a Text) -> &'a str {
self.span().slice(source) self.tag().slice(source)
} }
pub fn is_bare(&self) -> bool { 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 { match self {
TokenNode::Token(Tagged { TokenNode::Token(Tagged {
item: RawToken::ExternalCommand(span), item: RawToken::ExternalCommand(tag),
.. ..
}) => *span, }) => *tag,
_ => panic!("Only call expect_external if you checked is_external first"), _ => 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