mirror of
https://github.com/nushell/nushell
synced 2024-12-28 14:03:09 +00:00
Merge branch 'master' of github.com:jonnywalker81/nushell into initial-docker-command-impl
This commit is contained in:
commit
707af3f3ca
127 changed files with 2217 additions and 1438 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
@ -3,3 +3,10 @@
|
|||
**/*.rs.bk
|
||||
history.txt
|
||||
tests/fixtures/nuplayground
|
||||
|
||||
# Debian/Ubuntu
|
||||
debian/.debhelper/
|
||||
debian/debhelper-build-stamp
|
||||
debian/files
|
||||
debian/nu.substvars
|
||||
debian/nu/
|
||||
|
|
24
Cargo.lock
generated
24
Cargo.lock
generated
|
@ -1485,8 +1485,8 @@ dependencies = [
|
|||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom5_locate"
|
||||
version = "0.1.1"
|
||||
name = "nom_locate"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bytecount 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1543,7 +1543,7 @@ dependencies = [
|
|||
"natural 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"neso 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"onig_sys 69.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -1556,15 +1556,17 @@ dependencies = [
|
|||
"rawkey 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"roxmltree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustyline 5.0.2 (git+https://github.com/kkawakam/rustyline)",
|
||||
"rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde 1.0.100 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde-hjson 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_bytes 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_ini 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_urlencoded 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"serde_yaml 0.8.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"shellexpand 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"sublime_fuzzy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"surf 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syntect 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2195,8 +2197,8 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "5.0.2"
|
||||
source = "git+https://github.com/kkawakam/rustyline#5e68e972810133a7343b75db30addc98aea63ba0"
|
||||
version = "5.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.60 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
@ -2442,6 +2444,11 @@ name = "strsim"
|
|||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "sublime_fuzzy"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "subprocess"
|
||||
version = "0.1.18"
|
||||
|
@ -3142,7 +3149,7 @@ dependencies = [
|
|||
"checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945"
|
||||
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||
"checksum nom 5.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9761d859320e381010a4f7f8ed425f2c924de33ad121ace447367c713ad561b"
|
||||
"checksum nom5_locate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3d4312467f8b28d909344b934207e502212fa5a3adf1bff7428b0b86a666223d"
|
||||
"checksum nom_locate 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f932834fd8e391fc7710e2ba17e8f9f8645d846b55aa63207e17e110a1e1ce35"
|
||||
"checksum ntapi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f26e041cd983acbc087e30fcba770380cfa352d0e392e175b2344ebaf7ea0602"
|
||||
"checksum num-bigint 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9c3f34cdd24f334cb265d9bf8bfa8a241920d026916785747a92f0e55541a1a"
|
||||
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
|
||||
|
@ -3216,7 +3223,7 @@ dependencies = [
|
|||
"checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||
"checksum rustyline 5.0.2 (git+https://github.com/kkawakam/rustyline)" = "<none>"
|
||||
"checksum rustyline 5.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4795e277e6e57dec9df62b515cd4991371daa80e8dc8d80d596e58722b89c417"
|
||||
"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
|
||||
"checksum safemem 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e133ccc4f4d1cd4f89cc8a7ff618287d56dc7f638b8e38fc32c5fdcadc339dd5"
|
||||
"checksum same-file 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "585e8ddcedc187886a30fa705c47985c3fa88d06624095856b36ca0b82ff4421"
|
||||
|
@ -3246,6 +3253,7 @@ dependencies = [
|
|||
"checksum stackvector 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1c4725650978235083241fab0fdc8e694c3de37821524e7534a1a9061d1068af"
|
||||
"checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241"
|
||||
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
"checksum sublime_fuzzy 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97bd7ad698ea493a3a7f60c2ffa117c234f341e09f8cc2d39cef10cdde077acf"
|
||||
"checksum subprocess 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "28fc0f40f0c0da73339d347aa7d6d2b90341a95683a47722bc4eebed71ff3c00"
|
||||
"checksum surf 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "018eed64aede455beb88505d50c5c64882bebbe0996d4b660c272e3d8bb6f883"
|
||||
"checksum syn 0.15.43 (registry+https://github.com/rust-lang/crates.io-index)" = "ee06ea4b620ab59a2267c6b48be16244a3389f8bfa0986bdd15c35b890b00af3"
|
||||
|
|
|
@ -14,7 +14,7 @@ documentation = "https://book.nushell.sh"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
rustyline = { git = "https://github.com/kkawakam/rustyline" }
|
||||
rustyline = "5.0.3"
|
||||
chrono = { version = "0.4.9", features = ["serde"] }
|
||||
derive-new = "0.5.8"
|
||||
prettytable-rs = "0.8.0"
|
||||
|
@ -53,7 +53,7 @@ ctrlc = "3.1.3"
|
|||
surf = "1.0.2"
|
||||
url = "2.1.0"
|
||||
roxmltree = "0.7.0"
|
||||
nom5_locate = "0.1.1"
|
||||
nom_locate = "1.0.0"
|
||||
enum-utils = "0.1.1"
|
||||
unicode-xid = "0.2.0"
|
||||
serde_ini = "0.2.0"
|
||||
|
@ -72,6 +72,8 @@ pin-utils = "0.1.0-alpha.4"
|
|||
num-bigint = { version = "0.2.3", features = ["serde"] }
|
||||
bigdecimal = { version = "0.1.0", features = ["serde"] }
|
||||
natural = "0.3.0"
|
||||
serde_urlencoded = "0.6.1"
|
||||
sublime_fuzzy = "0.5"
|
||||
|
||||
neso = { version = "0.5.0", optional = true }
|
||||
crossterm = { version = "0.10.2", optional = true }
|
||||
|
@ -85,12 +87,12 @@ ptree = {version = "0.2", optional = true }
|
|||
image = { version = "0.22.2", default_features = false, features = ["png_codec", "jpeg"], optional = true }
|
||||
|
||||
[features]
|
||||
default = ["textview", "sys", "ps"]
|
||||
raw-key = ["rawkey", "neso"]
|
||||
textview = ["syntect", "onig_sys", "crossterm"]
|
||||
binaryview = ["image", "crossterm"]
|
||||
sys = ["heim", "battery"]
|
||||
ps = ["heim"]
|
||||
all = ["raw-key", "textview", "binaryview", "sys", "ps", "clipboard", "ptree"]
|
||||
|
||||
[dependencies.rusqlite]
|
||||
version = "0.20.0"
|
||||
|
|
|
@ -258,6 +258,7 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat
|
|||
| first amount | Show only the first number of rows |
|
||||
| last amount | Show only the last number of rows |
|
||||
| nth row-number | Return only the selected row |
|
||||
| pivot --header-row <headers> | Pivot the tables, making columns into rows and vice versa |
|
||||
| str (column) | Apply string function. Optionally use the column of a table |
|
||||
| tags | Read the tags (metadata) for values |
|
||||
| to-json | Convert table into .json text |
|
||||
|
@ -298,8 +299,6 @@ Nu adheres closely to a set of goals that make up its design philosophy. As feat
|
|||
| table | View the contents of the pipeline as a table |
|
||||
| textview | Autoview of text data |
|
||||
| tree | View the contents of the pipeline as a tree |
|
||||
| vtable | View the contents of the pipeline as a vertical (rotated) table |
|
||||
|
||||
# License
|
||||
|
||||
The project is made available under the MIT license. See "LICENSE" for more information.
|
||||
|
|
5
debian/changelog
vendored
Normal file
5
debian/changelog
vendored
Normal 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
1
debian/compat
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
10
|
18
debian/control
vendored
Normal file
18
debian/control
vendored
Normal 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
32
debian/copyright
vendored
Normal 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
10
debian/install
vendored
Normal 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
8
debian/postinst
vendored
Normal 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
17
debian/postrm
vendored
Normal 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
25
debian/rules
vendored
Executable 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
1
debian/source/format
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
3.0 (quilt)
|
17
docker/packaging/Dockerfile.ubuntu-bionic
Normal file
17
docker/packaging/Dockerfile.ubuntu-bionic
Normal 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
|
55
docker/packaging/README.md
Normal file
55
docker/packaging/README.md
Normal 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
|
110
src/cli.rs
110
src/cli.rs
|
@ -7,8 +7,10 @@ use crate::commands::plugin::JsonRpc;
|
|||
use crate::commands::plugin::{PluginCommand, PluginSink};
|
||||
use crate::commands::whole_stream_command;
|
||||
use crate::context::Context;
|
||||
use crate::data::config;
|
||||
use crate::data::Value;
|
||||
pub(crate) use crate::errors::ShellError;
|
||||
use crate::fuzzysearch::{interactive_fuzzy_search, SelectionResult};
|
||||
use crate::git::current_branch;
|
||||
use crate::parser::registry::Signature;
|
||||
use crate::parser::{hir, CallNode, Pipeline, PipelineElement, TokenNode};
|
||||
|
@ -21,6 +23,7 @@ use std::env;
|
|||
use std::error::Error;
|
||||
use std::io::{BufRead, BufReader, Write};
|
||||
use std::iter::Iterator;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -59,6 +62,7 @@ fn load_plugin(path: &std::path::Path, context: &mut Context) -> Result<(), Shel
|
|||
let result = match reader.read_line(&mut input) {
|
||||
Ok(count) => {
|
||||
trace!("processing response ({} bytes)", count);
|
||||
trace!("response: {}", input);
|
||||
|
||||
let response = serde_json::from_str::<JsonRpc<Result<Signature, ShellError>>>(&input);
|
||||
match response {
|
||||
|
@ -208,6 +212,20 @@ fn load_plugins(context: &mut Context) -> Result<(), ShellError> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub struct History;
|
||||
|
||||
impl History {
|
||||
pub fn path() -> PathBuf {
|
||||
const FNAME: &str = "history.txt";
|
||||
config::user_data()
|
||||
.map(|mut p| {
|
||||
p.push(FNAME);
|
||||
p
|
||||
})
|
||||
.unwrap_or(PathBuf::from(FNAME))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn cli() -> Result<(), Box<dyn Error>> {
|
||||
let mut context = Context::basic()?;
|
||||
|
||||
|
@ -239,11 +257,13 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
|||
whole_stream_command(ToDB),
|
||||
whole_stream_command(ToTOML),
|
||||
whole_stream_command(ToTSV),
|
||||
whole_stream_command(ToURL),
|
||||
whole_stream_command(ToYAML),
|
||||
whole_stream_command(SortBy),
|
||||
whole_stream_command(Tags),
|
||||
whole_stream_command(First),
|
||||
whole_stream_command(Last),
|
||||
whole_stream_command(Env),
|
||||
whole_stream_command(FromCSV),
|
||||
whole_stream_command(FromTSV),
|
||||
whole_stream_command(FromINI),
|
||||
|
@ -252,6 +272,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
|||
whole_stream_command(FromDB),
|
||||
whole_stream_command(FromSQLite),
|
||||
whole_stream_command(FromTOML),
|
||||
whole_stream_command(FromURL),
|
||||
whole_stream_command(FromXML),
|
||||
whole_stream_command(FromYAML),
|
||||
whole_stream_command(FromYML),
|
||||
|
@ -269,13 +290,13 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
|||
per_item_command(Help),
|
||||
whole_stream_command(Exit),
|
||||
whole_stream_command(Autoview),
|
||||
whole_stream_command(Pivot),
|
||||
per_item_command(Cpy),
|
||||
whole_stream_command(Date),
|
||||
per_item_command(Mkdir),
|
||||
per_item_command(Move),
|
||||
whole_stream_command(Save),
|
||||
whole_stream_command(Table),
|
||||
whole_stream_command(VTable),
|
||||
whole_stream_command(Version),
|
||||
whole_stream_command(Which),
|
||||
]);
|
||||
|
@ -298,7 +319,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
|
||||
// we are ok if history does not exist
|
||||
let _ = rl.load_history("history.txt");
|
||||
let _ = rl.load_history(&History::path());
|
||||
|
||||
let ctrl_c = Arc::new(AtomicBool::new(false));
|
||||
let cc = ctrl_c.clone();
|
||||
|
@ -319,7 +340,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
|||
context.shell_manager.clone(),
|
||||
)));
|
||||
|
||||
let edit_mode = crate::data::config::config(Span::unknown())?
|
||||
let edit_mode = config::config(Tag::unknown())?
|
||||
.get("edit_mode")
|
||||
.map(|s| match s.as_string().unwrap().as_ref() {
|
||||
"vi" => EditMode::Vi,
|
||||
|
@ -330,14 +351,47 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
|||
|
||||
rl.set_edit_mode(edit_mode);
|
||||
|
||||
let readline = rl.readline(&format!(
|
||||
// Register Ctrl-r for history fuzzy search
|
||||
// rustyline doesn't support custom commands, so we override Ctrl-D (EOF)
|
||||
#[cfg(not(windows))] // https://github.com/nushell/nushell/issues/689
|
||||
rl.bind_sequence(rustyline::KeyPress::Ctrl('R'), rustyline::Cmd::EndOfFile);
|
||||
// Redefine Ctrl-D to same command as Ctrl-C
|
||||
rl.bind_sequence(rustyline::KeyPress::Ctrl('D'), rustyline::Cmd::Interrupt);
|
||||
|
||||
let prompt = &format!(
|
||||
"{}{}> ",
|
||||
cwd,
|
||||
match current_branch() {
|
||||
Some(s) => format!("({})", s),
|
||||
None => "".to_string(),
|
||||
}
|
||||
));
|
||||
);
|
||||
let mut initial_command = Some(String::new());
|
||||
let mut readline = Err(ReadlineError::Eof);
|
||||
while let Some(ref cmd) = initial_command {
|
||||
readline = rl.readline_with_initial(prompt, (&cmd, ""));
|
||||
if let Err(ReadlineError::Eof) = &readline {
|
||||
// Fuzzy search in history
|
||||
let lines = rl.history().iter().rev().map(|s| s.as_str()).collect();
|
||||
let selection = interactive_fuzzy_search(&lines, 5); // Clears last line with prompt
|
||||
match selection {
|
||||
SelectionResult::Selected(line) => {
|
||||
println!("{}{}", &prompt, &line); // TODO: colorize prompt
|
||||
readline = Ok(line.clone());
|
||||
initial_command = None;
|
||||
}
|
||||
SelectionResult::Edit(line) => {
|
||||
initial_command = Some(line);
|
||||
}
|
||||
SelectionResult::NoSelection => {
|
||||
readline = Ok("".to_string());
|
||||
initial_command = None;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
initial_command = None;
|
||||
}
|
||||
}
|
||||
|
||||
match process_line(readline, &mut context).await {
|
||||
LineResult::Success(line) => {
|
||||
|
@ -346,6 +400,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
|||
|
||||
LineResult::CtrlC => {
|
||||
if ctrlcbreak {
|
||||
let _ = rl.save_history(&History::path());
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
context.with_host(|host| host.stdout("CTRL-C pressed (again to quit)"));
|
||||
|
@ -380,7 +435,7 @@ pub async fn cli() -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
|
||||
// we are ok if we can not save history
|
||||
let _ = rl.save_history("history.txt");
|
||||
let _ = rl.save_history(&History::path());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -397,7 +452,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
|||
Ok(line) if line.trim() == "" => LineResult::Success(line.clone()),
|
||||
|
||||
Ok(line) => {
|
||||
let result = match crate::parser::parse(&line) {
|
||||
let result = match crate::parser::parse(&line, uuid::Uuid::nil()) {
|
||||
Err(err) => {
|
||||
return LineResult::Error(line.clone(), err);
|
||||
}
|
||||
|
@ -419,7 +474,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
|||
.commands
|
||||
.push(ClassifiedCommand::Internal(InternalCommand {
|
||||
command: whole_stream_command(autoview::Autoview),
|
||||
name_span: Span::unknown(),
|
||||
name_tag: Tag::unknown(),
|
||||
args: hir::Call::new(
|
||||
Box::new(hir::Expression::synthetic_string("autoview")),
|
||||
None,
|
||||
|
@ -431,6 +486,7 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
|||
let mut input = ClassifiedInputStream::new();
|
||||
|
||||
let mut iter = pipeline.commands.into_iter().peekable();
|
||||
let mut is_first_command = true;
|
||||
|
||||
loop {
|
||||
let item: Option<ClassifiedCommand> = iter.next();
|
||||
|
@ -456,20 +512,29 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
|||
(
|
||||
Some(ClassifiedCommand::Internal(left)),
|
||||
Some(ClassifiedCommand::External(_)),
|
||||
) => match left.run(ctx, input, Text::from(line)).await {
|
||||
) => match left
|
||||
.run(ctx, input, Text::from(line), is_first_command)
|
||||
.await
|
||||
{
|
||||
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
||||
Err(err) => return LineResult::Error(line.clone(), err),
|
||||
},
|
||||
|
||||
(Some(ClassifiedCommand::Internal(left)), Some(_)) => {
|
||||
match left.run(ctx, input, Text::from(line)).await {
|
||||
match left
|
||||
.run(ctx, input, Text::from(line), is_first_command)
|
||||
.await
|
||||
{
|
||||
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
||||
Err(err) => return LineResult::Error(line.clone(), err),
|
||||
}
|
||||
}
|
||||
|
||||
(Some(ClassifiedCommand::Internal(left)), None) => {
|
||||
match left.run(ctx, input, Text::from(line)).await {
|
||||
match left
|
||||
.run(ctx, input, Text::from(line), is_first_command)
|
||||
.await
|
||||
{
|
||||
Ok(val) => ClassifiedInputStream::from_input_stream(val),
|
||||
Err(err) => return LineResult::Error(line.clone(), err),
|
||||
}
|
||||
|
@ -496,7 +561,9 @@ async fn process_line(readline: Result<String, ReadlineError>, ctx: &mut Context
|
|||
Err(err) => return LineResult::Error(line.clone(), err),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
is_first_command = false;
|
||||
}
|
||||
|
||||
LineResult::Success(line.clone())
|
||||
|
@ -539,10 +606,10 @@ fn classify_command(
|
|||
match call {
|
||||
// If the command starts with `^`, treat it as an external command no matter what
|
||||
call if call.head().is_external() => {
|
||||
let name_span = call.head().expect_external();
|
||||
let name = name_span.slice(source);
|
||||
let name_tag = call.head().expect_external();
|
||||
let name = name_tag.slice(source);
|
||||
|
||||
Ok(external_command(call, source, name.tagged(name_span)))
|
||||
Ok(external_command(call, source, name.tagged(name_tag)))
|
||||
}
|
||||
|
||||
// Otherwise, if the command is a bare word, we'll need to triage it
|
||||
|
@ -564,19 +631,19 @@ fn classify_command(
|
|||
|
||||
Ok(ClassifiedCommand::Internal(InternalCommand {
|
||||
command,
|
||||
name_span: head.span().clone(),
|
||||
name_tag: head.tag(),
|
||||
args,
|
||||
}))
|
||||
}
|
||||
|
||||
// otherwise, it's an external command
|
||||
false => Ok(external_command(call, source, name.tagged(head.span()))),
|
||||
false => Ok(external_command(call, source, name.tagged(head.tag()))),
|
||||
}
|
||||
}
|
||||
|
||||
// If the command is something else (like a number or a variable), that is currently unsupported.
|
||||
// We might support `$somevar` as a curried command in the future.
|
||||
call => Err(ShellError::invalid_command(call.head().span())),
|
||||
call => Err(ShellError::invalid_command(call.head().tag())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,10 +660,7 @@ fn external_command(
|
|||
.iter()
|
||||
.filter_map(|i| match i {
|
||||
TokenNode::Whitespace(_) => None,
|
||||
other => Some(Tagged::from_simple_spanned_item(
|
||||
other.as_external_arg(source),
|
||||
other.span(),
|
||||
)),
|
||||
other => Some(other.as_external_arg(source).tagged(other.tag())),
|
||||
})
|
||||
.collect(),
|
||||
None => vec![],
|
||||
|
@ -606,7 +670,7 @@ fn external_command(
|
|||
|
||||
ClassifiedCommand::External(ExternalCommand {
|
||||
name: name.to_string(),
|
||||
name_span: tag.span,
|
||||
name_tag: tag,
|
||||
args: arg_list_strings,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ pub(crate) mod debug;
|
|||
pub(crate) mod docker;
|
||||
pub(crate) mod echo;
|
||||
pub(crate) mod enter;
|
||||
pub(crate) mod env;
|
||||
pub(crate) mod exit;
|
||||
pub(crate) mod fetch;
|
||||
pub(crate) mod first;
|
||||
|
@ -24,6 +25,7 @@ pub(crate) mod from_json;
|
|||
pub(crate) mod from_sqlite;
|
||||
pub(crate) mod from_toml;
|
||||
pub(crate) mod from_tsv;
|
||||
pub(crate) mod from_url;
|
||||
pub(crate) mod from_xml;
|
||||
pub(crate) mod from_yaml;
|
||||
pub(crate) mod get;
|
||||
|
@ -37,6 +39,7 @@ pub(crate) mod next;
|
|||
pub(crate) mod nth;
|
||||
pub(crate) mod open;
|
||||
pub(crate) mod pick;
|
||||
pub(crate) mod pivot;
|
||||
pub(crate) mod plugin;
|
||||
pub(crate) mod post;
|
||||
pub(crate) mod prev;
|
||||
|
@ -59,10 +62,10 @@ pub(crate) mod to_json;
|
|||
pub(crate) mod to_sqlite;
|
||||
pub(crate) mod to_toml;
|
||||
pub(crate) mod to_tsv;
|
||||
pub(crate) mod to_url;
|
||||
pub(crate) mod to_yaml;
|
||||
pub(crate) mod trim;
|
||||
pub(crate) mod version;
|
||||
pub(crate) mod vtable;
|
||||
pub(crate) mod where_;
|
||||
pub(crate) mod which_;
|
||||
|
||||
|
@ -80,6 +83,7 @@ pub(crate) use debug::Debug;
|
|||
pub(crate) use docker::Docker;
|
||||
pub(crate) use echo::Echo;
|
||||
pub(crate) use enter::Enter;
|
||||
pub(crate) use env::Env;
|
||||
pub(crate) use exit::Exit;
|
||||
pub(crate) use fetch::Fetch;
|
||||
pub(crate) use first::First;
|
||||
|
@ -91,6 +95,7 @@ pub(crate) use from_sqlite::FromDB;
|
|||
pub(crate) use from_sqlite::FromSQLite;
|
||||
pub(crate) use from_toml::FromTOML;
|
||||
pub(crate) use from_tsv::FromTSV;
|
||||
pub(crate) use from_url::FromURL;
|
||||
pub(crate) use from_xml::FromXML;
|
||||
pub(crate) use from_yaml::FromYAML;
|
||||
pub(crate) use from_yaml::FromYML;
|
||||
|
@ -105,6 +110,7 @@ pub(crate) use next::Next;
|
|||
pub(crate) use nth::Nth;
|
||||
pub(crate) use open::Open;
|
||||
pub(crate) use pick::Pick;
|
||||
pub(crate) use pivot::Pivot;
|
||||
pub(crate) use post::Post;
|
||||
pub(crate) use prev::Previous;
|
||||
pub(crate) use pwd::PWD;
|
||||
|
@ -127,9 +133,9 @@ pub(crate) use to_sqlite::ToDB;
|
|||
pub(crate) use to_sqlite::ToSQLite;
|
||||
pub(crate) use to_toml::ToTOML;
|
||||
pub(crate) use to_tsv::ToTSV;
|
||||
pub(crate) use to_url::ToURL;
|
||||
pub(crate) use to_yaml::ToYAML;
|
||||
pub(crate) use trim::Trim;
|
||||
pub(crate) use version::Version;
|
||||
pub(crate) use vtable::VTable;
|
||||
pub(crate) use where_::Where;
|
||||
pub(crate) use which_::Which;
|
||||
|
|
|
@ -45,7 +45,7 @@ pub fn autoview(
|
|||
{
|
||||
let binary = context.get_command("binaryview");
|
||||
if let Some(binary) = binary {
|
||||
let result = binary.run(raw.with_input(input), &context.commands);
|
||||
let result = binary.run(raw.with_input(input), &context.commands, false);
|
||||
result.collect::<Vec<_>>().await;
|
||||
} else {
|
||||
for i in input {
|
||||
|
@ -61,7 +61,7 @@ pub fn autoview(
|
|||
} else if is_single_origined_text_value(&input) {
|
||||
let text = context.get_command("textview");
|
||||
if let Some(text) = text {
|
||||
let result = text.run(raw.with_input(input), &context.commands);
|
||||
let result = text.run(raw.with_input(input), &context.commands, false);
|
||||
result.collect::<Vec<_>>().await;
|
||||
} else {
|
||||
for i in input {
|
||||
|
@ -84,7 +84,7 @@ pub fn autoview(
|
|||
}
|
||||
} else {
|
||||
let table = context.expect_command("table");
|
||||
let result = table.run(raw.with_input(input), &context.commands);
|
||||
let result = table.run(raw.with_input(input), &context.commands, false);
|
||||
result.collect::<Vec<_>>().await;
|
||||
}
|
||||
}
|
||||
|
@ -110,14 +110,13 @@ fn is_single_origined_text_value(input: &Vec<Tagged<Value>>) -> bool {
|
|||
if input.len() != 1 {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Tagged {
|
||||
item: Value::Primitive(Primitive::String(_)),
|
||||
tag: Tag {
|
||||
origin: Some(_), ..
|
||||
},
|
||||
tag: Tag { origin, .. },
|
||||
} = input[0]
|
||||
{
|
||||
true
|
||||
origin != uuid::Uuid::nil()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ impl WholeStreamCommand for CD {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("cd").optional("directory", SyntaxType::Path)
|
||||
Signature::build("cd").optional("directory", SyntaxShape::Path)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
|
|
@ -86,7 +86,7 @@ pub(crate) enum ClassifiedCommand {
|
|||
|
||||
pub(crate) struct InternalCommand {
|
||||
pub(crate) command: Arc<Command>,
|
||||
pub(crate) name_span: Span,
|
||||
pub(crate) name_tag: Tag,
|
||||
pub(crate) args: hir::Call,
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,7 @@ impl InternalCommand {
|
|||
context: &mut Context,
|
||||
input: ClassifiedInputStream,
|
||||
source: Text,
|
||||
is_first_command: bool,
|
||||
) -> Result<InputStream, ShellError> {
|
||||
if log_enabled!(log::Level::Trace) {
|
||||
trace!(target: "nu::run::internal", "->");
|
||||
|
@ -108,11 +109,12 @@ impl InternalCommand {
|
|||
|
||||
let result = context.run_command(
|
||||
self.command,
|
||||
self.name_span.clone(),
|
||||
self.name_tag.clone(),
|
||||
context.source_map.clone(),
|
||||
self.args,
|
||||
&source,
|
||||
objects,
|
||||
is_first_command,
|
||||
);
|
||||
|
||||
let result = trace_out_stream!(target: "nu::trace_stream::internal", source: &source, "output" = result);
|
||||
|
@ -128,26 +130,23 @@ impl InternalCommand {
|
|||
CommandAction::AddSpanSource(uuid, span_source) => {
|
||||
context.add_span_source(uuid, span_source);
|
||||
}
|
||||
CommandAction::Exit => std::process::exit(0),
|
||||
CommandAction::Exit => std::process::exit(0), // TODO: save history.txt
|
||||
CommandAction::EnterHelpShell(value) => {
|
||||
match value {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::String(cmd)),
|
||||
..
|
||||
tag,
|
||||
} => {
|
||||
context.shell_manager.insert_at_current(Box::new(
|
||||
HelpShell::for_command(
|
||||
Tagged::from_simple_spanned_item(
|
||||
Value::string(cmd),
|
||||
Span::unknown(),
|
||||
),
|
||||
&context.registry().clone(),
|
||||
Value::string(cmd).tagged(tag),
|
||||
&context.registry(),
|
||||
)?,
|
||||
));
|
||||
}
|
||||
_ => {
|
||||
context.shell_manager.insert_at_current(Box::new(
|
||||
HelpShell::index(&context.registry().clone())?,
|
||||
HelpShell::index(&context.registry())?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +170,7 @@ impl InternalCommand {
|
|||
CommandAction::LeaveShell => {
|
||||
context.shell_manager.remove_at_current();
|
||||
if context.shell_manager.is_empty() {
|
||||
std::process::exit(0);
|
||||
std::process::exit(0); // TODO: save history.txt
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -189,7 +188,7 @@ impl InternalCommand {
|
|||
pub(crate) struct ExternalCommand {
|
||||
pub(crate) name: String,
|
||||
|
||||
pub(crate) name_span: Span,
|
||||
pub(crate) name_tag: Tag,
|
||||
pub(crate) args: Vec<Tagged<String>>,
|
||||
}
|
||||
|
||||
|
@ -208,7 +207,7 @@ impl ExternalCommand {
|
|||
) -> Result<ClassifiedInputStream, ShellError> {
|
||||
let stdin = input.stdin;
|
||||
let inputs: Vec<Tagged<Value>> = input.objects.into_vec().await;
|
||||
let name_span = self.name_span.clone();
|
||||
let name_tag = self.name_tag.clone();
|
||||
|
||||
trace!(target: "nu::run::external", "-> {}", self.name);
|
||||
trace!(target: "nu::run::external", "inputs = {:?}", inputs);
|
||||
|
@ -227,17 +226,17 @@ impl ExternalCommand {
|
|||
|
||||
for i in &inputs {
|
||||
if i.as_string().is_err() {
|
||||
let mut span = None;
|
||||
let mut tag = None;
|
||||
for arg in &self.args {
|
||||
if arg.item.contains("$it") {
|
||||
span = Some(arg.span());
|
||||
tag = Some(arg.tag());
|
||||
}
|
||||
}
|
||||
if let Some(span) = span {
|
||||
if let Some(tag) = tag {
|
||||
return Err(ShellError::labeled_error(
|
||||
"External $it needs string data",
|
||||
"given row instead of string data",
|
||||
span,
|
||||
tag,
|
||||
));
|
||||
} else {
|
||||
return Err(ShellError::string("Error: $it needs string data"));
|
||||
|
@ -316,9 +315,7 @@ impl ExternalCommand {
|
|||
let stdout = popen.stdout.take().unwrap();
|
||||
let file = futures::io::AllowStdIo::new(stdout);
|
||||
let stream = Framed::new(file, LinesCodec {});
|
||||
let stream = stream.map(move |line| {
|
||||
Tagged::from_simple_spanned_item(Value::string(line.unwrap()), name_span)
|
||||
});
|
||||
let stream = stream.map(move |line| Value::string(line.unwrap()).tagged(name_tag));
|
||||
Ok(ClassifiedInputStream::from_input_stream(
|
||||
stream.boxed() as BoxStream<'static, Tagged<Value>>
|
||||
))
|
||||
|
|
|
@ -51,7 +51,7 @@ pub mod clipboard {
|
|||
Ok(OutputStream::from(stream))
|
||||
}
|
||||
|
||||
async fn inner_clip(input: Vec<Tagged<Value>>, name: Span) -> OutputStream {
|
||||
async fn inner_clip(input: Vec<Tagged<Value>>, name: Tag) -> OutputStream {
|
||||
let mut clip_context: ClipboardContext = ClipboardProvider::new().unwrap();
|
||||
let mut new_copy_data = String::new();
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@ pub struct UnevaluatedCallInfo {
|
|||
pub args: hir::Call,
|
||||
pub source: Text,
|
||||
pub source_map: SourceMap,
|
||||
pub name_span: Span,
|
||||
pub name_tag: Tag,
|
||||
}
|
||||
|
||||
impl ToDebug for UnevaluatedCallInfo {
|
||||
|
@ -38,43 +38,16 @@ impl UnevaluatedCallInfo {
|
|||
Ok(CallInfo {
|
||||
args,
|
||||
source_map: self.source_map,
|
||||
name_span: self.name_span,
|
||||
name_tag: self.name_tag,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn has_it_or_block(&self) -> bool {
|
||||
use hir::RawExpression;
|
||||
use hir::Variable;
|
||||
|
||||
if let Some(positional) = &self.args.positional() {
|
||||
for pos in positional {
|
||||
match pos {
|
||||
Tagged {
|
||||
item: RawExpression::Variable(Variable::It(_)),
|
||||
..
|
||||
} => {
|
||||
return true;
|
||||
}
|
||||
Tagged {
|
||||
item: RawExpression::Block(_),
|
||||
..
|
||||
} => {
|
||||
return true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
||||
pub struct CallInfo {
|
||||
pub args: registry::EvaluatedArgs,
|
||||
pub source_map: SourceMap,
|
||||
pub name_span: Span,
|
||||
pub name_tag: Tag,
|
||||
}
|
||||
|
||||
impl CallInfo {
|
||||
|
@ -89,7 +62,7 @@ impl CallInfo {
|
|||
args: T::deserialize(&mut deserializer)?,
|
||||
context: RunnablePerItemContext {
|
||||
shell_manager: shell_manager.clone(),
|
||||
name: self.name_span,
|
||||
name: self.name_tag,
|
||||
},
|
||||
callback,
|
||||
})
|
||||
|
@ -158,7 +131,7 @@ impl CommandArgs {
|
|||
let host = self.host.clone();
|
||||
let args = self.evaluate_once(registry)?;
|
||||
let (input, args) = args.split();
|
||||
let name_span = args.call_info.name_span;
|
||||
let name_tag = args.call_info.name_tag;
|
||||
let mut deserializer = ConfigDeserializer::from_call_info(args.call_info);
|
||||
|
||||
Ok(RunnableArgs {
|
||||
|
@ -167,7 +140,7 @@ impl CommandArgs {
|
|||
input,
|
||||
commands: registry.clone(),
|
||||
shell_manager,
|
||||
name: name_span,
|
||||
name: name_tag,
|
||||
source_map,
|
||||
host,
|
||||
},
|
||||
|
@ -191,7 +164,7 @@ impl CommandArgs {
|
|||
let host = self.host.clone();
|
||||
let args = self.evaluate_once(registry)?;
|
||||
let (input, args) = args.split();
|
||||
let name_span = args.call_info.name_span;
|
||||
let name_tag = args.call_info.name_tag;
|
||||
let mut deserializer = ConfigDeserializer::from_call_info(args.call_info);
|
||||
|
||||
Ok(RunnableRawArgs {
|
||||
|
@ -200,7 +173,7 @@ impl CommandArgs {
|
|||
input,
|
||||
commands: registry.clone(),
|
||||
shell_manager,
|
||||
name: name_span,
|
||||
name: name_tag,
|
||||
source_map,
|
||||
host,
|
||||
},
|
||||
|
@ -212,7 +185,7 @@ impl CommandArgs {
|
|||
|
||||
pub struct RunnablePerItemContext {
|
||||
pub shell_manager: ShellManager,
|
||||
pub name: Span,
|
||||
pub name: Tag,
|
||||
}
|
||||
|
||||
impl RunnablePerItemContext {
|
||||
|
@ -227,7 +200,7 @@ pub struct RunnableContext {
|
|||
pub host: Arc<Mutex<dyn Host>>,
|
||||
pub commands: CommandRegistry,
|
||||
pub source_map: SourceMap,
|
||||
pub name: Span,
|
||||
pub name: Tag,
|
||||
}
|
||||
|
||||
impl RunnableContext {
|
||||
|
@ -311,8 +284,8 @@ impl EvaluatedWholeStreamCommandArgs {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn name_span(&self) -> Span {
|
||||
self.args.call_info.name_span
|
||||
pub fn name_tag(&self) -> Tag {
|
||||
self.args.call_info.name_tag
|
||||
}
|
||||
|
||||
pub fn parts(self) -> (InputStream, registry::EvaluatedArgs) {
|
||||
|
@ -471,12 +444,6 @@ impl ReturnSuccess {
|
|||
pub fn action(input: CommandAction) -> ReturnValue {
|
||||
Ok(ReturnSuccess::Action(input))
|
||||
}
|
||||
|
||||
pub fn spanned_value(input: Value, span: Span) -> ReturnValue {
|
||||
Ok(ReturnSuccess::Value(Tagged::from_simple_spanned_item(
|
||||
input, span,
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait WholeStreamCommand: Send + Sync {
|
||||
|
@ -562,13 +529,20 @@ impl Command {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn run(&self, args: CommandArgs, registry: ®istry::CommandRegistry) -> OutputStream {
|
||||
pub fn run(
|
||||
&self,
|
||||
args: CommandArgs,
|
||||
registry: ®istry::CommandRegistry,
|
||||
is_first_command: bool,
|
||||
) -> OutputStream {
|
||||
match self {
|
||||
Command::WholeStream(command) => match command.run(args, registry) {
|
||||
Ok(stream) => stream,
|
||||
Err(err) => OutputStream::one(Err(err)),
|
||||
},
|
||||
Command::PerItem(command) => self.run_helper(command.clone(), args, registry.clone()),
|
||||
Command::PerItem(command) => {
|
||||
self.run_helper(command.clone(), args, registry.clone(), is_first_command)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -577,6 +551,7 @@ impl Command {
|
|||
command: Arc<dyn PerItemCommand>,
|
||||
args: CommandArgs,
|
||||
registry: CommandRegistry,
|
||||
is_first_command: bool,
|
||||
) -> OutputStream {
|
||||
let raw_args = RawCommandArgs {
|
||||
host: args.host,
|
||||
|
@ -584,7 +559,7 @@ impl Command {
|
|||
call_info: args.call_info,
|
||||
};
|
||||
|
||||
if raw_args.call_info.has_it_or_block() {
|
||||
if !is_first_command {
|
||||
let out = args
|
||||
.input
|
||||
.values
|
||||
|
@ -609,7 +584,6 @@ impl Command {
|
|||
.call_info
|
||||
.evaluate(®istry, &Scope::it_value(nothing.clone()))
|
||||
.unwrap();
|
||||
// We don't have an $it or block, so just execute what we have
|
||||
|
||||
match command
|
||||
.run(&call_info, ®istry, &raw_args, nothing)
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::commands::WholeStreamCommand;
|
||||
use crate::data::{config, Value};
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::hir::SyntaxShape;
|
||||
use crate::parser::registry::{self};
|
||||
use crate::prelude::*;
|
||||
use std::iter::FromIterator;
|
||||
|
@ -26,10 +26,10 @@ impl WholeStreamCommand for Config {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config")
|
||||
.named("load", SyntaxType::Path)
|
||||
.named("set", SyntaxType::Any)
|
||||
.named("get", SyntaxType::Any)
|
||||
.named("remove", SyntaxType::Any)
|
||||
.named("load", SyntaxShape::Path)
|
||||
.named("set", SyntaxShape::Any)
|
||||
.named("get", SyntaxShape::Any)
|
||||
.named("remove", SyntaxShape::Any)
|
||||
.switch("clear")
|
||||
.switch("path")
|
||||
}
|
||||
|
@ -96,41 +96,21 @@ pub fn config(
|
|||
|
||||
config::write(&result, &configuration)?;
|
||||
|
||||
return Ok(stream![Tagged::from_simple_spanned_item(
|
||||
Value::Row(result.into()),
|
||||
value.span()
|
||||
)]
|
||||
.from_input_stream());
|
||||
return Ok(stream![Value::Row(result.into()).tagged(value.tag())].from_input_stream());
|
||||
}
|
||||
|
||||
if let Tagged {
|
||||
item: true,
|
||||
tag: Tag { span, .. },
|
||||
} = clear
|
||||
{
|
||||
if let Tagged { item: true, tag } = clear {
|
||||
result.clear();
|
||||
|
||||
config::write(&result, &configuration)?;
|
||||
|
||||
return Ok(stream![Tagged::from_simple_spanned_item(
|
||||
Value::Row(result.into()),
|
||||
span
|
||||
)]
|
||||
.from_input_stream());
|
||||
return Ok(stream![Value::Row(result.into()).tagged(tag)].from_input_stream());
|
||||
}
|
||||
|
||||
if let Tagged {
|
||||
item: true,
|
||||
tag: Tag { span, .. },
|
||||
} = path
|
||||
{
|
||||
if let Tagged { item: true, tag } = path {
|
||||
let path = config::default_path_for(&configuration)?;
|
||||
|
||||
return Ok(stream![Tagged::from_simple_spanned_item(
|
||||
Value::Primitive(Primitive::Path(path)),
|
||||
span
|
||||
)]
|
||||
.from_input_stream());
|
||||
return Ok(stream![Value::Primitive(Primitive::Path(path)).tagged(tag)].from_input_stream());
|
||||
}
|
||||
|
||||
if let Some(v) = remove {
|
||||
|
@ -146,9 +126,9 @@ pub fn config(
|
|||
)));
|
||||
}
|
||||
|
||||
let obj = VecDeque::from_iter(vec![Value::Row(result.into()).simple_spanned(v.span())]);
|
||||
let obj = VecDeque::from_iter(vec![Value::Row(result.into()).tagged(v.tag())]);
|
||||
return Ok(obj.from_input_stream());
|
||||
}
|
||||
|
||||
return Ok(vec![Value::Row(result.into()).simple_spanned(name)].into());
|
||||
return Ok(vec![Value::Row(result.into()).tagged(name)].into());
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::hir::SyntaxShape;
|
||||
use crate::parser::registry::{CommandRegistry, Signature};
|
||||
use crate::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
@ -21,9 +21,9 @@ impl PerItemCommand for Cpy {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("cp")
|
||||
.required("src", SyntaxType::Pattern)
|
||||
.required("dst", SyntaxType::Path)
|
||||
.named("file", SyntaxType::Any)
|
||||
.required("src", SyntaxShape::Pattern)
|
||||
.required("dst", SyntaxShape::Path)
|
||||
.named("file", SyntaxShape::Any)
|
||||
.switch("recursive")
|
||||
}
|
||||
|
||||
|
|
|
@ -33,58 +33,40 @@ impl WholeStreamCommand for Date {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn date_to_value<T: TimeZone>(dt: DateTime<T>, span: Span) -> Tagged<Value>
|
||||
pub fn date_to_value<T: TimeZone>(dt: DateTime<T>, tag: Tag) -> Tagged<Value>
|
||||
where
|
||||
T::Offset: Display,
|
||||
{
|
||||
let mut indexmap = IndexMap::new();
|
||||
|
||||
indexmap.insert(
|
||||
"year".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.year()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"month".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.month()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"day".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.day()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"hour".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.hour()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"minute".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.minute()), span),
|
||||
);
|
||||
indexmap.insert(
|
||||
"second".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::int(dt.second()), span),
|
||||
);
|
||||
indexmap.insert("year".to_string(), Value::int(dt.year()).tagged(tag));
|
||||
indexmap.insert("month".to_string(), Value::int(dt.month()).tagged(tag));
|
||||
indexmap.insert("day".to_string(), Value::int(dt.day()).tagged(tag));
|
||||
indexmap.insert("hour".to_string(), Value::int(dt.hour()).tagged(tag));
|
||||
indexmap.insert("minute".to_string(), Value::int(dt.minute()).tagged(tag));
|
||||
indexmap.insert("second".to_string(), Value::int(dt.second()).tagged(tag));
|
||||
|
||||
let tz = dt.offset();
|
||||
indexmap.insert(
|
||||
"timezone".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::string(format!("{}", tz)), span),
|
||||
Value::string(format!("{}", tz)).tagged(tag),
|
||||
);
|
||||
|
||||
Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span)
|
||||
Value::Row(Dictionary::from(indexmap)).tagged(tag)
|
||||
}
|
||||
|
||||
pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
|
||||
let mut date_out = VecDeque::new();
|
||||
let span = args.call_info.name_span;
|
||||
let tag = args.call_info.name_tag;
|
||||
|
||||
let value = if args.has("utc") {
|
||||
let utc: DateTime<Utc> = Utc::now();
|
||||
date_to_value(utc, span)
|
||||
date_to_value(utc, tag)
|
||||
} else {
|
||||
let local: DateTime<Local> = Local::now();
|
||||
date_to_value(local, span)
|
||||
date_to_value(local, tag)
|
||||
};
|
||||
|
||||
date_out.push_back(value);
|
||||
|
|
|
@ -12,7 +12,7 @@ impl PerItemCommand for Echo {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("echo").rest(SyntaxType::Any)
|
||||
Signature::build("echo").rest(SyntaxShape::Any)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -35,7 +35,7 @@ fn run(
|
|||
_registry: &CommandRegistry,
|
||||
_raw_args: &RawCommandArgs,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name = call_info.name_span;
|
||||
let name = call_info.name_tag;
|
||||
|
||||
let mut output = String::new();
|
||||
|
||||
|
@ -57,7 +57,7 @@ fn run(
|
|||
return Err(ShellError::labeled_error(
|
||||
"Expect a string from pipeline",
|
||||
"not a string-compatible value",
|
||||
i.span(),
|
||||
i.tag(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ fn run(
|
|||
}
|
||||
|
||||
let stream = VecDeque::from(vec![Ok(ReturnSuccess::Value(
|
||||
Value::string(output).simple_spanned(name),
|
||||
Value::string(output).tagged(name),
|
||||
))]);
|
||||
|
||||
Ok(stream.to_output_stream())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::commands::command::CommandAction;
|
||||
use crate::commands::PerItemCommand;
|
||||
use crate::commands::UnevaluatedCallInfo;
|
||||
use crate::data::meta::Span;
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::registry;
|
||||
use crate::prelude::*;
|
||||
|
@ -14,7 +15,7 @@ impl PerItemCommand for Enter {
|
|||
}
|
||||
|
||||
fn signature(&self) -> registry::Signature {
|
||||
Signature::build("enter").required("location", SyntaxType::Block)
|
||||
Signature::build("enter").required("location", SyntaxShape::Block)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -74,10 +75,10 @@ impl PerItemCommand for Enter {
|
|||
)
|
||||
.await.unwrap();
|
||||
|
||||
if let Some(uuid) = contents_tag.origin {
|
||||
if contents_tag.origin != uuid::Uuid::nil() {
|
||||
// If we have loaded something, track its source
|
||||
yield ReturnSuccess::action(CommandAction::AddSpanSource(
|
||||
uuid,
|
||||
contents_tag.origin,
|
||||
span_source,
|
||||
));
|
||||
}
|
||||
|
@ -103,12 +104,13 @@ impl PerItemCommand for Enter {
|
|||
},
|
||||
source: raw_args.call_info.source,
|
||||
source_map: raw_args.call_info.source_map,
|
||||
name_span: raw_args.call_info.name_span,
|
||||
name_tag: raw_args.call_info.name_tag,
|
||||
},
|
||||
};
|
||||
let mut result = converter.run(
|
||||
new_args.with_input(vec![tagged_contents]),
|
||||
®istry,
|
||||
false
|
||||
);
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
|
||||
result.drain_vec().await;
|
||||
|
|
76
src/commands/env.rs
Normal file
76
src/commands/env.rs
Normal 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())
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
use crate::commands::UnevaluatedCallInfo;
|
||||
use crate::context::SpanSource;
|
||||
use crate::data::meta::Span;
|
||||
use crate::data::Value;
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::hir::SyntaxShape;
|
||||
use crate::parser::registry::Signature;
|
||||
use crate::prelude::*;
|
||||
use mime::Mime;
|
||||
|
@ -19,7 +20,7 @@ impl PerItemCommand for Fetch {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(self.name())
|
||||
.required("path", SyntaxType::Path)
|
||||
.required("path", SyntaxShape::Path)
|
||||
.switch("raw")
|
||||
}
|
||||
|
||||
|
@ -75,10 +76,10 @@ fn run(
|
|||
file_extension.or(path_str.split('.').last().map(String::from))
|
||||
};
|
||||
|
||||
if let Some(uuid) = contents_tag.origin {
|
||||
if contents_tag.origin != uuid::Uuid::nil() {
|
||||
// If we have loaded something, track its source
|
||||
yield ReturnSuccess::action(CommandAction::AddSpanSource(
|
||||
uuid,
|
||||
contents_tag.origin,
|
||||
span_source,
|
||||
));
|
||||
}
|
||||
|
@ -99,10 +100,10 @@ fn run(
|
|||
},
|
||||
source: raw_args.call_info.source,
|
||||
source_map: raw_args.call_info.source_map,
|
||||
name_span: raw_args.call_info.name_span,
|
||||
name_tag: raw_args.call_info.name_tag,
|
||||
}
|
||||
};
|
||||
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry);
|
||||
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry, false);
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
|
||||
for res in result_vec {
|
||||
match res {
|
||||
|
@ -157,7 +158,7 @@ pub async fn fetch(
|
|||
})?),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
|
@ -172,7 +173,7 @@ pub async fn fetch(
|
|||
})?),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
|
@ -186,10 +187,10 @@ pub async fn fetch(
|
|||
})?;
|
||||
Ok((
|
||||
None,
|
||||
Value::Primitive(Primitive::Binary(buf)),
|
||||
Value::binary(buf),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::Url(location.to_string()),
|
||||
))
|
||||
|
@ -205,7 +206,7 @@ pub async fn fetch(
|
|||
})?),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
|
@ -219,10 +220,10 @@ pub async fn fetch(
|
|||
})?;
|
||||
Ok((
|
||||
Some(image_ty.to_string()),
|
||||
Value::Primitive(Primitive::Binary(buf)),
|
||||
Value::binary(buf),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::Url(location.to_string()),
|
||||
))
|
||||
|
@ -238,7 +239,7 @@ pub async fn fetch(
|
|||
})?),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
|
@ -265,7 +266,7 @@ pub async fn fetch(
|
|||
})?),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::Url(location.to_string()),
|
||||
))
|
||||
|
@ -275,7 +276,7 @@ pub async fn fetch(
|
|||
Value::string(format!("Not yet supported MIME type: {} {}", ty, sub_ty)),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
|
@ -286,7 +287,7 @@ pub async fn fetch(
|
|||
Value::string(format!("No content type found")),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
|
|
|
@ -16,7 +16,7 @@ impl WholeStreamCommand for First {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("first").required("amount", SyntaxType::Literal)
|
||||
Signature::build("first").required("amount", SyntaxShape::Literal)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
|
|
@ -198,7 +198,7 @@ pub fn from_bson_bytes_to_value(
|
|||
|
||||
fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let span = args.name_span();
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let stream = async_stream_block! {
|
||||
|
@ -208,24 +208,24 @@ fn from_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||
let value_tag = value.tag();
|
||||
match value.item {
|
||||
Value::Primitive(Primitive::Binary(vb)) =>
|
||||
match from_bson_bytes_to_value(vb, span) {
|
||||
match from_bson_bytes_to_value(vb, tag) {
|
||||
Ok(x) => yield ReturnSuccess::value(x),
|
||||
Err(_) => {
|
||||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as BSON",
|
||||
"input cannot be parsed as BSON",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
)),
|
||||
|
||||
}
|
||||
|
|
|
@ -86,7 +86,7 @@ fn from_csv(
|
|||
}: FromCSVArgs,
|
||||
RunnableContext { input, name, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_span = name;
|
||||
let name_tag = name;
|
||||
|
||||
let stream = async_stream_block! {
|
||||
let values: Vec<Tagged<Value>> = input.values.collect().await;
|
||||
|
@ -105,15 +105,15 @@ fn from_csv(
|
|||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
name_span,
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
)),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
match from_csv_string_to_value(concat_string, skip_headers, name_span) {
|
||||
match from_csv_string_to_value(concat_string, skip_headers, name_tag) {
|
||||
Ok(x) => match x {
|
||||
Tagged { item: Value::Table(list), .. } => {
|
||||
for l in list {
|
||||
|
@ -126,9 +126,9 @@ fn from_csv(
|
|||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as CSV",
|
||||
"input cannot be parsed as CSV",
|
||||
name_span,
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
last_tag.span,
|
||||
last_tag,
|
||||
))
|
||||
} ,
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ pub fn from_ini_string_to_value(
|
|||
|
||||
fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let span = args.name_span();
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let stream = async_stream_block! {
|
||||
|
@ -84,15 +84,15 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
|
|||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
)),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
match from_ini_string_to_value(concat_string, span) {
|
||||
match from_ini_string_to_value(concat_string, tag) {
|
||||
Ok(x) => match x {
|
||||
Tagged { item: Value::Table(list), .. } => {
|
||||
for l in list {
|
||||
|
@ -105,9 +105,9 @@ fn from_ini(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
|
|||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as INI",
|
||||
"input cannot be parsed as INI",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
last_tag.span,
|
||||
last_tag,
|
||||
))
|
||||
} ,
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ fn from_json(
|
|||
FromJSONArgs { objects }: FromJSONArgs,
|
||||
RunnableContext { input, name, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_span = name;
|
||||
let name_tag = name;
|
||||
|
||||
let stream = async_stream_block! {
|
||||
let values: Vec<Tagged<Value>> = input.values.collect().await;
|
||||
|
@ -91,9 +91,9 @@ fn from_json(
|
|||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
name_span,
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
)),
|
||||
|
||||
}
|
||||
|
@ -106,7 +106,7 @@ fn from_json(
|
|||
continue;
|
||||
}
|
||||
|
||||
match from_json_string_to_value(json_str.to_string(), name_span) {
|
||||
match from_json_string_to_value(json_str.to_string(), name_tag) {
|
||||
Ok(x) =>
|
||||
yield ReturnSuccess::value(x),
|
||||
Err(_) => {
|
||||
|
@ -114,15 +114,15 @@ fn from_json(
|
|||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could nnot parse as JSON",
|
||||
"input cannot be parsed as JSON",
|
||||
name_span,
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
last_tag.span))
|
||||
last_tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match from_json_string_to_value(concat_string, name_span) {
|
||||
match from_json_string_to_value(concat_string, name_tag) {
|
||||
Ok(x) =>
|
||||
match x {
|
||||
Tagged { item: Value::Table(list), .. } => {
|
||||
|
@ -137,9 +137,9 @@ fn from_json(
|
|||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as JSON",
|
||||
"input cannot be parsed as JSON",
|
||||
name_span,
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
last_tag.span))
|
||||
last_tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,7 +128,7 @@ pub fn from_sqlite_bytes_to_value(
|
|||
|
||||
fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let span = args.name_span();
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let stream = async_stream_block! {
|
||||
|
@ -138,7 +138,7 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
|||
let value_tag = value.tag();
|
||||
match value.item {
|
||||
Value::Primitive(Primitive::Binary(vb)) =>
|
||||
match from_sqlite_bytes_to_value(vb, span) {
|
||||
match from_sqlite_bytes_to_value(vb, tag) {
|
||||
Ok(x) => match x {
|
||||
Tagged { item: Value::Table(list), .. } => {
|
||||
for l in list {
|
||||
|
@ -151,18 +151,18 @@ fn from_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputSt
|
|||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as SQLite",
|
||||
"input cannot be parsed as SQLite",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
)),
|
||||
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ pub fn from_toml(
|
|||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let span = args.name_span();
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let stream = async_stream_block! {
|
||||
|
@ -88,15 +88,15 @@ pub fn from_toml(
|
|||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
)),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
match from_toml_string_to_value(concat_string, span) {
|
||||
match from_toml_string_to_value(concat_string, tag) {
|
||||
Ok(x) => match x {
|
||||
Tagged { item: Value::Table(list), .. } => {
|
||||
for l in list {
|
||||
|
@ -109,9 +109,9 @@ pub fn from_toml(
|
|||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as TOML",
|
||||
"input cannot be parsed as TOML",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
last_tag.span,
|
||||
last_tag,
|
||||
))
|
||||
} ,
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ fn from_tsv(
|
|||
}: FromTSVArgs,
|
||||
RunnableContext { input, name, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_span = name;
|
||||
let name_tag = name;
|
||||
|
||||
let stream = async_stream_block! {
|
||||
let values: Vec<Tagged<Value>> = input.values.collect().await;
|
||||
|
@ -106,15 +106,15 @@ fn from_tsv(
|
|||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
name_span,
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
)),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
match from_tsv_string_to_value(concat_string, skip_headers, name_span) {
|
||||
match from_tsv_string_to_value(concat_string, skip_headers, name_tag) {
|
||||
Ok(x) => match x {
|
||||
Tagged { item: Value::Table(list), .. } => {
|
||||
for l in list {
|
||||
|
@ -127,9 +127,9 @@ fn from_tsv(
|
|||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as TSV",
|
||||
"input cannot be parsed as TSV",
|
||||
name_span,
|
||||
name_tag,
|
||||
"value originates from here",
|
||||
last_tag.span,
|
||||
last_tag,
|
||||
))
|
||||
} ,
|
||||
}
|
||||
|
|
85
src/commands/from_url.rs
Normal file
85
src/commands/from_url.rs
Normal 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())
|
||||
}
|
|
@ -83,7 +83,7 @@ pub fn from_xml_string_to_value(
|
|||
|
||||
fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let span = args.name_span();
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let stream = async_stream_block! {
|
||||
|
@ -103,15 +103,15 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
|
|||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
)),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
match from_xml_string_to_value(concat_string, span) {
|
||||
match from_xml_string_to_value(concat_string, tag) {
|
||||
Ok(x) => match x {
|
||||
Tagged { item: Value::Table(list), .. } => {
|
||||
for l in list {
|
||||
|
@ -124,9 +124,9 @@ fn from_xml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStrea
|
|||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as XML",
|
||||
"input cannot be parsed as XML",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
last_tag.span,
|
||||
last_tag,
|
||||
))
|
||||
} ,
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ pub fn from_yaml_string_to_value(
|
|||
|
||||
fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let span = args.name_span();
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let stream = async_stream_block! {
|
||||
|
@ -117,15 +117,15 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
value_tag.span,
|
||||
value_tag,
|
||||
)),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
match from_yaml_string_to_value(concat_string, span) {
|
||||
match from_yaml_string_to_value(concat_string, tag) {
|
||||
Ok(x) => match x {
|
||||
Tagged { item: Value::Table(list), .. } => {
|
||||
for l in list {
|
||||
|
@ -138,9 +138,9 @@ fn from_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Could not parse as YAML",
|
||||
"input cannot be parsed as YAML",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
last_tag.span,
|
||||
last_tag,
|
||||
))
|
||||
} ,
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@ impl WholeStreamCommand for Get {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("get")
|
||||
.required("member", SyntaxType::Member)
|
||||
.rest(SyntaxType::Member)
|
||||
.required("member", SyntaxShape::Member)
|
||||
.rest(SyntaxShape::Member)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -61,7 +61,7 @@ fn get_member(path: &Tagged<String>, obj: &Tagged<Value>) -> Result<Tagged<Value
|
|||
return Err(ShellError::labeled_error(
|
||||
"Unknown column",
|
||||
format!("did you mean '{}'?", possible_matches[0].1),
|
||||
path.span(),
|
||||
path.tag(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ impl PerItemCommand for Help {
|
|||
}
|
||||
|
||||
fn signature(&self) -> registry::Signature {
|
||||
Signature::build("help").rest(SyntaxType::Any)
|
||||
Signature::build("help").rest(SyntaxShape::Any)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -26,13 +26,20 @@ impl PerItemCommand for Help {
|
|||
_raw_args: &RawCommandArgs,
|
||||
_input: Tagged<Value>,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let span = call_info.name_span;
|
||||
let tag = call_info.name_tag;
|
||||
|
||||
match call_info.args.nth(0) {
|
||||
Some(Tagged {
|
||||
if call_info.args.len() == 0 {
|
||||
return Ok(vec![Ok(ReturnSuccess::Action(CommandAction::EnterHelpShell(
|
||||
Value::nothing().tagged(tag),
|
||||
)))]
|
||||
.into());
|
||||
}
|
||||
|
||||
match call_info.args.expect_nth(0)? {
|
||||
Tagged {
|
||||
item: Value::Primitive(Primitive::String(document)),
|
||||
tag,
|
||||
}) => {
|
||||
} => {
|
||||
let mut help = VecDeque::new();
|
||||
if document == "commands" {
|
||||
let mut sorted_names = registry.names();
|
||||
|
@ -120,9 +127,7 @@ You can also learn more at http://book.nushell.sh"#;
|
|||
|
||||
let mut output_stream = VecDeque::new();
|
||||
|
||||
output_stream.push_back(ReturnSuccess::value(
|
||||
Value::string(msg).simple_spanned(span),
|
||||
));
|
||||
output_stream.push_back(ReturnSuccess::value(Value::string(msg).tagged(tag)));
|
||||
|
||||
Ok(output_stream.to_output_stream())
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ impl WholeStreamCommand for Last {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("last").required("amount", SyntaxType::Number)
|
||||
Signature::build("last").required("amount", SyntaxShape::Number)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
|
|
@ -32,7 +32,7 @@ impl WholeStreamCommand for Lines {
|
|||
|
||||
fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let span = args.name_span();
|
||||
let tag = args.name_tag();
|
||||
let input = args.input;
|
||||
|
||||
let input: InputStream = trace_stream!(target: "nu::trace_stream::lines", "input" = input);
|
||||
|
@ -58,9 +58,9 @@ fn lines(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream,
|
|||
result.push_back(Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
v.span(),
|
||||
v.tag(),
|
||||
)));
|
||||
result
|
||||
}
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
use crate::commands::WholeStreamCommand;
|
||||
use crate::errors::ShellError;
|
||||
use crate::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct LS;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct LsArgs {
|
||||
path: Option<Tagged<PathBuf>>,
|
||||
}
|
||||
|
||||
impl WholeStreamCommand for LS {
|
||||
fn name(&self) -> &str {
|
||||
"ls"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("ls").optional("path", SyntaxType::Pattern)
|
||||
Signature::build("ls").optional("path", SyntaxShape::Pattern)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -22,12 +28,11 @@ impl WholeStreamCommand for LS {
|
|||
args: CommandArgs,
|
||||
registry: &CommandRegistry,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
ls(args, registry)
|
||||
args.process(registry, ls)?.run()
|
||||
// ls(args, registry)
|
||||
}
|
||||
}
|
||||
|
||||
fn ls(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let shell_manager = args.shell_manager.clone();
|
||||
let args = args.evaluate_once(registry)?;
|
||||
shell_manager.ls(args)
|
||||
fn ls(LsArgs { path }: LsArgs, context: RunnableContext) -> Result<OutputStream, ShellError> {
|
||||
context.shell_manager.ls(path, context.name)
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ impl PerItemCommand for Mkdir {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("mkdir").rest(SyntaxType::Path)
|
||||
Signature::build("mkdir").rest(SyntaxShape::Path)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::hir::SyntaxShape;
|
||||
use crate::parser::registry::{CommandRegistry, Signature};
|
||||
use crate::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
@ -20,9 +20,9 @@ impl PerItemCommand for Move {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("mv")
|
||||
.required("source", SyntaxType::Path)
|
||||
.required("destination", SyntaxType::Path)
|
||||
.named("file", SyntaxType::Any)
|
||||
.required("source", SyntaxShape::Pattern)
|
||||
.required("destination", SyntaxShape::Path)
|
||||
.named("file", SyntaxShape::Any)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
|
|
@ -16,7 +16,7 @@ impl WholeStreamCommand for Nth {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("nth").required("row number", SyntaxType::Any)
|
||||
Signature::build("nth").required("amount", SyntaxShape::Any)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use crate::commands::UnevaluatedCallInfo;
|
||||
use crate::context::SpanSource;
|
||||
use crate::data::meta::Span;
|
||||
use crate::data::Value;
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::hir::SyntaxShape;
|
||||
use crate::parser::registry::Signature;
|
||||
use crate::prelude::*;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
@ -16,7 +17,7 @@ impl PerItemCommand for Open {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(self.name())
|
||||
.required("path", SyntaxType::Path)
|
||||
.required("path", SyntaxShape::Path)
|
||||
.switch("raw")
|
||||
}
|
||||
|
||||
|
@ -76,10 +77,10 @@ fn run(
|
|||
file_extension.or(path_str.split('.').last().map(String::from))
|
||||
};
|
||||
|
||||
if let Some(uuid) = contents_tag.origin {
|
||||
if contents_tag.origin != uuid::Uuid::nil() {
|
||||
// If we have loaded something, track its source
|
||||
yield ReturnSuccess::action(CommandAction::AddSpanSource(
|
||||
uuid,
|
||||
contents_tag.origin,
|
||||
span_source,
|
||||
));
|
||||
}
|
||||
|
@ -100,10 +101,10 @@ fn run(
|
|||
},
|
||||
source: raw_args.call_info.source,
|
||||
source_map: raw_args.call_info.source_map,
|
||||
name_span: raw_args.call_info.name_span,
|
||||
name_tag: raw_args.call_info.name_tag,
|
||||
}
|
||||
};
|
||||
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry);
|
||||
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry, false);
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
|
||||
for res in result_vec {
|
||||
match res {
|
||||
|
@ -146,7 +147,7 @@ pub async fn fetch(
|
|||
Value::string(s),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
)),
|
||||
|
@ -165,16 +166,16 @@ pub async fn fetch(
|
|||
Value::string(s),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
)),
|
||||
Err(_) => Ok((
|
||||
None,
|
||||
Value::Primitive(Primitive::Binary(bytes)),
|
||||
Value::binary(bytes),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
)),
|
||||
|
@ -182,10 +183,10 @@ pub async fn fetch(
|
|||
} else {
|
||||
Ok((
|
||||
None,
|
||||
Value::Primitive(Primitive::Binary(bytes)),
|
||||
Value::binary(bytes),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
))
|
||||
|
@ -203,16 +204,16 @@ pub async fn fetch(
|
|||
Value::string(s),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
)),
|
||||
Err(_) => Ok((
|
||||
None,
|
||||
Value::Primitive(Primitive::Binary(bytes)),
|
||||
Value::binary(bytes),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
)),
|
||||
|
@ -220,10 +221,10 @@ pub async fn fetch(
|
|||
} else {
|
||||
Ok((
|
||||
None,
|
||||
Value::Primitive(Primitive::Binary(bytes)),
|
||||
Value::binary(bytes),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
))
|
||||
|
@ -231,10 +232,10 @@ pub async fn fetch(
|
|||
}
|
||||
_ => Ok((
|
||||
None,
|
||||
Value::Primitive(Primitive::Binary(bytes)),
|
||||
Value::binary(bytes),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
origin: Uuid::new_v4(),
|
||||
},
|
||||
SpanSource::File(cwd.to_string_lossy().to_string()),
|
||||
)),
|
||||
|
|
|
@ -17,7 +17,7 @@ impl WholeStreamCommand for Pick {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("pick").rest(SyntaxType::Any)
|
||||
Signature::build("pick").rest(SyntaxShape::Any)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
|
133
src/commands/pivot.rs
Normal file
133
src/commands/pivot.rs
Normal 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))
|
||||
}
|
|
@ -2,7 +2,7 @@ use crate::commands::UnevaluatedCallInfo;
|
|||
use crate::context::SpanSource;
|
||||
use crate::data::Value;
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::hir::SyntaxShape;
|
||||
use crate::parser::registry::Signature;
|
||||
use crate::prelude::*;
|
||||
use base64::encode;
|
||||
|
@ -10,7 +10,7 @@ use mime::Mime;
|
|||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use surf::mime;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub struct Post;
|
||||
|
||||
impl PerItemCommand for Post {
|
||||
|
@ -20,10 +20,10 @@ impl PerItemCommand for Post {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(self.name())
|
||||
.required("path", SyntaxType::Any)
|
||||
.required("body", SyntaxType::Any)
|
||||
.named("user", SyntaxType::Any)
|
||||
.named("password", SyntaxType::Any)
|
||||
.required("path", SyntaxShape::Any)
|
||||
.required("body", SyntaxShape::Any)
|
||||
.named("user", SyntaxShape::Any)
|
||||
.named("password", SyntaxShape::Any)
|
||||
.switch("raw")
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ fn run(
|
|||
file => file.clone(),
|
||||
};
|
||||
let path_str = path.as_string()?;
|
||||
let path_span = path.span();
|
||||
let path_span = path.tag();
|
||||
let has_raw = call_info.args.has("raw");
|
||||
let user = call_info.args.get("user").map(|x| x.as_string().unwrap());
|
||||
let password = call_info
|
||||
|
@ -85,10 +85,10 @@ fn run(
|
|||
file_extension.or(path_str.split('.').last().map(String::from))
|
||||
};
|
||||
|
||||
if let Some(uuid) = contents_tag.origin {
|
||||
if contents_tag.origin != uuid::Uuid::nil() {
|
||||
// If we have loaded something, track its source
|
||||
yield ReturnSuccess::action(CommandAction::AddSpanSource(
|
||||
uuid,
|
||||
contents_tag.origin,
|
||||
span_source,
|
||||
));
|
||||
}
|
||||
|
@ -109,10 +109,10 @@ fn run(
|
|||
},
|
||||
source: raw_args.call_info.source,
|
||||
source_map: raw_args.call_info.source_map,
|
||||
name_span: raw_args.call_info.name_span,
|
||||
name_tag: raw_args.call_info.name_tag,
|
||||
}
|
||||
};
|
||||
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry);
|
||||
let mut result = converter.run(new_args.with_input(vec![tagged_contents]), ®istry, false);
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
|
||||
for res in result_vec {
|
||||
match res {
|
||||
|
@ -143,7 +143,7 @@ pub async fn post(
|
|||
body: &Tagged<Value>,
|
||||
user: Option<String>,
|
||||
password: Option<String>,
|
||||
span: Span,
|
||||
tag: Tag,
|
||||
registry: &CommandRegistry,
|
||||
raw_args: &RawCommandArgs,
|
||||
) -> Result<(Option<String>, Value, Tag, SpanSource), ShellError> {
|
||||
|
@ -189,12 +189,13 @@ pub async fn post(
|
|||
},
|
||||
source: raw_args.call_info.source,
|
||||
source_map: raw_args.call_info.source_map,
|
||||
name_span: raw_args.call_info.name_span,
|
||||
name_tag: raw_args.call_info.name_tag,
|
||||
},
|
||||
};
|
||||
let mut result = converter.run(
|
||||
new_args.with_input(vec![item.clone().tagged(tag.clone())]),
|
||||
®istry,
|
||||
false,
|
||||
);
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> =
|
||||
result.drain_vec().await;
|
||||
|
@ -211,7 +212,7 @@ pub async fn post(
|
|||
return Err(ShellError::labeled_error(
|
||||
"Save could not successfully save",
|
||||
"unexpected data during save",
|
||||
span,
|
||||
*tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +228,7 @@ pub async fn post(
|
|||
return Err(ShellError::labeled_error(
|
||||
"Could not automatically convert table",
|
||||
"needs manual conversion",
|
||||
tag.span,
|
||||
*tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -243,13 +244,10 @@ pub async fn post(
|
|||
ShellError::labeled_error(
|
||||
"Could not load text from remote url",
|
||||
"could not load",
|
||||
span,
|
||||
tag,
|
||||
)
|
||||
})?),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
tag,
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
(mime::APPLICATION, mime::JSON) => Ok((
|
||||
|
@ -258,13 +256,10 @@ pub async fn post(
|
|||
ShellError::labeled_error(
|
||||
"Could not load text from remote url",
|
||||
"could not load",
|
||||
span,
|
||||
tag,
|
||||
)
|
||||
})?),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
tag,
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
(mime::APPLICATION, mime::OCTET_STREAM) => {
|
||||
|
@ -272,16 +267,13 @@ pub async fn post(
|
|||
ShellError::labeled_error(
|
||||
"Could not load binary file",
|
||||
"could not load",
|
||||
span,
|
||||
tag,
|
||||
)
|
||||
})?;
|
||||
Ok((
|
||||
None,
|
||||
Value::Primitive(Primitive::Binary(buf)),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
Value::binary(buf),
|
||||
tag,
|
||||
SpanSource::Url(location.to_string()),
|
||||
))
|
||||
}
|
||||
|
@ -290,16 +282,13 @@ pub async fn post(
|
|||
ShellError::labeled_error(
|
||||
"Could not load image file",
|
||||
"could not load",
|
||||
span,
|
||||
tag,
|
||||
)
|
||||
})?;
|
||||
Ok((
|
||||
Some(image_ty.to_string()),
|
||||
Value::Primitive(Primitive::Binary(buf)),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
Value::binary(buf),
|
||||
tag,
|
||||
SpanSource::Url(location.to_string()),
|
||||
))
|
||||
}
|
||||
|
@ -309,13 +298,10 @@ pub async fn post(
|
|||
ShellError::labeled_error(
|
||||
"Could not load text from remote url",
|
||||
"could not load",
|
||||
span,
|
||||
tag,
|
||||
)
|
||||
})?),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
tag,
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
(mime::TEXT, mime::PLAIN) => {
|
||||
|
@ -336,13 +322,10 @@ pub async fn post(
|
|||
ShellError::labeled_error(
|
||||
"Could not load text from remote url",
|
||||
"could not load",
|
||||
span,
|
||||
tag,
|
||||
)
|
||||
})?),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
tag,
|
||||
SpanSource::Url(location.to_string()),
|
||||
))
|
||||
}
|
||||
|
@ -352,10 +335,7 @@ pub async fn post(
|
|||
"Not yet supported MIME type: {} {}",
|
||||
ty, sub_ty
|
||||
)),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
tag,
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
}
|
||||
|
@ -363,10 +343,7 @@ pub async fn post(
|
|||
None => Ok((
|
||||
None,
|
||||
Value::string(format!("No content type found")),
|
||||
Tag {
|
||||
span,
|
||||
origin: Some(Uuid::new_v4()),
|
||||
},
|
||||
tag,
|
||||
SpanSource::Url(location.to_string()),
|
||||
)),
|
||||
},
|
||||
|
@ -374,7 +351,7 @@ pub async fn post(
|
|||
return Err(ShellError::labeled_error(
|
||||
"URL could not be opened",
|
||||
"url not found",
|
||||
span,
|
||||
tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -382,7 +359,7 @@ pub async fn post(
|
|||
Err(ShellError::labeled_error(
|
||||
"Expected a url",
|
||||
"needs a url",
|
||||
span,
|
||||
tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ impl WholeStreamCommand for Reject {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("reject").rest(SyntaxType::Member)
|
||||
Signature::build("reject").rest(SyntaxShape::Member)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::commands::command::RunnablePerItemContext;
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::hir::SyntaxShape;
|
||||
use crate::parser::registry::{CommandRegistry, Signature};
|
||||
use crate::prelude::*;
|
||||
use std::path::PathBuf;
|
||||
|
@ -20,7 +20,7 @@ impl PerItemCommand for Remove {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("rm")
|
||||
.required("path", SyntaxType::Path)
|
||||
.required("path", SyntaxShape::Pattern)
|
||||
.switch("recursive")
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::path::{Path, PathBuf};
|
|||
pub struct Save;
|
||||
|
||||
macro_rules! process_string {
|
||||
($input:ident, $name_span:ident) => {{
|
||||
($input:ident, $name_tag:ident) => {{
|
||||
let mut result_string = String::new();
|
||||
for res in $input {
|
||||
match res {
|
||||
|
@ -21,7 +21,7 @@ macro_rules! process_string {
|
|||
yield core::task::Poll::Ready(Err(ShellError::labeled_error(
|
||||
"Save could not successfully save",
|
||||
"unexpected data during save",
|
||||
$name_span,
|
||||
$name_tag,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ macro_rules! process_string {
|
|||
}
|
||||
|
||||
macro_rules! process_string_return_success {
|
||||
($result_vec:ident, $name_span:ident) => {{
|
||||
($result_vec:ident, $name_tag:ident) => {{
|
||||
let mut result_string = String::new();
|
||||
for res in $result_vec {
|
||||
match res {
|
||||
|
@ -45,7 +45,7 @@ macro_rules! process_string_return_success {
|
|||
yield core::task::Poll::Ready(Err(ShellError::labeled_error(
|
||||
"Save could not successfully save",
|
||||
"unexpected data during text save",
|
||||
$name_span,
|
||||
$name_tag,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ macro_rules! process_string_return_success {
|
|||
}
|
||||
|
||||
macro_rules! process_binary_return_success {
|
||||
($result_vec:ident, $name_span:ident) => {{
|
||||
($result_vec:ident, $name_tag:ident) => {{
|
||||
let mut result_binary: Vec<u8> = Vec::new();
|
||||
for res in $result_vec {
|
||||
match res {
|
||||
|
@ -71,7 +71,7 @@ macro_rules! process_binary_return_success {
|
|||
yield core::task::Poll::Ready(Err(ShellError::labeled_error(
|
||||
"Save could not successfully save",
|
||||
"unexpected data during binary save",
|
||||
$name_span,
|
||||
$name_tag,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ impl WholeStreamCommand for Save {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("save")
|
||||
.optional("path", SyntaxType::Path)
|
||||
.optional("path", SyntaxShape::Path)
|
||||
.switch("raw")
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ fn save(
|
|||
raw_args: RawCommandArgs,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let mut full_path = PathBuf::from(shell_manager.path());
|
||||
let name_span = name;
|
||||
let name_tag = name;
|
||||
|
||||
let source_map = source_map.clone();
|
||||
let stream = async_stream_block! {
|
||||
|
@ -136,7 +136,7 @@ fn save(
|
|||
// If there is no filename, check the metadata for the origin filename
|
||||
if input.len() > 0 {
|
||||
let origin = input[0].origin();
|
||||
match origin.and_then(|x| source_map.get(&x)) {
|
||||
match source_map.get(&origin) {
|
||||
Some(path) => match path {
|
||||
SpanSource::File(file) => {
|
||||
full_path.push(Path::new(file));
|
||||
|
@ -145,7 +145,7 @@ fn save(
|
|||
yield Err(ShellError::labeled_error(
|
||||
"Save requires a filepath",
|
||||
"needs path",
|
||||
name_span,
|
||||
name_tag,
|
||||
));
|
||||
}
|
||||
},
|
||||
|
@ -153,7 +153,7 @@ fn save(
|
|||
yield Err(ShellError::labeled_error(
|
||||
"Save requires a filepath",
|
||||
"needs path",
|
||||
name_span,
|
||||
name_tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ fn save(
|
|||
yield Err(ShellError::labeled_error(
|
||||
"Save requires a filepath",
|
||||
"needs path",
|
||||
name_span,
|
||||
name_tag,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
|
@ -185,21 +185,21 @@ fn save(
|
|||
},
|
||||
source: raw_args.call_info.source,
|
||||
source_map: raw_args.call_info.source_map,
|
||||
name_span: raw_args.call_info.name_span,
|
||||
name_tag: raw_args.call_info.name_tag,
|
||||
}
|
||||
};
|
||||
let mut result = converter.run(new_args.with_input(input), ®istry);
|
||||
let mut result = converter.run(new_args.with_input(input), ®istry, false);
|
||||
let result_vec: Vec<Result<ReturnSuccess, ShellError>> = result.drain_vec().await;
|
||||
if converter.is_binary() {
|
||||
process_binary_return_success!(result_vec, name_span)
|
||||
process_binary_return_success!(result_vec, name_tag)
|
||||
} else {
|
||||
process_string_return_success!(result_vec, name_span)
|
||||
process_string_return_success!(result_vec, name_tag)
|
||||
}
|
||||
} else {
|
||||
process_string!(input, name_span)
|
||||
process_string!(input, name_tag)
|
||||
}
|
||||
} else {
|
||||
process_string!(input, name_span)
|
||||
process_string!(input, name_tag)
|
||||
}
|
||||
} else {
|
||||
Ok(string_from(&input).into_bytes())
|
||||
|
|
|
@ -29,10 +29,10 @@ impl WholeStreamCommand for Shells {
|
|||
|
||||
fn shells(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let mut shells_out = VecDeque::new();
|
||||
let span = args.call_info.name_span;
|
||||
let tag = args.call_info.name_tag;
|
||||
|
||||
for (index, shell) in args.shell_manager.shells.lock().unwrap().iter().enumerate() {
|
||||
let mut dict = TaggedDictBuilder::new(Tag::unknown_origin(span));
|
||||
let mut dict = TaggedDictBuilder::new(tag);
|
||||
|
||||
if index == args.shell_manager.current_shell {
|
||||
dict.insert(" ", "X".to_string());
|
||||
|
|
|
@ -29,7 +29,7 @@ impl WholeStreamCommand for Size {
|
|||
|
||||
fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let input = args.input;
|
||||
let span = args.call_info.name_span;
|
||||
let tag = args.call_info.name_tag;
|
||||
Ok(input
|
||||
.values
|
||||
.map(move |v| match v.item {
|
||||
|
@ -37,9 +37,9 @@ fn size(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream,
|
|||
_ => Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a string from pipeline",
|
||||
"requires string input",
|
||||
span,
|
||||
tag,
|
||||
"value originates from here",
|
||||
v.span(),
|
||||
v.tag(),
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
|
@ -71,7 +71,7 @@ fn count(contents: &str, tag: impl Into<Tag>) -> Tagged<Value> {
|
|||
}
|
||||
|
||||
let mut dict = TaggedDictBuilder::new(tag);
|
||||
//TODO: add back in name when we have it in the span
|
||||
//TODO: add back in name when we have it in the tag
|
||||
//dict.insert("name", Value::string(name));
|
||||
dict.insert("lines", Value::int(lines));
|
||||
dict.insert("words", Value::int(words));
|
||||
|
|
|
@ -16,7 +16,7 @@ impl WholeStreamCommand for SkipWhile {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("skip-while")
|
||||
.required("condition", SyntaxType::Block)
|
||||
.required("condition", SyntaxShape::Block)
|
||||
.filter()
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ impl WholeStreamCommand for SortBy {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("sort-by").rest(SyntaxType::String)
|
||||
Signature::build("sort-by").rest(SyntaxShape::String)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
|
|
@ -21,9 +21,9 @@ impl WholeStreamCommand for SplitColumn {
|
|||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("split-column")
|
||||
.required("separator", SyntaxType::Any)
|
||||
.required("separator", SyntaxShape::Any)
|
||||
.switch("collapse-empty")
|
||||
.rest(SyntaxType::Member)
|
||||
.rest(SyntaxShape::Member)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -96,7 +96,7 @@ fn split_column(
|
|||
"requires string input",
|
||||
name,
|
||||
"value originates from here",
|
||||
v.span(),
|
||||
v.tag(),
|
||||
)),
|
||||
})
|
||||
.to_output_stream())
|
||||
|
|
|
@ -17,7 +17,7 @@ impl WholeStreamCommand for SplitRow {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("split-row").required("separator", SyntaxType::Any)
|
||||
Signature::build("split-row").required("separator", SyntaxShape::Any)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -62,7 +62,7 @@ fn split_row(
|
|||
"requires string input",
|
||||
name,
|
||||
"value originates from here",
|
||||
v.span(),
|
||||
v.tag(),
|
||||
)));
|
||||
result
|
||||
}
|
||||
|
|
|
@ -36,13 +36,13 @@ fn tags(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream,
|
|||
let mut tags = TaggedDictBuilder::new(v.tag());
|
||||
{
|
||||
let origin = v.origin();
|
||||
let span = v.span();
|
||||
let span = v.tag().span;
|
||||
let mut dict = TaggedDictBuilder::new(v.tag());
|
||||
dict.insert("start", Value::int(span.start as i64));
|
||||
dict.insert("end", Value::int(span.end as i64));
|
||||
tags.insert_tagged("span", dict.into_tagged_value());
|
||||
|
||||
match origin.and_then(|x| source_map.get(&x)) {
|
||||
match source_map.get(&origin) {
|
||||
Some(SpanSource::File(source)) => {
|
||||
tags.insert("origin", Value::string(source));
|
||||
}
|
||||
|
|
|
@ -190,44 +190,40 @@ fn generic_object_value_to_bson(o: &Dictionary) -> Result<Bson, ShellError> {
|
|||
Ok(Bson::Document(doc))
|
||||
}
|
||||
|
||||
fn shell_encode_document(
|
||||
writer: &mut Vec<u8>,
|
||||
doc: Document,
|
||||
span: Span,
|
||||
) -> Result<(), ShellError> {
|
||||
fn shell_encode_document(writer: &mut Vec<u8>, doc: Document, tag: Tag) -> Result<(), ShellError> {
|
||||
match encode_document(writer, &doc) {
|
||||
Err(e) => Err(ShellError::labeled_error(
|
||||
format!("Failed to encode document due to: {:?}", e),
|
||||
"requires BSON-compatible document",
|
||||
span,
|
||||
tag,
|
||||
)),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn bson_value_to_bytes(bson: Bson, span: Span) -> Result<Vec<u8>, ShellError> {
|
||||
fn bson_value_to_bytes(bson: Bson, tag: Tag) -> Result<Vec<u8>, ShellError> {
|
||||
let mut out = Vec::new();
|
||||
match bson {
|
||||
Bson::Array(a) => {
|
||||
for v in a.into_iter() {
|
||||
match v {
|
||||
Bson::Document(d) => shell_encode_document(&mut out, d, span)?,
|
||||
Bson::Document(d) => shell_encode_document(&mut out, d, tag)?,
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
format!("All top level values must be Documents, got {:?}", v),
|
||||
"requires BSON-compatible document",
|
||||
span,
|
||||
tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Bson::Document(d) => shell_encode_document(&mut out, d, span)?,
|
||||
Bson::Document(d) => shell_encode_document(&mut out, d, tag)?,
|
||||
_ => {
|
||||
return Err(ShellError::labeled_error(
|
||||
format!("All top level values must be Documents, got {:?}", bson),
|
||||
"requires BSON-compatible document",
|
||||
span,
|
||||
tag,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -236,7 +232,7 @@ fn bson_value_to_bytes(bson: Bson, span: Span) -> Result<Vec<u8>, ShellError> {
|
|||
|
||||
fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let name_span = args.name_span();
|
||||
let name_tag = args.name_tag();
|
||||
let stream = async_stream_block! {
|
||||
let input: Vec<Tagged<Value>> = args.input.values.collect().await;
|
||||
|
||||
|
@ -252,23 +248,23 @@ fn to_bson(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
|||
for value in to_process_input {
|
||||
match value_to_bson_value(&value) {
|
||||
Ok(bson_value) => {
|
||||
match bson_value_to_bytes(bson_value, name_span) {
|
||||
match bson_value_to_bytes(bson_value, name_tag) {
|
||||
Ok(x) => yield ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::Binary(x)).simple_spanned(name_span),
|
||||
Value::binary(x).tagged(name_tag),
|
||||
),
|
||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a table with BSON-compatible structure.span() from pipeline",
|
||||
"Expected a table with BSON-compatible structure.tag() from pipeline",
|
||||
"requires BSON-compatible input",
|
||||
name_span,
|
||||
name_tag,
|
||||
"originates from here".to_string(),
|
||||
value.span(),
|
||||
value.tag(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => yield Err(ShellError::labeled_error(
|
||||
"Expected a table with BSON-compatible structure from pipeline",
|
||||
"requires BSON-compatible input",
|
||||
name_span))
|
||||
name_tag))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -134,7 +134,7 @@ fn to_csv(
|
|||
ToCSVArgs { headerless }: ToCSVArgs,
|
||||
RunnableContext { input, name, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_span = name;
|
||||
let name_tag = name;
|
||||
let stream = async_stream_block! {
|
||||
let input: Vec<Tagged<Value>> = input.values.collect().await;
|
||||
|
||||
|
@ -155,15 +155,15 @@ fn to_csv(
|
|||
} else {
|
||||
x
|
||||
};
|
||||
yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span))
|
||||
yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag))
|
||||
}
|
||||
_ => {
|
||||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a table with CSV-compatible structure.span() from pipeline",
|
||||
"Expected a table with CSV-compatible structure.tag() from pipeline",
|
||||
"requires CSV-compatible input",
|
||||
name_span,
|
||||
name_tag,
|
||||
"originates from here".to_string(),
|
||||
value.span(),
|
||||
value.tag(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,7 +80,7 @@ fn json_list(input: &Vec<Tagged<Value>>) -> Result<Vec<serde_json::Value>, Shell
|
|||
|
||||
fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let name_span = args.name_span();
|
||||
let name_tag = args.name_tag();
|
||||
let stream = async_stream_block! {
|
||||
let input: Vec<Tagged<Value>> = args.input.values.collect().await;
|
||||
|
||||
|
@ -98,21 +98,21 @@ fn to_json(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
|||
Ok(json_value) => {
|
||||
match serde_json::to_string(&json_value) {
|
||||
Ok(x) => yield ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(x)).simple_spanned(name_span),
|
||||
Value::Primitive(Primitive::String(x)).tagged(name_tag),
|
||||
),
|
||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a table with JSON-compatible structure.span() from pipeline",
|
||||
"Expected a table with JSON-compatible structure.tag() from pipeline",
|
||||
"requires JSON-compatible input",
|
||||
name_span,
|
||||
name_tag,
|
||||
"originates from here".to_string(),
|
||||
value.span(),
|
||||
value.tag(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => yield Err(ShellError::labeled_error(
|
||||
"Expected a table with JSON-compatible structure from pipeline",
|
||||
"requires JSON-compatible input",
|
||||
name_span))
|
||||
name_tag))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -200,7 +200,7 @@ fn sqlite_input_stream_to_bytes(
|
|||
|
||||
fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let name_span = args.name_span();
|
||||
let name_tag = args.name_tag();
|
||||
let stream = async_stream_block! {
|
||||
let input: Vec<Tagged<Value>> = args.input.values.collect().await;
|
||||
|
||||
|
@ -208,9 +208,9 @@ fn to_sqlite(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||
Ok(out) => yield ReturnSuccess::value(out),
|
||||
_ => {
|
||||
yield Err(ShellError::labeled_error(
|
||||
"Expected a table with SQLite-compatible structure.span() from pipeline",
|
||||
"Expected a table with SQLite-compatible structure.tag() from pipeline",
|
||||
"requires SQLite-compatible input",
|
||||
name_span,
|
||||
name_tag,
|
||||
))
|
||||
},
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ fn collect_values(input: &Vec<Tagged<Value>>) -> Result<Vec<toml::Value>, ShellE
|
|||
|
||||
fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let name_span = args.name_span();
|
||||
let name_tag = args.name_tag();
|
||||
let stream = async_stream_block! {
|
||||
let input: Vec<Tagged<Value>> = args.input.values.collect().await;
|
||||
|
||||
|
@ -93,21 +93,21 @@ fn to_toml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
|||
Ok(toml_value) => {
|
||||
match toml::to_string(&toml_value) {
|
||||
Ok(x) => yield ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(x)).simple_spanned(name_span),
|
||||
Value::Primitive(Primitive::String(x)).tagged(name_tag),
|
||||
),
|
||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a table with TOML-compatible structure.span() from pipeline",
|
||||
"Expected a table with TOML-compatible structure.tag() from pipeline",
|
||||
"requires TOML-compatible input",
|
||||
name_span,
|
||||
name_tag,
|
||||
"originates from here".to_string(),
|
||||
value.span(),
|
||||
value.tag(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => yield Err(ShellError::labeled_error(
|
||||
"Expected a table with TOML-compatible structure from pipeline",
|
||||
"requires TOML-compatible input",
|
||||
name_span))
|
||||
name_tag))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -133,7 +133,7 @@ fn to_tsv(
|
|||
ToTSVArgs { headerless }: ToTSVArgs,
|
||||
RunnableContext { input, name, .. }: RunnableContext,
|
||||
) -> Result<OutputStream, ShellError> {
|
||||
let name_span = name;
|
||||
let name_tag = name;
|
||||
let stream = async_stream_block! {
|
||||
let input: Vec<Tagged<Value>> = input.values.collect().await;
|
||||
|
||||
|
@ -154,15 +154,15 @@ fn to_tsv(
|
|||
} else {
|
||||
x
|
||||
};
|
||||
yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).simple_spanned(name_span))
|
||||
yield ReturnSuccess::value(Value::Primitive(Primitive::String(converted)).tagged(name_tag))
|
||||
}
|
||||
_ => {
|
||||
yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a table with TSV-compatible structure.span() from pipeline",
|
||||
"Expected a table with TSV-compatible structure.tag() from pipeline",
|
||||
"requires TSV-compatible input",
|
||||
name_span,
|
||||
name_tag,
|
||||
"originates from here".to_string(),
|
||||
value.span(),
|
||||
value.tag(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
85
src/commands/to_url.rs
Normal file
85
src/commands/to_url.rs
Normal 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())
|
||||
}
|
|
@ -76,7 +76,7 @@ pub fn value_to_yaml_value(v: &Tagged<Value>) -> Result<serde_yaml::Value, Shell
|
|||
|
||||
fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let name_span = args.name_span();
|
||||
let name_tag = args.name_tag();
|
||||
let stream = async_stream_block! {
|
||||
let input: Vec<Tagged<Value>> = args.input.values.collect().await;
|
||||
|
||||
|
@ -94,21 +94,21 @@ fn to_yaml(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream
|
|||
Ok(yaml_value) => {
|
||||
match serde_yaml::to_string(&yaml_value) {
|
||||
Ok(x) => yield ReturnSuccess::value(
|
||||
Value::Primitive(Primitive::String(x)).simple_spanned(name_span),
|
||||
Value::Primitive(Primitive::String(x)).tagged(name_tag),
|
||||
),
|
||||
_ => yield Err(ShellError::labeled_error_with_secondary(
|
||||
"Expected a table with YAML-compatible structure.span() from pipeline",
|
||||
"Expected a table with YAML-compatible structure.tag() from pipeline",
|
||||
"requires YAML-compatible input",
|
||||
name_span,
|
||||
name_tag,
|
||||
"originates from here".to_string(),
|
||||
value.span(),
|
||||
value.tag(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => yield Err(ShellError::labeled_error(
|
||||
"Expected a table with YAML-compatible structure from pipeline",
|
||||
"requires YAML-compatible input",
|
||||
name_span))
|
||||
name_tag))
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -34,7 +34,7 @@ fn trim(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream,
|
|||
.values
|
||||
.map(move |v| {
|
||||
let string = String::extract(&v)?;
|
||||
ReturnSuccess::value(Value::string(string.trim()).simple_spanned(v.span()))
|
||||
ReturnSuccess::value(Value::string(string.trim()).tagged(v.tag()))
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
|
|
@ -31,14 +31,14 @@ impl WholeStreamCommand for Version {
|
|||
|
||||
pub fn date(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
||||
let args = args.evaluate_once(registry)?;
|
||||
let span = args.call_info.name_span;
|
||||
let tag = args.call_info.name_tag;
|
||||
|
||||
let mut indexmap = IndexMap::new();
|
||||
indexmap.insert(
|
||||
"version".to_string(),
|
||||
Tagged::from_simple_spanned_item(Value::string(clap::crate_version!()), span),
|
||||
Value::string(clap::crate_version!()).tagged(tag),
|
||||
);
|
||||
|
||||
let value = Tagged::from_simple_spanned_item(Value::Row(Dictionary::from(indexmap)), span);
|
||||
let value = Value::Row(Dictionary::from(indexmap)).tagged(tag);
|
||||
Ok(OutputStream::one(value))
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
use crate::commands::PerItemCommand;
|
||||
use crate::errors::ShellError;
|
||||
use crate::parser::hir::SyntaxType;
|
||||
use crate::parser::hir::SyntaxShape;
|
||||
use crate::parser::registry;
|
||||
use crate::prelude::*;
|
||||
|
||||
|
@ -12,7 +12,7 @@ impl PerItemCommand for Where {
|
|||
}
|
||||
|
||||
fn signature(&self) -> registry::Signature {
|
||||
Signature::build("where").required("condition", SyntaxType::Block)
|
||||
Signature::build("where").required("condition", SyntaxShape::Block)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -49,7 +49,7 @@ impl PerItemCommand for Where {
|
|||
return Err(ShellError::labeled_error(
|
||||
"Expected a condition",
|
||||
"where needs a condition",
|
||||
tag.span,
|
||||
*tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
|
|
@ -13,7 +13,7 @@ impl WholeStreamCommand for Which {
|
|||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("which").required("name", SyntaxType::Any)
|
||||
Signature::build("which").required("name", SyntaxShape::Any)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
|
@ -33,7 +33,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||
let args = args.evaluate_once(registry)?;
|
||||
|
||||
let mut which_out = VecDeque::new();
|
||||
let span = args.call_info.name_span;
|
||||
let tag = args.call_info.name_tag;
|
||||
|
||||
if let Some(v) = &args.call_info.args.positional {
|
||||
if v.len() > 0 {
|
||||
|
@ -52,7 +52,7 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||
return Err(ShellError::labeled_error(
|
||||
"Expected a filename to find",
|
||||
"needs a filename",
|
||||
tag.span,
|
||||
*tag,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -60,14 +60,14 @@ pub fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStre
|
|||
return Err(ShellError::labeled_error(
|
||||
"Expected a binary to find",
|
||||
"needs application name",
|
||||
span,
|
||||
tag,
|
||||
));
|
||||
}
|
||||
} else {
|
||||
return Err(ShellError::labeled_error(
|
||||
"Expected a binary to find",
|
||||
"needs application name",
|
||||
span,
|
||||
tag,
|
||||
));
|
||||
}
|
||||
|
||||
|
|
|
@ -120,14 +120,15 @@ impl Context {
|
|||
pub(crate) fn run_command<'a>(
|
||||
&mut self,
|
||||
command: Arc<Command>,
|
||||
name_span: Span,
|
||||
name_tag: Tag,
|
||||
source_map: SourceMap,
|
||||
args: hir::Call,
|
||||
source: &Text,
|
||||
input: InputStream,
|
||||
is_first_command: bool,
|
||||
) -> OutputStream {
|
||||
let command_args = self.command_args(args, input, source, source_map, name_span);
|
||||
command.run(command_args, self.registry())
|
||||
let command_args = self.command_args(args, input, source, source_map, name_tag);
|
||||
command.run(command_args, self.registry(), is_first_command)
|
||||
}
|
||||
|
||||
fn call_info(
|
||||
|
@ -135,13 +136,13 @@ impl Context {
|
|||
args: hir::Call,
|
||||
source: &Text,
|
||||
source_map: SourceMap,
|
||||
name_span: Span,
|
||||
name_tag: Tag,
|
||||
) -> UnevaluatedCallInfo {
|
||||
UnevaluatedCallInfo {
|
||||
args,
|
||||
source: source.clone(),
|
||||
source_map,
|
||||
name_span,
|
||||
name_tag,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,12 +152,12 @@ impl Context {
|
|||
input: InputStream,
|
||||
source: &Text,
|
||||
source_map: SourceMap,
|
||||
name_span: Span,
|
||||
name_tag: Tag,
|
||||
) -> CommandArgs {
|
||||
CommandArgs {
|
||||
host: self.host.clone(),
|
||||
shell_manager: self.shell_manager.clone(),
|
||||
call_info: self.call_info(args, source, source_map, name_span),
|
||||
call_info: self.call_info(args, source, source_map, name_tag),
|
||||
input,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,64 @@ use std::fmt;
|
|||
use std::path::PathBuf;
|
||||
use std::time::SystemTime;
|
||||
|
||||
mod serde_bigint {
|
||||
use num_traits::cast::FromPrimitive;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
pub fn serialize<S>(big_int: &super::BigInt, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serde::Serialize::serialize(
|
||||
&big_int
|
||||
.to_i64()
|
||||
.ok_or(serde::ser::Error::custom("expected a i64-sized bignum"))?,
|
||||
serializer,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<super::BigInt, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let x: i64 = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(super::BigInt::from_i64(x)
|
||||
.ok_or(serde::de::Error::custom("expected a i64-sized bignum"))?)
|
||||
}
|
||||
}
|
||||
|
||||
mod serde_bigdecimal {
|
||||
use num_traits::cast::FromPrimitive;
|
||||
use num_traits::cast::ToPrimitive;
|
||||
|
||||
pub fn serialize<S>(big_decimal: &super::BigDecimal, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
serde::Serialize::serialize(
|
||||
&big_decimal
|
||||
.to_f64()
|
||||
.ok_or(serde::ser::Error::custom("expected a f64-sized bignum"))?,
|
||||
serializer,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn deserialize<'de, D>(deserializer: D) -> Result<super::BigDecimal, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let x: f64 = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(super::BigDecimal::from_f64(x)
|
||||
.ok_or(serde::de::Error::custom("expected a f64-sized bigdecimal"))?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Deserialize, Serialize)]
|
||||
pub enum Primitive {
|
||||
Nothing,
|
||||
#[serde(with = "serde_bigint")]
|
||||
Int(BigInt),
|
||||
#[serde(with = "serde_bigdecimal")]
|
||||
Decimal(BigDecimal),
|
||||
Bytes(u64),
|
||||
String(String),
|
||||
|
@ -150,7 +204,7 @@ pub struct Operation {
|
|||
pub struct Block {
|
||||
pub(crate) expressions: Vec<hir::Expression>,
|
||||
pub(crate) source: Text,
|
||||
pub(crate) span: Span,
|
||||
pub(crate) tag: Tag,
|
||||
}
|
||||
|
||||
impl Block {
|
||||
|
@ -158,7 +212,7 @@ impl Block {
|
|||
let scope = Scope::new(value.clone());
|
||||
|
||||
if self.expressions.len() == 0 {
|
||||
return Ok(Value::nothing().simple_spanned(self.span));
|
||||
return Ok(Value::nothing().tagged(self.tag));
|
||||
}
|
||||
|
||||
let mut last = None;
|
||||
|
@ -249,7 +303,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for Block {
|
|||
Value::Block(block) => Ok(block.clone()),
|
||||
v => Err(ShellError::type_error(
|
||||
"Block",
|
||||
value.copy_span(v.type_name()),
|
||||
value.copy_tag(v.type_name()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -265,7 +319,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for i64 {
|
|||
}
|
||||
v => Err(ShellError::type_error(
|
||||
"Integer",
|
||||
value.copy_span(v.type_name()),
|
||||
value.copy_tag(v.type_name()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -279,7 +333,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for String {
|
|||
Value::Primitive(Primitive::String(s)) => Ok(s.clone()),
|
||||
v => Err(ShellError::type_error(
|
||||
"String",
|
||||
value.copy_span(v.type_name()),
|
||||
value.copy_tag(v.type_name()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -293,7 +347,7 @@ impl std::convert::TryFrom<&Tagged<Value>> for Vec<u8> {
|
|||
Value::Primitive(Primitive::Binary(b)) => Ok(b.clone()),
|
||||
v => Err(ShellError::type_error(
|
||||
"Binary",
|
||||
value.copy_span(v.type_name()),
|
||||
value.copy_tag(v.type_name()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -307,7 +361,7 @@ impl<'a> std::convert::TryFrom<&'a Tagged<Value>> for &'a crate::data::Dictionar
|
|||
Value::Row(d) => Ok(d),
|
||||
v => Err(ShellError::type_error(
|
||||
"Dictionary",
|
||||
value.copy_span(v.type_name()),
|
||||
value.copy_tag(v.type_name()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +383,7 @@ impl std::convert::TryFrom<Option<&Tagged<Value>>> for Switch {
|
|||
Value::Primitive(Primitive::Boolean(true)) => Ok(Switch::Present),
|
||||
v => Err(ShellError::type_error(
|
||||
"Boolean",
|
||||
value.copy_span(v.type_name()),
|
||||
value.copy_tag(v.type_name()),
|
||||
)),
|
||||
},
|
||||
}
|
||||
|
@ -641,7 +695,7 @@ impl Tagged<Value> {
|
|||
Value::Primitive(Primitive::Path(path)) => Ok(path.clone()),
|
||||
other => Err(ShellError::type_error(
|
||||
"Path",
|
||||
other.type_name().tagged(self.span()),
|
||||
other.type_name().tagged(self.tag()),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,7 @@ pub const APP_INFO: AppInfo = AppInfo {
|
|||
};
|
||||
|
||||
pub fn config_path() -> Result<PathBuf, ShellError> {
|
||||
let path = app_root(AppDataType::UserConfig, &APP_INFO)
|
||||
.map_err(|err| ShellError::string(&format!("Couldn't open config path:\n{}", err)))?;
|
||||
|
||||
Ok(path)
|
||||
app_path(AppDataType::UserConfig, "config")
|
||||
}
|
||||
|
||||
pub fn default_path() -> Result<PathBuf, ShellError> {
|
||||
|
@ -49,8 +46,19 @@ pub fn default_path_for(file: &Option<PathBuf>) -> Result<PathBuf, ShellError> {
|
|||
Ok(filename.clone())
|
||||
}
|
||||
|
||||
pub fn user_data() -> Result<PathBuf, ShellError> {
|
||||
app_path(AppDataType::UserData, "user data")
|
||||
}
|
||||
|
||||
pub fn app_path(app_data_type: AppDataType, display: &str) -> Result<PathBuf, ShellError> {
|
||||
let path = app_root(app_data_type, &APP_INFO)
|
||||
.map_err(|err| ShellError::string(&format!("Couldn't open {} path:\n{}", display, err)))?;
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
pub fn read(
|
||||
span: impl Into<Span>,
|
||||
tag: impl Into<Tag>,
|
||||
at: &Option<PathBuf>,
|
||||
) -> Result<IndexMap<String, Tagged<Value>>, ShellError> {
|
||||
let filename = default_path()?;
|
||||
|
@ -64,15 +72,15 @@ pub fn read(
|
|||
|
||||
trace!("config file = {}", filename.display());
|
||||
|
||||
let span = span.into();
|
||||
let tag = tag.into();
|
||||
let contents = fs::read_to_string(filename)
|
||||
.map(|v| v.simple_spanned(span))
|
||||
.map(|v| v.tagged(tag))
|
||||
.map_err(|err| ShellError::string(&format!("Couldn't read config file:\n{}", err)))?;
|
||||
|
||||
let parsed: toml::Value = toml::from_str(&contents)
|
||||
.map_err(|err| ShellError::string(&format!("Couldn't parse config file:\n{}", err)))?;
|
||||
|
||||
let value = convert_toml_value_to_nu_value(&parsed, Tag::unknown_origin(span));
|
||||
let value = convert_toml_value_to_nu_value(&parsed, tag);
|
||||
let tag = value.tag();
|
||||
match value.item {
|
||||
Value::Row(Dictionary { entries }) => Ok(entries),
|
||||
|
@ -83,8 +91,8 @@ pub fn read(
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn config(span: impl Into<Span>) -> Result<IndexMap<String, Tagged<Value>>, ShellError> {
|
||||
read(span, &None)
|
||||
pub(crate) fn config(tag: impl Into<Tag>) -> Result<IndexMap<String, Tagged<Value>>, ShellError> {
|
||||
read(tag, &None)
|
||||
}
|
||||
|
||||
pub fn write(
|
||||
|
|
|
@ -169,6 +169,10 @@ impl TaggedDictBuilder {
|
|||
pub fn into_tagged_dict(self) -> Tagged<Dictionary> {
|
||||
Dictionary { entries: self.dict }.tagged(self.tag)
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.dict.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TaggedDictBuilder> for Tagged<Value> {
|
||||
|
|
|
@ -15,8 +15,8 @@ impl From<String> for Value {
|
|||
|
||||
impl<T: Into<Value>> Tagged<T> {
|
||||
pub fn into_tagged_value(self) -> Tagged<Value> {
|
||||
let value_span = self.span();
|
||||
let value_tag = self.tag();
|
||||
let value = self.item.into();
|
||||
value.simple_spanned(value_span)
|
||||
value.tagged(value_tag)
|
||||
}
|
||||
}
|
||||
|
|
200
src/data/meta.rs
200
src/data/meta.rs
|
@ -5,6 +5,7 @@ use derive_new::new;
|
|||
use getset::Getters;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::path::{Path, PathBuf};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
|
@ -13,9 +14,15 @@ pub struct Tagged<T> {
|
|||
pub item: T,
|
||||
}
|
||||
|
||||
impl<T> HasSpan for Tagged<T> {
|
||||
fn span(&self) -> Span {
|
||||
self.tag.span
|
||||
impl<T> HasTag for Tagged<T> {
|
||||
fn tag(&self) -> Tag {
|
||||
self.tag
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Path> for Tagged<PathBuf> {
|
||||
fn as_ref(&self) -> &Path {
|
||||
self.item.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,10 +31,6 @@ pub trait TaggedItem: Sized {
|
|||
Tagged::from_item(self, tag.into())
|
||||
}
|
||||
|
||||
fn simple_spanned(self, span: impl Into<Span>) -> Tagged<Self> {
|
||||
Tagged::from_simple_spanned_item(self, span.into())
|
||||
}
|
||||
|
||||
// For now, this is a temporary facility. In many cases, there are other useful spans that we
|
||||
// could be using, such as the original source spans of JSON or Toml files, but we don't yet
|
||||
// have the infrastructure to make that work.
|
||||
|
@ -36,7 +39,7 @@ pub trait TaggedItem: Sized {
|
|||
self,
|
||||
Tag {
|
||||
span: Span::unknown(),
|
||||
origin: None,
|
||||
origin: uuid::Uuid::nil(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -53,14 +56,8 @@ impl<T> std::ops::Deref for Tagged<T> {
|
|||
}
|
||||
|
||||
impl<T> Tagged<T> {
|
||||
pub fn spanned(self, span: impl Into<Span>) -> Tagged<T> {
|
||||
Tagged::from_item(
|
||||
self.item,
|
||||
Tag {
|
||||
span: span.into(),
|
||||
origin: None,
|
||||
},
|
||||
)
|
||||
pub fn with_tag(self, tag: impl Into<Tag>) -> Tagged<T> {
|
||||
Tagged::from_item(self.item, tag)
|
||||
}
|
||||
|
||||
pub fn from_item(item: T, tag: impl Into<Tag>) -> Tagged<T> {
|
||||
|
@ -70,49 +67,37 @@ impl<T> Tagged<T> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_simple_spanned_item(item: T, span: impl Into<Span>) -> Tagged<T> {
|
||||
Tagged::from_item(
|
||||
item,
|
||||
Tag {
|
||||
span: span.into(),
|
||||
origin: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn map<U>(self, input: impl FnOnce(T) -> U) -> Tagged<U> {
|
||||
let tag = self.tag();
|
||||
|
||||
let mapped = input(self.item);
|
||||
Tagged::from_item(mapped, tag.clone())
|
||||
Tagged::from_item(mapped, tag)
|
||||
}
|
||||
|
||||
pub(crate) fn copy_span<U>(&self, output: U) -> Tagged<U> {
|
||||
let span = self.span();
|
||||
|
||||
Tagged::from_simple_spanned_item(output, span)
|
||||
pub(crate) fn copy_tag<U>(&self, output: U) -> Tagged<U> {
|
||||
Tagged::from_item(output, self.tag())
|
||||
}
|
||||
|
||||
pub fn source(&self, source: &Text) -> Text {
|
||||
Text::from(self.span().slice(source))
|
||||
}
|
||||
|
||||
pub fn span(&self) -> Span {
|
||||
self.tag.span
|
||||
Text::from(self.tag().slice(source))
|
||||
}
|
||||
|
||||
pub fn tag(&self) -> Tag {
|
||||
self.tag
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> Option<uuid::Uuid> {
|
||||
pub fn span(&self) -> Span {
|
||||
self.tag.span
|
||||
}
|
||||
|
||||
pub fn origin(&self) -> uuid::Uuid {
|
||||
self.tag.origin
|
||||
}
|
||||
|
||||
pub fn origin_name(&self, source_map: &SourceMap) -> Option<String> {
|
||||
match self.tag.origin.map(|x| source_map.get(&x)) {
|
||||
Some(Some(SpanSource::File(file))) => Some(file.clone()),
|
||||
Some(Some(SpanSource::Url(url))) => Some(url.clone()),
|
||||
match source_map.get(&self.tag.origin) {
|
||||
Some(SpanSource::File(file)) => Some(file.clone()),
|
||||
Some(SpanSource::Url(url)) => Some(url.clone()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -126,20 +111,14 @@ impl<T> Tagged<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<&Tagged<T>> for Span {
|
||||
fn from(input: &Tagged<T>) -> Span {
|
||||
input.span()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Span> for Span {
|
||||
fn from(input: &Span) -> Span {
|
||||
impl From<&Tag> for Tag {
|
||||
fn from(input: &Tag) -> Tag {
|
||||
*input
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nom5_locate::LocatedSpan<&str>> for Span {
|
||||
fn from(input: nom5_locate::LocatedSpan<&str>) -> Span {
|
||||
impl From<nom_locate::LocatedSpanEx<&str, Uuid>> for Span {
|
||||
fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Span {
|
||||
Span {
|
||||
start: input.offset,
|
||||
end: input.offset + input.fragment.len(),
|
||||
|
@ -147,8 +126,18 @@ impl From<nom5_locate::LocatedSpan<&str>> for Span {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> From<(nom5_locate::LocatedSpan<T>, nom5_locate::LocatedSpan<T>)> for Span {
|
||||
fn from(input: (nom5_locate::LocatedSpan<T>, nom5_locate::LocatedSpan<T>)) -> Span {
|
||||
impl<T>
|
||||
From<(
|
||||
nom_locate::LocatedSpanEx<T, Uuid>,
|
||||
nom_locate::LocatedSpanEx<T, Uuid>,
|
||||
)> for Span
|
||||
{
|
||||
fn from(
|
||||
input: (
|
||||
nom_locate::LocatedSpanEx<T, Uuid>,
|
||||
nom_locate::LocatedSpanEx<T, Uuid>,
|
||||
),
|
||||
) -> Span {
|
||||
Span {
|
||||
start: input.0.offset,
|
||||
end: input.1.offset,
|
||||
|
@ -178,25 +167,62 @@ impl From<&std::ops::Range<usize>> for Span {
|
|||
Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash, Getters,
|
||||
)]
|
||||
pub struct Tag {
|
||||
pub origin: Option<Uuid>,
|
||||
pub origin: Uuid,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl From<Span> for Tag {
|
||||
fn from(span: Span) -> Self {
|
||||
Tag { origin: None, span }
|
||||
Tag {
|
||||
origin: uuid::Uuid::nil(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Span> for Tag {
|
||||
fn from(span: &Span) -> Self {
|
||||
Tag {
|
||||
origin: None,
|
||||
origin: uuid::Uuid::nil(),
|
||||
span: *span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize, usize, Uuid)> for Tag {
|
||||
fn from((start, end, origin): (usize, usize, Uuid)) -> Self {
|
||||
Tag {
|
||||
origin,
|
||||
span: Span { start, end },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<(usize, usize, Option<Uuid>)> for Tag {
|
||||
fn from((start, end, origin): (usize, usize, Option<Uuid>)) -> Self {
|
||||
Tag {
|
||||
origin: if let Some(uuid) = origin {
|
||||
uuid
|
||||
} else {
|
||||
uuid::Uuid::nil()
|
||||
},
|
||||
span: Span { start, end },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<nom_locate::LocatedSpanEx<&str, Uuid>> for Tag {
|
||||
fn from(input: nom_locate::LocatedSpanEx<&str, Uuid>) -> Tag {
|
||||
Tag {
|
||||
origin: input.extra,
|
||||
span: Span {
|
||||
start: input.offset,
|
||||
end: input.offset + input.fragment.len(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Tag> for Span {
|
||||
fn from(tag: Tag) -> Self {
|
||||
tag.span
|
||||
|
@ -211,15 +237,45 @@ impl From<&Tag> for Span {
|
|||
|
||||
impl Tag {
|
||||
pub fn unknown_origin(span: Span) -> Tag {
|
||||
Tag { origin: None, span }
|
||||
Tag {
|
||||
origin: uuid::Uuid::nil(),
|
||||
span,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unknown_span(origin: Uuid) -> Tag {
|
||||
Tag {
|
||||
origin,
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unknown() -> Tag {
|
||||
Tag {
|
||||
origin: None,
|
||||
origin: uuid::Uuid::nil(),
|
||||
span: Span::unknown(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn until(&self, other: impl Into<Tag>) -> Tag {
|
||||
let other = other.into();
|
||||
debug_assert!(
|
||||
self.origin == other.origin,
|
||||
"Can only merge two tags with the same origin"
|
||||
);
|
||||
|
||||
Tag {
|
||||
span: Span {
|
||||
start: self.span.start,
|
||||
end: other.span.end,
|
||||
},
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn slice<'a>(&self, source: &'a str) -> &'a str {
|
||||
self.span.slice(source)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
|
||||
|
@ -284,3 +340,33 @@ impl language_reporting::ReportingSpan for Span {
|
|||
self.end
|
||||
}
|
||||
}
|
||||
|
||||
impl language_reporting::ReportingSpan for Tag {
|
||||
fn with_start(&self, start: usize) -> Self {
|
||||
Tag {
|
||||
span: Span {
|
||||
start,
|
||||
end: self.span.end,
|
||||
},
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
|
||||
fn with_end(&self, end: usize) -> Self {
|
||||
Tag {
|
||||
span: Span {
|
||||
start: self.span.start,
|
||||
end,
|
||||
},
|
||||
origin: self.origin,
|
||||
}
|
||||
}
|
||||
|
||||
fn start(&self) -> usize {
|
||||
self.span.start
|
||||
}
|
||||
|
||||
fn end(&self) -> usize {
|
||||
self.span.end
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,20 +14,22 @@ pub enum Description {
|
|||
|
||||
impl Description {
|
||||
pub fn from(value: Tagged<impl Into<String>>) -> Description {
|
||||
let value_span = value.span();
|
||||
let value_tag = value.tag();
|
||||
|
||||
match value_span {
|
||||
Span { start: 0, end: 0 } => Description::Synthetic(value.item.into()),
|
||||
match value_tag {
|
||||
Tag {
|
||||
span: crate::data::meta::Span { start: 0, end: 0 },
|
||||
..
|
||||
} => Description::Synthetic(value.item.into()),
|
||||
_ => Description::Source(Tagged::from_item(value.item.into(), value_tag)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Description {
|
||||
fn into_label(self) -> Result<Label<Span>, String> {
|
||||
fn into_label(self) -> Result<Label<Tag>, String> {
|
||||
match self {
|
||||
Description::Source(s) => Ok(Label::new_primary(s.span()).with_message(s.item)),
|
||||
Description::Source(s) => Ok(Label::new_primary(s.tag()).with_message(s.item)),
|
||||
Description::Synthetic(s) => Err(s),
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +83,7 @@ impl ShellError {
|
|||
) -> ShellError {
|
||||
ProximateShellError::RangeError {
|
||||
kind: expected.into(),
|
||||
actual_kind: actual.copy_span(format!("{:?}", actual.item)),
|
||||
actual_kind: actual.copy_tag(format!("{:?}", actual.item)),
|
||||
operation,
|
||||
}
|
||||
.start()
|
||||
|
@ -116,9 +118,9 @@ impl ShellError {
|
|||
ProximateShellError::MissingProperty { subpath, expr }.start()
|
||||
}
|
||||
|
||||
pub(crate) fn missing_value(span: Option<Span>, reason: impl Into<String>) -> ShellError {
|
||||
pub(crate) fn missing_value(tag: Option<Tag>, reason: impl Into<String>) -> ShellError {
|
||||
ProximateShellError::MissingValue {
|
||||
span,
|
||||
tag,
|
||||
reason: reason.into(),
|
||||
}
|
||||
.start()
|
||||
|
@ -127,28 +129,31 @@ impl ShellError {
|
|||
pub(crate) fn argument_error(
|
||||
command: impl Into<String>,
|
||||
kind: ArgumentError,
|
||||
span: Span,
|
||||
tag: Tag,
|
||||
) -> ShellError {
|
||||
ProximateShellError::ArgumentError {
|
||||
command: command.into(),
|
||||
error: kind,
|
||||
span,
|
||||
tag,
|
||||
}
|
||||
.start()
|
||||
}
|
||||
|
||||
pub(crate) fn invalid_external_word(span: Span) -> ShellError {
|
||||
pub(crate) fn invalid_external_word(tag: Tag) -> ShellError {
|
||||
ProximateShellError::ArgumentError {
|
||||
command: "Invalid argument to Nu command (did you mean to call an external command?)"
|
||||
.into(),
|
||||
error: ArgumentError::InvalidExternalWord,
|
||||
span,
|
||||
tag,
|
||||
}
|
||||
.start()
|
||||
}
|
||||
|
||||
pub(crate) fn parse_error(
|
||||
error: nom::Err<(nom5_locate::LocatedSpan<&str>, nom::error::ErrorKind)>,
|
||||
error: nom::Err<(
|
||||
nom_locate::LocatedSpanEx<&str, uuid::Uuid>,
|
||||
nom::error::ErrorKind,
|
||||
)>,
|
||||
) -> ShellError {
|
||||
use language_reporting::*;
|
||||
|
||||
|
@ -164,34 +169,34 @@ impl ShellError {
|
|||
}
|
||||
nom::Err::Failure(span) | nom::Err::Error(span) => {
|
||||
let diagnostic = Diagnostic::new(Severity::Error, format!("Parse Error"))
|
||||
.with_label(Label::new_primary(Span::from(span.0)));
|
||||
.with_label(Label::new_primary(Tag::from(span.0)));
|
||||
|
||||
ShellError::diagnostic(diagnostic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn diagnostic(diagnostic: Diagnostic<Span>) -> ShellError {
|
||||
pub(crate) fn diagnostic(diagnostic: Diagnostic<Tag>) -> ShellError {
|
||||
ProximateShellError::Diagnostic(ShellDiagnostic { diagnostic }).start()
|
||||
}
|
||||
|
||||
pub(crate) fn to_diagnostic(self) -> Diagnostic<Span> {
|
||||
pub(crate) fn to_diagnostic(self) -> Diagnostic<Tag> {
|
||||
match self.error {
|
||||
ProximateShellError::String(StringError { title, .. }) => {
|
||||
Diagnostic::new(Severity::Error, title)
|
||||
}
|
||||
ProximateShellError::InvalidCommand { command } => {
|
||||
Diagnostic::new(Severity::Error, "Invalid command")
|
||||
.with_label(Label::new_primary(command.span))
|
||||
.with_label(Label::new_primary(command))
|
||||
}
|
||||
ProximateShellError::MissingValue { span, reason } => {
|
||||
ProximateShellError::MissingValue { tag, reason } => {
|
||||
let mut d = Diagnostic::new(
|
||||
Severity::Bug,
|
||||
format!("Internal Error (missing value) :: {}", reason),
|
||||
);
|
||||
|
||||
if let Some(span) = span {
|
||||
d = d.with_label(Label::new_primary(span));
|
||||
if let Some(tag) = tag {
|
||||
d = d.with_label(Label::new_primary(tag));
|
||||
}
|
||||
|
||||
d
|
||||
|
@ -199,12 +204,12 @@ impl ShellError {
|
|||
ProximateShellError::ArgumentError {
|
||||
command,
|
||||
error,
|
||||
span,
|
||||
tag,
|
||||
} => match error {
|
||||
ArgumentError::InvalidExternalWord => Diagnostic::new(
|
||||
Severity::Error,
|
||||
format!("Invalid bare word for Nu command (did you intend to invoke an external command?)"))
|
||||
.with_label(Label::new_primary(span)),
|
||||
.with_label(Label::new_primary(tag)),
|
||||
ArgumentError::MissingMandatoryFlag(name) => Diagnostic::new(
|
||||
Severity::Error,
|
||||
format!(
|
||||
|
@ -214,7 +219,7 @@ impl ShellError {
|
|||
Color::Black.bold().paint(name)
|
||||
),
|
||||
)
|
||||
.with_label(Label::new_primary(span)),
|
||||
.with_label(Label::new_primary(tag)),
|
||||
ArgumentError::MissingMandatoryPositional(name) => Diagnostic::new(
|
||||
Severity::Error,
|
||||
format!(
|
||||
|
@ -224,7 +229,7 @@ impl ShellError {
|
|||
),
|
||||
)
|
||||
.with_label(
|
||||
Label::new_primary(span).with_message(format!("requires {} parameter", name)),
|
||||
Label::new_primary(tag).with_message(format!("requires {} parameter", name)),
|
||||
),
|
||||
ArgumentError::MissingValueForName(name) => Diagnostic::new(
|
||||
Severity::Error,
|
||||
|
@ -235,17 +240,17 @@ impl ShellError {
|
|||
Color::Black.bold().paint(name)
|
||||
),
|
||||
)
|
||||
.with_label(Label::new_primary(span)),
|
||||
.with_label(Label::new_primary(tag)),
|
||||
},
|
||||
ProximateShellError::TypeError {
|
||||
expected,
|
||||
actual:
|
||||
Tagged {
|
||||
item: Some(actual),
|
||||
tag: Tag { span, .. },
|
||||
tag,
|
||||
},
|
||||
} => Diagnostic::new(Severity::Error, "Type Error").with_label(
|
||||
Label::new_primary(span)
|
||||
Label::new_primary(tag)
|
||||
.with_message(format!("Expected {}, found {}", expected, actual)),
|
||||
),
|
||||
|
||||
|
@ -254,10 +259,10 @@ impl ShellError {
|
|||
actual:
|
||||
Tagged {
|
||||
item: None,
|
||||
tag: Tag { span, .. },
|
||||
tag
|
||||
},
|
||||
} => Diagnostic::new(Severity::Error, "Type Error")
|
||||
.with_label(Label::new_primary(span).with_message(expected)),
|
||||
.with_label(Label::new_primary(tag).with_message(expected)),
|
||||
|
||||
ProximateShellError::RangeError {
|
||||
kind,
|
||||
|
@ -265,10 +270,10 @@ impl ShellError {
|
|||
actual_kind:
|
||||
Tagged {
|
||||
item,
|
||||
tag: Tag { span, .. },
|
||||
tag
|
||||
},
|
||||
} => Diagnostic::new(Severity::Error, "Range Error").with_label(
|
||||
Label::new_primary(span).with_message(format!(
|
||||
Label::new_primary(tag).with_message(format!(
|
||||
"Expected to convert {} to {} while {}, but it was out of range",
|
||||
item,
|
||||
kind.desc(),
|
||||
|
@ -279,11 +284,11 @@ impl ShellError {
|
|||
ProximateShellError::SyntaxError {
|
||||
problem:
|
||||
Tagged {
|
||||
tag: Tag { span, .. },
|
||||
tag,
|
||||
..
|
||||
},
|
||||
} => Diagnostic::new(Severity::Error, "Syntax Error")
|
||||
.with_label(Label::new_primary(span).with_message("Unexpected external command")),
|
||||
.with_label(Label::new_primary(tag).with_message("Unexpected external command")),
|
||||
|
||||
ProximateShellError::MissingProperty { subpath, expr } => {
|
||||
let subpath = subpath.into_label();
|
||||
|
@ -306,8 +311,8 @@ impl ShellError {
|
|||
ProximateShellError::Diagnostic(diag) => diag.diagnostic,
|
||||
ProximateShellError::CoerceError { left, right } => {
|
||||
Diagnostic::new(Severity::Error, "Coercion error")
|
||||
.with_label(Label::new_primary(left.span()).with_message(left.item))
|
||||
.with_label(Label::new_secondary(right.span()).with_message(right.item))
|
||||
.with_label(Label::new_primary(left.tag()).with_message(left.item))
|
||||
.with_label(Label::new_secondary(right.tag()).with_message(right.item))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -315,26 +320,29 @@ impl ShellError {
|
|||
pub fn labeled_error(
|
||||
msg: impl Into<String>,
|
||||
label: impl Into<String>,
|
||||
span: impl Into<Span>,
|
||||
tag: impl Into<Tag>,
|
||||
) -> ShellError {
|
||||
ShellError::diagnostic(
|
||||
Diagnostic::new(Severity::Error, msg.into())
|
||||
.with_label(Label::new_primary(span.into()).with_message(label.into())),
|
||||
.with_label(Label::new_primary(tag.into()).with_message(label.into())),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn labeled_error_with_secondary(
|
||||
msg: impl Into<String>,
|
||||
primary_label: impl Into<String>,
|
||||
primary_span: Span,
|
||||
primary_span: impl Into<Tag>,
|
||||
secondary_label: impl Into<String>,
|
||||
secondary_span: Span,
|
||||
secondary_span: impl Into<Tag>,
|
||||
) -> ShellError {
|
||||
ShellError::diagnostic(
|
||||
Diagnostic::new_error(msg.into())
|
||||
.with_label(Label::new_primary(primary_span).with_message(primary_label.into()))
|
||||
.with_label(
|
||||
Label::new_secondary(secondary_span).with_message(secondary_label.into()),
|
||||
Label::new_primary(primary_span.into()).with_message(primary_label.into()),
|
||||
)
|
||||
.with_label(
|
||||
Label::new_secondary(secondary_span.into())
|
||||
.with_message(secondary_label.into()),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -409,13 +417,13 @@ pub enum ProximateShellError {
|
|||
expr: Description,
|
||||
},
|
||||
MissingValue {
|
||||
span: Option<Span>,
|
||||
tag: Option<Tag>,
|
||||
reason: String,
|
||||
},
|
||||
ArgumentError {
|
||||
command: String,
|
||||
error: ArgumentError,
|
||||
span: Span,
|
||||
tag: Tag,
|
||||
},
|
||||
RangeError {
|
||||
kind: ExpectedRange,
|
||||
|
@ -447,7 +455,7 @@ impl ToDebug for ProximateShellError {
|
|||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct ShellDiagnostic {
|
||||
pub(crate) diagnostic: Diagnostic<Span>,
|
||||
pub(crate) diagnostic: Diagnostic<Tag>,
|
||||
}
|
||||
|
||||
impl PartialEq for ShellDiagnostic {
|
||||
|
|
|
@ -38,13 +38,13 @@ pub(crate) fn evaluate_baseline_expr(
|
|||
source: &Text,
|
||||
) -> Result<Tagged<Value>, ShellError> {
|
||||
match &expr.item {
|
||||
RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_span(literal), source)),
|
||||
RawExpression::Literal(literal) => Ok(evaluate_literal(expr.copy_tag(literal), source)),
|
||||
RawExpression::ExternalWord => Err(ShellError::argument_error(
|
||||
"Invalid external word",
|
||||
ArgumentError::InvalidExternalWord,
|
||||
expr.span(),
|
||||
expr.tag(),
|
||||
)),
|
||||
RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.span())),
|
||||
RawExpression::FilePath(path) => Ok(Value::path(path.clone()).tagged(expr.tag())),
|
||||
RawExpression::Synthetic(hir::Synthetic::String(s)) => {
|
||||
Ok(Value::string(s).tagged_unknown())
|
||||
}
|
||||
|
@ -55,13 +55,10 @@ pub(crate) fn evaluate_baseline_expr(
|
|||
let right = evaluate_baseline_expr(binary.right(), registry, scope, source)?;
|
||||
|
||||
match left.compare(binary.op(), &*right) {
|
||||
Ok(result) => Ok(Tagged::from_simple_spanned_item(
|
||||
Value::boolean(result),
|
||||
expr.span(),
|
||||
)),
|
||||
Ok(result) => Ok(Value::boolean(result).tagged(expr.tag())),
|
||||
Err((left_type, right_type)) => Err(ShellError::coerce_error(
|
||||
binary.left().copy_span(left_type),
|
||||
binary.right().copy_span(right_type),
|
||||
binary.left().copy_tag(left_type),
|
||||
binary.right().copy_tag(right_type),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
@ -73,12 +70,14 @@ pub(crate) fn evaluate_baseline_expr(
|
|||
exprs.push(expr);
|
||||
}
|
||||
|
||||
Ok(Value::Table(exprs).tagged(Tag::unknown_origin(expr.span())))
|
||||
Ok(Value::Table(exprs).tagged(expr.tag()))
|
||||
}
|
||||
RawExpression::Block(block) => {
|
||||
Ok(
|
||||
Value::Block(Block::new(block.clone(), source.clone(), expr.tag()))
|
||||
.tagged(expr.tag()),
|
||||
)
|
||||
}
|
||||
RawExpression::Block(block) => Ok(Tagged::from_simple_spanned_item(
|
||||
Value::Block(Block::new(block.clone(), source.clone(), expr.span())),
|
||||
expr.span(),
|
||||
)),
|
||||
RawExpression::Path(path) => {
|
||||
let value = evaluate_baseline_expr(path.head(), registry, scope, source)?;
|
||||
let mut item = value;
|
||||
|
@ -94,18 +93,12 @@ pub(crate) fn evaluate_baseline_expr(
|
|||
))
|
||||
}
|
||||
Some(next) => {
|
||||
item = Tagged::from_simple_spanned_item(
|
||||
next.clone().item,
|
||||
(expr.span().start, name.span().end),
|
||||
)
|
||||
item = next.clone().item.tagged(expr.tag());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Ok(Tagged::from_simple_spanned_item(
|
||||
item.item().clone(),
|
||||
expr.span(),
|
||||
))
|
||||
Ok(item.item().clone().tagged(expr.tag()))
|
||||
}
|
||||
RawExpression::Boolean(_boolean) => unimplemented!(),
|
||||
}
|
||||
|
@ -115,9 +108,9 @@ fn evaluate_literal(literal: Tagged<&hir::Literal>, source: &Text) -> Tagged<Val
|
|||
let result = match literal.item {
|
||||
hir::Literal::Number(int) => int.into(),
|
||||
hir::Literal::Size(int, unit) => unit.compute(int),
|
||||
hir::Literal::String(span) => Value::string(span.slice(source)),
|
||||
hir::Literal::GlobPattern => Value::pattern(literal.span().slice(source)),
|
||||
hir::Literal::Bare => Value::string(literal.span().slice(source)),
|
||||
hir::Literal::String(tag) => Value::string(tag.slice(source)),
|
||||
hir::Literal::GlobPattern => Value::pattern(literal.tag().slice(source)),
|
||||
hir::Literal::Bare => Value::string(literal.tag().slice(source)),
|
||||
};
|
||||
|
||||
literal.map(|_| result)
|
||||
|
@ -129,12 +122,12 @@ fn evaluate_reference(
|
|||
source: &Text,
|
||||
) -> Result<Tagged<Value>, ShellError> {
|
||||
match name {
|
||||
hir::Variable::It(span) => Ok(scope.it.item.clone().simple_spanned(span)),
|
||||
hir::Variable::Other(span) => Ok(scope
|
||||
hir::Variable::It(tag) => Ok(scope.it.item.clone().tagged(*tag)),
|
||||
hir::Variable::Other(tag) => Ok(scope
|
||||
.vars
|
||||
.get(span.slice(source))
|
||||
.get(tag.slice(source))
|
||||
.map(|v| v.clone())
|
||||
.unwrap_or_else(|| Value::nothing().simple_spanned(span))),
|
||||
.unwrap_or_else(|| Value::nothing().tagged(*tag))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -144,6 +137,6 @@ fn evaluate_external(
|
|||
_source: &Text,
|
||||
) -> Result<Tagged<Value>, ShellError> {
|
||||
Err(ShellError::syntax_error(
|
||||
"Unexpected external command".tagged(external.name()),
|
||||
"Unexpected external command".tagged(*external.name()),
|
||||
))
|
||||
}
|
||||
|
|
|
@ -2,14 +2,12 @@ pub(crate) mod entries;
|
|||
pub(crate) mod generic;
|
||||
pub(crate) mod list;
|
||||
pub(crate) mod table;
|
||||
pub(crate) mod vtable;
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
pub(crate) use entries::EntriesView;
|
||||
|
||||
pub(crate) use table::TableView;
|
||||
pub(crate) use vtable::VTableView;
|
||||
|
||||
pub(crate) trait RenderView {
|
||||
fn render_view(&self, host: &mut dyn Host) -> Result<(), ShellError>;
|
||||
|
|
|
@ -204,7 +204,7 @@ impl RenderView for TableView {
|
|||
|
||||
let mut table = Table::new();
|
||||
|
||||
let table_mode = crate::data::config::config(Span::unknown())?
|
||||
let table_mode = crate::data::config::config(Tag::unknown())?
|
||||
.get("table_mode")
|
||||
.map(|s| match s.as_string().unwrap().as_ref() {
|
||||
"light" => TableMode::Light,
|
||||
|
|
|
@ -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
184
src/fuzzysearch.rs
Normal 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");
|
||||
}
|
|
@ -12,6 +12,7 @@ mod env;
|
|||
mod errors;
|
||||
mod evaluate;
|
||||
mod format;
|
||||
mod fuzzysearch;
|
||||
mod git;
|
||||
mod parser;
|
||||
mod plugin;
|
||||
|
@ -23,7 +24,7 @@ mod utils;
|
|||
pub use crate::commands::command::{CallInfo, ReturnSuccess, ReturnValue};
|
||||
pub use crate::context::{SourceMap, SpanSource};
|
||||
pub use crate::env::host::BasicHost;
|
||||
pub use crate::parser::hir::SyntaxType;
|
||||
pub use crate::parser::hir::SyntaxShape;
|
||||
pub use crate::parser::parse::token_tree_builder::TokenTreeBuilder;
|
||||
pub use crate::plugin::{serve_plugin, Plugin};
|
||||
pub use crate::utils::{AbsoluteFile, AbsolutePath, RelativePath};
|
||||
|
@ -31,7 +32,7 @@ pub use cli::cli;
|
|||
pub use data::base::{Primitive, Value};
|
||||
pub use data::config::{config_path, APP_INFO};
|
||||
pub use data::dict::{Dictionary, TaggedDictBuilder};
|
||||
pub use data::meta::{Span, Tag, Tagged, TaggedItem};
|
||||
pub use data::meta::{Tag, Tagged, TaggedItem};
|
||||
pub use errors::{CoerceInto, ShellError};
|
||||
pub use num_traits::cast::ToPrimitive;
|
||||
pub use parser::parse::text::Text;
|
||||
|
|
|
@ -21,10 +21,10 @@ pub(crate) use parse::unit::Unit;
|
|||
pub(crate) use parse_command::parse_command;
|
||||
pub(crate) use registry::CommandRegistry;
|
||||
|
||||
pub fn parse(input: &str) -> Result<TokenNode, ShellError> {
|
||||
pub fn parse(input: &str, origin: uuid::Uuid) -> Result<TokenNode, ShellError> {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
|
||||
match pipeline(nom_input(input)) {
|
||||
match pipeline(nom_input(input, origin)) {
|
||||
Ok((_rest, val)) => Ok(val),
|
||||
Err(err) => Err(ShellError::parse_error(err)),
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ impl<'de> ConfigDeserializer<'de> {
|
|||
let value: Option<Tagged<Value>> = if name == "rest" {
|
||||
let positional = self.call.args.slice_from(self.position);
|
||||
self.position += positional.len();
|
||||
Some(Value::Table(positional).tagged_unknown()) // TODO: correct span
|
||||
Some(Value::Table(positional).tagged_unknown()) // TODO: correct tag
|
||||
} else {
|
||||
if self.call.args.has(name) {
|
||||
self.call.args.get(name).map(|x| x.clone())
|
||||
|
@ -52,9 +52,7 @@ impl<'de> ConfigDeserializer<'de> {
|
|||
|
||||
self.stack.push(DeserializerItem {
|
||||
key_struct_field: Some((name.to_string(), name)),
|
||||
val: value.unwrap_or_else(|| {
|
||||
Value::nothing().tagged(Tag::unknown_origin(self.call.name_span))
|
||||
}),
|
||||
val: value.unwrap_or_else(|| Value::nothing().tagged(self.call.name_tag)),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -25,7 +25,7 @@ pub(crate) use self::external_command::ExternalCommand;
|
|||
pub(crate) use self::named::NamedArguments;
|
||||
pub(crate) use self::path::Path;
|
||||
|
||||
pub use self::baseline_parse_tokens::SyntaxType;
|
||||
pub use self::baseline_parse_tokens::SyntaxShape;
|
||||
|
||||
pub fn path(head: impl Into<Expression>, tail: Vec<Tagged<impl Into<String>>>) -> Path {
|
||||
Path::new(
|
||||
|
@ -131,72 +131,57 @@ impl RawExpression {
|
|||
pub type Expression = Tagged<RawExpression>;
|
||||
|
||||
impl Expression {
|
||||
pub(crate) fn number(i: impl Into<Number>, span: impl Into<Span>) -> Expression {
|
||||
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Number(i.into())), span)
|
||||
pub(crate) fn number(i: impl Into<Number>, tag: impl Into<Tag>) -> Expression {
|
||||
RawExpression::Literal(Literal::Number(i.into())).tagged(tag.into())
|
||||
}
|
||||
|
||||
pub(crate) fn size(
|
||||
i: impl Into<Number>,
|
||||
unit: impl Into<Unit>,
|
||||
span: impl Into<Span>,
|
||||
tag: impl Into<Tag>,
|
||||
) -> Expression {
|
||||
Tagged::from_simple_spanned_item(
|
||||
RawExpression::Literal(Literal::Size(i.into(), unit.into())),
|
||||
span,
|
||||
)
|
||||
RawExpression::Literal(Literal::Size(i.into(), unit.into())).tagged(tag.into())
|
||||
}
|
||||
|
||||
pub(crate) fn synthetic_string(s: impl Into<String>) -> Expression {
|
||||
RawExpression::Synthetic(Synthetic::String(s.into())).tagged_unknown()
|
||||
}
|
||||
|
||||
pub(crate) fn string(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
|
||||
Tagged::from_simple_spanned_item(
|
||||
RawExpression::Literal(Literal::String(inner.into())),
|
||||
outer.into(),
|
||||
)
|
||||
pub(crate) fn string(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
|
||||
RawExpression::Literal(Literal::String(inner.into())).tagged(outer.into())
|
||||
}
|
||||
|
||||
pub(crate) fn file_path(path: impl Into<PathBuf>, outer: impl Into<Span>) -> Expression {
|
||||
Tagged::from_simple_spanned_item(RawExpression::FilePath(path.into()), outer.into())
|
||||
pub(crate) fn file_path(path: impl Into<PathBuf>, outer: impl Into<Tag>) -> Expression {
|
||||
RawExpression::FilePath(path.into()).tagged(outer)
|
||||
}
|
||||
|
||||
pub(crate) fn bare(span: impl Into<Span>) -> Expression {
|
||||
Tagged::from_simple_spanned_item(RawExpression::Literal(Literal::Bare), span.into())
|
||||
pub(crate) fn bare(tag: impl Into<Tag>) -> Expression {
|
||||
RawExpression::Literal(Literal::Bare).tagged(tag)
|
||||
}
|
||||
|
||||
pub(crate) fn pattern(tag: impl Into<Tag>) -> Expression {
|
||||
RawExpression::Literal(Literal::GlobPattern).tagged(tag.into())
|
||||
}
|
||||
|
||||
pub(crate) fn variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
|
||||
Tagged::from_simple_spanned_item(
|
||||
RawExpression::Variable(Variable::Other(inner.into())),
|
||||
outer.into(),
|
||||
)
|
||||
pub(crate) fn variable(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
|
||||
RawExpression::Variable(Variable::Other(inner.into())).tagged(outer)
|
||||
}
|
||||
|
||||
pub(crate) fn external_command(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
|
||||
Tagged::from_simple_spanned_item(
|
||||
RawExpression::ExternalCommand(ExternalCommand::new(inner.into())),
|
||||
outer.into(),
|
||||
)
|
||||
pub(crate) fn external_command(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
|
||||
RawExpression::ExternalCommand(ExternalCommand::new(inner.into())).tagged(outer)
|
||||
}
|
||||
|
||||
pub(crate) fn it_variable(inner: impl Into<Span>, outer: impl Into<Span>) -> Expression {
|
||||
Tagged::from_simple_spanned_item(
|
||||
RawExpression::Variable(Variable::It(inner.into())),
|
||||
outer.into(),
|
||||
)
|
||||
pub(crate) fn it_variable(inner: impl Into<Tag>, outer: impl Into<Tag>) -> Expression {
|
||||
RawExpression::Variable(Variable::It(inner.into())).tagged(outer)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToDebug for Expression {
|
||||
fn fmt_debug(&self, f: &mut fmt::Formatter, source: &str) -> fmt::Result {
|
||||
match self.item() {
|
||||
RawExpression::Literal(l) => l.tagged(self.span()).fmt_debug(f, source),
|
||||
RawExpression::Literal(l) => l.tagged(self.tag()).fmt_debug(f, source),
|
||||
RawExpression::FilePath(p) => write!(f, "{}", p.display()),
|
||||
RawExpression::ExternalWord => write!(f, "{}", self.span().slice(source)),
|
||||
RawExpression::ExternalWord => write!(f, "{}", self.tag().slice(source)),
|
||||
RawExpression::Synthetic(Synthetic::String(s)) => write!(f, "{:?}", s),
|
||||
RawExpression::Variable(Variable::It(_)) => write!(f, "$it"),
|
||||
RawExpression::Variable(Variable::Other(s)) => write!(f, "${}", s.slice(source)),
|
||||
|
@ -242,7 +227,7 @@ impl From<Tagged<Path>> for Expression {
|
|||
pub enum Literal {
|
||||
Number(Number),
|
||||
Size(Number, Unit),
|
||||
String(Span),
|
||||
String(Tag),
|
||||
GlobPattern,
|
||||
Bare,
|
||||
}
|
||||
|
@ -252,9 +237,9 @@ impl ToDebug for Tagged<&Literal> {
|
|||
match self.item() {
|
||||
Literal::Number(number) => write!(f, "{:?}", *number),
|
||||
Literal::Size(number, unit) => write!(f, "{:?}{:?}", *number, unit),
|
||||
Literal::String(span) => write!(f, "{}", span.slice(source)),
|
||||
Literal::GlobPattern => write!(f, "{}", self.span().slice(source)),
|
||||
Literal::Bare => write!(f, "{}", self.span().slice(source)),
|
||||
Literal::String(tag) => write!(f, "{}", tag.slice(source)),
|
||||
Literal::GlobPattern => write!(f, "{}", self.tag().slice(source)),
|
||||
Literal::Bare => write!(f, "{}", self.tag().slice(source)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -273,6 +258,6 @@ impl Literal {
|
|||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Serialize, Deserialize)]
|
||||
pub enum Variable {
|
||||
It(Span),
|
||||
Other(Span),
|
||||
It(Tag),
|
||||
Other(Tag),
|
||||
}
|
||||
|
|
|
@ -10,19 +10,19 @@ pub fn baseline_parse_single_token(
|
|||
source: &Text,
|
||||
) -> Result<hir::Expression, ShellError> {
|
||||
Ok(match *token.item() {
|
||||
RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()),
|
||||
RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()),
|
||||
RawToken::Size(int, unit) => {
|
||||
hir::Expression::size(int.to_number(source), unit, token.span())
|
||||
hir::Expression::size(int.to_number(source), unit, token.tag())
|
||||
}
|
||||
RawToken::String(span) => hir::Expression::string(span, token.span()),
|
||||
RawToken::Variable(span) if span.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(span, token.span())
|
||||
RawToken::String(tag) => hir::Expression::string(tag, token.tag()),
|
||||
RawToken::Variable(tag) if tag.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(tag, token.tag())
|
||||
}
|
||||
RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
|
||||
RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()),
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())),
|
||||
RawToken::GlobPattern => hir::Expression::pattern(token.span()),
|
||||
RawToken::Bare => hir::Expression::bare(token.span()),
|
||||
RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()),
|
||||
RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()),
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())),
|
||||
RawToken::GlobPattern => hir::Expression::pattern(token.tag()),
|
||||
RawToken::Bare => hir::Expression::bare(token.tag()),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -31,24 +31,24 @@ pub fn baseline_parse_token_as_number(
|
|||
source: &Text,
|
||||
) -> Result<hir::Expression, ShellError> {
|
||||
Ok(match *token.item() {
|
||||
RawToken::Variable(span) if span.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(span, token.span())
|
||||
RawToken::Variable(tag) if tag.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(tag, token.tag())
|
||||
}
|
||||
RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()),
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())),
|
||||
RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
|
||||
RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.span()),
|
||||
RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()),
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())),
|
||||
RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()),
|
||||
RawToken::Number(number) => hir::Expression::number(number.to_number(source), token.tag()),
|
||||
RawToken::Size(number, unit) => {
|
||||
hir::Expression::size(number.to_number(source), unit, token.span())
|
||||
hir::Expression::size(number.to_number(source), unit, token.tag())
|
||||
}
|
||||
RawToken::Bare => hir::Expression::bare(token.span()),
|
||||
RawToken::Bare => hir::Expression::bare(token.tag()),
|
||||
RawToken::GlobPattern => {
|
||||
return Err(ShellError::type_error(
|
||||
"Number",
|
||||
"glob pattern".to_string().tagged(token.tag()),
|
||||
))
|
||||
}
|
||||
RawToken::String(span) => hir::Expression::string(span, token.span()),
|
||||
RawToken::String(tag) => hir::Expression::string(tag, token.tag()),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -57,22 +57,22 @@ pub fn baseline_parse_token_as_string(
|
|||
source: &Text,
|
||||
) -> Result<hir::Expression, ShellError> {
|
||||
Ok(match *token.item() {
|
||||
RawToken::Variable(span) if span.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(span, token.span())
|
||||
RawToken::Variable(tag) if tag.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(tag, token.tag())
|
||||
}
|
||||
RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()),
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())),
|
||||
RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
|
||||
RawToken::Number(_) => hir::Expression::bare(token.span()),
|
||||
RawToken::Size(_, _) => hir::Expression::bare(token.span()),
|
||||
RawToken::Bare => hir::Expression::bare(token.span()),
|
||||
RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()),
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())),
|
||||
RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()),
|
||||
RawToken::Number(_) => hir::Expression::bare(token.tag()),
|
||||
RawToken::Size(_, _) => hir::Expression::bare(token.tag()),
|
||||
RawToken::Bare => hir::Expression::bare(token.tag()),
|
||||
RawToken::GlobPattern => {
|
||||
return Err(ShellError::type_error(
|
||||
"String",
|
||||
"glob pattern".tagged(token.tag()),
|
||||
))
|
||||
}
|
||||
RawToken::String(span) => hir::Expression::string(span, token.span()),
|
||||
RawToken::String(tag) => hir::Expression::string(tag, token.tag()),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -82,26 +82,25 @@ pub fn baseline_parse_token_as_path(
|
|||
source: &Text,
|
||||
) -> Result<hir::Expression, ShellError> {
|
||||
Ok(match *token.item() {
|
||||
RawToken::Variable(span) if span.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(span, token.span())
|
||||
RawToken::Variable(tag) if tag.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(tag, token.tag())
|
||||
}
|
||||
RawToken::ExternalCommand(tag) => hir::Expression::external_command(tag, token.tag()),
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())),
|
||||
RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()),
|
||||
RawToken::Number(_) => hir::Expression::bare(token.tag()),
|
||||
RawToken::Size(_, _) => hir::Expression::bare(token.tag()),
|
||||
RawToken::Bare => {
|
||||
hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag())
|
||||
}
|
||||
RawToken::ExternalCommand(span) => hir::Expression::external_command(span, token.span()),
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())),
|
||||
RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
|
||||
RawToken::Number(_) => hir::Expression::bare(token.span()),
|
||||
RawToken::Size(_, _) => hir::Expression::bare(token.span()),
|
||||
RawToken::Bare => hir::Expression::file_path(
|
||||
expand_path(token.span().slice(source), context),
|
||||
token.span(),
|
||||
),
|
||||
RawToken::GlobPattern => {
|
||||
return Err(ShellError::type_error(
|
||||
"Path",
|
||||
"glob pattern".tagged(token.tag()),
|
||||
))
|
||||
}
|
||||
RawToken::String(span) => {
|
||||
hir::Expression::file_path(expand_path(span.slice(source), context), token.span())
|
||||
RawToken::String(tag) => {
|
||||
hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -112,25 +111,24 @@ pub fn baseline_parse_token_as_pattern(
|
|||
source: &Text,
|
||||
) -> Result<hir::Expression, ShellError> {
|
||||
Ok(match *token.item() {
|
||||
RawToken::Variable(span) if span.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(span, token.span())
|
||||
RawToken::Variable(tag) if tag.slice(source) == "it" => {
|
||||
hir::Expression::it_variable(tag, token.tag())
|
||||
}
|
||||
RawToken::ExternalCommand(_) => {
|
||||
return Err(ShellError::syntax_error(
|
||||
"Invalid external command".to_string().tagged(token.tag()),
|
||||
))
|
||||
}
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.span())),
|
||||
RawToken::Variable(span) => hir::Expression::variable(span, token.span()),
|
||||
RawToken::Number(_) => hir::Expression::bare(token.span()),
|
||||
RawToken::Size(_, _) => hir::Expression::bare(token.span()),
|
||||
RawToken::GlobPattern => hir::Expression::pattern(token.span()),
|
||||
RawToken::Bare => hir::Expression::file_path(
|
||||
expand_path(token.span().slice(source), context),
|
||||
token.span(),
|
||||
),
|
||||
RawToken::String(span) => {
|
||||
hir::Expression::file_path(expand_path(span.slice(source), context), token.span())
|
||||
RawToken::ExternalWord => return Err(ShellError::invalid_external_word(token.tag())),
|
||||
RawToken::Variable(tag) => hir::Expression::variable(tag, token.tag()),
|
||||
RawToken::Number(_) => hir::Expression::bare(token.tag()),
|
||||
RawToken::Size(_, _) => hir::Expression::bare(token.tag()),
|
||||
RawToken::GlobPattern => hir::Expression::pattern(token.tag()),
|
||||
RawToken::Bare => {
|
||||
hir::Expression::file_path(expand_path(token.tag().slice(source), context), token.tag())
|
||||
}
|
||||
RawToken::String(tag) => {
|
||||
hir::Expression::file_path(expand_path(tag.slice(source), context), token.tag())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::parser::{
|
|||
},
|
||||
DelimitedNode, Delimiter, PathNode, RawToken, TokenNode,
|
||||
};
|
||||
use crate::{Span, Tag, Tagged, TaggedItem, Text};
|
||||
use crate::{Tag, Tagged, TaggedItem, Text};
|
||||
use derive_new::new;
|
||||
use log::trace;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -17,7 +17,7 @@ pub fn baseline_parse_tokens(
|
|||
token_nodes: &mut TokensIterator<'_>,
|
||||
context: &Context,
|
||||
source: &Text,
|
||||
syntax_type: SyntaxType,
|
||||
syntax_type: SyntaxShape,
|
||||
) -> Result<Vec<hir::Expression>, ShellError> {
|
||||
let mut exprs: Vec<hir::Expression> = vec![];
|
||||
|
||||
|
@ -34,7 +34,7 @@ pub fn baseline_parse_tokens(
|
|||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum SyntaxType {
|
||||
pub enum SyntaxShape {
|
||||
Any,
|
||||
List,
|
||||
Literal,
|
||||
|
@ -49,21 +49,21 @@ pub enum SyntaxType {
|
|||
Boolean,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SyntaxType {
|
||||
impl std::fmt::Display for SyntaxShape {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
SyntaxType::Any => write!(f, "Any"),
|
||||
SyntaxType::List => write!(f, "List"),
|
||||
SyntaxType::Literal => write!(f, "Literal"),
|
||||
SyntaxType::String => write!(f, "String"),
|
||||
SyntaxType::Member => write!(f, "Member"),
|
||||
SyntaxType::Variable => write!(f, "Variable"),
|
||||
SyntaxType::Number => write!(f, "Number"),
|
||||
SyntaxType::Path => write!(f, "Path"),
|
||||
SyntaxType::Pattern => write!(f, "Pattern"),
|
||||
SyntaxType::Binary => write!(f, "Binary"),
|
||||
SyntaxType::Block => write!(f, "Block"),
|
||||
SyntaxType::Boolean => write!(f, "Boolean"),
|
||||
SyntaxShape::Any => write!(f, "Any"),
|
||||
SyntaxShape::List => write!(f, "List"),
|
||||
SyntaxShape::Literal => write!(f, "Literal"),
|
||||
SyntaxShape::String => write!(f, "String"),
|
||||
SyntaxShape::Member => write!(f, "Member"),
|
||||
SyntaxShape::Variable => write!(f, "Variable"),
|
||||
SyntaxShape::Number => write!(f, "Number"),
|
||||
SyntaxShape::Path => write!(f, "Path"),
|
||||
SyntaxShape::Pattern => write!(f, "Pattern"),
|
||||
SyntaxShape::Binary => write!(f, "Binary"),
|
||||
SyntaxShape::Block => write!(f, "Block"),
|
||||
SyntaxShape::Boolean => write!(f, "Boolean"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ pub fn baseline_parse_next_expr(
|
|||
tokens: &mut TokensIterator,
|
||||
context: &Context,
|
||||
source: &Text,
|
||||
syntax_type: SyntaxType,
|
||||
syntax_type: SyntaxShape,
|
||||
) -> Result<hir::Expression, ShellError> {
|
||||
let next = tokens
|
||||
.next()
|
||||
|
@ -81,69 +81,69 @@ pub fn baseline_parse_next_expr(
|
|||
trace!(target: "nu::parser::parse_one_expr", "syntax_type={:?}, token={:?}", syntax_type, next);
|
||||
|
||||
match (syntax_type, next) {
|
||||
(SyntaxType::Path, TokenNode::Token(token)) => {
|
||||
(SyntaxShape::Path, TokenNode::Token(token)) => {
|
||||
return baseline_parse_token_as_path(token, context, source)
|
||||
}
|
||||
|
||||
(SyntaxType::Path, token) => {
|
||||
(SyntaxShape::Path, token) => {
|
||||
return Err(ShellError::type_error(
|
||||
"Path",
|
||||
token.type_name().simple_spanned(token.span()),
|
||||
token.type_name().tagged(token.tag()),
|
||||
))
|
||||
}
|
||||
|
||||
(SyntaxType::Pattern, TokenNode::Token(token)) => {
|
||||
(SyntaxShape::Pattern, TokenNode::Token(token)) => {
|
||||
return baseline_parse_token_as_pattern(token, context, source)
|
||||
}
|
||||
|
||||
(SyntaxType::Pattern, token) => {
|
||||
(SyntaxShape::Pattern, token) => {
|
||||
return Err(ShellError::type_error(
|
||||
"Path",
|
||||
token.type_name().simple_spanned(token.span()),
|
||||
token.type_name().tagged(token.tag()),
|
||||
))
|
||||
}
|
||||
|
||||
(SyntaxType::String, TokenNode::Token(token)) => {
|
||||
(SyntaxShape::String, TokenNode::Token(token)) => {
|
||||
return baseline_parse_token_as_string(token, source);
|
||||
}
|
||||
|
||||
(SyntaxType::String, token) => {
|
||||
(SyntaxShape::String, token) => {
|
||||
return Err(ShellError::type_error(
|
||||
"String",
|
||||
token.type_name().simple_spanned(token.span()),
|
||||
token.type_name().tagged(token.tag()),
|
||||
))
|
||||
}
|
||||
|
||||
(SyntaxType::Number, TokenNode::Token(token)) => {
|
||||
(SyntaxShape::Number, TokenNode::Token(token)) => {
|
||||
return Ok(baseline_parse_token_as_number(token, source)?);
|
||||
}
|
||||
|
||||
(SyntaxType::Number, token) => {
|
||||
(SyntaxShape::Number, token) => {
|
||||
return Err(ShellError::type_error(
|
||||
"Numeric",
|
||||
token.type_name().simple_spanned(token.span()),
|
||||
token.type_name().tagged(token.tag()),
|
||||
))
|
||||
}
|
||||
|
||||
// TODO: More legit member processing
|
||||
(SyntaxType::Member, TokenNode::Token(token)) => {
|
||||
(SyntaxShape::Member, TokenNode::Token(token)) => {
|
||||
return baseline_parse_token_as_string(token, source);
|
||||
}
|
||||
|
||||
(SyntaxType::Member, token) => {
|
||||
(SyntaxShape::Member, token) => {
|
||||
return Err(ShellError::type_error(
|
||||
"member",
|
||||
token.type_name().simple_spanned(token.span()),
|
||||
token.type_name().tagged(token.tag()),
|
||||
))
|
||||
}
|
||||
|
||||
(SyntaxType::Any, _) => {}
|
||||
(SyntaxType::List, _) => {}
|
||||
(SyntaxType::Literal, _) => {}
|
||||
(SyntaxType::Variable, _) => {}
|
||||
(SyntaxType::Binary, _) => {}
|
||||
(SyntaxType::Block, _) => {}
|
||||
(SyntaxType::Boolean, _) => {}
|
||||
(SyntaxShape::Any, _) => {}
|
||||
(SyntaxShape::List, _) => {}
|
||||
(SyntaxShape::Literal, _) => {}
|
||||
(SyntaxShape::Variable, _) => {}
|
||||
(SyntaxShape::Binary, _) => {}
|
||||
(SyntaxShape::Block, _) => {}
|
||||
(SyntaxShape::Boolean, _) => {}
|
||||
};
|
||||
|
||||
let first = baseline_parse_semantic_token(next, context, source)?;
|
||||
|
@ -162,7 +162,7 @@ pub fn baseline_parse_next_expr(
|
|||
return Err(ShellError::labeled_error(
|
||||
"Expected something after an operator",
|
||||
"operator",
|
||||
op.span(),
|
||||
op.tag(),
|
||||
))
|
||||
}
|
||||
Some(token) => baseline_parse_semantic_token(token, context, source)?,
|
||||
|
@ -171,75 +171,66 @@ pub fn baseline_parse_next_expr(
|
|||
// We definitely have a binary expression here -- let's see if we should coerce it into a block
|
||||
|
||||
match syntax_type {
|
||||
SyntaxType::Any => {
|
||||
let span = (first.span().start, second.span().end);
|
||||
SyntaxShape::Any => {
|
||||
let tag = first.tag().until(second.tag());
|
||||
let binary = hir::Binary::new(first, op, second);
|
||||
let binary = hir::RawExpression::Binary(Box::new(binary));
|
||||
let binary = Tagged::from_simple_spanned_item(binary, span);
|
||||
let binary = binary.tagged(tag);
|
||||
|
||||
Ok(binary)
|
||||
}
|
||||
|
||||
SyntaxType::Block => {
|
||||
let span = (first.span().start, second.span().end);
|
||||
SyntaxShape::Block => {
|
||||
let tag = first.tag().until(second.tag());
|
||||
|
||||
let path: Tagged<hir::RawExpression> = match first {
|
||||
Tagged {
|
||||
item: hir::RawExpression::Literal(hir::Literal::Bare),
|
||||
tag: Tag { span, .. },
|
||||
tag,
|
||||
} => {
|
||||
let string =
|
||||
Tagged::from_simple_spanned_item(span.slice(source).to_string(), span);
|
||||
let string = tag.slice(source).to_string().tagged(tag);
|
||||
let path = hir::Path::new(
|
||||
Tagged::from_simple_spanned_item(
|
||||
// TODO: Deal with synthetic nodes that have no representation at all in source
|
||||
hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))),
|
||||
(0, 0),
|
||||
),
|
||||
// TODO: Deal with synthetic nodes that have no representation at all in source
|
||||
hir::RawExpression::Variable(hir::Variable::It(Tag::unknown()))
|
||||
.tagged(Tag::unknown()),
|
||||
vec![string],
|
||||
);
|
||||
let path = hir::RawExpression::Path(Box::new(path));
|
||||
Tagged::from_simple_spanned_item(path, first.span())
|
||||
path.tagged(first.tag())
|
||||
}
|
||||
Tagged {
|
||||
item: hir::RawExpression::Literal(hir::Literal::String(inner)),
|
||||
tag: Tag { span, .. },
|
||||
tag,
|
||||
} => {
|
||||
let string =
|
||||
Tagged::from_simple_spanned_item(inner.slice(source).to_string(), span);
|
||||
let string = inner.slice(source).to_string().tagged(tag);
|
||||
let path = hir::Path::new(
|
||||
Tagged::from_simple_spanned_item(
|
||||
// TODO: Deal with synthetic nodes that have no representation at all in source
|
||||
hir::RawExpression::Variable(hir::Variable::It(Span::from((0, 0)))),
|
||||
(0, 0),
|
||||
),
|
||||
// TODO: Deal with synthetic nodes that have no representation at all in source
|
||||
hir::RawExpression::Variable(hir::Variable::It(Tag::unknown()))
|
||||
.tagged_unknown(),
|
||||
vec![string],
|
||||
);
|
||||
let path = hir::RawExpression::Path(Box::new(path));
|
||||
Tagged::from_simple_spanned_item(path, first.span())
|
||||
path.tagged(first.tag())
|
||||
}
|
||||
Tagged {
|
||||
item: hir::RawExpression::Variable(..),
|
||||
..
|
||||
} => first,
|
||||
Tagged {
|
||||
tag: Tag { span, .. },
|
||||
item,
|
||||
} => {
|
||||
Tagged { tag, item } => {
|
||||
return Err(ShellError::labeled_error(
|
||||
"The first part of an un-braced block must be a column name",
|
||||
item.type_name(),
|
||||
span,
|
||||
tag,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
let binary = hir::Binary::new(path, op, second);
|
||||
let binary = hir::RawExpression::Binary(Box::new(binary));
|
||||
let binary = Tagged::from_simple_spanned_item(binary, span);
|
||||
let binary = binary.tagged(tag);
|
||||
|
||||
let block = hir::RawExpression::Block(vec![binary]);
|
||||
let block = Tagged::from_simple_spanned_item(block, span);
|
||||
let block = block.tagged(tag);
|
||||
|
||||
Ok(block)
|
||||
}
|
||||
|
@ -265,11 +256,11 @@ pub fn baseline_parse_semantic_token(
|
|||
"Unexpected operator".tagged(op.tag),
|
||||
)),
|
||||
TokenNode::Flag(flag) => Err(ShellError::syntax_error("Unexpected flag".tagged(flag.tag))),
|
||||
TokenNode::Member(span) => Err(ShellError::syntax_error(
|
||||
"BUG: Top-level member".tagged(span),
|
||||
TokenNode::Member(tag) => Err(ShellError::syntax_error(
|
||||
"BUG: Top-level member".tagged(*tag),
|
||||
)),
|
||||
TokenNode::Whitespace(span) => Err(ShellError::syntax_error(
|
||||
"BUG: Whitespace found during parse".tagged(span),
|
||||
TokenNode::Whitespace(tag) => Err(ShellError::syntax_error(
|
||||
"BUG: Whitespace found during parse".tagged(*tag),
|
||||
)),
|
||||
TokenNode::Error(error) => Err(*error.item.clone()),
|
||||
TokenNode::Path(path) => baseline_parse_path(path, context, source),
|
||||
|
@ -288,11 +279,11 @@ pub fn baseline_parse_delimited(
|
|||
&mut TokensIterator::new(children),
|
||||
context,
|
||||
source,
|
||||
SyntaxType::Any,
|
||||
SyntaxShape::Any,
|
||||
)?;
|
||||
|
||||
let expr = hir::RawExpression::Block(exprs);
|
||||
Ok(Tagged::from_simple_spanned_item(expr, token.span()))
|
||||
Ok(expr.tagged(token.tag()))
|
||||
}
|
||||
Delimiter::Paren => unimplemented!(),
|
||||
Delimiter::Square => {
|
||||
|
@ -301,11 +292,11 @@ pub fn baseline_parse_delimited(
|
|||
&mut TokensIterator::new(children),
|
||||
context,
|
||||
source,
|
||||
SyntaxType::Any,
|
||||
SyntaxShape::Any,
|
||||
)?;
|
||||
|
||||
let expr = hir::RawExpression::List(exprs);
|
||||
Ok(expr.tagged(Tag::unknown_origin(token.span())))
|
||||
Ok(expr.tagged(token.tag()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -322,8 +313,8 @@ pub fn baseline_parse_path(
|
|||
for part in token.tail() {
|
||||
let string = match part {
|
||||
TokenNode::Token(token) => match token.item() {
|
||||
RawToken::Bare => token.span().slice(source),
|
||||
RawToken::String(span) => span.slice(source),
|
||||
RawToken::Bare => token.tag().slice(source),
|
||||
RawToken::String(tag) => tag.slice(source),
|
||||
RawToken::Number(_)
|
||||
| RawToken::Size(..)
|
||||
| RawToken::Variable(_)
|
||||
|
@ -332,26 +323,26 @@ pub fn baseline_parse_path(
|
|||
| RawToken::ExternalWord => {
|
||||
return Err(ShellError::type_error(
|
||||
"String",
|
||||
token.type_name().simple_spanned(part),
|
||||
token.type_name().tagged(part.tag()),
|
||||
))
|
||||
}
|
||||
},
|
||||
|
||||
TokenNode::Member(span) => span.slice(source),
|
||||
TokenNode::Member(tag) => tag.slice(source),
|
||||
|
||||
// TODO: Make this impossible
|
||||
other => {
|
||||
return Err(ShellError::syntax_error(
|
||||
format!("{} in path", other.type_name()).tagged(other.span()),
|
||||
format!("{} in path", other.type_name()).tagged(other.tag()),
|
||||
))
|
||||
}
|
||||
}
|
||||
.to_string();
|
||||
|
||||
tail.push(string.simple_spanned(part));
|
||||
tail.push(string.tagged(part.tag()));
|
||||
}
|
||||
|
||||
Ok(hir::path(head, tail).simple_spanned(token).into())
|
||||
Ok(hir::path(head, tail).tagged(token.tag()).into())
|
||||
}
|
||||
|
||||
#[derive(Debug, new)]
|
||||
|
|
|
@ -9,7 +9,7 @@ use std::fmt;
|
|||
)]
|
||||
#[get = "pub(crate)"]
|
||||
pub struct ExternalCommand {
|
||||
name: Span,
|
||||
name: Tag,
|
||||
}
|
||||
|
||||
impl ToDebug for ExternalCommand {
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::parser::hir::Expression;
|
||||
use crate::parser::Flag;
|
||||
use crate::prelude::*;
|
||||
use crate::Span;
|
||||
use derive_new::new;
|
||||
use indexmap::IndexMap;
|
||||
use log::trace;
|
||||
|
@ -11,7 +10,7 @@ use std::fmt;
|
|||
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum NamedValue {
|
||||
AbsentSwitch,
|
||||
PresentSwitch(Span),
|
||||
PresentSwitch(Tag),
|
||||
AbsentValue,
|
||||
Value(Expression),
|
||||
}
|
||||
|
@ -27,7 +26,7 @@ impl ToDebug for NamedArguments {
|
|||
for (name, value) in &self.named {
|
||||
match value {
|
||||
NamedValue::AbsentSwitch => continue,
|
||||
NamedValue::PresentSwitch(span) => write!(f, " --{}", span.slice(source))?,
|
||||
NamedValue::PresentSwitch(tag) => write!(f, " --{}", tag.slice(source))?,
|
||||
NamedValue::AbsentValue => continue,
|
||||
NamedValue::Value(expr) => write!(f, " --{} {}", name, expr.debug(source))?,
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::Span;
|
||||
use crate::Tag;
|
||||
use derive_new::new;
|
||||
use language_reporting::{FileName, Location};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(new, Debug, Clone)]
|
||||
pub struct Files {
|
||||
|
@ -8,26 +9,30 @@ pub struct Files {
|
|||
}
|
||||
|
||||
impl language_reporting::ReportingFiles for Files {
|
||||
type Span = Span;
|
||||
type FileId = usize;
|
||||
type Span = Tag;
|
||||
type FileId = Uuid;
|
||||
|
||||
fn byte_span(
|
||||
&self,
|
||||
_file: Self::FileId,
|
||||
file: Self::FileId,
|
||||
from_index: usize,
|
||||
to_index: usize,
|
||||
) -> Option<Self::Span> {
|
||||
Some(Span::from((from_index, to_index)))
|
||||
Some(Tag::from((from_index, to_index, file)))
|
||||
}
|
||||
fn file_id(&self, _span: Self::Span) -> Self::FileId {
|
||||
0
|
||||
|
||||
fn file_id(&self, tag: Self::Span) -> Self::FileId {
|
||||
tag.origin
|
||||
}
|
||||
|
||||
fn file_name(&self, _file: Self::FileId) -> FileName {
|
||||
FileName::Verbatim(format!("shell"))
|
||||
}
|
||||
|
||||
fn byte_index(&self, _file: Self::FileId, _line: usize, _column: usize) -> Option<usize> {
|
||||
unimplemented!("byte_index")
|
||||
}
|
||||
|
||||
fn location(&self, _file: Self::FileId, byte_index: usize) -> Option<Location> {
|
||||
let source = &self.snippet;
|
||||
let mut seen_lines = 0;
|
||||
|
@ -51,14 +56,15 @@ impl language_reporting::ReportingFiles for Files {
|
|||
None
|
||||
}
|
||||
}
|
||||
fn line_span(&self, _file: Self::FileId, lineno: usize) -> Option<Self::Span> {
|
||||
|
||||
fn line_span(&self, file: Self::FileId, lineno: usize) -> Option<Self::Span> {
|
||||
let source = &self.snippet;
|
||||
let mut seen_lines = 0;
|
||||
let mut seen_bytes = 0;
|
||||
|
||||
for (pos, _) in source.match_indices('\n') {
|
||||
if seen_lines == lineno {
|
||||
return Some(Span::from((seen_bytes, pos)));
|
||||
return Some(Tag::from((seen_bytes, pos, file)));
|
||||
} else {
|
||||
seen_lines += 1;
|
||||
seen_bytes = pos + 1;
|
||||
|
@ -66,17 +72,18 @@ impl language_reporting::ReportingFiles for Files {
|
|||
}
|
||||
|
||||
if seen_lines == 0 {
|
||||
Some(Span::from((0, self.snippet.len() - 1)))
|
||||
Some(Tag::from((0, self.snippet.len() - 1, file)))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
fn source(&self, span: Self::Span) -> Option<String> {
|
||||
if span.start > span.end {
|
||||
|
||||
fn source(&self, tag: Self::Span) -> Option<String> {
|
||||
if tag.span.start > tag.span.end {
|
||||
return None;
|
||||
} else if span.end >= self.snippet.len() {
|
||||
} else if tag.span.end >= self.snippet.len() {
|
||||
return None;
|
||||
}
|
||||
Some(self.snippet[span.start..span.end].to_string())
|
||||
Some(tag.slice(&self.snippet).to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::Span;
|
||||
use crate::Tag;
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -13,5 +13,5 @@ pub enum FlagKind {
|
|||
#[get = "pub(crate)"]
|
||||
pub struct Flag {
|
||||
kind: FlagKind,
|
||||
name: Span,
|
||||
name: Tag,
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use crate::parser::parse::{
|
|||
tokens::*, unit::*,
|
||||
};
|
||||
use crate::prelude::*;
|
||||
use crate::{Span, Tagged};
|
||||
use crate::{Tag, Tagged};
|
||||
use nom;
|
||||
use nom::branch::*;
|
||||
use nom::bytes::complete::*;
|
||||
|
@ -18,15 +18,16 @@ use log::trace;
|
|||
use nom::dbg;
|
||||
use nom::*;
|
||||
use nom::{AsBytes, FindSubstring, IResult, InputLength, InputTake, Slice};
|
||||
use nom5_locate::{position, LocatedSpan};
|
||||
use nom_locate::{position, LocatedSpanEx};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::fmt::Debug;
|
||||
use std::str::FromStr;
|
||||
use uuid::Uuid;
|
||||
|
||||
pub type NomSpan<'a> = LocatedSpan<&'a str>;
|
||||
pub type NomSpan<'a> = LocatedSpanEx<&'a str, Uuid>;
|
||||
|
||||
pub fn nom_input(s: &str) -> NomSpan<'_> {
|
||||
LocatedSpan::new(s)
|
||||
pub fn nom_input(s: &str, origin: Uuid) -> NomSpan<'_> {
|
||||
LocatedSpanEx::new_extra(s, origin)
|
||||
}
|
||||
|
||||
macro_rules! operator {
|
||||
|
@ -38,7 +39,7 @@ macro_rules! operator {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_op(tag.fragment, (start, end)),
|
||||
TokenTreeBuilder::tagged_op(tag.fragment, (start, end, input.extra)),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
@ -159,14 +160,14 @@ pub fn raw_number(input: NomSpan) -> IResult<NomSpan, Tagged<RawNumber>> {
|
|||
Ok((input, dot)) => input,
|
||||
|
||||
// it's just an integer
|
||||
Err(_) => return Ok((input, RawNumber::int((start, input.offset)))),
|
||||
Err(_) => return Ok((input, RawNumber::int((start, input.offset, input.extra)))),
|
||||
};
|
||||
|
||||
let (input, tail) = digit1(input)?;
|
||||
|
||||
let end = input.offset;
|
||||
|
||||
Ok((input, RawNumber::decimal((start, end))))
|
||||
Ok((input, RawNumber::decimal((start, end, input.extra))))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -189,7 +190,7 @@ pub fn dq_string(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
let end = input.offset;
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_string((start1, end1), (start, end)),
|
||||
TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -206,7 +207,7 @@ pub fn sq_string(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_string((start1, end1), (start, end)),
|
||||
TokenTreeBuilder::tagged_string((start1, end1, input.extra), (start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -226,7 +227,7 @@ pub fn external(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_external(bare, (start, end)),
|
||||
TokenTreeBuilder::tagged_external(bare, (start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -250,7 +251,10 @@ pub fn pattern(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
let end = input.offset;
|
||||
|
||||
Ok((input, TokenTreeBuilder::spanned_pattern((start, end))))
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::tagged_pattern((start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -263,7 +267,7 @@ pub fn bare(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
let next_char = &input.fragment.chars().nth(0);
|
||||
|
||||
if let Some(next_char) = next_char {
|
||||
if is_external_word_char(*next_char) || *next_char == '*' {
|
||||
if is_external_word_char(*next_char) || is_glob_specific_char(*next_char) {
|
||||
return Err(nom::Err::Error(nom::error::make_error(
|
||||
input,
|
||||
nom::error::ErrorKind::TakeWhile1,
|
||||
|
@ -273,7 +277,10 @@ pub fn bare(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
let end = input.offset;
|
||||
|
||||
Ok((input, TokenTreeBuilder::spanned_bare((start, end))))
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::tagged_bare((start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -283,7 +290,10 @@ pub fn external_word(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
let (input, _) = take_while1(is_external_word_char)(input)?;
|
||||
let end = input.offset;
|
||||
|
||||
Ok((input, TokenTreeBuilder::spanned_external_word((start, end))))
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::tagged_external_word((start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -296,7 +306,7 @@ pub fn var(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_var(bare.span(), (start, end)),
|
||||
TokenTreeBuilder::tagged_var(bare.tag(), (start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -309,7 +319,10 @@ pub fn member(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
let end = input.offset;
|
||||
|
||||
Ok((input, TokenTreeBuilder::spanned_member((start, end))))
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::tagged_member((start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -322,7 +335,7 @@ pub fn flag(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_flag(bare.span(), (start, end)),
|
||||
TokenTreeBuilder::tagged_flag(bare.tag(), (start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -336,7 +349,7 @@ pub fn shorthand(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_shorthand(bare.span(), (start, end)),
|
||||
TokenTreeBuilder::tagged_shorthand(bare.tag(), (start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -369,7 +382,7 @@ pub fn raw_unit(input: NomSpan) -> IResult<NomSpan, Tagged<Unit>> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
Tagged::from_simple_spanned_item(Unit::from(unit.fragment), (start, end)),
|
||||
Unit::from(unit.fragment).tagged((start, end, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -389,7 +402,7 @@ pub fn size(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_size((number.item, *size), (start, end)),
|
||||
TokenTreeBuilder::tagged_size((number.item, *size), (start, end, input.extra)),
|
||||
))
|
||||
} else {
|
||||
let end = input.offset;
|
||||
|
@ -401,7 +414,7 @@ pub fn size(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_number(number.item, number.tag),
|
||||
TokenTreeBuilder::tagged_number(number.item, number.tag),
|
||||
))
|
||||
}
|
||||
})
|
||||
|
@ -455,18 +468,18 @@ fn make_token_list(
|
|||
let mut nodes = vec![];
|
||||
|
||||
if let Some(sp_left) = sp_left {
|
||||
nodes.push(TokenNode::Whitespace(Span::from(sp_left)));
|
||||
nodes.push(TokenNode::Whitespace(Tag::from(sp_left)));
|
||||
}
|
||||
|
||||
nodes.push(first);
|
||||
|
||||
for (ws, token) in list {
|
||||
nodes.push(TokenNode::Whitespace(Span::from(ws)));
|
||||
nodes.push(TokenNode::Whitespace(Tag::from(ws)));
|
||||
nodes.push(token);
|
||||
}
|
||||
|
||||
if let Some(sp_right) = sp_right {
|
||||
nodes.push(TokenNode::Whitespace(Span::from(sp_right)));
|
||||
nodes.push(TokenNode::Whitespace(Tag::from(sp_right)));
|
||||
}
|
||||
|
||||
nodes
|
||||
|
@ -478,7 +491,10 @@ pub fn whitespace(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
let (input, ws1) = space1(input)?;
|
||||
let right = input.offset;
|
||||
|
||||
Ok((input, TokenTreeBuilder::spanned_ws((left, right))))
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::tagged_ws((left, right, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -508,7 +524,7 @@ pub fn delimited_paren(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_parens(items, (left, right)),
|
||||
TokenTreeBuilder::tagged_parens(items, (left, right, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -539,7 +555,7 @@ pub fn delimited_square(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_square(items, (left, right)),
|
||||
TokenTreeBuilder::tagged_square(items, (left, right, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -556,7 +572,10 @@ pub fn delimited_brace(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_brace(items.unwrap_or_else(|| vec![]), (left, right)),
|
||||
TokenTreeBuilder::tagged_brace(
|
||||
items.unwrap_or_else(|| vec![]),
|
||||
(left, right, input.extra),
|
||||
),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -567,7 +586,10 @@ pub fn raw_call(input: NomSpan) -> IResult<NomSpan, Tagged<CallNode>> {
|
|||
let (input, items) = token_list(input)?;
|
||||
let right = input.offset;
|
||||
|
||||
Ok((input, TokenTreeBuilder::spanned_call(items, (left, right))))
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::tagged_call(items, (left, right, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -581,7 +603,7 @@ pub fn path(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_path((head, tail), (left, right)),
|
||||
TokenTreeBuilder::tagged_path((head, tail), (left, right, input.extra)),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
@ -628,9 +650,9 @@ pub fn pipeline(input: NomSpan) -> IResult<NomSpan, TokenNode> {
|
|||
|
||||
Ok((
|
||||
input,
|
||||
TokenTreeBuilder::spanned_pipeline(
|
||||
(make_call_list(head, items), tail.map(Span::from)),
|
||||
(start, end),
|
||||
TokenTreeBuilder::tagged_pipeline(
|
||||
(make_call_list(head, items), tail.map(Tag::from)),
|
||||
(start, end, input.extra),
|
||||
),
|
||||
))
|
||||
})
|
||||
|
@ -643,17 +665,17 @@ fn make_call_list(
|
|||
let mut out = vec![];
|
||||
|
||||
if let Some(head) = head {
|
||||
let el = PipelineElement::new(None, head.0.map(Span::from), head.1, head.2.map(Span::from));
|
||||
let el = PipelineElement::new(None, head.0.map(Tag::from), head.1, head.2.map(Tag::from));
|
||||
|
||||
out.push(el);
|
||||
}
|
||||
|
||||
for (pipe, ws1, call, ws2) in items {
|
||||
let el = PipelineElement::new(
|
||||
Some(pipe).map(Span::from),
|
||||
ws1.map(Span::from),
|
||||
Some(pipe).map(Tag::from),
|
||||
ws1.map(Tag::from),
|
||||
call,
|
||||
ws2.map(Span::from),
|
||||
ws2.map(Tag::from),
|
||||
);
|
||||
|
||||
out.push(el);
|
||||
|
@ -679,12 +701,17 @@ fn is_external_word_char(c: char) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
/// These characters appear in globs and not bare words
|
||||
fn is_glob_specific_char(c: char) -> bool {
|
||||
c == '*' || c == '?'
|
||||
}
|
||||
|
||||
fn is_start_glob_char(c: char) -> bool {
|
||||
is_start_bare_char(c) || c == '*'
|
||||
is_start_bare_char(c) || is_glob_specific_char(c)
|
||||
}
|
||||
|
||||
fn is_glob_char(c: char) -> bool {
|
||||
is_bare_char(c) || c == '*'
|
||||
is_bare_char(c) || is_glob_specific_char(c)
|
||||
}
|
||||
|
||||
fn is_start_bare_char(c: char) -> bool {
|
||||
|
@ -779,7 +806,7 @@ mod tests {
|
|||
macro_rules! equal_tokens {
|
||||
($source:tt -> $tokens:expr) => {
|
||||
let result = apply(pipeline, "pipeline", $source);
|
||||
let (expected_tree, expected_source) = TokenTreeBuilder::build($tokens);
|
||||
let (expected_tree, expected_source) = TokenTreeBuilder::build(uuid::Uuid::nil(), $tokens);
|
||||
|
||||
if result != expected_tree {
|
||||
let debug_result = format!("{}", result.debug($source));
|
||||
|
@ -818,12 +845,12 @@ mod tests {
|
|||
fn test_integer() {
|
||||
assert_leaf! {
|
||||
parsers [ size ]
|
||||
"123" -> 0..3 { Number(RawNumber::int((0, 3)).item) }
|
||||
"123" -> 0..3 { Number(RawNumber::int((0, 3, test_uuid())).item) }
|
||||
}
|
||||
|
||||
assert_leaf! {
|
||||
parsers [ size ]
|
||||
"-123" -> 0..4 { Number(RawNumber::int((0, 4)).item) }
|
||||
"-123" -> 0..4 { Number(RawNumber::int((0, 4, test_uuid())).item) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -831,12 +858,12 @@ mod tests {
|
|||
fn test_size() {
|
||||
assert_leaf! {
|
||||
parsers [ size ]
|
||||
"123MB" -> 0..5 { Size(RawNumber::int((0, 3)).item, Unit::MB) }
|
||||
"123MB" -> 0..5 { Size(RawNumber::int((0, 3, test_uuid())).item, Unit::MB) }
|
||||
}
|
||||
|
||||
assert_leaf! {
|
||||
parsers [ size ]
|
||||
"10GB" -> 0..4 { Size(RawNumber::int((0, 2)).item, Unit::GB) }
|
||||
"10GB" -> 0..4 { Size(RawNumber::int((0, 2, test_uuid())).item, Unit::GB) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -874,12 +901,12 @@ mod tests {
|
|||
fn test_string() {
|
||||
assert_leaf! {
|
||||
parsers [ string dq_string ]
|
||||
r#""hello world""# -> 0..13 { String(span(1, 12)) }
|
||||
r#""hello world""# -> 0..13 { String(tag(1, 12)) }
|
||||
}
|
||||
|
||||
assert_leaf! {
|
||||
parsers [ string sq_string ]
|
||||
r"'hello world'" -> 0..13 { String(span(1, 12)) }
|
||||
r"'hello world'" -> 0..13 { String(tag(1, 12)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -931,12 +958,12 @@ mod tests {
|
|||
fn test_variable() {
|
||||
assert_leaf! {
|
||||
parsers [ var ]
|
||||
"$it" -> 0..3 { Variable(span(1, 3)) }
|
||||
"$it" -> 0..3 { Variable(tag(1, 3)) }
|
||||
}
|
||||
|
||||
assert_leaf! {
|
||||
parsers [ var ]
|
||||
"$name" -> 0..5 { Variable(span(1, 5)) }
|
||||
"$name" -> 0..5 { Variable(tag(1, 5)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -944,7 +971,7 @@ mod tests {
|
|||
fn test_external() {
|
||||
assert_leaf! {
|
||||
parsers [ external ]
|
||||
"^ls" -> 0..3 { ExternalCommand(span(1, 3)) }
|
||||
"^ls" -> 0..3 { ExternalCommand(tag(1, 3)) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1260,7 +1287,7 @@ mod tests {
|
|||
desc: &str,
|
||||
string: &str,
|
||||
) -> T {
|
||||
match f(NomSpan::new(string)) {
|
||||
match f(NomSpan::new_extra(string, uuid::Uuid::nil())) {
|
||||
Ok(v) => v.1,
|
||||
Err(other) => {
|
||||
println!("{:?}", other);
|
||||
|
@ -1270,44 +1297,46 @@ mod tests {
|
|||
}
|
||||
}
|
||||
|
||||
fn span(left: usize, right: usize) -> Span {
|
||||
Span::from((left, right))
|
||||
fn tag(left: usize, right: usize) -> Tag {
|
||||
Tag::from((left, right, uuid::Uuid::nil()))
|
||||
}
|
||||
|
||||
fn delimited(
|
||||
delimiter: Delimiter,
|
||||
delimiter: Tagged<Delimiter>,
|
||||
children: Vec<TokenNode>,
|
||||
left: usize,
|
||||
right: usize,
|
||||
) -> TokenNode {
|
||||
let node = DelimitedNode::new(delimiter, children);
|
||||
let spanned = Tagged::from_simple_spanned_item(node, (left, right));
|
||||
let node = DelimitedNode::new(*delimiter, children);
|
||||
let spanned = node.tagged((left, right, delimiter.tag.origin));
|
||||
TokenNode::Delimited(spanned)
|
||||
}
|
||||
|
||||
fn path(head: TokenNode, tail: Vec<Token>, left: usize, right: usize) -> TokenNode {
|
||||
let tag = head.tag();
|
||||
|
||||
let node = PathNode::new(
|
||||
Box::new(head),
|
||||
tail.into_iter().map(TokenNode::Token).collect(),
|
||||
);
|
||||
let spanned = Tagged::from_simple_spanned_item(node, (left, right));
|
||||
let spanned = node.tagged((left, right, tag.origin));
|
||||
TokenNode::Path(spanned)
|
||||
}
|
||||
|
||||
fn leaf_token(token: RawToken, left: usize, right: usize) -> TokenNode {
|
||||
TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right)))
|
||||
}
|
||||
|
||||
fn token(token: RawToken, left: usize, right: usize) -> TokenNode {
|
||||
TokenNode::Token(Tagged::from_simple_spanned_item(token, (left, right)))
|
||||
TokenNode::Token(token.tagged((left, right, uuid::Uuid::nil())))
|
||||
}
|
||||
|
||||
fn build<T>(block: CurriedNode<T>) -> T {
|
||||
let mut builder = TokenTreeBuilder::new();
|
||||
let mut builder = TokenTreeBuilder::new(uuid::Uuid::nil());
|
||||
block(&mut builder)
|
||||
}
|
||||
|
||||
fn build_token(block: CurriedToken) -> TokenNode {
|
||||
TokenTreeBuilder::build(block).0
|
||||
TokenTreeBuilder::build(uuid::Uuid::nil(), block).0
|
||||
}
|
||||
|
||||
fn test_uuid() -> uuid::Uuid {
|
||||
uuid::Uuid::nil()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::parser::CallNode;
|
||||
use crate::traits::ToDebug;
|
||||
use crate::{Span, Tagged};
|
||||
use crate::{Tag, Tagged};
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
use std::fmt;
|
||||
|
@ -8,7 +8,7 @@ use std::fmt;
|
|||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, new)]
|
||||
pub struct Pipeline {
|
||||
pub(crate) parts: Vec<PipelineElement>,
|
||||
pub(crate) post_ws: Option<Span>,
|
||||
pub(crate) post_ws: Option<Tag>,
|
||||
}
|
||||
|
||||
impl ToDebug for Pipeline {
|
||||
|
@ -27,11 +27,11 @@ impl ToDebug for Pipeline {
|
|||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Getters, new)]
|
||||
pub struct PipelineElement {
|
||||
pub pipe: Option<Span>,
|
||||
pub pre_ws: Option<Span>,
|
||||
pub pipe: Option<Tag>,
|
||||
pub pre_ws: Option<Tag>,
|
||||
#[get = "pub(crate)"]
|
||||
call: Tagged<CallNode>,
|
||||
pub post_ws: Option<Span>,
|
||||
pub post_ws: Option<Tag>,
|
||||
}
|
||||
|
||||
impl ToDebug for PipelineElement {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::errors::ShellError;
|
||||
use crate::parser::parse::{call_node::*, flag::*, operator::*, pipeline::*, tokens::*};
|
||||
use crate::traits::ToDebug;
|
||||
use crate::{Span, Tagged, Text};
|
||||
use crate::{Tag, Tagged, Text};
|
||||
use derive_new::new;
|
||||
use enum_utils::FromStr;
|
||||
use getset::Getters;
|
||||
|
@ -16,8 +16,8 @@ pub enum TokenNode {
|
|||
Pipeline(Tagged<Pipeline>),
|
||||
Operator(Tagged<Operator>),
|
||||
Flag(Tagged<Flag>),
|
||||
Member(Span),
|
||||
Whitespace(Span),
|
||||
Member(Tag),
|
||||
Whitespace(Tag),
|
||||
|
||||
Error(Tagged<Box<ShellError>>),
|
||||
Path(Tagged<PathNode>),
|
||||
|
@ -78,31 +78,31 @@ impl fmt::Debug for DebugTokenNode<'_> {
|
|||
)
|
||||
}
|
||||
TokenNode::Pipeline(pipeline) => write!(f, "{}", pipeline.debug(self.source)),
|
||||
TokenNode::Error(s) => write!(f, "<error> for {:?}", s.span().slice(self.source)),
|
||||
rest => write!(f, "{}", rest.span().slice(self.source)),
|
||||
TokenNode::Error(s) => write!(f, "<error> for {:?}", s.tag().slice(self.source)),
|
||||
rest => write!(f, "{}", rest.tag().slice(self.source)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TokenNode> for Span {
|
||||
fn from(token: &TokenNode) -> Span {
|
||||
token.span()
|
||||
impl From<&TokenNode> for Tag {
|
||||
fn from(token: &TokenNode) -> Tag {
|
||||
token.tag()
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenNode {
|
||||
pub fn span(&self) -> Span {
|
||||
pub fn tag(&self) -> Tag {
|
||||
match self {
|
||||
TokenNode::Token(t) => t.span(),
|
||||
TokenNode::Call(s) => s.span(),
|
||||
TokenNode::Delimited(s) => s.span(),
|
||||
TokenNode::Pipeline(s) => s.span(),
|
||||
TokenNode::Operator(s) => s.span(),
|
||||
TokenNode::Flag(s) => s.span(),
|
||||
TokenNode::Token(t) => t.tag(),
|
||||
TokenNode::Call(s) => s.tag(),
|
||||
TokenNode::Delimited(s) => s.tag(),
|
||||
TokenNode::Pipeline(s) => s.tag(),
|
||||
TokenNode::Operator(s) => s.tag(),
|
||||
TokenNode::Flag(s) => s.tag(),
|
||||
TokenNode::Member(s) => *s,
|
||||
TokenNode::Whitespace(s) => *s,
|
||||
TokenNode::Error(s) => s.span(),
|
||||
TokenNode::Path(s) => s.span(),
|
||||
TokenNode::Error(s) => s.tag(),
|
||||
TokenNode::Path(s) => s.tag(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,11 +127,11 @@ impl TokenNode {
|
|||
}
|
||||
|
||||
pub fn as_external_arg(&self, source: &Text) -> String {
|
||||
self.span().slice(source).to_string()
|
||||
self.tag().slice(source).to_string()
|
||||
}
|
||||
|
||||
pub fn source<'a>(&self, source: &'a Text) -> &'a str {
|
||||
self.span().slice(source)
|
||||
self.tag().slice(source)
|
||||
}
|
||||
|
||||
pub fn is_bare(&self) -> bool {
|
||||
|
@ -154,12 +154,12 @@ impl TokenNode {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn expect_external(&self) -> Span {
|
||||
pub fn expect_external(&self) -> Tag {
|
||||
match self {
|
||||
TokenNode::Token(Tagged {
|
||||
item: RawToken::ExternalCommand(span),
|
||||
item: RawToken::ExternalCommand(tag),
|
||||
..
|
||||
}) => *span,
|
||||
}) => *tag,
|
||||
_ => panic!("Only call expect_external if you checked is_external first"),
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue