mirror of
https://github.com/nushell/nushell
synced 2024-11-10 15:14:14 +00:00
Merge branch 'main' of https://github.com/nushell/engine-q into plugins
This commit is contained in:
commit
51e48bee53
95 changed files with 2796 additions and 1607 deletions
126
Cargo.lock
generated
126
Cargo.lock
generated
|
@ -74,9 +74,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.61"
|
version = "0.3.62"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e7a905d892734eea339e896738c14b9afce22b5318f64b951e70bf3844419b01"
|
checksum = "091bcdf2da9950f96aa522681ce805e6857f6ca8df73833d35736ab2dc78e152"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"addr2line",
|
"addr2line",
|
||||||
"cc",
|
"cc",
|
||||||
|
@ -93,6 +93,15 @@ version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitmaps"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "031043d04099746d8db04daf1fa424b2bc8bd69d92b25962dcde24da39ab64a2"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bstr"
|
name = "bstr"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
@ -106,9 +115,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byte-unit"
|
name = "byte-unit"
|
||||||
version = "4.0.12"
|
version = "4.0.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "063197e6eb4b775b64160dedde7a0986bb2836cce140e9492e9e96f28e18bcd8"
|
checksum = "956ffc5b0ec7d7a6949e3f21fd63ba5af4cffdc2ba1e0b7bf62b481458c4ae7f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8-width",
|
"utf8-width",
|
||||||
]
|
]
|
||||||
|
@ -270,6 +279,16 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctrlc"
|
||||||
|
version = "3.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a19c6cedffdc8c03a3346d723eb20bd85a13362bb96dc2ac000842c6381ec7bf"
|
||||||
|
dependencies = [
|
||||||
|
"nix",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dialoguer"
|
name = "dialoguer"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
@ -345,6 +364,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
|
"ctrlc",
|
||||||
"dialoguer",
|
"dialoguer",
|
||||||
"miette",
|
"miette",
|
||||||
"nu-cli",
|
"nu-cli",
|
||||||
|
@ -394,10 +414,24 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "im"
|
||||||
version = "0.1.11"
|
version = "15.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
|
checksum = "111c1983f3c5bb72732df25cddacee9b546d08325fb584b5ebd38148be7b0246"
|
||||||
|
dependencies = [
|
||||||
|
"bitmaps",
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
"rand_xoshiro",
|
||||||
|
"sized-chunks",
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "instant"
|
||||||
|
version = "0.1.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
@ -431,9 +465,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.103"
|
version = "0.2.105"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked-hash-map"
|
name = "linked-hash-map"
|
||||||
|
@ -530,9 +564,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mio"
|
name = "mio"
|
||||||
version = "0.7.13"
|
version = "0.7.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16"
|
checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
|
@ -550,6 +584,19 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nix"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f305c2c2e4c39a82f7bf0bf65fb557f9070ce06781d4f2454295cc34b1c43188"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cc",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"memoffset",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -601,6 +648,7 @@ dependencies = [
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-table",
|
"nu-table",
|
||||||
"nu-term-grid",
|
"nu-term-grid",
|
||||||
|
"rayon",
|
||||||
"sysinfo",
|
"sysinfo",
|
||||||
"terminal_size",
|
"terminal_size",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -665,6 +713,7 @@ dependencies = [
|
||||||
"byte-unit",
|
"byte-unit",
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-humanize",
|
"chrono-humanize",
|
||||||
|
"im",
|
||||||
"miette",
|
"miette",
|
||||||
"serde",
|
"serde",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
@ -718,9 +767,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "object"
|
name = "object"
|
||||||
version = "0.26.2"
|
version = "0.27.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39f37e50073ccad23b6d09bcb5b263f4e76d3bb6038e4a3c08e52162ffa8abc2"
|
checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
@ -779,9 +828,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ppv-lite86"
|
name = "ppv-lite86"
|
||||||
version = "0.2.14"
|
version = "0.2.15"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c3ca011bd0129ff4ae15cd04c4eef202cadf6c51c21e47aba319b4e0501db741"
|
checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "predicates"
|
name = "predicates"
|
||||||
|
@ -848,7 +897,7 @@ checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core 0.6.3",
|
||||||
"rand_hc",
|
"rand_hc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -859,9 +908,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.6.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rand_core"
|
name = "rand_core"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
|
@ -877,7 +932,16 @@ version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rand_core",
|
"rand_core 0.6.3",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_xoshiro"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9fcdd2e881d02f1d9390ae47ad8e5696a9e4be7b547a1da2afbc61973217004"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core 0.5.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1057,6 +1121,16 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sized-chunks"
|
||||||
|
version = "0.6.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "16d69225bde7a69b235da73377861095455d298f2b970996eec25ddbb42b3d1e"
|
||||||
|
dependencies = [
|
||||||
|
"bitmaps",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "smallvec"
|
name = "smallvec"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
|
@ -1168,9 +1242,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "termtree"
|
name = "termtree"
|
||||||
version = "0.2.1"
|
version = "0.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78fbf2dd23e79c28ccfa2472d3e6b3b189866ffef1aeb91f17c2d968b6586378"
|
checksum = "76565a2f8df1d2170b5c365aa39d0623fd93fec20545edde299233cea82d0f16"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "textwrap"
|
name = "textwrap"
|
||||||
|
@ -1223,6 +1297,12 @@ dependencies = [
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.14.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-linebreak"
|
name = "unicode-linebreak"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
|
@ -1262,6 +1342,12 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vte"
|
name = "vte"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
|
|
|
@ -29,6 +29,7 @@ nu-protocol = { path = "./crates/nu-protocol" }
|
||||||
nu-table = { path = "./crates/nu-table" }
|
nu-table = { path = "./crates/nu-table" }
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid" }
|
nu-term-grid = { path = "./crates/nu-term-grid" }
|
||||||
miette = "3.0.0"
|
miette = "3.0.0"
|
||||||
|
ctrlc = "3.2.1"
|
||||||
# mimalloc = { version = "*", default-features = false }
|
# mimalloc = { version = "*", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
|
|
@ -3,3 +3,6 @@
|
||||||
Engine-q is an experimental project to replace the core functionality in Nushell (parser, engine, protocol). It's still in an alpha state, and there is still a lot to do: please see TODO.md
|
Engine-q is an experimental project to replace the core functionality in Nushell (parser, engine, protocol). It's still in an alpha state, and there is still a lot to do: please see TODO.md
|
||||||
|
|
||||||
If you'd like to help out, come join us on the [discord](https://discord.gg/NtAbbGn) or propose some work in an issue or PR draft. We're currently looking to begin porting Nushell commands to engine-q.
|
If you'd like to help out, come join us on the [discord](https://discord.gg/NtAbbGn) or propose some work in an issue or PR draft. We're currently looking to begin porting Nushell commands to engine-q.
|
||||||
|
|
||||||
|
If you are interested in porting a command from Nushell to engine-q you are welcome to
|
||||||
|
[comment on this issue 242](https://github.com/nushell/engine-q/issues/242) with the command name you would like to port.
|
||||||
|
|
15
TODO.md
15
TODO.md
|
@ -30,13 +30,20 @@
|
||||||
- [x] Handling rows with missing columns during a cell path
|
- [x] Handling rows with missing columns during a cell path
|
||||||
- [x] finish operator type-checking
|
- [x] finish operator type-checking
|
||||||
- [x] Config file loading
|
- [x] Config file loading
|
||||||
- [ ] Input/output types
|
- [x] block variable captures
|
||||||
|
- [x] improved history and config paths
|
||||||
|
- [x] ctrl-c support
|
||||||
- [ ] Support for `$in`
|
- [ ] Support for `$in`
|
||||||
- [ ] ctrl-c support
|
|
||||||
- [ ] operator overflow
|
- [ ] operator overflow
|
||||||
- [ ] Overlays (replacement for `autoenv`)
|
- [ ] shells
|
||||||
|
- [ ] plugins
|
||||||
|
- [ ] dataframes
|
||||||
|
|
||||||
|
## Post-nushell merge:
|
||||||
|
- [ ] Overlays (replacement for `autoenv`), adding modules to shells
|
||||||
|
- [ ] Input/output types
|
||||||
|
- [ ] let [first, rest] = [1, 2, 3] (design question: how do you pattern match a table?)
|
||||||
|
|
||||||
## Maybe:
|
## Maybe:
|
||||||
- [ ] default param values?
|
- [ ] default param values?
|
||||||
- [ ] Unary not?
|
- [ ] Unary not?
|
||||||
- [ ] let [first, rest] = [1, 2, 3] (design question: how do you pattern match a table?)
|
|
||||||
|
|
|
@ -1,29 +1,27 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::{flatten_block, parse};
|
use nu_parser::{flatten_block, parse};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, EvaluationContext, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Value,
|
PipelineData,
|
||||||
};
|
};
|
||||||
use reedline::Completer;
|
use reedline::Completer;
|
||||||
|
|
||||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
const SEP: char = std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct NuCompleter {
|
pub struct NuCompleter {
|
||||||
engine_state: Rc<RefCell<EngineState>>,
|
engine_state: EngineState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NuCompleter {
|
impl NuCompleter {
|
||||||
pub fn new(engine_state: Rc<RefCell<EngineState>>) -> Self {
|
pub fn new(engine_state: EngineState) -> Self {
|
||||||
Self { engine_state }
|
Self { engine_state }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Completer for NuCompleter {
|
impl Completer for NuCompleter {
|
||||||
fn complete(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> {
|
fn complete(&self, line: &str, pos: usize) -> Vec<(reedline::Span, String)> {
|
||||||
let engine_state = self.engine_state.borrow();
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let mut working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
let offset = working_set.next_span_start();
|
let offset = working_set.next_span_start();
|
||||||
let pos = offset + pos;
|
let pos = offset + pos;
|
||||||
let (output, _err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false);
|
let (output, _err) = parse(&mut working_set, Some("completer"), line.as_bytes(), false);
|
||||||
|
@ -49,7 +47,7 @@ impl Completer for NuCompleter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for scope in &engine_state.scope {
|
for scope in &self.engine_state.scope {
|
||||||
for v in &scope.vars {
|
for v in &scope.vars {
|
||||||
if v.0.starts_with(prefix) {
|
if v.0.starts_with(prefix) {
|
||||||
output.push((
|
output.push((
|
||||||
|
@ -72,14 +70,13 @@ impl Completer for NuCompleter {
|
||||||
|
|
||||||
let (block, ..) =
|
let (block, ..) =
|
||||||
parse(&mut working_set, None, custom_completion.as_bytes(), false);
|
parse(&mut working_set, None, custom_completion.as_bytes(), false);
|
||||||
let context = EvaluationContext {
|
|
||||||
engine_state: self.engine_state.clone(),
|
let mut stack = Stack::default();
|
||||||
stack: Stack::default(),
|
let result =
|
||||||
};
|
eval_block(&self.engine_state, &mut stack, &block, PipelineData::new());
|
||||||
let result = eval_block(&context, &block, Value::nothing());
|
|
||||||
|
|
||||||
let v: Vec<_> = match result {
|
let v: Vec<_> = match result {
|
||||||
Ok(Value::List { vals, .. }) => vals
|
Ok(pd) => pd
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| {
|
.map(move |x| {
|
||||||
let s = x.as_string().expect(
|
let s = x.as_string().expect(
|
||||||
|
|
|
@ -2,21 +2,19 @@ use nu_ansi_term::Style;
|
||||||
use nu_parser::{flatten_block, parse, FlatShape};
|
use nu_parser::{flatten_block, parse, FlatShape};
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
pub struct NuHighlighter {
|
pub struct NuHighlighter {
|
||||||
pub engine_state: Rc<RefCell<EngineState>>,
|
pub engine_state: EngineState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Highlighter for NuHighlighter {
|
impl Highlighter for NuHighlighter {
|
||||||
fn highlight(&self, line: &str) -> StyledText {
|
fn highlight(&self, line: &str) -> StyledText {
|
||||||
let (shapes, global_span_offset) = {
|
let (shapes, global_span_offset) = {
|
||||||
let engine_state = self.engine_state.borrow();
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let mut working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false);
|
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false);
|
||||||
|
|
||||||
let shapes = flatten_block(&working_set, &block);
|
let shapes = flatten_block(&working_set, &block);
|
||||||
(shapes, engine_state.next_span_start())
|
(shapes, self.engine_state.next_span_start())
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut output = StyledText::default();
|
let mut output = StyledText::default();
|
||||||
|
|
|
@ -1,17 +1,14 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
use nu_parser::{parse, ParseError};
|
use nu_parser::{parse, ParseError};
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
use reedline::{ValidationResult, Validator};
|
use reedline::{ValidationResult, Validator};
|
||||||
|
|
||||||
pub struct NuValidator {
|
pub struct NuValidator {
|
||||||
pub engine_state: Rc<RefCell<EngineState>>,
|
pub engine_state: EngineState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Validator for NuValidator {
|
impl Validator for NuValidator {
|
||||||
fn validate(&self, line: &str) -> ValidationResult {
|
fn validate(&self, line: &str) -> ValidationResult {
|
||||||
let engine_state = self.engine_state.borrow();
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let mut working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
let (_, err) = parse(&mut working_set, None, line.as_bytes(), false);
|
let (_, err) = parse(&mut working_set, None, line.as_bytes(), false);
|
||||||
|
|
||||||
if matches!(err, Some(ParseError::UnexpectedEof(..))) {
|
if matches!(err, Some(ParseError::UnexpectedEof(..))) {
|
||||||
|
|
|
@ -27,6 +27,7 @@ terminal_size = "0.1.17"
|
||||||
lscolors = { version = "0.8.0", features = ["crossterm"] }
|
lscolors = { version = "0.8.0", features = ["crossterm"] }
|
||||||
bytesize = "1.1.0"
|
bytesize = "1.1.0"
|
||||||
dialoguer = "0.9.0"
|
dialoguer = "0.9.0"
|
||||||
|
rayon = "1.5.1"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
trash-support = ["trash"]
|
trash-support = ["trash"]
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
Example, ShellError, Signature, Span, SyntaxShape, Value,
|
Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
impl Command for SubCommand {
|
impl Command for SubCommand {
|
||||||
|
@ -25,11 +26,12 @@ impl Command for SubCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
into_binary(context, call, input)
|
into_binary(engine_state, call, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -84,14 +86,15 @@ impl Command for SubCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_binary(
|
fn into_binary(
|
||||||
_context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
// let column_paths: Vec<CellPath> = call.rest(context, 0)?;
|
// let column_paths: Vec<CellPath> = call.rest(context, 0)?;
|
||||||
|
|
||||||
input.map(head, move |v| {
|
input.map(
|
||||||
|
move |v| {
|
||||||
action(v, head)
|
action(v, head)
|
||||||
// FIXME: Add back in cell_path support
|
// FIXME: Add back in cell_path support
|
||||||
// if column_paths.is_empty() {
|
// if column_paths.is_empty() {
|
||||||
|
@ -105,7 +108,9 @@ fn into_binary(
|
||||||
|
|
||||||
// Ok(ret)
|
// Ok(ret)
|
||||||
// }
|
// }
|
||||||
})
|
},
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int_to_endian(n: i64) -> Vec<u8> {
|
fn int_to_endian(n: i64) -> Vec<u8> {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use nu_engine::get_full_help;
|
use nu_engine::get_full_help;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
Signature, Value,
|
IntoPipelineData, PipelineData, Signature, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Into;
|
pub struct Into;
|
||||||
|
|
||||||
impl Command for Into {
|
impl Command for Into {
|
||||||
|
@ -22,14 +23,16 @@ impl Command for Into {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Into.signature(), &[], context),
|
val: get_full_help(&Into.signature(), &[], engine_state),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
Example, ShellError, Signature, Span, SyntaxShape, Value,
|
Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
impl Command for SubCommand {
|
impl Command for SubCommand {
|
||||||
|
@ -25,11 +26,12 @@ impl Command for SubCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
into_filesize(context, call, input)
|
into_filesize(engine_state, call, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -112,14 +114,15 @@ impl Command for SubCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_filesize(
|
fn into_filesize(
|
||||||
_context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
// let call_paths: Vec<ColumnPath> = args.rest(0)?;
|
// let call_paths: Vec<ColumnPath> = args.rest(0)?;
|
||||||
|
|
||||||
input.map(head, move |v| {
|
input.map(
|
||||||
|
move |v| {
|
||||||
action(v, head)
|
action(v, head)
|
||||||
|
|
||||||
// FIXME: Add back cell_path support
|
// FIXME: Add back cell_path support
|
||||||
|
@ -136,7 +139,9 @@ fn into_filesize(
|
||||||
|
|
||||||
// Ok(ret)
|
// Ok(ret)
|
||||||
// }
|
// }
|
||||||
})
|
},
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn action(input: Value, span: Span) -> Value {
|
pub fn action(input: Value, span: Span) -> Value {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
Example, IntoValueStream, ShellError, Signature, Span, SyntaxShape, Value,
|
Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
impl Command for SubCommand {
|
impl Command for SubCommand {
|
||||||
|
@ -25,11 +26,12 @@ impl Command for SubCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
into_int(context, call, input)
|
into_int(engine_state, call, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -78,10 +80,8 @@ impl Command for SubCommand {
|
||||||
Example {
|
Example {
|
||||||
description: "Convert bool to integer",
|
description: "Convert bool to integer",
|
||||||
example: "[$false, $true] | into int",
|
example: "[$false, $true] | into int",
|
||||||
result: Some(Value::Stream {
|
result: Some(Value::List {
|
||||||
stream: vec![Value::test_int(0), Value::test_int(1)]
|
vals: vec![Value::test_int(0), Value::test_int(1)],
|
||||||
.into_iter()
|
|
||||||
.into_value_stream(),
|
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
|
@ -90,14 +90,15 @@ impl Command for SubCommand {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn into_int(
|
fn into_int(
|
||||||
_context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
// let column_paths: Vec<CellPath> = call.rest(context, 0)?;
|
// let column_paths: Vec<CellPath> = call.rest(context, 0)?;
|
||||||
|
|
||||||
input.map(head, move |v| {
|
input.map(
|
||||||
|
move |v| {
|
||||||
action(v, head)
|
action(v, head)
|
||||||
// FIXME: Add back cell_path support
|
// FIXME: Add back cell_path support
|
||||||
// if column_paths.is_empty() {
|
// if column_paths.is_empty() {
|
||||||
|
@ -111,7 +112,9 @@ fn into_int(
|
||||||
|
|
||||||
// Ok(ret)
|
// Ok(ret)
|
||||||
// }
|
// }
|
||||||
})
|
},
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn action(input: Value, span: Span) -> Value {
|
pub fn action(input: Value, span: Span) -> Value {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Alias;
|
pub struct Alias;
|
||||||
|
|
||||||
impl Command for Alias {
|
impl Command for Alias {
|
||||||
|
@ -25,10 +26,11 @@ impl Command for Alias {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
call: &Call,
|
_stack: &mut Stack,
|
||||||
_input: Value,
|
_call: &Call,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
_input: PipelineData,
|
||||||
Ok(Value::Nothing { span: call.head })
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Def;
|
pub struct Def;
|
||||||
|
|
||||||
impl Command for Def {
|
impl Command for Def {
|
||||||
|
@ -26,10 +27,11 @@ impl Command for Def {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
call: &Call,
|
_stack: &mut Stack,
|
||||||
_input: Value,
|
_call: &Call,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
_input: PipelineData,
|
||||||
Ok(Value::Nothing { span: call.head })
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use nu_engine::{eval_block, CallExt};
|
use nu_engine::{eval_block, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Do;
|
pub struct Do;
|
||||||
|
|
||||||
impl Command for Do {
|
impl Command for Do {
|
||||||
|
@ -27,19 +28,19 @@ impl Command for Do {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let block_id = call.positional[0]
|
let block_id = call.positional[0]
|
||||||
.as_block()
|
.as_block()
|
||||||
.expect("internal error: expected block");
|
.expect("internal error: expected block");
|
||||||
let rest: Vec<Value> = call.rest(context, 1)?;
|
let rest: Vec<Value> = call.rest(engine_state, stack, 1)?;
|
||||||
|
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
let state = context.enter_scope();
|
let mut stack = stack.collect_captures(&block.captures);
|
||||||
|
|
||||||
let params: Vec<_> = block
|
let params: Vec<_> = block
|
||||||
.signature
|
.signature
|
||||||
|
@ -50,7 +51,7 @@ impl Command for Do {
|
||||||
|
|
||||||
for param in params.iter().zip(&rest) {
|
for param in params.iter().zip(&rest) {
|
||||||
if let Some(var_id) = param.0.var_id {
|
if let Some(var_id) = param.0.var_id {
|
||||||
state.add_var(var_id, param.1.clone())
|
stack.add_var(var_id, param.1.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,7 +69,7 @@ impl Command for Do {
|
||||||
call.head
|
call.head
|
||||||
};
|
};
|
||||||
|
|
||||||
state.add_var(
|
stack.add_var(
|
||||||
param
|
param
|
||||||
.var_id
|
.var_id
|
||||||
.expect("Internal error: rest positional parameter lacks var_id"),
|
.expect("Internal error: rest positional parameter lacks var_id"),
|
||||||
|
@ -79,6 +80,6 @@ impl Command for Do {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eval_block(&state, block, input)
|
eval_block(engine_state, &mut stack, block, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ExportDef;
|
pub struct ExportDef;
|
||||||
|
|
||||||
impl Command for ExportDef {
|
impl Command for ExportDef {
|
||||||
|
@ -26,10 +27,11 @@ impl Command for ExportDef {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
call: &Call,
|
_stack: &mut Stack,
|
||||||
_input: Value,
|
_call: &Call,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
_input: PipelineData,
|
||||||
Ok(Value::Nothing { span: call.head })
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
use nu_engine::{eval_block, eval_expression};
|
use nu_engine::{eval_block, eval_expression};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Example, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
Example, IntoInterruptiblePipelineData, PipelineData, Signature, Span, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct For;
|
pub struct For;
|
||||||
|
|
||||||
impl Command for For {
|
impl Command for For {
|
||||||
|
@ -36,10 +39,11 @@ impl Command for For {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let var_id = call.positional[0]
|
let var_id = call.positional[0]
|
||||||
.as_var()
|
.as_var()
|
||||||
.expect("internal error: missing variable");
|
.expect("internal error: missing variable");
|
||||||
|
@ -47,27 +51,48 @@ impl Command for For {
|
||||||
let keyword_expr = call.positional[1]
|
let keyword_expr = call.positional[1]
|
||||||
.as_keyword()
|
.as_keyword()
|
||||||
.expect("internal error: missing keyword");
|
.expect("internal error: missing keyword");
|
||||||
let values = eval_expression(context, keyword_expr)?;
|
let values = eval_expression(engine_state, stack, keyword_expr)?;
|
||||||
|
|
||||||
let block = call.positional[2]
|
let block_id = call.positional[2]
|
||||||
.as_block()
|
.as_block()
|
||||||
.expect("internal error: expected block");
|
.expect("internal error: expected block");
|
||||||
|
|
||||||
let context = context.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let engine_state = engine_state.clone();
|
||||||
|
let block = engine_state.get_block(block_id).clone();
|
||||||
|
let mut stack = stack.collect_captures(&block.captures);
|
||||||
|
|
||||||
values.map(call.head, move |x| {
|
match values {
|
||||||
let engine_state = context.engine_state.borrow();
|
Value::List { vals, .. } => Ok(vals
|
||||||
let block = engine_state.get_block(block);
|
.into_iter()
|
||||||
|
.map(move |x| {
|
||||||
|
stack.add_var(var_id, x);
|
||||||
|
|
||||||
let state = context.enter_scope();
|
//let block = engine_state.get_block(block_id);
|
||||||
|
match eval_block(&engine_state, &mut stack, &block, PipelineData::new()) {
|
||||||
state.add_var(var_id, x);
|
Ok(pipeline_data) => pipeline_data.into_value(),
|
||||||
|
|
||||||
match eval_block(&state, block, Value::nothing()) {
|
|
||||||
Ok(value) => value,
|
|
||||||
Err(error) => Value::Error { error },
|
Err(error) => Value::Error { error },
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.into_pipeline_data(ctrlc)),
|
||||||
|
Value::Range { val, .. } => Ok(val
|
||||||
|
.into_range_iter()?
|
||||||
|
.map(move |x| {
|
||||||
|
stack.add_var(var_id, x);
|
||||||
|
|
||||||
|
//let block = engine_state.get_block(block_id);
|
||||||
|
match eval_block(&engine_state, &mut stack, &block, PipelineData::new()) {
|
||||||
|
Ok(pipeline_data) => pipeline_data.into_value(),
|
||||||
|
Err(error) => Value::Error { error },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.into_pipeline_data(ctrlc)),
|
||||||
|
x => {
|
||||||
|
stack.add_var(var_id, x);
|
||||||
|
|
||||||
|
eval_block(&engine_state, &mut stack, &block, PipelineData::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
span, Example, ShellError, Signature, Spanned, SyntaxShape, Value,
|
span, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||||
|
Signature, Spanned, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
use nu_engine::{get_full_help, CallExt};
|
use nu_engine::{get_full_help, CallExt};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Help;
|
pub struct Help;
|
||||||
|
|
||||||
impl Command for Help {
|
impl Command for Help {
|
||||||
|
@ -34,11 +36,12 @@ impl Command for Help {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
help(context, call)
|
help(engine_state, stack, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -72,12 +75,16 @@ impl Command for Help {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
fn help(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let find: Option<Spanned<String>> = call.get_flag(context, "find")?;
|
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
|
||||||
let rest: Vec<Spanned<String>> = call.rest(context, 0)?;
|
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
|
||||||
|
|
||||||
let full_commands = context.get_signatures_with_examples();
|
let full_commands = engine_state.get_signatures_with_examples();
|
||||||
|
|
||||||
if let Some(f) = find {
|
if let Some(f) = find {
|
||||||
let search_string = f.item;
|
let search_string = f.item;
|
||||||
|
@ -114,10 +121,9 @@ fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(Value::List {
|
return Ok(found_cmds_vec
|
||||||
vals: found_cmds_vec,
|
.into_iter()
|
||||||
span: head,
|
.into_pipeline_data(engine_state.ctrlc.clone()));
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !rest.is_empty() {
|
if !rest.is_empty() {
|
||||||
|
@ -151,10 +157,9 @@ fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::List {
|
Ok(found_cmds_vec
|
||||||
vals: found_cmds_vec,
|
.into_iter()
|
||||||
span: head,
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
let mut name = String::new();
|
let mut name = String::new();
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
|
@ -168,7 +173,7 @@ fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
||||||
|
|
||||||
for cmd in full_commands {
|
for cmd in full_commands {
|
||||||
if cmd.0.name == name {
|
if cmd.0.name == name {
|
||||||
let help = get_full_help(&cmd.0, &cmd.1, context);
|
let help = get_full_help(&cmd.0, &cmd.1, engine_state);
|
||||||
output.push_str(&help);
|
output.push_str(&help);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +182,8 @@ fn help(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: output,
|
val: output,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::CommandNotFound(span(&[
|
Err(ShellError::CommandNotFound(span(&[
|
||||||
rest[0].span,
|
rest[0].span,
|
||||||
|
@ -355,7 +361,8 @@ You can also learn more at https://www.nushell.sh/book/"#;
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: msg.into(),
|
val: msg.into(),
|
||||||
span: head,
|
span: head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Hide;
|
pub struct Hide;
|
||||||
|
|
||||||
impl Command for Hide {
|
impl Command for Hide {
|
||||||
|
@ -19,10 +20,11 @@ impl Command for Hide {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
call: &Call,
|
_stack: &mut Stack,
|
||||||
_input: Value,
|
_call: &Call,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
_input: PipelineData,
|
||||||
Ok(Value::Nothing { span: call.head })
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use nu_engine::{eval_block, eval_expression};
|
use nu_engine::{eval_block, eval_expression};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
|
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct If;
|
pub struct If;
|
||||||
|
|
||||||
impl Command for If {
|
impl Command for If {
|
||||||
|
@ -27,38 +28,40 @@ impl Command for If {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let cond = &call.positional[0];
|
let cond = &call.positional[0];
|
||||||
let then_block = call.positional[1]
|
let then_block = call.positional[1]
|
||||||
.as_block()
|
.as_block()
|
||||||
.expect("internal error: expected block");
|
.expect("internal error: expected block");
|
||||||
let else_case = call.positional.get(2);
|
let else_case = call.positional.get(2);
|
||||||
|
|
||||||
let result = eval_expression(context, cond)?;
|
let result = eval_expression(engine_state, stack, cond)?;
|
||||||
match result {
|
match result {
|
||||||
Value::Bool { val, span } => {
|
Value::Bool { val, .. } => {
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
if val {
|
if val {
|
||||||
let block = engine_state.get_block(then_block);
|
let block = engine_state.get_block(then_block);
|
||||||
let state = context.enter_scope();
|
let mut stack = stack.collect_captures(&block.captures);
|
||||||
eval_block(&state, block, input)
|
eval_block(engine_state, &mut stack, block, input)
|
||||||
} else if let Some(else_case) = else_case {
|
} else if let Some(else_case) = else_case {
|
||||||
if let Some(else_expr) = else_case.as_keyword() {
|
if let Some(else_expr) = else_case.as_keyword() {
|
||||||
if let Some(block_id) = else_expr.as_block() {
|
if let Some(block_id) = else_expr.as_block() {
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
let state = context.enter_scope();
|
let mut stack = stack.collect_captures(&block.captures);
|
||||||
eval_block(&state, block, input)
|
eval_block(engine_state, &mut stack, block, input)
|
||||||
} else {
|
} else {
|
||||||
eval_expression(context, else_expr)
|
eval_expression(engine_state, stack, else_expr)
|
||||||
|
.map(|x| x.into_pipeline_data())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
eval_expression(context, else_case)
|
eval_expression(engine_state, stack, else_case)
|
||||||
|
.map(|x| x.into_pipeline_data())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nothing { span })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::CantConvert("bool".into(), result.span()?)),
|
_ => Err(ShellError::CantConvert("bool".into(), result.span()?)),
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use nu_engine::eval_expression;
|
use nu_engine::eval_expression;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Let;
|
pub struct Let;
|
||||||
|
|
||||||
impl Command for Let {
|
impl Command for Let {
|
||||||
|
@ -26,10 +27,11 @@ impl Command for Let {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let var_id = call.positional[0]
|
let var_id = call.positional[0]
|
||||||
.as_var()
|
.as_var()
|
||||||
.expect("internal error: missing variable");
|
.expect("internal error: missing variable");
|
||||||
|
@ -38,13 +40,11 @@ impl Command for Let {
|
||||||
.as_keyword()
|
.as_keyword()
|
||||||
.expect("internal error: missing keyword");
|
.expect("internal error: missing keyword");
|
||||||
|
|
||||||
let rhs = eval_expression(context, keyword_expr)?;
|
let rhs = eval_expression(engine_state, stack, keyword_expr)?;
|
||||||
|
|
||||||
//println!("Adding: {:?} to {}", rhs, var_id);
|
//println!("Adding: {:?} to {}", rhs, var_id);
|
||||||
|
|
||||||
context.add_var(var_id, rhs);
|
stack.add_var(var_id, rhs);
|
||||||
Ok(Value::Nothing {
|
Ok(PipelineData::new())
|
||||||
span: call.positional[0].span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Module;
|
pub struct Module;
|
||||||
|
|
||||||
impl Command for Module {
|
impl Command for Module {
|
||||||
|
@ -25,10 +26,11 @@ impl Command for Module {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
call: &Call,
|
_stack: &mut Stack,
|
||||||
_input: Value,
|
_call: &Call,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
_input: PipelineData,
|
||||||
Ok(Value::Nothing { span: call.head })
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use nu_engine::{eval_block, CallExt};
|
use nu_engine::{eval_block, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape};
|
||||||
|
|
||||||
/// Source a file for environment variables.
|
/// Source a file for environment variables.
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Source;
|
pub struct Source;
|
||||||
|
|
||||||
impl Command for Source {
|
impl Command for Source {
|
||||||
|
@ -25,19 +26,16 @@ impl Command for Source {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
// Note: this hidden positional is the block_id that corresponded to the 0th position
|
// Note: this hidden positional is the block_id that corresponded to the 0th position
|
||||||
// it is put here by the parser
|
// it is put here by the parser
|
||||||
let block_id: i64 = call.req(context, 1)?;
|
let block_id: i64 = call.req(engine_state, stack, 1)?;
|
||||||
|
|
||||||
let block = context
|
let block = engine_state.get_block(block_id as usize).clone();
|
||||||
.engine_state
|
eval_block(engine_state, stack, &block, input)
|
||||||
.borrow()
|
|
||||||
.get_block(block_id as usize)
|
|
||||||
.clone();
|
|
||||||
eval_block(context, &block, input)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Use;
|
pub struct Use;
|
||||||
|
|
||||||
impl Command for Use {
|
impl Command for Use {
|
||||||
|
@ -19,10 +20,11 @@ impl Command for Use {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
call: &Call,
|
_stack: &mut Stack,
|
||||||
_input: Value,
|
_call: &Call,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
_input: PipelineData,
|
||||||
Ok(Value::Nothing { span: call.head })
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
engine::{EngineState, StateWorkingSet},
|
||||||
Signature,
|
Signature,
|
||||||
|
@ -7,11 +5,11 @@ use nu_protocol::{
|
||||||
|
|
||||||
use crate::*;
|
use crate::*;
|
||||||
|
|
||||||
pub fn create_default_context() -> Rc<RefCell<EngineState>> {
|
pub fn create_default_context() -> EngineState {
|
||||||
let engine_state = Rc::new(RefCell::new(EngineState::new()));
|
let mut engine_state = EngineState::new();
|
||||||
|
|
||||||
let delta = {
|
let delta = {
|
||||||
let engine_state = engine_state.borrow();
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let mut working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
|
|
||||||
macro_rules! bind_command {
|
macro_rules! bind_command {
|
||||||
( $command:expr ) => {
|
( $command:expr ) => {
|
||||||
|
@ -46,14 +44,19 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
|
||||||
IntoBinary,
|
IntoBinary,
|
||||||
IntoFilesize,
|
IntoFilesize,
|
||||||
IntoInt,
|
IntoInt,
|
||||||
|
Last,
|
||||||
Length,
|
Length,
|
||||||
Let,
|
Let,
|
||||||
LetEnv,
|
LetEnv,
|
||||||
Lines,
|
Lines,
|
||||||
Ls,
|
Ls,
|
||||||
|
Math,
|
||||||
|
MathAbs,
|
||||||
|
MathAvg,
|
||||||
Mkdir,
|
Mkdir,
|
||||||
Module,
|
Module,
|
||||||
Mv,
|
Mv,
|
||||||
|
ParEach,
|
||||||
Ps,
|
Ps,
|
||||||
Rm,
|
Rm,
|
||||||
RunPlugin,
|
RunPlugin,
|
||||||
|
@ -91,7 +94,7 @@ pub fn create_default_context() -> Rc<RefCell<EngineState>> {
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
EngineState::merge_delta(&mut *engine_state.borrow_mut(), delta);
|
EngineState::merge_delta(&mut engine_state, delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
engine_state
|
engine_state
|
||||||
|
|
20
crates/nu-command/src/env/let_env.rs
vendored
20
crates/nu-command/src/env/let_env.rs
vendored
|
@ -1,8 +1,9 @@
|
||||||
use nu_engine::eval_expression;
|
use nu_engine::eval_expression;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct LetEnv;
|
pub struct LetEnv;
|
||||||
|
|
||||||
impl Command for LetEnv {
|
impl Command for LetEnv {
|
||||||
|
@ -26,10 +27,11 @@ impl Command for LetEnv {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let env_var = call.positional[0]
|
let env_var = call.positional[0]
|
||||||
.as_string()
|
.as_string()
|
||||||
.expect("internal error: missing variable");
|
.expect("internal error: missing variable");
|
||||||
|
@ -38,14 +40,12 @@ impl Command for LetEnv {
|
||||||
.as_keyword()
|
.as_keyword()
|
||||||
.expect("internal error: missing keyword");
|
.expect("internal error: missing keyword");
|
||||||
|
|
||||||
let rhs = eval_expression(context, keyword_expr)?;
|
let rhs = eval_expression(engine_state, stack, keyword_expr)?;
|
||||||
let rhs = rhs.as_string()?;
|
let rhs = rhs.as_string()?;
|
||||||
|
|
||||||
//println!("Adding: {:?} to {}", rhs, var_id);
|
//println!("Adding: {:?} to {}", rhs, var_id);
|
||||||
|
|
||||||
context.add_env_var(env_var, rhs);
|
stack.add_env_var(env_var, rhs);
|
||||||
Ok(Value::Nothing {
|
Ok(PipelineData::new())
|
||||||
span: call.positional[0].span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +1,24 @@
|
||||||
use std::{cell::RefCell, rc::Rc};
|
|
||||||
|
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Command, EngineState, EvaluationContext, StateWorkingSet},
|
engine::{Command, EngineState, Stack, StateWorkingSet},
|
||||||
Value,
|
PipelineData,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{From, Into, Split};
|
use super::{From, Into, Math, Split};
|
||||||
|
|
||||||
pub fn test_examples(cmd: impl Command + 'static) {
|
pub fn test_examples(cmd: impl Command + 'static) {
|
||||||
let examples = cmd.examples();
|
let examples = cmd.examples();
|
||||||
let engine_state = Rc::new(RefCell::new(EngineState::new()));
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
|
||||||
let delta = {
|
let delta = {
|
||||||
// Base functions that are needed for testing
|
// Base functions that are needed for testing
|
||||||
// Try to keep this working set small to keep tests running as fast as possible
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
let engine_state = engine_state.borrow();
|
|
||||||
let mut working_set = StateWorkingSet::new(&*engine_state);
|
let mut working_set = StateWorkingSet::new(&*engine_state);
|
||||||
working_set.add_decl(Box::new(From));
|
working_set.add_decl(Box::new(From));
|
||||||
working_set.add_decl(Box::new(Into));
|
working_set.add_decl(Box::new(Into));
|
||||||
working_set.add_decl(Box::new(Split));
|
working_set.add_decl(Box::new(Split));
|
||||||
|
working_set.add_decl(Box::new(Math));
|
||||||
|
|
||||||
// Adding the command that is being tested to the working set
|
// Adding the command that is being tested to the working set
|
||||||
working_set.add_decl(Box::new(cmd));
|
working_set.add_decl(Box::new(cmd));
|
||||||
|
@ -28,7 +26,7 @@ pub fn test_examples(cmd: impl Command + 'static) {
|
||||||
working_set.render()
|
working_set.render()
|
||||||
};
|
};
|
||||||
|
|
||||||
EngineState::merge_delta(&mut *engine_state.borrow_mut(), delta);
|
EngineState::merge_delta(&mut *engine_state, delta);
|
||||||
|
|
||||||
for example in examples {
|
for example in examples {
|
||||||
// Skip tests that don't have results to compare to
|
// Skip tests that don't have results to compare to
|
||||||
|
@ -38,7 +36,6 @@ pub fn test_examples(cmd: impl Command + 'static) {
|
||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let engine_state = engine_state.borrow();
|
|
||||||
let mut working_set = StateWorkingSet::new(&*engine_state);
|
let mut working_set = StateWorkingSet::new(&*engine_state);
|
||||||
let (output, err) = parse(&mut working_set, None, example.example.as_bytes(), false);
|
let (output, err) = parse(&mut working_set, None, example.example.as_bytes(), false);
|
||||||
|
|
||||||
|
@ -49,16 +46,14 @@ pub fn test_examples(cmd: impl Command + 'static) {
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
};
|
};
|
||||||
|
|
||||||
EngineState::merge_delta(&mut *engine_state.borrow_mut(), delta);
|
EngineState::merge_delta(&mut engine_state, delta);
|
||||||
|
|
||||||
let state = EvaluationContext {
|
let mut stack = Stack::new();
|
||||||
engine_state: engine_state.clone(),
|
|
||||||
stack: nu_protocol::engine::Stack::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match eval_block(&state, &block, Value::nothing()) {
|
match eval_block(&engine_state, &mut stack, &block, PipelineData::new()) {
|
||||||
Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err),
|
Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err),
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
|
let result = result.into_value();
|
||||||
println!("input: {}", example.example);
|
println!("input: {}", example.example);
|
||||||
println!("result: {:?}", result);
|
println!("result: {:?}", result);
|
||||||
println!("done: {:?}", start.elapsed());
|
println!("done: {:?}", start.elapsed());
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, Value};
|
use nu_protocol::{IntoPipelineData, PipelineData, Signature, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Git;
|
pub struct Git;
|
||||||
|
|
||||||
impl Command for Git {
|
impl Command for Git {
|
||||||
|
@ -19,10 +20,11 @@ impl Command for Git {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
use std::process::Command as ProcessCommand;
|
use std::process::Command as ProcessCommand;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
|
||||||
|
@ -37,17 +39,18 @@ impl Command for Git {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: String::from_utf8_lossy(&result).to_string(),
|
val: String::from_utf8_lossy(&result).to_string(),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
// FIXME: Move this to an external signature and add better error handling
|
// FIXME: Move this to an external signature and add better error handling
|
||||||
Ok(Value::nothing())
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
// FIXME: Move this to an external signature and add better error handling
|
// FIXME: Move this to an external signature and add better error handling
|
||||||
Ok(Value::nothing())
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use nu_engine::eval_expression;
|
use nu_engine::eval_expression;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{IntoPipelineData, PipelineData, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct GitCheckout;
|
pub struct GitCheckout;
|
||||||
|
|
||||||
impl Command for GitCheckout {
|
impl Command for GitCheckout {
|
||||||
|
@ -24,16 +25,17 @@ impl Command for GitCheckout {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
use std::process::Command as ProcessCommand;
|
use std::process::Command as ProcessCommand;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
|
||||||
let block = &call.positional[0];
|
let block = &call.positional[0];
|
||||||
|
|
||||||
let out = eval_expression(context, block)?;
|
let out = eval_expression(engine_state, stack, block)?;
|
||||||
|
|
||||||
let out = out.as_string()?;
|
let out = out.as_string()?;
|
||||||
|
|
||||||
|
@ -52,17 +54,18 @@ impl Command for GitCheckout {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: String::from_utf8_lossy(&result).to_string(),
|
val: String::from_utf8_lossy(&result).to_string(),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
// FIXME: Move this to an external signature and add better error handling
|
// FIXME: Move this to an external signature and add better error handling
|
||||||
Ok(Value::nothing())
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_err) => {
|
Err(_err) => {
|
||||||
// FIXME: Move this to an external signature and add better error handling
|
// FIXME: Move this to an external signature and add better error handling
|
||||||
Ok(Value::nothing())
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,9 +4,14 @@ use std::process::Command as ProcessCommand;
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::Command;
|
||||||
|
use nu_protocol::engine::EngineState;
|
||||||
|
use nu_protocol::engine::Stack;
|
||||||
|
use nu_protocol::IntoInterruptiblePipelineData;
|
||||||
|
use nu_protocol::PipelineData;
|
||||||
use nu_protocol::{Signature, Value};
|
use nu_protocol::{Signature, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ListGitBranches;
|
pub struct ListGitBranches;
|
||||||
|
|
||||||
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
|
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
|
||||||
|
@ -25,10 +30,11 @@ impl Command for ListGitBranches {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let list_branches = ProcessCommand::new("git")
|
let list_branches = ProcessCommand::new("git")
|
||||||
.arg("branch")
|
.arg("branch")
|
||||||
.stdout(Stdio::piped())
|
.stdout(Stdio::piped())
|
||||||
|
@ -40,6 +46,7 @@ impl Command for ListGitBranches {
|
||||||
|
|
||||||
let s = String::from_utf8_lossy(&val).to_string();
|
let s = String::from_utf8_lossy(&val).to_string();
|
||||||
|
|
||||||
|
#[allow(clippy::needless_collect)]
|
||||||
let lines: Vec<_> = s
|
let lines: Vec<_> = s
|
||||||
.lines()
|
.lines()
|
||||||
.filter_map(|x| {
|
.filter_map(|x| {
|
||||||
|
@ -55,15 +62,14 @@ impl Command for ListGitBranches {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(Value::List {
|
Ok(lines
|
||||||
vals: lines,
|
.into_iter()
|
||||||
span: call.head,
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Cd;
|
pub struct Cd;
|
||||||
|
|
||||||
impl Command for Cd {
|
impl Command for Cd {
|
||||||
|
@ -20,11 +21,12 @@ impl Command for Cd {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let path: Option<String> = call.opt(context, 0)?;
|
let path: Option<String> = call.opt(engine_state, stack, 0)?;
|
||||||
|
|
||||||
let path = match path {
|
let path = match path {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
|
@ -40,7 +42,7 @@ impl Command for Cd {
|
||||||
|
|
||||||
//FIXME: this only changes the current scope, but instead this environment variable
|
//FIXME: this only changes the current scope, but instead this environment variable
|
||||||
//should probably be a block that loads the information from the state in the overlay
|
//should probably be a block that loads the information from the state in the overlay
|
||||||
context.add_env_var("PWD".into(), path);
|
stack.add_env_var("PWD".into(), path);
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,16 @@
|
||||||
use std::env::current_dir;
|
use std::env::current_dir;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::interactive_helper::get_confirmation;
|
use super::util::get_interactive_confirmation;
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape};
|
||||||
|
|
||||||
use crate::filesystem::util::FileStructure;
|
use crate::filesystem::util::FileStructure;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Cp;
|
pub struct Cp;
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
|
@ -37,12 +38,13 @@ impl Command for Cp {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let source: String = call.req(context, 0)?;
|
let source: String = call.req(engine_state, stack, 0)?;
|
||||||
let destination: String = call.req(context, 1)?;
|
let destination: String = call.req(engine_state, stack, 1)?;
|
||||||
let interactive = call.has_flag("interactive");
|
let interactive = call.has_flag("interactive");
|
||||||
let force = call.has_flag("force");
|
let force = call.has_flag("force");
|
||||||
|
|
||||||
|
@ -88,7 +90,7 @@ impl Command for Cp {
|
||||||
destination.file_name().unwrap().to_str().unwrap()
|
destination.file_name().unwrap().to_str().unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let input = get_confirmation(prompt)?;
|
let input = get_interactive_confirmation(prompt)?;
|
||||||
|
|
||||||
if !input {
|
if !input {
|
||||||
remove.push(index);
|
remove.push(index);
|
||||||
|
@ -202,6 +204,6 @@ impl Command for Cp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
use dialoguer::Input;
|
|
||||||
use std::error::Error;
|
|
||||||
|
|
||||||
pub fn get_confirmation(prompt: String) -> Result<bool, Box<dyn Error>> {
|
|
||||||
let input = Input::new()
|
|
||||||
.with_prompt(prompt)
|
|
||||||
.validate_with(|c_input: &String| -> Result<(), String> {
|
|
||||||
if c_input.len() == 1
|
|
||||||
&& (c_input == "y" || c_input == "Y" || c_input == "n" || c_input == "N")
|
|
||||||
{
|
|
||||||
Ok(())
|
|
||||||
} else if c_input.len() > 1 {
|
|
||||||
Err("Enter only one letter (Y/N)".to_string())
|
|
||||||
} else {
|
|
||||||
Err("Input not valid".to_string())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.default("Y/N".into())
|
|
||||||
.interact_text()?;
|
|
||||||
|
|
||||||
if input == "y" || input == "Y" {
|
|
||||||
Ok(true)
|
|
||||||
} else {
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,10 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use nu_engine::eval_expression;
|
use nu_engine::eval_expression;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{IntoValueStream, Signature, SyntaxShape, Value};
|
use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, Signature, SyntaxShape, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Ls;
|
pub struct Ls;
|
||||||
|
|
||||||
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
|
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
|
||||||
|
@ -26,12 +27,13 @@ impl Command for Ls {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let pattern = if let Some(expr) = call.positional.get(0) {
|
let pattern = if let Some(expr) = call.positional.get(0) {
|
||||||
let result = eval_expression(context, expr)?;
|
let result = eval_expression(engine_state, stack, expr)?;
|
||||||
let mut result = result.as_string()?;
|
let mut result = result.as_string()?;
|
||||||
|
|
||||||
let path = std::path::Path::new(&result);
|
let path = std::path::Path::new(&result);
|
||||||
|
@ -50,8 +52,7 @@ impl Command for Ls {
|
||||||
let call_span = call.head;
|
let call_span = call.head;
|
||||||
let glob = glob::glob(&pattern).unwrap();
|
let glob = glob::glob(&pattern).unwrap();
|
||||||
|
|
||||||
Ok(Value::Stream {
|
Ok(glob
|
||||||
stream: glob
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| match x {
|
.map(move |x| match x {
|
||||||
Ok(path) => match std::fs::symlink_metadata(&path) {
|
Ok(path) => match std::fs::symlink_metadata(&path) {
|
||||||
|
@ -68,9 +69,9 @@ impl Command for Ls {
|
||||||
span: call_span,
|
span: call_span,
|
||||||
},
|
},
|
||||||
if is_file {
|
if is_file {
|
||||||
Value::string("File", call_span)
|
Value::string("file", call_span)
|
||||||
} else if is_dir {
|
} else if is_dir {
|
||||||
Value::string("Dir", call_span)
|
Value::string("dir", call_span)
|
||||||
} else {
|
} else {
|
||||||
Value::Nothing { span: call_span }
|
Value::Nothing { span: call_span }
|
||||||
},
|
},
|
||||||
|
@ -111,8 +112,6 @@ impl Command for Ls {
|
||||||
},
|
},
|
||||||
_ => Value::Nothing { span: call_span },
|
_ => Value::Nothing { span: call_span },
|
||||||
})
|
})
|
||||||
.into_value_stream(),
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
span: call_span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,12 @@ use std::env::current_dir;
|
||||||
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, SyntaxShape, Value, ValueStream};
|
use nu_protocol::{
|
||||||
|
IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Mkdir;
|
pub struct Mkdir;
|
||||||
|
|
||||||
impl Command for Mkdir {
|
impl Command for Mkdir {
|
||||||
|
@ -29,13 +32,14 @@ impl Command for Mkdir {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let path = current_dir()?;
|
let path = current_dir()?;
|
||||||
let mut directories = call
|
let mut directories = call
|
||||||
.rest::<String>(context, 0)?
|
.rest::<String>(engine_state, stack, 0)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|dir| path.join(dir))
|
.map(|dir| path.join(dir))
|
||||||
.peekable();
|
.peekable();
|
||||||
|
@ -67,8 +71,8 @@ impl Command for Mkdir {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let stream = ValueStream::from_stream(stream.into_iter());
|
Ok(stream
|
||||||
let span = call.head;
|
.into_iter()
|
||||||
Ok(Value::Stream { stream, span })
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
mod cd;
|
mod cd;
|
||||||
mod cp;
|
mod cp;
|
||||||
mod interactive_helper;
|
|
||||||
mod ls;
|
mod ls;
|
||||||
mod mkdir;
|
mod mkdir;
|
||||||
mod mv;
|
mod mv;
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
use std::env::current_dir;
|
use std::env::current_dir;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
use super::interactive_helper::get_confirmation;
|
use super::util::get_interactive_confirmation;
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Mv;
|
pub struct Mv;
|
||||||
|
|
||||||
#[allow(unused_must_use)]
|
#[allow(unused_must_use)]
|
||||||
|
@ -37,13 +38,14 @@ impl Command for Mv {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
// TODO: handle invalid directory or insufficient permissions when moving
|
// TODO: handle invalid directory or insufficient permissions when moving
|
||||||
let source: String = call.req(context, 0)?;
|
let source: String = call.req(engine_state, stack, 0)?;
|
||||||
let destination: String = call.req(context, 1)?;
|
let destination: String = call.req(engine_state, stack, 1)?;
|
||||||
let interactive = call.has_flag("interactive");
|
let interactive = call.has_flag("interactive");
|
||||||
let force = call.has_flag("force");
|
let force = call.has_flag("force");
|
||||||
|
|
||||||
|
@ -74,7 +76,7 @@ impl Command for Mv {
|
||||||
destination.file_name().unwrap().to_str().unwrap()
|
destination.file_name().unwrap().to_str().unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let input = get_confirmation(prompt)?;
|
let input = get_interactive_confirmation(prompt)?;
|
||||||
|
|
||||||
if !input {
|
if !input {
|
||||||
remove.push(index);
|
remove.push(index);
|
||||||
|
@ -128,7 +130,7 @@ impl Command for Mv {
|
||||||
move_file(call, &entry, &destination)?
|
move_file(call, &entry, &destination)?
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,13 +3,16 @@ use std::env::current_dir;
|
||||||
use std::os::unix::prelude::FileTypeExt;
|
use std::os::unix::prelude::FileTypeExt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use super::interactive_helper::get_confirmation;
|
use super::util::get_interactive_confirmation;
|
||||||
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, SyntaxShape, Value, ValueStream};
|
use nu_protocol::{
|
||||||
|
IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Rm;
|
pub struct Rm;
|
||||||
|
|
||||||
// Where self.0 is the unexpanded target's positional index (i.e. call.positional[self.0].span)
|
// Where self.0 is the unexpanded target's positional index (i.e. call.positional[self.0].span)
|
||||||
|
@ -56,15 +59,20 @@ impl Command for Rm {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
rm(context, call)
|
rm(engine_state, stack, call)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn rm(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
fn rm(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
let trash = call.has_flag("trash");
|
let trash = call.has_flag("trash");
|
||||||
let permanent = call.has_flag("permanent");
|
let permanent = call.has_flag("permanent");
|
||||||
let interactive = call.has_flag("interactive");
|
let interactive = call.has_flag("interactive");
|
||||||
|
@ -95,7 +103,7 @@ fn rm(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
||||||
|
|
||||||
let current_path = current_dir()?;
|
let current_path = current_dir()?;
|
||||||
let mut paths = call
|
let mut paths = call
|
||||||
.rest::<String>(context, 0)?
|
.rest::<String>(engine_state, stack, 0)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|path| current_path.join(path))
|
.map(|path| current_path.join(path))
|
||||||
.peekable();
|
.peekable();
|
||||||
|
@ -134,7 +142,7 @@ fn rm(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
||||||
file.1.file_name().unwrap().to_str().unwrap()
|
file.1.file_name().unwrap().to_str().unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let input = get_confirmation(prompt)?;
|
let input = get_interactive_confirmation(prompt)?;
|
||||||
|
|
||||||
if !input {
|
if !input {
|
||||||
remove.push(index);
|
remove.push(index);
|
||||||
|
@ -164,11 +172,9 @@ fn rm(context: &EvaluationContext, call: &Call) -> Result<Value, ShellError> {
|
||||||
// let temp = rm_helper(call, args).flatten();
|
// let temp = rm_helper(call, args).flatten();
|
||||||
// let temp = input.flatten(call.head, move |_| rm_helper(call, args));
|
// let temp = input.flatten(call.head, move |_| rm_helper(call, args));
|
||||||
|
|
||||||
Ok(Value::Stream {
|
Ok(response
|
||||||
stream: ValueStream::from_stream(response.into_iter()),
|
.into_iter()
|
||||||
span: call.head,
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
})
|
|
||||||
|
|
||||||
// Ok(Value::Nothing { span })
|
// Ok(Value::Nothing { span })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,10 @@ use std::fs::OpenOptions;
|
||||||
|
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Touch;
|
pub struct Touch;
|
||||||
|
|
||||||
impl Command for Touch {
|
impl Command for Touch {
|
||||||
|
@ -28,12 +29,13 @@ impl Command for Touch {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let target: String = call.req(context, 0)?;
|
let target: String = call.req(engine_state, stack, 0)?;
|
||||||
let rest: Vec<String> = call.rest(context, 1)?;
|
let rest: Vec<String> = call.rest(engine_state, stack, 1)?;
|
||||||
|
|
||||||
for (index, item) in vec![target].into_iter().chain(rest).enumerate() {
|
for (index, item) in vec![target].into_iter().chain(rest).enumerate() {
|
||||||
match OpenOptions::new().write(true).create(true).open(&item) {
|
match OpenOptions::new().write(true).create(true).open(&item) {
|
||||||
|
@ -47,6 +49,6 @@ impl Command for Touch {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ use std::path::{Path, PathBuf};
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::ShellError;
|
use nu_protocol::ShellError;
|
||||||
|
|
||||||
|
use dialoguer::Input;
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FileStructure {
|
pub struct FileStructure {
|
||||||
pub resources: Vec<Resource>,
|
pub resources: Vec<Resource>,
|
||||||
|
@ -79,3 +82,27 @@ pub struct Resource {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Resource {}
|
impl Resource {}
|
||||||
|
|
||||||
|
pub fn get_interactive_confirmation(prompt: String) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let input = Input::new()
|
||||||
|
.with_prompt(prompt)
|
||||||
|
.validate_with(|c_input: &String| -> Result<(), String> {
|
||||||
|
if c_input.len() == 1
|
||||||
|
&& (c_input == "y" || c_input == "Y" || c_input == "n" || c_input == "N")
|
||||||
|
{
|
||||||
|
Ok(())
|
||||||
|
} else if c_input.len() > 1 {
|
||||||
|
Err("Enter only one letter (Y/N)".to_string())
|
||||||
|
} else {
|
||||||
|
Err("Input not valid".to_string())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.default("Y/N".into())
|
||||||
|
.interact_text()?;
|
||||||
|
|
||||||
|
if input == "y" || input == "Y" {
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Example, IntoValueStream, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, Span,
|
||||||
|
SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Each;
|
pub struct Each;
|
||||||
|
|
||||||
impl Command for Each {
|
impl Command for Each {
|
||||||
|
@ -43,8 +47,8 @@ impl Command for Each {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
example: "[1 2 3] | each { 2 * $it }",
|
example: "[1 2 3] | each { 2 * $it }",
|
||||||
description: "Multiplies elements in list",
|
description: "Multiplies elements in list",
|
||||||
result: Some(Value::Stream {
|
result: Some(Value::List {
|
||||||
stream: stream_test_1.into_iter().into_value_stream(),
|
vals: stream_test_1,
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
}),
|
}),
|
||||||
}]
|
}]
|
||||||
|
@ -52,33 +56,31 @@ impl Command for Each {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let block_id = call.positional[0]
|
let block_id = call.positional[0]
|
||||||
.as_block()
|
.as_block()
|
||||||
.expect("internal error: expected block");
|
.expect("internal error: expected block");
|
||||||
|
|
||||||
let numbered = call.has_flag("numbered");
|
let numbered = call.has_flag("numbered");
|
||||||
let context = context.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let engine_state = engine_state.clone();
|
||||||
|
let block = engine_state.get_block(block_id).clone();
|
||||||
|
let mut stack = stack.collect_captures(&block.captures);
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
Value::Range { val, .. } => Ok(Value::Stream {
|
PipelineData::Value(Value::Range { val, .. }) => Ok(val
|
||||||
stream: val
|
|
||||||
.into_range_iter()?
|
.into_range_iter()?
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(move |(idx, x)| {
|
.map(move |(idx, x)| {
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
|
|
||||||
let state = context.enter_scope();
|
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
if let Some(var_id) = &var.var_id {
|
if let Some(var_id) = &var.var_id {
|
||||||
if numbered {
|
if numbered {
|
||||||
state.add_var(
|
stack.add_var(
|
||||||
*var_id,
|
*var_id,
|
||||||
Value::Record {
|
Value::Record {
|
||||||
cols: vec!["index".into(), "item".into()],
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
@ -93,32 +95,25 @@ impl Command for Each {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
state.add_var(*var_id, x);
|
stack.add_var(*var_id, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match eval_block(&state, block, Value::nothing()) {
|
match eval_block(&engine_state, &mut stack, &block, PipelineData::new()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v.into_value(),
|
||||||
Err(error) => Value::Error { error },
|
Err(error) => Value::Error { error },
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into_value_stream(),
|
.into_pipeline_data(ctrlc)),
|
||||||
span: call.head,
|
PipelineData::Value(Value::List { vals: val, .. }) => Ok(val
|
||||||
}),
|
|
||||||
Value::List { vals: val, .. } => Ok(Value::Stream {
|
|
||||||
stream: val
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(move |(idx, x)| {
|
.map(move |(idx, x)| {
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
|
|
||||||
let state = context.enter_scope();
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
if let Some(var_id) = &var.var_id {
|
if let Some(var_id) = &var.var_id {
|
||||||
if numbered {
|
if numbered {
|
||||||
state.add_var(
|
stack.add_var(
|
||||||
*var_id,
|
*var_id,
|
||||||
Value::Record {
|
Value::Record {
|
||||||
cols: vec!["index".into(), "item".into()],
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
@ -133,31 +128,24 @@ impl Command for Each {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
state.add_var(*var_id, x);
|
stack.add_var(*var_id, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match eval_block(&state, block, Value::nothing()) {
|
match eval_block(&engine_state, &mut stack, &block, PipelineData::new()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v.into_value(),
|
||||||
Err(error) => Value::Error { error },
|
Err(error) => Value::Error { error },
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into_value_stream(),
|
.into_pipeline_data(ctrlc)),
|
||||||
span: call.head,
|
PipelineData::Stream(stream) => Ok(stream
|
||||||
}),
|
|
||||||
Value::Stream { stream, .. } => Ok(Value::Stream {
|
|
||||||
stream: stream
|
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(move |(idx, x)| {
|
.map(move |(idx, x)| {
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
|
|
||||||
let state = context.enter_scope();
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
if let Some(var_id) = &var.var_id {
|
if let Some(var_id) = &var.var_id {
|
||||||
if numbered {
|
if numbered {
|
||||||
state.add_var(
|
stack.add_var(
|
||||||
*var_id,
|
*var_id,
|
||||||
Value::Record {
|
Value::Record {
|
||||||
cols: vec!["index".into(), "item".into()],
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
@ -172,31 +160,29 @@ impl Command for Each {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
state.add_var(*var_id, x);
|
stack.add_var(*var_id, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match eval_block(&state, block, Value::nothing()) {
|
match eval_block(&engine_state, &mut stack, &block, PipelineData::new()) {
|
||||||
Ok(v) => v,
|
Ok(v) => v.into_value(),
|
||||||
Err(error) => Value::Error { error },
|
Err(error) => Value::Error { error },
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into_value_stream(),
|
.into_pipeline_data(ctrlc)),
|
||||||
span: call.head,
|
PipelineData::Value(Value::Record { cols, vals, .. }) => {
|
||||||
}),
|
|
||||||
Value::Record { cols, vals, .. } => {
|
|
||||||
let mut output_cols = vec![];
|
let mut output_cols = vec![];
|
||||||
let mut output_vals = vec![];
|
let mut output_vals = vec![];
|
||||||
|
|
||||||
for (col, val) in cols.into_iter().zip(vals.into_iter()) {
|
for (col, val) in cols.into_iter().zip(vals.into_iter()) {
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
let state = context.enter_scope();
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
if let Some(var_id) = &var.var_id {
|
if let Some(var_id) = &var.var_id {
|
||||||
state.add_var(
|
stack.add_var(
|
||||||
*var_id,
|
*var_id,
|
||||||
Value::Record {
|
Value::Record {
|
||||||
cols: vec!["column".into(), "value".into()],
|
cols: vec!["column".into(), "value".into()],
|
||||||
|
@ -213,17 +199,17 @@ impl Command for Each {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match eval_block(&state, block, Value::nothing())? {
|
match eval_block(&engine_state, &mut stack, block, PipelineData::new())? {
|
||||||
Value::Record {
|
PipelineData::Value(Value::Record {
|
||||||
mut cols, mut vals, ..
|
mut cols, mut vals, ..
|
||||||
} => {
|
}) => {
|
||||||
// TODO check that the lengths match when traversing record
|
// TODO check that the lengths match when traversing record
|
||||||
output_cols.append(&mut cols);
|
output_cols.append(&mut cols);
|
||||||
output_vals.append(&mut vals);
|
output_vals.append(&mut vals);
|
||||||
}
|
}
|
||||||
x => {
|
x => {
|
||||||
output_cols.push(col);
|
output_cols.push(col);
|
||||||
output_vals.push(x);
|
output_vals.push(x.into_value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,20 +218,19 @@ impl Command for Each {
|
||||||
cols: output_cols,
|
cols: output_cols,
|
||||||
vals: output_vals,
|
vals: output_vals,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
|
||||||
}
|
}
|
||||||
x => {
|
.into_pipeline_data())
|
||||||
let engine_state = context.engine_state.borrow();
|
}
|
||||||
|
PipelineData::Value(x) => {
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
let state = context.enter_scope();
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
if let Some(var_id) = &var.var_id {
|
if let Some(var_id) = &var.var_id {
|
||||||
state.add_var(*var_id, x);
|
stack.add_var(*var_id, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_block(&state, block, Value::nothing())
|
eval_block(&engine_state, &mut stack, block, PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::{Call, CellPath};
|
use nu_protocol::ast::{Call, CellPath};
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{IntoPipelineData, PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Get;
|
pub struct Get;
|
||||||
|
|
||||||
impl Command for Get {
|
impl Command for Get {
|
||||||
|
@ -24,12 +25,15 @@ impl Command for Get {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let cell_path: CellPath = call.req(context, 0)?;
|
let cell_path: CellPath = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
input.follow_cell_path(&cell_path.members)
|
input
|
||||||
|
.follow_cell_path(&cell_path.members)
|
||||||
|
.map(|x| x.into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
86
crates/nu-command/src/filters/last.rs
Normal file
86
crates/nu-command/src/filters/last.rs
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
use nu_engine::CallExt;
|
||||||
|
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{
|
||||||
|
Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape,
|
||||||
|
Value,
|
||||||
|
};
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Last;
|
||||||
|
|
||||||
|
impl Command for Last {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"last"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("last").optional(
|
||||||
|
"rows",
|
||||||
|
SyntaxShape::Int,
|
||||||
|
"starting from the back, the number of rows to return",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Show only the last number of rows."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
example: "[1,2,3] | last 2",
|
||||||
|
description: "Get the last 2 items",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![Value::test_int(2), Value::test_int(3)],
|
||||||
|
span: Span::unknown(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let rows: Option<i64> = call.opt(engine_state, stack, 0)?;
|
||||||
|
let v: Vec<_> = input.into_iter().collect();
|
||||||
|
let vlen: i64 = v.len() as i64;
|
||||||
|
let beginning_rows_to_skip = rows_to_skip(vlen, rows);
|
||||||
|
|
||||||
|
let iter = v
|
||||||
|
.into_iter()
|
||||||
|
.skip(beginning_rows_to_skip.try_into().unwrap());
|
||||||
|
|
||||||
|
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rows_to_skip(count: i64, rows: Option<i64>) -> i64 {
|
||||||
|
let end_rows_desired = if let Some(quantity) = rows {
|
||||||
|
quantity
|
||||||
|
} else {
|
||||||
|
1
|
||||||
|
};
|
||||||
|
|
||||||
|
if end_rows_desired < count {
|
||||||
|
count - end_rows_desired
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(Last {})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,8 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, Value};
|
use nu_protocol::{IntoPipelineData, PipelineData, Signature, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Length;
|
pub struct Length;
|
||||||
|
|
||||||
impl Command for Length {
|
impl Command for Length {
|
||||||
|
@ -19,35 +20,22 @@ impl Command for Length {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
match input {
|
match input {
|
||||||
Value::List { vals: val, .. } => {
|
PipelineData::Value(Value::Nothing { .. }) => Ok(Value::Int {
|
||||||
let length = val.len();
|
|
||||||
|
|
||||||
Ok(Value::Int {
|
|
||||||
val: length as i64,
|
|
||||||
span: call.head,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Value::Stream { stream, .. } => {
|
|
||||||
let length = stream.count();
|
|
||||||
|
|
||||||
Ok(Value::Int {
|
|
||||||
val: length as i64,
|
|
||||||
span: call.head,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Value::Nothing { .. } => Ok(Value::Int {
|
|
||||||
val: 0,
|
val: 0,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}),
|
}
|
||||||
|
.into_pipeline_data()),
|
||||||
_ => Ok(Value::Int {
|
_ => Ok(Value::Int {
|
||||||
val: 1,
|
val: input.into_iter().count() as i64,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}),
|
}
|
||||||
|
.into_pipeline_data()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, Value, ValueStream};
|
use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Lines;
|
pub struct Lines;
|
||||||
|
|
||||||
const SPLIT_CHAR: char = '\n';
|
const SPLIT_CHAR: char = '\n';
|
||||||
|
@ -24,17 +22,17 @@ impl Command for Lines {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let span = call.head;
|
|
||||||
match input {
|
match input {
|
||||||
#[allow(clippy::needless_collect)]
|
#[allow(clippy::needless_collect)]
|
||||||
// Collect is needed because the string may not live long enough for
|
// Collect is needed because the string may not live long enough for
|
||||||
// the Rc structure to continue using it. If split could take ownership
|
// the Rc structure to continue using it. If split could take ownership
|
||||||
// of the split values, then this wouldn't be needed
|
// of the split values, then this wouldn't be needed
|
||||||
Value::String { val, span } => {
|
PipelineData::Value(Value::String { val, span }) => {
|
||||||
let lines = val
|
let lines = val
|
||||||
.split(SPLIT_CHAR)
|
.split(SPLIT_CHAR)
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
|
@ -48,12 +46,9 @@ impl Command for Lines {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(Value::Stream {
|
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
stream: ValueStream(Rc::new(RefCell::new(iter))),
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Value::Stream { stream, span: _ } => {
|
PipelineData::Stream(stream) => {
|
||||||
let iter = stream
|
let iter = stream
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|value| {
|
.filter_map(|value| {
|
||||||
|
@ -79,12 +74,9 @@ impl Command for Lines {
|
||||||
})
|
})
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
Ok(Value::Stream {
|
Ok(iter.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
stream: ValueStream(Rc::new(RefCell::new(iter))),
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
val => Err(ShellError::UnsupportedInput(
|
PipelineData::Value(val) => Err(ShellError::UnsupportedInput(
|
||||||
format!("Not supported input: {}", val.as_string()?),
|
format!("Not supported input: {}", val.as_string()?),
|
||||||
call.head,
|
call.head,
|
||||||
)),
|
)),
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
mod each;
|
mod each;
|
||||||
mod get;
|
mod get;
|
||||||
|
mod last;
|
||||||
mod length;
|
mod length;
|
||||||
mod lines;
|
mod lines;
|
||||||
|
mod par_each;
|
||||||
mod select;
|
mod select;
|
||||||
mod where_;
|
mod where_;
|
||||||
mod wrap;
|
mod wrap;
|
||||||
|
|
||||||
pub use each::Each;
|
pub use each::Each;
|
||||||
pub use get::Get;
|
pub use get::Get;
|
||||||
|
pub use last::Last;
|
||||||
pub use length::Length;
|
pub use length::Length;
|
||||||
pub use lines::Lines;
|
pub use lines::Lines;
|
||||||
|
pub use par_each::ParEach;
|
||||||
pub use select::Select;
|
pub use select::Select;
|
||||||
pub use where_::Where;
|
pub use where_::Where;
|
||||||
pub use wrap::Wrap;
|
pub use wrap::Wrap;
|
||||||
|
|
256
crates/nu-command/src/filters/par_each.rs
Normal file
256
crates/nu-command/src/filters/par_each.rs
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
use nu_engine::eval_block;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{
|
||||||
|
Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, SyntaxShape,
|
||||||
|
Value,
|
||||||
|
};
|
||||||
|
use rayon::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ParEach;
|
||||||
|
|
||||||
|
impl Command for ParEach {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"par-each"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Run a block on each element of input in parallel"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
|
Signature::build("par-each")
|
||||||
|
.required(
|
||||||
|
"block",
|
||||||
|
SyntaxShape::Block(Some(vec![SyntaxShape::Any])),
|
||||||
|
"the block to run",
|
||||||
|
)
|
||||||
|
.switch("numbered", "iterate with an index", Some('n'))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
example: "[1 2 3] | par-each { 2 * $it }",
|
||||||
|
description: "Multiplies elements in list",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let block_id = call.positional[0]
|
||||||
|
.as_block()
|
||||||
|
.expect("internal error: expected block");
|
||||||
|
|
||||||
|
let numbered = call.has_flag("numbered");
|
||||||
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let engine_state = engine_state.clone();
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
let mut stack = stack.collect_captures(&block.captures);
|
||||||
|
let span = call.head;
|
||||||
|
|
||||||
|
match input {
|
||||||
|
PipelineData::Value(Value::Range { val, .. }) => Ok(val
|
||||||
|
.into_range_iter()?
|
||||||
|
.enumerate()
|
||||||
|
.par_bridge()
|
||||||
|
.map(move |(idx, x)| {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
if numbered {
|
||||||
|
stack.add_var(
|
||||||
|
*var_id,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: idx as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
x,
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
stack.add_var(*var_id, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match eval_block(&engine_state, &mut stack, block, PipelineData::new()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(error) => Value::Error { error }.into_pipeline_data(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.into_pipeline_data(ctrlc)),
|
||||||
|
PipelineData::Value(Value::List { vals: val, .. }) => Ok(val
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.par_bridge()
|
||||||
|
.map(move |(idx, x)| {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
if numbered {
|
||||||
|
stack.add_var(
|
||||||
|
*var_id,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: idx as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
x,
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
stack.add_var(*var_id, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match eval_block(&engine_state, &mut stack, block, PipelineData::new()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(error) => Value::Error { error }.into_pipeline_data(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.into_pipeline_data(ctrlc)),
|
||||||
|
PipelineData::Stream(stream) => Ok(stream
|
||||||
|
.enumerate()
|
||||||
|
.par_bridge()
|
||||||
|
.map(move |(idx, x)| {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
if numbered {
|
||||||
|
stack.add_var(
|
||||||
|
*var_id,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["index".into(), "item".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::Int {
|
||||||
|
val: idx as i64,
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
x,
|
||||||
|
],
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
stack.add_var(*var_id, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match eval_block(&engine_state, &mut stack, block, PipelineData::new()) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(error) => Value::Error { error }.into_pipeline_data(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.into_pipeline_data(ctrlc)),
|
||||||
|
PipelineData::Value(Value::Record { cols, vals, .. }) => {
|
||||||
|
let mut output_cols = vec![];
|
||||||
|
let mut output_vals = vec![];
|
||||||
|
|
||||||
|
for (col, val) in cols.into_iter().zip(vals.into_iter()) {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
stack.add_var(
|
||||||
|
*var_id,
|
||||||
|
Value::Record {
|
||||||
|
cols: vec!["column".into(), "value".into()],
|
||||||
|
vals: vec![
|
||||||
|
Value::String {
|
||||||
|
val: col.clone(),
|
||||||
|
span: call.head,
|
||||||
|
},
|
||||||
|
val,
|
||||||
|
],
|
||||||
|
span: call.head,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match eval_block(&engine_state, &mut stack, block, PipelineData::new())? {
|
||||||
|
PipelineData::Value(Value::Record {
|
||||||
|
mut cols, mut vals, ..
|
||||||
|
}) => {
|
||||||
|
// TODO check that the lengths match when traversing record
|
||||||
|
output_cols.append(&mut cols);
|
||||||
|
output_vals.append(&mut vals);
|
||||||
|
}
|
||||||
|
x => {
|
||||||
|
output_cols.push(col);
|
||||||
|
output_vals.push(x.into_value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Value::Record {
|
||||||
|
cols: output_cols,
|
||||||
|
vals: output_vals,
|
||||||
|
span: call.head,
|
||||||
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
|
}
|
||||||
|
PipelineData::Value(x) => {
|
||||||
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
if let Some(var) = block.signature.get_positional(0) {
|
||||||
|
if let Some(var_id) = &var.var_id {
|
||||||
|
stack.add_var(*var_id, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
eval_block(&engine_state, &mut stack, block, PipelineData::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(ParEach {})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,12 @@
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::{Call, CellPath};
|
use nu_protocol::ast::{Call, CellPath};
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Example, IntoValueStream, ShellError, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature,
|
||||||
|
Span, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Select;
|
pub struct Select;
|
||||||
|
|
||||||
impl Command for Select {
|
impl Command for Select {
|
||||||
|
@ -24,14 +28,15 @@ impl Command for Select {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let columns: Vec<CellPath> = call.rest(context, 0)?;
|
let columns: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
|
|
||||||
select(span, columns, input)
|
select(engine_state, span, columns, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -50,16 +55,21 @@ impl Command for Select {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select(span: Span, columns: Vec<CellPath>, input: Value) -> Result<Value, ShellError> {
|
fn select(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
span: Span,
|
||||||
|
columns: Vec<CellPath>,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
if columns.is_empty() {
|
if columns.is_empty() {
|
||||||
return Err(ShellError::CantFindColumn(span, input.span()?));
|
return Err(ShellError::CantFindColumn(span, span)); //FIXME?
|
||||||
}
|
}
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
Value::List {
|
PipelineData::Value(Value::List {
|
||||||
vals: input_vals,
|
vals: input_vals,
|
||||||
span,
|
span,
|
||||||
} => {
|
}) => {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
for input_val in input_vals {
|
for input_val in input_vals {
|
||||||
|
@ -76,10 +86,11 @@ fn select(span: Span, columns: Vec<CellPath>, input: Value) -> Result<Value, She
|
||||||
output.push(Value::Record { cols, vals, span })
|
output.push(Value::Record { cols, vals, span })
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::List { vals: output, span })
|
Ok(output
|
||||||
|
.into_iter()
|
||||||
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
}
|
}
|
||||||
Value::Stream { stream, span } => Ok(Value::Stream {
|
PipelineData::Stream(stream) => Ok(stream
|
||||||
stream: stream
|
|
||||||
.map(move |x| {
|
.map(move |x| {
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
@ -99,10 +110,8 @@ fn select(span: Span, columns: Vec<CellPath>, input: Value) -> Result<Value, She
|
||||||
|
|
||||||
Value::Record { cols, vals, span }
|
Value::Record { cols, vals, span }
|
||||||
})
|
})
|
||||||
.into_value_stream(),
|
.into_pipeline_data(engine_state.ctrlc.clone())),
|
||||||
span,
|
PipelineData::Value(v) => {
|
||||||
}),
|
|
||||||
v => {
|
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
|
@ -114,7 +123,7 @@ fn select(span: Span, columns: Vec<CellPath>, input: Value) -> Result<Value, She
|
||||||
vals.push(result);
|
vals.push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Record { cols, vals, span })
|
Ok(Value::Record { cols, vals, span }.into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
use nu_engine::eval_expression;
|
use nu_engine::eval_expression;
|
||||||
use nu_protocol::ast::{Call, Expr, Expression};
|
use nu_protocol::ast::{Call, Expr, Expression};
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{IntoValueStream, ShellError, Signature, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature,
|
||||||
|
SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Where;
|
pub struct Where;
|
||||||
|
|
||||||
impl Command for Where {
|
impl Command for Where {
|
||||||
|
@ -20,13 +24,18 @@ impl Command for Where {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let cond = call.positional[0].clone();
|
let cond = call.positional[0].clone();
|
||||||
|
|
||||||
let context = context.enter_scope();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
let engine_state = engine_state.clone();
|
||||||
|
|
||||||
|
// FIXME: very expensive
|
||||||
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
let (var_id, cond) = match cond {
|
let (var_id, cond) = match cond {
|
||||||
Expression {
|
Expression {
|
||||||
|
@ -37,54 +46,40 @@ impl Command for Where {
|
||||||
};
|
};
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
Value::Stream { stream, span } => {
|
PipelineData::Stream(stream) => Ok(stream
|
||||||
let output_stream = stream
|
|
||||||
.filter(move |value| {
|
.filter(move |value| {
|
||||||
context.add_var(var_id, value.clone());
|
stack.add_var(var_id, value.clone());
|
||||||
|
|
||||||
let result = eval_expression(&context, &cond);
|
let result = eval_expression(&engine_state, &mut stack, &cond);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(result) => result.is_true(),
|
Ok(result) => result.is_true(),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into_value_stream();
|
.into_pipeline_data(ctrlc)),
|
||||||
|
PipelineData::Value(Value::List { vals, .. }) => Ok(vals
|
||||||
Ok(Value::Stream {
|
|
||||||
stream: output_stream,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Value::List { vals, span } => {
|
|
||||||
let output_stream = vals
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(move |value| {
|
.filter(move |value| {
|
||||||
context.add_var(var_id, value.clone());
|
stack.add_var(var_id, value.clone());
|
||||||
|
|
||||||
let result = eval_expression(&context, &cond);
|
let result = eval_expression(&engine_state, &mut stack, &cond);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(result) => result.is_true(),
|
Ok(result) => result.is_true(),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into_value_stream();
|
.into_pipeline_data(ctrlc)),
|
||||||
|
PipelineData::Value(x) => {
|
||||||
|
stack.add_var(var_id, x.clone());
|
||||||
|
|
||||||
Ok(Value::Stream {
|
let result = eval_expression(&engine_state, &mut stack, &cond)?;
|
||||||
stream: output_stream,
|
|
||||||
span,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
x => {
|
|
||||||
context.add_var(var_id, x.clone());
|
|
||||||
|
|
||||||
let result = eval_expression(&context, &cond)?;
|
|
||||||
|
|
||||||
if result.is_true() {
|
if result.is_true() {
|
||||||
Ok(x)
|
Ok(x.into_pipeline_data())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{IntoValueStream, Signature, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Wrap;
|
pub struct Wrap;
|
||||||
|
|
||||||
impl Command for Wrap {
|
impl Command for Wrap {
|
||||||
|
@ -20,40 +23,36 @@ impl Command for Wrap {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
let name: String = call.req(context, 0)?;
|
let name: String = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
Value::List { vals, .. } => Ok(Value::List {
|
PipelineData::Value(Value::List { vals, .. }) => Ok(vals
|
||||||
vals: vals
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |x| Value::Record {
|
.map(move |x| Value::Record {
|
||||||
cols: vec![name.clone()],
|
cols: vec![name.clone()],
|
||||||
vals: vec![x],
|
vals: vec![x],
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
.collect(),
|
.into_pipeline_data(engine_state.ctrlc.clone())),
|
||||||
span,
|
PipelineData::Stream(stream) => Ok(stream
|
||||||
}),
|
|
||||||
Value::Stream { stream, .. } => Ok(Value::Stream {
|
|
||||||
stream: stream
|
|
||||||
.map(move |x| Value::Record {
|
.map(move |x| Value::Record {
|
||||||
cols: vec![name.clone()],
|
cols: vec![name.clone()],
|
||||||
vals: vec![x],
|
vals: vec![x],
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
.into_value_stream(),
|
.into_pipeline_data(engine_state.ctrlc.clone())),
|
||||||
span,
|
PipelineData::Value(input) => Ok(Value::Record {
|
||||||
}),
|
|
||||||
_ => Ok(Value::Record {
|
|
||||||
cols: vec![name],
|
cols: vec![name],
|
||||||
vals: vec![input],
|
vals: vec![input],
|
||||||
span,
|
span,
|
||||||
}),
|
}
|
||||||
|
.into_pipeline_data()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, Value};
|
use nu_protocol::{PipelineData, ShellError, Signature};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct From;
|
pub struct From;
|
||||||
|
|
||||||
impl Command for From {
|
impl Command for From {
|
||||||
|
@ -19,10 +20,11 @@ impl Command for From {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
_call: &Call,
|
_call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, ShellError> {
|
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||||
Ok(Value::nothing())
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Example, IntoValueStream, ShellError, Signature, Span, Value};
|
use nu_protocol::{
|
||||||
|
Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError, Signature,
|
||||||
|
Span, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct FromJson;
|
pub struct FromJson;
|
||||||
|
|
||||||
impl Command for FromJson {
|
impl Command for FromJson {
|
||||||
|
@ -67,11 +71,12 @@ impl Command for FromJson {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, ShellError> {
|
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||||
let span = input.span()?;
|
let span = call.head;
|
||||||
let mut string_input = input.collect_string();
|
let mut string_input = input.collect_string();
|
||||||
string_input.push('\n');
|
string_input.push('\n');
|
||||||
|
|
||||||
|
@ -79,8 +84,7 @@ impl Command for FromJson {
|
||||||
if call.has_flag("objects") {
|
if call.has_flag("objects") {
|
||||||
#[allow(clippy::needless_collect)]
|
#[allow(clippy::needless_collect)]
|
||||||
let lines: Vec<String> = string_input.lines().map(|x| x.to_string()).collect();
|
let lines: Vec<String> = string_input.lines().map(|x| x.to_string()).collect();
|
||||||
Ok(Value::Stream {
|
Ok(lines
|
||||||
stream: lines
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |mut x| {
|
.map(move |mut x| {
|
||||||
x.push('\n');
|
x.push('\n');
|
||||||
|
@ -89,11 +93,9 @@ impl Command for FromJson {
|
||||||
Err(error) => Value::Error { error },
|
Err(error) => Value::Error { error },
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.into_value_stream(),
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
span,
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
convert_string_to_value(string_input, span)
|
Ok(convert_string_to_value(string_input, span)?.into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ mod experimental;
|
||||||
mod filesystem;
|
mod filesystem;
|
||||||
mod filters;
|
mod filters;
|
||||||
mod formats;
|
mod formats;
|
||||||
|
mod math;
|
||||||
mod strings;
|
mod strings;
|
||||||
mod system;
|
mod system;
|
||||||
mod viewers;
|
mod viewers;
|
||||||
|
@ -20,6 +21,7 @@ pub use experimental::*;
|
||||||
pub use filesystem::*;
|
pub use filesystem::*;
|
||||||
pub use filters::*;
|
pub use filters::*;
|
||||||
pub use formats::*;
|
pub use formats::*;
|
||||||
|
pub use math::*;
|
||||||
pub use strings::*;
|
pub use strings::*;
|
||||||
pub use system::*;
|
pub use system::*;
|
||||||
pub use viewers::*;
|
pub use viewers::*;
|
||||||
|
|
84
crates/nu-command/src/math/abs.rs
Normal file
84
crates/nu-command/src/math/abs.rs
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SubCommand;
|
||||||
|
|
||||||
|
impl Command for SubCommand {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"math abs"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("math abs")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Returns absolute values of a list of numbers"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
input.map(
|
||||||
|
move |value| abs_helper(value, head),
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Get absolute of each value in a list of numbers",
|
||||||
|
example: "[-50 -100.0 25] | math abs",
|
||||||
|
result: Some(Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::test_int(50),
|
||||||
|
Value::Float {
|
||||||
|
val: 100.0,
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
Value::test_int(25),
|
||||||
|
],
|
||||||
|
span: Span::unknown(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn abs_helper(val: Value, head: Span) -> Value {
|
||||||
|
match val {
|
||||||
|
Value::Int { val, span } => Value::int(val.abs(), span),
|
||||||
|
Value::Float { val, span } => Value::Float {
|
||||||
|
val: val.abs(),
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
Value::Duration { val, span } => Value::Duration {
|
||||||
|
val: val.abs(),
|
||||||
|
span,
|
||||||
|
},
|
||||||
|
_ => Value::Error {
|
||||||
|
error: ShellError::UnsupportedInput(
|
||||||
|
String::from("Only numerical values are supported"),
|
||||||
|
head,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(SubCommand {})
|
||||||
|
}
|
||||||
|
}
|
83
crates/nu-command/src/math/avg.rs
Normal file
83
crates/nu-command/src/math/avg.rs
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
use crate::math::reducers::{reducer_for, Reduce};
|
||||||
|
use crate::math::utils::run_with_function;
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SubCommand;
|
||||||
|
|
||||||
|
impl Command for SubCommand {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"math avg"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("math avg")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Finds the average of a list of numbers or tables"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
run_with_function(call, input, average)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Get the average of a list of numbers",
|
||||||
|
example: "[-50 100.0 25] | math avg",
|
||||||
|
result: Some(Value::Float {
|
||||||
|
val: 25.0,
|
||||||
|
span: Span::unknown(),
|
||||||
|
}),
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn average(values: &[Value], head: &Span) -> Result<Value, ShellError> {
|
||||||
|
let sum = reducer_for(Reduce::Summation);
|
||||||
|
let total = &sum(
|
||||||
|
Value::Int {
|
||||||
|
val: 0,
|
||||||
|
span: Span::unknown(),
|
||||||
|
},
|
||||||
|
values.to_vec(),
|
||||||
|
)?;
|
||||||
|
match total {
|
||||||
|
Value::Filesize { val, span } => Ok(Value::Filesize {
|
||||||
|
val: val / values.len() as i64,
|
||||||
|
span: *span,
|
||||||
|
}),
|
||||||
|
Value::Duration { val, span } => Ok(Value::Duration {
|
||||||
|
val: val / values.len() as i64,
|
||||||
|
span: *span,
|
||||||
|
}),
|
||||||
|
_ => total.div(
|
||||||
|
*head,
|
||||||
|
&Value::Int {
|
||||||
|
val: values.len() as i64,
|
||||||
|
span: *head,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(SubCommand {})
|
||||||
|
}
|
||||||
|
}
|
41
crates/nu-command/src/math/command.rs
Normal file
41
crates/nu-command/src/math/command.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
use nu_engine::get_full_help;
|
||||||
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{Command, EngineState, Stack},
|
||||||
|
IntoPipelineData, PipelineData, Signature, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct MathCommand;
|
||||||
|
|
||||||
|
impl Command for MathCommand {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"math"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("math")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Use mathematical functions as aggregate functions on a list of numbers or tables."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
Ok(Value::String {
|
||||||
|
val: get_full_help(
|
||||||
|
&MathCommand.signature(),
|
||||||
|
&MathCommand.examples(),
|
||||||
|
engine_state,
|
||||||
|
),
|
||||||
|
span: call.head,
|
||||||
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
|
}
|
||||||
|
}
|
9
crates/nu-command/src/math/mod.rs
Normal file
9
crates/nu-command/src/math/mod.rs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
mod abs;
|
||||||
|
mod avg;
|
||||||
|
pub mod command;
|
||||||
|
mod reducers;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
|
pub use abs::SubCommand as MathAbs;
|
||||||
|
pub use avg::SubCommand as MathAvg;
|
||||||
|
pub use command::MathCommand as Math;
|
60
crates/nu-command/src/math/reducers.rs
Normal file
60
crates/nu-command/src/math/reducers.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use nu_protocol::{ShellError, Span, Value};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub enum Reduce {
|
||||||
|
Summation,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reducer_for(
|
||||||
|
command: Reduce,
|
||||||
|
) -> Box<dyn Fn(Value, Vec<Value>) -> Result<Value, ShellError> + Send + Sync + 'static> {
|
||||||
|
match command {
|
||||||
|
Reduce::Summation => Box::new(|_, values| sum(values)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sum(data: Vec<Value>) -> Result<Value, ShellError> {
|
||||||
|
let initial_value = data.get(0);
|
||||||
|
|
||||||
|
let mut acc = match initial_value {
|
||||||
|
Some(Value::Filesize { span, .. }) => Ok(Value::Filesize {
|
||||||
|
val: 0,
|
||||||
|
span: *span,
|
||||||
|
}),
|
||||||
|
Some(Value::Duration { span, .. }) => Ok(Value::Duration {
|
||||||
|
val: 0,
|
||||||
|
span: *span,
|
||||||
|
}),
|
||||||
|
Some(Value::Int { span, .. }) | Some(Value::Float { span, .. }) => Ok(Value::Int {
|
||||||
|
val: 0,
|
||||||
|
span: *span,
|
||||||
|
}),
|
||||||
|
None => Err(ShellError::UnsupportedInput(
|
||||||
|
"Empty input".to_string(),
|
||||||
|
Span::unknown(),
|
||||||
|
)),
|
||||||
|
_ => Ok(Value::nothing()),
|
||||||
|
}?;
|
||||||
|
|
||||||
|
for value in &data {
|
||||||
|
match value {
|
||||||
|
Value::Int { .. }
|
||||||
|
| Value::Float { .. }
|
||||||
|
| Value::Filesize { .. }
|
||||||
|
| Value::Duration { .. } => {
|
||||||
|
let new_value = acc.add(acc.span().unwrap_or_else(|_| Span::unknown()), value);
|
||||||
|
if new_value.is_err() {
|
||||||
|
return new_value;
|
||||||
|
}
|
||||||
|
acc = new_value.expect("This should never trigger")
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
return Err(ShellError::UnsupportedInput(
|
||||||
|
"Attempted to compute the sum of a value that cannot be summed".to_string(),
|
||||||
|
other.span().unwrap_or_else(|_| Span::unknown()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(acc)
|
||||||
|
}
|
87
crates/nu-command/src/math/utils.rs
Normal file
87
crates/nu-command/src/math/utils.rs
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
use nu_protocol::ast::Call;
|
||||||
|
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Span, Value};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub type MathFunction = fn(values: &[Value], span: &Span) -> Result<Value, ShellError>;
|
||||||
|
|
||||||
|
pub fn run_with_function(
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
mf: MathFunction,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let name = call.head;
|
||||||
|
let res = calculate(input, name, mf);
|
||||||
|
match res {
|
||||||
|
Ok(v) => Ok(v.into_pipeline_data()),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn helper_for_tables(
|
||||||
|
values: PipelineData,
|
||||||
|
name: Span,
|
||||||
|
mf: MathFunction,
|
||||||
|
) -> Result<Value, ShellError> {
|
||||||
|
// If we are not dealing with Primitives, then perhaps we are dealing with a table
|
||||||
|
// Create a key for each column name
|
||||||
|
let mut column_values = HashMap::new();
|
||||||
|
for val in values {
|
||||||
|
if let Value::Record { cols, vals, .. } = val {
|
||||||
|
for (key, value) in cols.iter().zip(vals.iter()) {
|
||||||
|
column_values
|
||||||
|
.entry(key.clone())
|
||||||
|
.and_modify(|v: &mut Vec<Value>| v.push(value.clone()))
|
||||||
|
.or_insert_with(|| vec![value.clone()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// The mathematical function operates over the columns of the table
|
||||||
|
let mut column_totals = HashMap::new();
|
||||||
|
for (col_name, col_vals) in column_values {
|
||||||
|
if let Ok(out) = mf(&col_vals, &name) {
|
||||||
|
column_totals.insert(col_name, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if column_totals.keys().len() == 0 {
|
||||||
|
return Err(ShellError::UnsupportedInput(
|
||||||
|
"Unable to give a result with this input".to_string(),
|
||||||
|
name,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let (cols, vals) = column_totals
|
||||||
|
.into_iter()
|
||||||
|
.fold((vec![], vec![]), |mut acc, (k, v)| {
|
||||||
|
acc.0.push(k);
|
||||||
|
acc.1.push(v);
|
||||||
|
acc
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Value::Record {
|
||||||
|
cols,
|
||||||
|
vals,
|
||||||
|
span: name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn calculate(values: PipelineData, name: Span, mf: MathFunction) -> Result<Value, ShellError> {
|
||||||
|
match values {
|
||||||
|
PipelineData::Stream(_) => helper_for_tables(values, name, mf),
|
||||||
|
PipelineData::Value(Value::List { ref vals, .. }) => match &vals[..] {
|
||||||
|
[Value::Record { .. }, _end @ ..] => helper_for_tables(values, name, mf),
|
||||||
|
_ => mf(vals, &name),
|
||||||
|
},
|
||||||
|
PipelineData::Value(Value::Record { vals, cols, span }) => {
|
||||||
|
let new_vals: Result<Vec<Value>, ShellError> =
|
||||||
|
vals.into_iter().map(|val| mf(&[val], &name)).collect();
|
||||||
|
match new_vals {
|
||||||
|
Ok(vec) => Ok(Value::Record {
|
||||||
|
cols,
|
||||||
|
vals: vec,
|
||||||
|
span,
|
||||||
|
}),
|
||||||
|
Err(err) => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PipelineData::Value(val) => mf(&[val], &name),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,11 @@
|
||||||
use nu_engine::eval_expression;
|
use nu_engine::eval_expression;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Example, ShellError, Signature, Span, SyntaxShape, Value};
|
use nu_protocol::{
|
||||||
|
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct BuildString;
|
pub struct BuildString;
|
||||||
|
|
||||||
impl Command for BuildString {
|
impl Command for BuildString {
|
||||||
|
@ -41,20 +44,22 @@ impl Command for BuildString {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let output = call
|
let output = call
|
||||||
.positional
|
.positional
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| eval_expression(context, expr).map(|val| val.into_string()))
|
.map(|expr| eval_expression(engine_state, stack, expr).map(|val| val.into_string()))
|
||||||
.collect::<Result<Vec<String>, ShellError>>()?;
|
.collect::<Result<Vec<String>, ShellError>>()?;
|
||||||
|
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: output.join(""),
|
val: output.join(""),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,10 @@ extern crate unicode_segmentation;
|
||||||
use unicode_segmentation::UnicodeSegmentation;
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Example, ShellError, Signature, Span, Type, Value};
|
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, Type, Value};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Size;
|
pub struct Size;
|
||||||
|
|
||||||
impl Command for Size {
|
impl Command for Size {
|
||||||
|
@ -23,11 +24,12 @@ impl Command for Size {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
size(context, call, input)
|
size(engine_state, call, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -98,9 +100,14 @@ impl Command for Size {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn size(_context: &EvaluationContext, call: &Call, input: Value) -> Result<Value, ShellError> {
|
fn size(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
input.map(span, move |v| match v.as_string() {
|
input.map(
|
||||||
|
move |v| match v.as_string() {
|
||||||
Ok(s) => count(&s, span),
|
Ok(s) => count(&s, span),
|
||||||
Err(_) => Value::Error {
|
Err(_) => Value::Error {
|
||||||
error: ShellError::PipelineMismatch {
|
error: ShellError::PipelineMismatch {
|
||||||
|
@ -109,7 +116,9 @@ fn size(_context: &EvaluationContext, call: &Call, input: Value) -> Result<Value
|
||||||
origin: span,
|
origin: span,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
},
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn count(contents: &str, span: Span) -> Value {
|
fn count(contents: &str, span: Span) -> Value {
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
Example, ShellError, Signature, Span, Type, Value,
|
Example, PipelineData, ShellError, Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
impl Command for SubCommand {
|
impl Command for SubCommand {
|
||||||
|
@ -38,18 +39,26 @@ impl Command for SubCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
split_chars(call, input)
|
split_chars(engine_state, call, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_chars(call: &Call, input: Value) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
fn split_chars(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
|
|
||||||
Ok(input.flat_map(span, move |x| split_chars_helper(&x, span)))
|
input.flat_map(
|
||||||
|
move |x| split_chars_helper(&x, span),
|
||||||
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_chars_helper(v: &Value, name: Span) -> Vec<Value> {
|
fn split_chars_helper(v: &Value, name: Span) -> Vec<Value> {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
impl Command for SubCommand {
|
impl Command for SubCommand {
|
||||||
|
@ -33,27 +34,30 @@ impl Command for SubCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
split_column(context, call, input)
|
split_column(engine_state, stack, call, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_column(
|
fn split_column(
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let name_span = call.head;
|
let name_span = call.head;
|
||||||
let separator: Spanned<String> = call.req(context, 0)?;
|
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
let rest: Vec<Spanned<String>> = call.rest(context, 1)?;
|
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
|
||||||
let collapse_empty = call.has_flag("collapse-empty");
|
let collapse_empty = call.has_flag("collapse-empty");
|
||||||
|
|
||||||
input.map(name_span, move |x| {
|
input.flat_map(
|
||||||
split_column_helper(&x, &separator, &rest, collapse_empty, name_span)
|
move |x| split_column_helper(&x, &separator, &rest, collapse_empty, name_span),
|
||||||
})
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_column_helper(
|
fn split_column_helper(
|
||||||
|
@ -62,7 +66,7 @@ fn split_column_helper(
|
||||||
rest: &[Spanned<String>],
|
rest: &[Spanned<String>],
|
||||||
collapse_empty: bool,
|
collapse_empty: bool,
|
||||||
head: Span,
|
head: Span,
|
||||||
) -> Value {
|
) -> Vec<Value> {
|
||||||
if let Ok(s) = v.as_string() {
|
if let Ok(s) = v.as_string() {
|
||||||
let splitter = separator.item.replace("\\n", "\n");
|
let splitter = separator.item.replace("\\n", "\n");
|
||||||
|
|
||||||
|
@ -94,24 +98,21 @@ fn split_column_helper(
|
||||||
vals.push(Value::string(k, head));
|
vals.push(Value::string(k, head));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::List {
|
vec![Value::Record {
|
||||||
vals: vec![Value::Record {
|
|
||||||
cols,
|
cols,
|
||||||
vals,
|
vals,
|
||||||
span: head,
|
span: head,
|
||||||
}],
|
}]
|
||||||
span: head,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
match v.span() {
|
match v.span() {
|
||||||
Ok(span) => Value::Error {
|
Ok(span) => vec![Value::Error {
|
||||||
error: ShellError::PipelineMismatch {
|
error: ShellError::PipelineMismatch {
|
||||||
expected: Type::String,
|
expected: Type::String,
|
||||||
expected_span: head,
|
expected_span: head,
|
||||||
origin: span,
|
origin: span,
|
||||||
},
|
},
|
||||||
},
|
}],
|
||||||
Err(error) => Value::Error { error },
|
Err(error) => vec![Value::Error { error }],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use nu_engine::get_full_help;
|
use nu_engine::get_full_help;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
Signature, Value,
|
IntoPipelineData, PipelineData, Signature, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -23,14 +23,20 @@ impl Command for SplitCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&SplitCommand.signature(), &SplitCommand.examples(), context),
|
val: get_full_help(
|
||||||
|
&SplitCommand.signature(),
|
||||||
|
&SplitCommand.examples(),
|
||||||
|
engine_state,
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct SubCommand;
|
pub struct SubCommand;
|
||||||
|
|
||||||
impl Command for SubCommand {
|
impl Command for SubCommand {
|
||||||
|
@ -26,25 +27,28 @@ impl Command for SubCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
split_row(context, call, input)
|
split_row(engine_state, stack, call, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_row(
|
fn split_row(
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let name_span = call.head;
|
let name_span = call.head;
|
||||||
let separator: Spanned<String> = call.req(context, 0)?;
|
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
Ok(input.flat_map(name_span, move |x| {
|
input.flat_map(
|
||||||
split_row_helper(&x, &separator, name_span)
|
move |x| split_row_helper(&x, &separator, name_span),
|
||||||
}))
|
engine_state.ctrlc.clone(),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_row_helper(v: &Value, separator: &Spanned<String>, name: Span) -> Vec<Value> {
|
fn split_row_helper(v: &Value, separator: &Spanned<String>, name: Span) -> Vec<Value> {
|
||||||
|
|
|
@ -2,9 +2,10 @@ use std::time::Instant;
|
||||||
|
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{Signature, SyntaxShape, Value};
|
use nu_protocol::{PipelineData, Signature, SyntaxShape};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Benchmark;
|
pub struct Benchmark;
|
||||||
|
|
||||||
impl Command for Benchmark {
|
impl Command for Benchmark {
|
||||||
|
@ -26,23 +27,22 @@ impl Command for Benchmark {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let block = call.positional[0]
|
let block = call.positional[0]
|
||||||
.as_block()
|
.as_block()
|
||||||
.expect("internal error: expected block");
|
.expect("internal error: expected block");
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
let block = engine_state.get_block(block);
|
let block = engine_state.get_block(block);
|
||||||
|
|
||||||
let state = context.enter_scope();
|
let mut stack = stack.collect_captures(&block.captures);
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
eval_block(&state, block, Value::nothing())?;
|
eval_block(engine_state, &mut stack, block, PipelineData::new())?.into_value();
|
||||||
|
|
||||||
let end_time = Instant::now();
|
let end_time = Instant::now();
|
||||||
println!("{} ms", (end_time - start_time).as_millis());
|
println!("{} ms", (end_time - start_time).as_millis());
|
||||||
Ok(Value::Nothing {
|
Ok(PipelineData::new())
|
||||||
span: call.positional[0].span,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
Example, ShellError, Signature, Value,
|
Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Value,
|
||||||
};
|
};
|
||||||
use sysinfo::{ProcessExt, System, SystemExt};
|
use sysinfo::{ProcessExt, System, SystemExt};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Ps;
|
pub struct Ps;
|
||||||
|
|
||||||
impl Command for Ps {
|
impl Command for Ps {
|
||||||
|
@ -29,11 +30,12 @@ impl Command for Ps {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
run_ps(call)
|
run_ps(engine_state, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -45,7 +47,7 @@ impl Command for Ps {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ps(call: &Call) -> Result<Value, ShellError> {
|
fn run_ps(engine_state: &EngineState, call: &Call) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
let long = call.has_flag("long");
|
let long = call.has_flag("long");
|
||||||
let mut sys = System::new_all();
|
let mut sys = System::new_all();
|
||||||
|
@ -124,5 +126,7 @@ fn run_ps(call: &Call) -> Result<Value, ShellError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::List { vals: output, span })
|
Ok(output
|
||||||
|
.into_iter()
|
||||||
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cell::RefCell;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::{BufRead, BufReader, Write};
|
use std::io::{BufRead, BufReader, Write};
|
||||||
use std::process::{ChildStdin, Command as CommandSys, Stdio};
|
use std::process::{Command as CommandSys, Stdio};
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
use nu_protocol::{
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
ast::{Call, Expression},
|
use nu_protocol::{ast::Call, engine::Command, ShellError, Signature, SyntaxShape, Value};
|
||||||
engine::{Command, EvaluationContext},
|
use nu_protocol::{IntoInterruptiblePipelineData, PipelineData, Span, Spanned};
|
||||||
ShellError, Signature, SyntaxShape, Value,
|
|
||||||
};
|
|
||||||
use nu_protocol::{Span, ValueStream};
|
|
||||||
|
|
||||||
use nu_engine::eval_expression;
|
use nu_engine::CallExt;
|
||||||
|
|
||||||
const OUTPUT_BUFFER_SIZE: usize = 8192;
|
const OUTPUT_BUFFER_SIZE: usize = 8192;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct External;
|
pub struct External;
|
||||||
|
|
||||||
impl Command for External {
|
impl Command for External {
|
||||||
|
@ -36,62 +33,49 @@ impl Command for External {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let command = ExternalCommand::try_new(call, context)?;
|
let name: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
command.run_with_input(input)
|
let args: Vec<String> = call.rest(engine_state, stack, 1)?;
|
||||||
|
let last_expression = call.has_flag("last_expression");
|
||||||
|
let env_vars = stack.get_env_vars();
|
||||||
|
|
||||||
|
let command = ExternalCommand {
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
last_expression,
|
||||||
|
env_vars,
|
||||||
|
};
|
||||||
|
command.run_with_input(engine_state, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ExternalCommand<'call, 'contex> {
|
pub struct ExternalCommand {
|
||||||
pub name: &'call Expression,
|
pub name: Spanned<String>,
|
||||||
pub args: &'call [Expression],
|
pub args: Vec<String>,
|
||||||
pub context: &'contex EvaluationContext,
|
|
||||||
pub last_expression: bool,
|
pub last_expression: bool,
|
||||||
|
pub env_vars: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'call, 'contex> ExternalCommand<'call, 'contex> {
|
impl ExternalCommand {
|
||||||
pub fn try_new(
|
pub fn run_with_input(
|
||||||
call: &'call Call,
|
&self,
|
||||||
context: &'contex EvaluationContext,
|
engine_state: &EngineState,
|
||||||
) -> Result<Self, ShellError> {
|
input: PipelineData,
|
||||||
if call.positional.is_empty() {
|
) -> Result<PipelineData, ShellError> {
|
||||||
return Err(ShellError::ExternalNotSupported(call.head));
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(Self {
|
|
||||||
name: &call.positional[0],
|
|
||||||
args: &call.positional[1..],
|
|
||||||
context,
|
|
||||||
last_expression: call.has_flag("last_expression"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_name(&self) -> Result<String, ShellError> {
|
|
||||||
let value = eval_expression(self.context, self.name)?;
|
|
||||||
value.as_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_args(&self) -> Vec<String> {
|
|
||||||
self.args
|
|
||||||
.iter()
|
|
||||||
.filter_map(|expr| eval_expression(self.context, expr).ok())
|
|
||||||
.filter_map(|value| value.as_string().ok())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run_with_input(&self, input: Value) -> Result<Value, ShellError> {
|
|
||||||
let mut process = self.create_command();
|
let mut process = self.create_command();
|
||||||
|
|
||||||
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
|
||||||
// TODO. We don't have a way to know the current directory
|
// TODO. We don't have a way to know the current directory
|
||||||
// This should be information from the EvaluationContex or EngineState
|
// This should be information from the EvaluationContex or EngineState
|
||||||
let path = env::current_dir().unwrap();
|
let path = env::current_dir().unwrap();
|
||||||
process.current_dir(path);
|
process.current_dir(path);
|
||||||
|
|
||||||
let envs = self.context.stack.get_env_vars();
|
process.envs(&self.env_vars);
|
||||||
process.envs(envs);
|
|
||||||
|
|
||||||
// If the external is not the last command, its output will get piped
|
// If the external is not the last command, its output will get piped
|
||||||
// either as a string or binary
|
// either as a string or binary
|
||||||
|
@ -101,11 +85,7 @@ impl<'call, 'contex> ExternalCommand<'call, 'contex> {
|
||||||
|
|
||||||
// If there is an input from the pipeline. The stdin from the process
|
// If there is an input from the pipeline. The stdin from the process
|
||||||
// is piped so it can be used to send the input information
|
// is piped so it can be used to send the input information
|
||||||
if let Value::String { .. } = input {
|
if !matches!(input, PipelineData::Value(Value::Nothing { .. })) {
|
||||||
process.stdin(Stdio::piped());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Value::Stream { .. } = input {
|
|
||||||
process.stdin(Stdio::piped());
|
process.stdin(Stdio::piped());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -116,33 +96,29 @@ impl<'call, 'contex> ExternalCommand<'call, 'contex> {
|
||||||
)),
|
)),
|
||||||
Ok(mut child) => {
|
Ok(mut child) => {
|
||||||
// if there is a string or a stream, that is sent to the pipe std
|
// if there is a string or a stream, that is sent to the pipe std
|
||||||
match input {
|
|
||||||
Value::String { val, span: _ } => {
|
|
||||||
if let Some(mut stdin_write) = child.stdin.take() {
|
if let Some(mut stdin_write) = child.stdin.take() {
|
||||||
self.write_to_stdin(&mut stdin_write, val.as_bytes())?
|
std::thread::spawn(move || {
|
||||||
}
|
for value in input.into_iter() {
|
||||||
}
|
|
||||||
Value::Binary { val, span: _ } => {
|
|
||||||
if let Some(mut stdin_write) = child.stdin.take() {
|
|
||||||
self.write_to_stdin(&mut stdin_write, &val)?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Stream { stream, span: _ } => {
|
|
||||||
if let Some(mut stdin_write) = child.stdin.take() {
|
|
||||||
for value in stream {
|
|
||||||
match value {
|
match value {
|
||||||
Value::String { val, span: _ } => {
|
Value::String { val, span: _ } => {
|
||||||
self.write_to_stdin(&mut stdin_write, val.as_bytes())?
|
if stdin_write.write(val.as_bytes()).is_err() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Value::Binary { val, span: _ } => {
|
Value::Binary { val, span: _ } => {
|
||||||
self.write_to_stdin(&mut stdin_write, &val)?
|
if stdin_write.write(&val).is_err() {
|
||||||
|
return Ok(());
|
||||||
}
|
}
|
||||||
_ => continue,
|
}
|
||||||
|
x => {
|
||||||
|
if stdin_write.write(x.into_string().as_bytes()).is_err() {
|
||||||
|
return Err(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => (),
|
Ok(())
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// If this external is not the last expression, then its output is piped to a channel
|
// If this external is not the last expression, then its output is piped to a channel
|
||||||
|
@ -185,12 +161,9 @@ impl<'call, 'contex> ExternalCommand<'call, 'contex> {
|
||||||
});
|
});
|
||||||
|
|
||||||
// The ValueStream is consumed by the next expression in the pipeline
|
// The ValueStream is consumed by the next expression in the pipeline
|
||||||
Value::Stream {
|
ChannelReceiver::new(rx).into_pipeline_data(ctrlc)
|
||||||
stream: ValueStream(Rc::new(RefCell::new(ChannelReceiver::new(rx)))),
|
|
||||||
span: Span::unknown(),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Value::nothing()
|
PipelineData::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
match child.wait() {
|
match child.wait() {
|
||||||
|
@ -212,8 +185,8 @@ impl<'call, 'contex> ExternalCommand<'call, 'contex> {
|
||||||
// for minimal builds cwd is unused
|
// for minimal builds cwd is unused
|
||||||
let mut process = CommandSys::new("cmd");
|
let mut process = CommandSys::new("cmd");
|
||||||
process.arg("/c");
|
process.arg("/c");
|
||||||
process.arg(&self.get_name().unwrap());
|
process.arg(&self.name.item);
|
||||||
for arg in self.get_args() {
|
for arg in &self.args {
|
||||||
// Clean the args before we use them:
|
// Clean the args before we use them:
|
||||||
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
|
// https://stackoverflow.com/questions/1200235/how-to-pass-a-quoted-pipe-character-to-cmd-exe
|
||||||
// cmd.exe needs to have a caret to escape a pipe
|
// cmd.exe needs to have a caret to escape a pipe
|
||||||
|
@ -222,23 +195,12 @@ impl<'call, 'contex> ExternalCommand<'call, 'contex> {
|
||||||
}
|
}
|
||||||
process
|
process
|
||||||
} else {
|
} else {
|
||||||
let cmd_with_args = vec![self.get_name().unwrap(), self.get_args().join(" ")].join(" ");
|
let cmd_with_args = vec![self.name.item.clone(), self.args.join(" ")].join(" ");
|
||||||
let mut process = CommandSys::new("sh");
|
let mut process = CommandSys::new("sh");
|
||||||
process.arg("-c").arg(cmd_with_args);
|
process.arg("-c").arg(cmd_with_args);
|
||||||
process
|
process
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_to_stdin(&self, stdin_write: &mut ChildStdin, val: &[u8]) -> Result<(), ShellError> {
|
|
||||||
if stdin_write.write(val).is_err() {
|
|
||||||
Err(ShellError::ExternalCommand(
|
|
||||||
"Error writing input to stdin".to_string(),
|
|
||||||
self.name.span,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The piped data from stdout from the external command can be either String
|
// The piped data from stdout from the external command can be either String
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
Example, ShellError, Signature, Span, Value,
|
Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value,
|
||||||
};
|
};
|
||||||
use sysinfo::{ComponentExt, DiskExt, NetworkExt, ProcessorExt, System, SystemExt, UserExt};
|
use sysinfo::{ComponentExt, DiskExt, NetworkExt, ProcessorExt, System, SystemExt, UserExt};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Sys;
|
pub struct Sys;
|
||||||
|
|
||||||
impl Command for Sys {
|
impl Command for Sys {
|
||||||
|
@ -24,10 +25,11 @@ impl Command for Sys {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
run_sys(call)
|
run_sys(call)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +42,7 @@ impl Command for Sys {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_sys(call: &Call) -> Result<Value, ShellError> {
|
fn run_sys(call: &Call) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
let mut sys = System::new();
|
let mut sys = System::new();
|
||||||
|
|
||||||
|
@ -76,7 +78,8 @@ fn run_sys(call: &Call) -> Result<Value, ShellError> {
|
||||||
cols: headers,
|
cols: headers,
|
||||||
vals: values,
|
vals: values,
|
||||||
span,
|
span,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trim_cstyle_null(s: String) -> String {
|
pub fn trim_cstyle_null(s: String) -> String {
|
||||||
|
|
|
@ -2,12 +2,13 @@ use lscolors::{LsColors, Style};
|
||||||
use nu_engine::CallExt;
|
use nu_engine::CallExt;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, PathMember},
|
ast::{Call, PathMember},
|
||||||
engine::{Command, EvaluationContext},
|
engine::{Command, EngineState, Stack},
|
||||||
Signature, Span, SyntaxShape, Value,
|
IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Value,
|
||||||
};
|
};
|
||||||
use nu_term_grid::grid::{Alignment, Cell, Direction, Filling, Grid, GridOptions};
|
use nu_term_grid::grid::{Alignment, Cell, Direction, Filling, Grid, GridOptions};
|
||||||
use terminal_size::{Height, Width};
|
use terminal_size::{Height, Width};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Griddle;
|
pub struct Griddle;
|
||||||
|
|
||||||
impl Command for Griddle {
|
impl Command for Griddle {
|
||||||
|
@ -47,16 +48,17 @@ prints out the list properly."#
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let width_param: Option<String> = call.get_flag(context, "width")?;
|
let width_param: Option<String> = call.get_flag(engine_state, stack, "width")?;
|
||||||
let color_param: bool = call.has_flag("color");
|
let color_param: bool = call.has_flag("color");
|
||||||
let separator_param: Option<String> = call.get_flag(context, "separator")?;
|
let separator_param: Option<String> = call.get_flag(engine_state, stack, "separator")?;
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
Value::List { vals, .. } => {
|
PipelineData::Value(Value::List { vals, .. }) => {
|
||||||
// dbg!("value::list");
|
// dbg!("value::list");
|
||||||
let data = convert_to_list2(vals);
|
let data = convert_to_list2(vals);
|
||||||
if let Some(items) = data {
|
if let Some(items) = data {
|
||||||
|
@ -68,10 +70,10 @@ prints out the list properly."#
|
||||||
separator_param,
|
separator_param,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Stream { stream, .. } => {
|
PipelineData::Stream(stream) => {
|
||||||
// dbg!("value::stream");
|
// dbg!("value::stream");
|
||||||
let data = convert_to_list2(stream);
|
let data = convert_to_list2(stream);
|
||||||
if let Some(items) = data {
|
if let Some(items) = data {
|
||||||
|
@ -84,10 +86,10 @@ prints out the list properly."#
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
// dbg!(data);
|
// dbg!(data);
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Record { cols, vals, .. } => {
|
PipelineData::Value(Value::Record { cols, vals, .. }) => {
|
||||||
// dbg!("value::record");
|
// dbg!("value::record");
|
||||||
let mut items = vec![];
|
let mut items = vec![];
|
||||||
|
|
||||||
|
@ -118,7 +120,7 @@ fn create_grid_output2(
|
||||||
width_param: Option<String>,
|
width_param: Option<String>,
|
||||||
color_param: bool,
|
color_param: bool,
|
||||||
separator_param: Option<String>,
|
separator_param: Option<String>,
|
||||||
) -> Value {
|
) -> PipelineData {
|
||||||
let ls_colors = LsColors::from_env().unwrap_or_default();
|
let ls_colors = LsColors::from_env().unwrap_or_default();
|
||||||
let cols = if let Some(col) = width_param {
|
let cols = if let Some(col) = width_param {
|
||||||
col.parse::<u16>().unwrap_or(80)
|
col.parse::<u16>().unwrap_or(80)
|
||||||
|
@ -166,6 +168,7 @@ fn create_grid_output2(
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.into_pipeline_data()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_to_list2(iter: impl IntoIterator<Item = Value>) -> Option<Vec<(usize, String, String)>> {
|
fn convert_to_list2(iter: impl IntoIterator<Item = Value>) -> Option<Vec<(usize, String, String)>> {
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use nu_protocol::ast::{Call, PathMember};
|
use nu_protocol::ast::{Call, PathMember};
|
||||||
use nu_protocol::engine::{Command, EvaluationContext};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
use nu_protocol::{ShellError, Signature, Span, Value};
|
use nu_protocol::{IntoPipelineData, PipelineData, ShellError, Signature, Span, Value};
|
||||||
use nu_table::StyledString;
|
use nu_table::StyledString;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use terminal_size::{Height, Width};
|
use terminal_size::{Height, Width};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Table;
|
pub struct Table;
|
||||||
|
|
||||||
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
|
//NOTE: this is not a real implementation :D. It's just a simple one to test with until we port the real one.
|
||||||
|
@ -23,10 +24,11 @@ impl Command for Table {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
let term_width = if let Some((Width(w), Height(_h))) = terminal_size::terminal_size() {
|
let term_width = if let Some((Width(w), Height(_h))) = terminal_size::terminal_size() {
|
||||||
w as usize
|
w as usize
|
||||||
} else {
|
} else {
|
||||||
|
@ -34,7 +36,7 @@ impl Command for Table {
|
||||||
};
|
};
|
||||||
|
|
||||||
match input {
|
match input {
|
||||||
Value::List { vals, .. } => {
|
PipelineData::Value(Value::List { vals, .. }) => {
|
||||||
let table = convert_to_table(vals)?;
|
let table = convert_to_table(vals)?;
|
||||||
|
|
||||||
if let Some(table) = table {
|
if let Some(table) = table {
|
||||||
|
@ -43,12 +45,13 @@ impl Command for Table {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: result,
|
val: result,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Stream { stream, .. } => {
|
PipelineData::Stream(stream) => {
|
||||||
let table = convert_to_table(stream)?;
|
let table = convert_to_table(stream)?;
|
||||||
|
|
||||||
if let Some(table) = table {
|
if let Some(table) = table {
|
||||||
|
@ -57,12 +60,13 @@ impl Command for Table {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: result,
|
val: result,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
} else {
|
} else {
|
||||||
Ok(Value::Nothing { span: call.head })
|
Ok(PipelineData::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Record { cols, vals, .. } => {
|
PipelineData::Value(Value::Record { cols, vals, .. }) => {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
for (c, v) in cols.into_iter().zip(vals.into_iter()) {
|
for (c, v) in cols.into_iter().zip(vals.into_iter()) {
|
||||||
|
@ -89,9 +93,10 @@ impl Command for Table {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: result,
|
val: result,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Value::Error { error } => Err(error),
|
.into_pipeline_data())
|
||||||
|
}
|
||||||
|
PipelineData::Value(Value::Error { error }) => Err(error),
|
||||||
x => Ok(x),
|
x => Ok(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,37 +1,50 @@
|
||||||
use nu_protocol::{ast::Call, engine::EvaluationContext, ShellError};
|
use nu_protocol::{
|
||||||
|
ast::Call,
|
||||||
|
engine::{EngineState, Stack},
|
||||||
|
ShellError,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{eval_expression, FromValue};
|
use crate::{eval_expression, FromValue};
|
||||||
|
|
||||||
pub trait CallExt {
|
pub trait CallExt {
|
||||||
fn get_flag<T: FromValue>(
|
fn get_flag<T: FromValue>(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Result<Option<T>, ShellError>;
|
) -> Result<Option<T>, ShellError>;
|
||||||
|
|
||||||
fn rest<T: FromValue>(
|
fn rest<T: FromValue>(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
starting_pos: usize,
|
starting_pos: usize,
|
||||||
) -> Result<Vec<T>, ShellError>;
|
) -> Result<Vec<T>, ShellError>;
|
||||||
|
|
||||||
fn opt<T: FromValue>(
|
fn opt<T: FromValue>(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
) -> Result<Option<T>, ShellError>;
|
) -> Result<Option<T>, ShellError>;
|
||||||
|
|
||||||
fn req<T: FromValue>(&self, context: &EvaluationContext, pos: usize) -> Result<T, ShellError>;
|
fn req<T: FromValue>(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
pos: usize,
|
||||||
|
) -> Result<T, ShellError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CallExt for Call {
|
impl CallExt for Call {
|
||||||
fn get_flag<T: FromValue>(
|
fn get_flag<T: FromValue>(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Result<Option<T>, ShellError> {
|
) -> Result<Option<T>, ShellError> {
|
||||||
if let Some(expr) = self.get_flag_expr(name) {
|
if let Some(expr) = self.get_flag_expr(name) {
|
||||||
let result = eval_expression(context, &expr)?;
|
let result = eval_expression(engine_state, stack, &expr)?;
|
||||||
FromValue::from_value(&result).map(Some)
|
FromValue::from_value(&result).map(Some)
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -40,13 +53,14 @@ impl CallExt for Call {
|
||||||
|
|
||||||
fn rest<T: FromValue>(
|
fn rest<T: FromValue>(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
starting_pos: usize,
|
starting_pos: usize,
|
||||||
) -> Result<Vec<T>, ShellError> {
|
) -> Result<Vec<T>, ShellError> {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
for expr in self.positional.iter().skip(starting_pos) {
|
for expr in self.positional.iter().skip(starting_pos) {
|
||||||
let result = eval_expression(context, expr)?;
|
let result = eval_expression(engine_state, stack, expr)?;
|
||||||
output.push(FromValue::from_value(&result)?);
|
output.push(FromValue::from_value(&result)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,20 +69,26 @@ impl CallExt for Call {
|
||||||
|
|
||||||
fn opt<T: FromValue>(
|
fn opt<T: FromValue>(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
) -> Result<Option<T>, ShellError> {
|
) -> Result<Option<T>, ShellError> {
|
||||||
if let Some(expr) = self.nth(pos) {
|
if let Some(expr) = self.nth(pos) {
|
||||||
let result = eval_expression(context, &expr)?;
|
let result = eval_expression(engine_state, stack, &expr)?;
|
||||||
FromValue::from_value(&result).map(Some)
|
FromValue::from_value(&result).map(Some)
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn req<T: FromValue>(&self, context: &EvaluationContext, pos: usize) -> Result<T, ShellError> {
|
fn req<T: FromValue>(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
pos: usize,
|
||||||
|
) -> Result<T, ShellError> {
|
||||||
if let Some(expr) = self.nth(pos) {
|
if let Some(expr) = self.nth(pos) {
|
||||||
let result = eval_expression(context, &expr)?;
|
let result = eval_expression(engine_state, stack, &expr)?;
|
||||||
FromValue::from_value(&result)
|
FromValue::from_value(&result)
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::AccessBeyondEnd(
|
Err(ShellError::AccessBeyondEnd(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nu_protocol::{engine::EvaluationContext, Example, Signature, Span, Value};
|
use nu_protocol::{engine::EngineState, Example, Signature, Span, Value};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
const COMMANDS_DOCS_DIR: &str = "docs/commands";
|
const COMMANDS_DOCS_DIR: &str = "docs/commands";
|
||||||
|
@ -22,12 +22,10 @@ impl Default for DocumentationConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_doc(name: &str, context: &EvaluationContext) -> (Vec<String>, Vec<Value>) {
|
fn generate_doc(name: &str, engine_state: &EngineState) -> (Vec<String>, Vec<Value>) {
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
|
|
||||||
let command = engine_state
|
let command = engine_state
|
||||||
.find_decl(name.as_bytes())
|
.find_decl(name.as_bytes())
|
||||||
.map(|decl_id| engine_state.get_decl(decl_id))
|
.map(|decl_id| engine_state.get_decl(decl_id))
|
||||||
|
@ -58,7 +56,7 @@ fn generate_doc(name: &str, context: &EvaluationContext) -> (Vec<String>, Vec<Va
|
||||||
val: get_documentation(
|
val: get_documentation(
|
||||||
&command.signature(),
|
&command.signature(),
|
||||||
&command.examples(),
|
&command.examples(),
|
||||||
context,
|
engine_state,
|
||||||
&DocumentationConfig {
|
&DocumentationConfig {
|
||||||
no_subcommands: true,
|
no_subcommands: true,
|
||||||
no_color: true,
|
no_color: true,
|
||||||
|
@ -72,8 +70,8 @@ fn generate_doc(name: &str, context: &EvaluationContext) -> (Vec<String>, Vec<Va
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate_docs gets the documentation from each command and returns a Table as output
|
// generate_docs gets the documentation from each command and returns a Table as output
|
||||||
pub fn generate_docs(context: &EvaluationContext) -> Value {
|
pub fn generate_docs(engine_state: &EngineState) -> Value {
|
||||||
let signatures = context.get_signatures();
|
let signatures = engine_state.get_signatures();
|
||||||
|
|
||||||
// cmap will map parent commands to it's subcommands e.g. to -> [to csv, to yaml, to bson]
|
// cmap will map parent commands to it's subcommands e.g. to -> [to csv, to yaml, to bson]
|
||||||
let mut cmap: HashMap<String, Vec<String>> = HashMap::new();
|
let mut cmap: HashMap<String, Vec<String>> = HashMap::new();
|
||||||
|
@ -99,11 +97,11 @@ pub fn generate_docs(context: &EvaluationContext) -> Value {
|
||||||
if !cmap.contains_key(&sig.name) {
|
if !cmap.contains_key(&sig.name) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let mut row_entries = generate_doc(&sig.name, context);
|
let mut row_entries = generate_doc(&sig.name, engine_state);
|
||||||
// Iterate over all the subcommands of the parent command
|
// Iterate over all the subcommands of the parent command
|
||||||
let mut sub_table = Vec::new();
|
let mut sub_table = Vec::new();
|
||||||
for sub_name in cmap.get(&sig.name).unwrap_or(&Vec::new()) {
|
for sub_name in cmap.get(&sig.name).unwrap_or(&Vec::new()) {
|
||||||
let (cols, vals) = generate_doc(sub_name, context);
|
let (cols, vals) = generate_doc(sub_name, engine_state);
|
||||||
sub_table.push(Value::Record {
|
sub_table.push(Value::Record {
|
||||||
cols,
|
cols,
|
||||||
vals,
|
vals,
|
||||||
|
@ -149,7 +147,7 @@ fn retrieve_doc_link(name: &str) -> Option<String> {
|
||||||
pub fn get_documentation(
|
pub fn get_documentation(
|
||||||
sig: &Signature,
|
sig: &Signature,
|
||||||
examples: &[Example],
|
examples: &[Example],
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
config: &DocumentationConfig,
|
config: &DocumentationConfig,
|
||||||
) -> String {
|
) -> String {
|
||||||
let cmd_name = &sig.name;
|
let cmd_name = &sig.name;
|
||||||
|
@ -169,7 +167,7 @@ pub fn get_documentation(
|
||||||
|
|
||||||
let mut subcommands = vec![];
|
let mut subcommands = vec![];
|
||||||
if !config.no_subcommands {
|
if !config.no_subcommands {
|
||||||
let signatures = context.get_signatures();
|
let signatures = engine_state.get_signatures();
|
||||||
for sig in signatures {
|
for sig in signatures {
|
||||||
if sig.name.starts_with(&format!("{} ", cmd_name)) {
|
if sig.name.starts_with(&format!("{} ", cmd_name)) {
|
||||||
subcommands.push(format!(" {} - {}", sig.name, sig.usage));
|
subcommands.push(format!(" {} - {}", sig.name, sig.usage));
|
||||||
|
@ -325,15 +323,11 @@ fn get_flags_section(signature: &Signature) -> String {
|
||||||
long_desc
|
long_desc
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_brief_help(
|
pub fn get_brief_help(sig: &Signature, examples: &[Example], engine_state: &EngineState) -> String {
|
||||||
sig: &Signature,
|
|
||||||
examples: &[Example],
|
|
||||||
context: &EvaluationContext,
|
|
||||||
) -> String {
|
|
||||||
get_documentation(
|
get_documentation(
|
||||||
sig,
|
sig,
|
||||||
examples,
|
examples,
|
||||||
context,
|
engine_state,
|
||||||
&DocumentationConfig {
|
&DocumentationConfig {
|
||||||
no_subcommands: false,
|
no_subcommands: false,
|
||||||
no_color: false,
|
no_color: false,
|
||||||
|
@ -342,6 +336,6 @@ pub fn get_brief_help(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_full_help(sig: &Signature, examples: &[Example], context: &EvaluationContext) -> String {
|
pub fn get_full_help(sig: &Signature, examples: &[Example], engine_state: &EngineState) -> String {
|
||||||
get_documentation(sig, examples, context, &DocumentationConfig::default())
|
get_documentation(sig, examples, engine_state, &DocumentationConfig::default())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
use nu_protocol::ast::{Block, Call, Expr, Expression, Operator, Statement};
|
use nu_protocol::ast::{Block, Call, Expr, Expression, Operator, Statement};
|
||||||
use nu_protocol::engine::EvaluationContext;
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
use nu_protocol::{Range, ShellError, Span, Spanned, Type, Unit, Value};
|
use nu_protocol::{
|
||||||
|
IntoPipelineData, PipelineData, Range, ShellError, Span, Spanned, Type, Unit, Value,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::get_full_help;
|
use crate::get_full_help;
|
||||||
|
|
||||||
|
@ -16,29 +18,36 @@ pub fn eval_operator(op: &Expression) -> Result<Operator, ShellError> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result<Value, ShellError> {
|
fn eval_call(
|
||||||
let engine_state = context.engine_state.borrow();
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
let decl = engine_state.get_decl(call.decl_id);
|
let decl = engine_state.get_decl(call.decl_id);
|
||||||
if call.named.iter().any(|(flag, _)| flag.item == "help") {
|
if call.named.iter().any(|(flag, _)| flag.item == "help") {
|
||||||
let full_help = get_full_help(&decl.signature(), &decl.examples(), context);
|
let full_help = get_full_help(&decl.signature(), &decl.examples(), engine_state);
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: full_help,
|
val: full_help,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
})
|
}
|
||||||
|
.into_pipeline_data())
|
||||||
} else if let Some(block_id) = decl.get_block_id() {
|
} else if let Some(block_id) = decl.get_block_id() {
|
||||||
let state = context.enter_scope();
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
let mut stack = stack.collect_captures(&block.captures);
|
||||||
for (arg, param) in call.positional.iter().zip(
|
for (arg, param) in call.positional.iter().zip(
|
||||||
decl.signature()
|
decl.signature()
|
||||||
.required_positional
|
.required_positional
|
||||||
.iter()
|
.iter()
|
||||||
.chain(decl.signature().optional_positional.iter()),
|
.chain(decl.signature().optional_positional.iter()),
|
||||||
) {
|
) {
|
||||||
let result = eval_expression(&state, arg)?;
|
let result = eval_expression(engine_state, &mut stack, arg)?;
|
||||||
let var_id = param
|
let var_id = param
|
||||||
.var_id
|
.var_id
|
||||||
.expect("internal error: all custom parameters must have var_ids");
|
.expect("internal error: all custom parameters must have var_ids");
|
||||||
|
|
||||||
state.add_var(var_id, result);
|
stack.add_var(var_id, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(rest_positional) = decl.signature().rest_positional {
|
if let Some(rest_positional) = decl.signature().rest_positional {
|
||||||
|
@ -48,7 +57,7 @@ fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result<V
|
||||||
decl.signature().required_positional.len()
|
decl.signature().required_positional.len()
|
||||||
+ decl.signature().optional_positional.len(),
|
+ decl.signature().optional_positional.len(),
|
||||||
) {
|
) {
|
||||||
let result = eval_expression(&state, arg)?;
|
let result = eval_expression(engine_state, &mut stack, arg)?;
|
||||||
rest_items.push(result);
|
rest_items.push(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +67,7 @@ fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result<V
|
||||||
Span::unknown()
|
Span::unknown()
|
||||||
};
|
};
|
||||||
|
|
||||||
state.add_var(
|
stack.add_var(
|
||||||
rest_positional
|
rest_positional
|
||||||
.var_id
|
.var_id
|
||||||
.expect("Internal error: rest positional parameter lacks var_id"),
|
.expect("Internal error: rest positional parameter lacks var_id"),
|
||||||
|
@ -75,11 +84,11 @@ fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result<V
|
||||||
for call_named in &call.named {
|
for call_named in &call.named {
|
||||||
if call_named.0.item == named.long {
|
if call_named.0.item == named.long {
|
||||||
if let Some(arg) = &call_named.1 {
|
if let Some(arg) = &call_named.1 {
|
||||||
let result = eval_expression(&state, arg)?;
|
let result = eval_expression(engine_state, &mut stack, arg)?;
|
||||||
|
|
||||||
state.add_var(var_id, result);
|
stack.add_var(var_id, result);
|
||||||
} else {
|
} else {
|
||||||
state.add_var(
|
stack.add_var(
|
||||||
var_id,
|
var_id,
|
||||||
Value::Bool {
|
Value::Bool {
|
||||||
val: true,
|
val: true,
|
||||||
|
@ -92,7 +101,7 @@ fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result<V
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found && named.arg.is_none() {
|
if !found && named.arg.is_none() {
|
||||||
state.add_var(
|
stack.add_var(
|
||||||
var_id,
|
var_id,
|
||||||
Value::Bool {
|
Value::Bool {
|
||||||
val: false,
|
val: false,
|
||||||
|
@ -102,24 +111,21 @@ fn eval_call(context: &EvaluationContext, call: &Call, input: Value) -> Result<V
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let engine_state = state.engine_state.borrow();
|
eval_block(engine_state, &mut stack, block, input)
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
eval_block(&state, block, input)
|
|
||||||
} else {
|
} else {
|
||||||
decl.run(context, call, input)
|
decl.run(engine_state, stack, call, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_external(
|
fn eval_external(
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
name: &str,
|
name: &str,
|
||||||
name_span: &Span,
|
name_span: &Span,
|
||||||
args: &[Expression],
|
args: &[Expression],
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
last_expression: bool,
|
last_expression: bool,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
|
|
||||||
let decl_id = engine_state
|
let decl_id = engine_state
|
||||||
.find_decl("run_external".as_bytes())
|
.find_decl("run_external".as_bytes())
|
||||||
.ok_or_else(|| ShellError::ExternalNotSupported(*name_span))?;
|
.ok_or_else(|| ShellError::ExternalNotSupported(*name_span))?;
|
||||||
|
@ -149,11 +155,12 @@ fn eval_external(
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
command.run(context, &call, input)
|
command.run(engine_state, stack, &call, input)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_expression(
|
pub fn eval_expression(
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
expr: &Expression,
|
expr: &Expression,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
match &expr.expr {
|
match &expr.expr {
|
||||||
|
@ -169,13 +176,13 @@ pub fn eval_expression(
|
||||||
val: *f,
|
val: *f,
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
}),
|
}),
|
||||||
Expr::ValueWithUnit(e, unit) => match eval_expression(context, e)? {
|
Expr::ValueWithUnit(e, unit) => match eval_expression(engine_state, stack, e)? {
|
||||||
Value::Int { val, .. } => Ok(compute(val, unit.item, unit.span)),
|
Value::Int { val, .. } => Ok(compute(val, unit.item, unit.span)),
|
||||||
_ => Err(ShellError::CantConvert("unit value".into(), e.span)),
|
_ => Err(ShellError::CantConvert("unit value".into(), e.span)),
|
||||||
},
|
},
|
||||||
Expr::Range(from, next, to, operator) => {
|
Expr::Range(from, next, to, operator) => {
|
||||||
let from = if let Some(f) = from {
|
let from = if let Some(f) = from {
|
||||||
eval_expression(context, f)?
|
eval_expression(engine_state, stack, f)?
|
||||||
} else {
|
} else {
|
||||||
Value::Nothing {
|
Value::Nothing {
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
|
@ -183,7 +190,7 @@ pub fn eval_expression(
|
||||||
};
|
};
|
||||||
|
|
||||||
let next = if let Some(s) = next {
|
let next = if let Some(s) = next {
|
||||||
eval_expression(context, s)?
|
eval_expression(engine_state, stack, s)?
|
||||||
} else {
|
} else {
|
||||||
Value::Nothing {
|
Value::Nothing {
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
|
@ -191,7 +198,7 @@ pub fn eval_expression(
|
||||||
};
|
};
|
||||||
|
|
||||||
let to = if let Some(t) = to {
|
let to = if let Some(t) = to {
|
||||||
eval_expression(context, t)?
|
eval_expression(engine_state, stack, t)?
|
||||||
} else {
|
} else {
|
||||||
Value::Nothing {
|
Value::Nothing {
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
|
@ -203,29 +210,43 @@ pub fn eval_expression(
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Expr::Var(var_id) => context
|
Expr::Var(var_id) => stack
|
||||||
.get_var(*var_id)
|
.get_var(*var_id)
|
||||||
.map_err(move |_| ShellError::VariableNotFoundAtRuntime(expr.span)),
|
.map_err(move |_| ShellError::VariableNotFoundAtRuntime(expr.span)),
|
||||||
|
Expr::VarDecl(_) => Ok(Value::Nothing { span: expr.span }),
|
||||||
Expr::CellPath(cell_path) => Ok(Value::CellPath {
|
Expr::CellPath(cell_path) => Ok(Value::CellPath {
|
||||||
val: cell_path.clone(),
|
val: cell_path.clone(),
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
}),
|
}),
|
||||||
Expr::FullCellPath(cell_path) => {
|
Expr::FullCellPath(cell_path) => {
|
||||||
let value = eval_expression(context, &cell_path.head)?;
|
let value = eval_expression(engine_state, stack, &cell_path.head)?;
|
||||||
|
|
||||||
value.follow_cell_path(&cell_path.tail)
|
value.follow_cell_path(&cell_path.tail)
|
||||||
}
|
}
|
||||||
Expr::RowCondition(_, expr) => eval_expression(context, expr),
|
Expr::RowCondition(_, expr) => eval_expression(engine_state, stack, expr),
|
||||||
Expr::Call(call) => eval_call(context, call, Value::nothing()),
|
Expr::Call(call) => {
|
||||||
|
// FIXME: protect this collect with ctrl-c
|
||||||
|
Ok(eval_call(engine_state, stack, call, PipelineData::new())?.into_value())
|
||||||
|
}
|
||||||
Expr::ExternalCall(name, span, args) => {
|
Expr::ExternalCall(name, span, args) => {
|
||||||
eval_external(context, name, span, args, Value::nothing(), true)
|
// FIXME: protect this collect with ctrl-c
|
||||||
|
Ok(eval_external(
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
name,
|
||||||
|
span,
|
||||||
|
args,
|
||||||
|
PipelineData::new(),
|
||||||
|
true,
|
||||||
|
)?
|
||||||
|
.into_value())
|
||||||
}
|
}
|
||||||
Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }),
|
Expr::Operator(_) => Ok(Value::Nothing { span: expr.span }),
|
||||||
Expr::BinaryOp(lhs, op, rhs) => {
|
Expr::BinaryOp(lhs, op, rhs) => {
|
||||||
let op_span = op.span;
|
let op_span = op.span;
|
||||||
let lhs = eval_expression(context, lhs)?;
|
let lhs = eval_expression(engine_state, stack, lhs)?;
|
||||||
let op = eval_operator(op)?;
|
let op = eval_operator(op)?;
|
||||||
let rhs = eval_expression(context, rhs)?;
|
let rhs = eval_expression(engine_state, stack, rhs)?;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
Operator::Plus => lhs.add(op_span, &rhs),
|
Operator::Plus => lhs.add(op_span, &rhs),
|
||||||
|
@ -249,11 +270,10 @@ pub fn eval_expression(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Subexpression(block_id) => {
|
Expr::Subexpression(block_id) => {
|
||||||
let engine_state = context.engine_state.borrow();
|
|
||||||
let block = engine_state.get_block(*block_id);
|
let block = engine_state.get_block(*block_id);
|
||||||
|
|
||||||
let state = context.enter_scope();
|
// FIXME: protect this collect with ctrl-c
|
||||||
eval_block(&state, block, Value::nothing())
|
Ok(eval_block(engine_state, stack, block, PipelineData::new())?.into_value())
|
||||||
}
|
}
|
||||||
Expr::Block(block_id) => Ok(Value::Block {
|
Expr::Block(block_id) => Ok(Value::Block {
|
||||||
val: *block_id,
|
val: *block_id,
|
||||||
|
@ -262,7 +282,7 @@ pub fn eval_expression(
|
||||||
Expr::List(x) => {
|
Expr::List(x) => {
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
for expr in x {
|
for expr in x {
|
||||||
output.push(eval_expression(context, expr)?);
|
output.push(eval_expression(engine_state, stack, expr)?);
|
||||||
}
|
}
|
||||||
Ok(Value::List {
|
Ok(Value::List {
|
||||||
vals: output,
|
vals: output,
|
||||||
|
@ -272,14 +292,14 @@ pub fn eval_expression(
|
||||||
Expr::Table(headers, vals) => {
|
Expr::Table(headers, vals) => {
|
||||||
let mut output_headers = vec![];
|
let mut output_headers = vec![];
|
||||||
for expr in headers {
|
for expr in headers {
|
||||||
output_headers.push(eval_expression(context, expr)?.as_string()?);
|
output_headers.push(eval_expression(engine_state, stack, expr)?.as_string()?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut output_rows = vec![];
|
let mut output_rows = vec![];
|
||||||
for val in vals {
|
for val in vals {
|
||||||
let mut row = vec![];
|
let mut row = vec![];
|
||||||
for expr in val {
|
for expr in val {
|
||||||
row.push(eval_expression(context, expr)?);
|
row.push(eval_expression(engine_state, stack, expr)?);
|
||||||
}
|
}
|
||||||
output_rows.push(Value::Record {
|
output_rows.push(Value::Record {
|
||||||
cols: output_headers.clone(),
|
cols: output_headers.clone(),
|
||||||
|
@ -292,7 +312,7 @@ pub fn eval_expression(
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Expr::Keyword(_, _, expr) => eval_expression(context, expr),
|
Expr::Keyword(_, _, expr) => eval_expression(engine_state, stack, expr),
|
||||||
Expr::String(s) => Ok(Value::String {
|
Expr::String(s) => Ok(Value::String {
|
||||||
val: s.clone(),
|
val: s.clone(),
|
||||||
span: expr.span,
|
span: expr.span,
|
||||||
|
@ -311,10 +331,11 @@ pub fn eval_expression(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_block(
|
pub fn eval_block(
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
block: &Block,
|
block: &Block,
|
||||||
mut input: Value,
|
mut input: PipelineData,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
for stmt in block.stmts.iter() {
|
for stmt in block.stmts.iter() {
|
||||||
if let Statement::Pipeline(pipeline) = stmt {
|
if let Statement::Pipeline(pipeline) = stmt {
|
||||||
for (i, elem) in pipeline.expressions.iter().enumerate() {
|
for (i, elem) in pipeline.expressions.iter().enumerate() {
|
||||||
|
@ -323,14 +344,15 @@ pub fn eval_block(
|
||||||
expr: Expr::Call(call),
|
expr: Expr::Call(call),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
input = eval_call(context, call, input)?;
|
input = eval_call(engine_state, stack, call, input)?;
|
||||||
}
|
}
|
||||||
Expression {
|
Expression {
|
||||||
expr: Expr::ExternalCall(name, name_span, args),
|
expr: Expr::ExternalCall(name, name_span, args),
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
input = eval_external(
|
input = eval_external(
|
||||||
context,
|
engine_state,
|
||||||
|
stack,
|
||||||
name,
|
name,
|
||||||
name_span,
|
name_span,
|
||||||
args,
|
args,
|
||||||
|
@ -340,7 +362,7 @@ pub fn eval_block(
|
||||||
}
|
}
|
||||||
|
|
||||||
elem => {
|
elem => {
|
||||||
input = eval_expression(context, elem)?;
|
input = eval_expression(engine_state, stack, elem)?.into_pipeline_data();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,7 @@ pub fn flatten_expression(
|
||||||
}
|
}
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
Expr::Var(_) => {
|
Expr::Var(_) | Expr::VarDecl(_) => {
|
||||||
vec![(expr.span, FlatShape::Variable)]
|
vec![(expr.span, FlatShape::Variable)]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,4 +13,4 @@ pub use lite_parse::{lite_parse, LiteBlock};
|
||||||
pub use parse_keywords::{
|
pub use parse_keywords::{
|
||||||
parse_alias, parse_def, parse_def_predecl, parse_let, parse_module, parse_use,
|
parse_alias, parse_def, parse_def_predecl, parse_let, parse_module, parse_use,
|
||||||
};
|
};
|
||||||
pub use parser::{parse, Import, VarDecl};
|
pub use parser::{find_captures_in_expr, parse, Import, VarDecl};
|
||||||
|
|
|
@ -71,7 +71,7 @@ pub fn parse_def(
|
||||||
named: vec![],
|
named: vec![],
|
||||||
});
|
});
|
||||||
|
|
||||||
let call = if let Some(name_span) = spans.get(1) {
|
if let Some(name_span) = spans.get(1) {
|
||||||
let (name_expr, err) = parse_string(working_set, *name_span);
|
let (name_expr, err) = parse_string(working_set, *name_span);
|
||||||
error = error.or(err);
|
error = error.or(err);
|
||||||
|
|
||||||
|
@ -142,8 +142,6 @@ pub fn parse_def(
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
call
|
|
||||||
} else {
|
} else {
|
||||||
let err_span = Span {
|
let err_span = Span {
|
||||||
start: name_span.end,
|
start: name_span.end,
|
||||||
|
@ -152,8 +150,6 @@ pub fn parse_def(
|
||||||
|
|
||||||
error = error
|
error = error
|
||||||
.or_else(|| Some(ParseError::MissingPositional("parameters".into(), err_span)));
|
.or_else(|| Some(ParseError::MissingPositional("parameters".into(), err_span)));
|
||||||
|
|
||||||
call
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let err_span = Span {
|
let err_span = Span {
|
||||||
|
@ -167,9 +163,7 @@ pub fn parse_def(
|
||||||
err_span,
|
err_span,
|
||||||
))
|
))
|
||||||
});
|
});
|
||||||
|
}
|
||||||
call
|
|
||||||
};
|
|
||||||
|
|
||||||
(
|
(
|
||||||
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
Statement::Pipeline(Pipeline::from_vec(vec![Expression {
|
||||||
|
|
|
@ -1806,7 +1806,7 @@ pub fn parse_var_with_opt_type(
|
||||||
|
|
||||||
(
|
(
|
||||||
Expression {
|
Expression {
|
||||||
expr: Expr::Var(id),
|
expr: Expr::VarDecl(id),
|
||||||
span: span(&spans[*spans_idx - 1..*spans_idx + 1]),
|
span: span(&spans[*spans_idx - 1..*spans_idx + 1]),
|
||||||
ty,
|
ty,
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
|
@ -1817,7 +1817,7 @@ pub fn parse_var_with_opt_type(
|
||||||
let id = working_set.add_variable(bytes[0..(bytes.len() - 1)].to_vec(), Type::Unknown);
|
let id = working_set.add_variable(bytes[0..(bytes.len() - 1)].to_vec(), Type::Unknown);
|
||||||
(
|
(
|
||||||
Expression {
|
Expression {
|
||||||
expr: Expr::Var(id),
|
expr: Expr::VarDecl(id),
|
||||||
span: spans[*spans_idx],
|
span: spans[*spans_idx],
|
||||||
ty: Type::Unknown,
|
ty: Type::Unknown,
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
|
@ -1830,7 +1830,7 @@ pub fn parse_var_with_opt_type(
|
||||||
|
|
||||||
(
|
(
|
||||||
Expression {
|
Expression {
|
||||||
expr: Expr::Var(id),
|
expr: Expr::VarDecl(id),
|
||||||
span: span(&spans[*spans_idx..*spans_idx + 1]),
|
span: span(&spans[*spans_idx..*spans_idx + 1]),
|
||||||
ty: Type::Unknown,
|
ty: Type::Unknown,
|
||||||
custom_completion: None,
|
custom_completion: None,
|
||||||
|
@ -2574,6 +2574,11 @@ pub fn parse_block_expression(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut seen = vec![];
|
||||||
|
let captures = find_captures_in_block(working_set, &output, &mut seen);
|
||||||
|
|
||||||
|
output.captures = captures;
|
||||||
|
|
||||||
working_set.exit_scope();
|
working_set.exit_scope();
|
||||||
|
|
||||||
let block_id = working_set.add_block(output);
|
let block_id = working_set.add_block(output);
|
||||||
|
@ -3041,6 +3046,177 @@ pub fn parse_block(
|
||||||
(block, error)
|
(block, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn find_captures_in_block(
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
block: &Block,
|
||||||
|
seen: &mut Vec<VarId>,
|
||||||
|
) -> Vec<VarId> {
|
||||||
|
let mut output = vec![];
|
||||||
|
|
||||||
|
for flag in &block.signature.named {
|
||||||
|
if let Some(var_id) = flag.var_id {
|
||||||
|
seen.push(var_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for positional in &block.signature.required_positional {
|
||||||
|
if let Some(var_id) = positional.var_id {
|
||||||
|
seen.push(var_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for positional in &block.signature.optional_positional {
|
||||||
|
if let Some(var_id) = positional.var_id {
|
||||||
|
seen.push(var_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for positional in &block.signature.rest_positional {
|
||||||
|
if let Some(var_id) = positional.var_id {
|
||||||
|
seen.push(var_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for stmt in &block.stmts {
|
||||||
|
match stmt {
|
||||||
|
Statement::Pipeline(pipeline) => {
|
||||||
|
let result = find_captures_in_pipeline(working_set, pipeline, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
Statement::Declaration(_) => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
fn find_captures_in_pipeline(
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
pipeline: &Pipeline,
|
||||||
|
seen: &mut Vec<VarId>,
|
||||||
|
) -> Vec<VarId> {
|
||||||
|
let mut output = vec![];
|
||||||
|
for expr in &pipeline.expressions {
|
||||||
|
let result = find_captures_in_expr(working_set, expr, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_captures_in_expr(
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
expr: &Expression,
|
||||||
|
seen: &mut Vec<VarId>,
|
||||||
|
) -> Vec<VarId> {
|
||||||
|
let mut output = vec![];
|
||||||
|
match &expr.expr {
|
||||||
|
Expr::BinaryOp(lhs, _, rhs) => {
|
||||||
|
let lhs_result = find_captures_in_expr(working_set, lhs, seen);
|
||||||
|
let rhs_result = find_captures_in_expr(working_set, rhs, seen);
|
||||||
|
|
||||||
|
output.extend(&lhs_result);
|
||||||
|
output.extend(&rhs_result);
|
||||||
|
}
|
||||||
|
Expr::Block(block_id) => {
|
||||||
|
let block = working_set.get_block(*block_id);
|
||||||
|
let result = find_captures_in_block(working_set, block, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
Expr::Bool(_) => {}
|
||||||
|
Expr::Call(call) => {
|
||||||
|
for named in &call.named {
|
||||||
|
if let Some(arg) = &named.1 {
|
||||||
|
let result = find_captures_in_expr(working_set, arg, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for positional in &call.positional {
|
||||||
|
let result = find_captures_in_expr(working_set, positional, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::CellPath(_) => {}
|
||||||
|
Expr::ExternalCall(_, _, exprs) => {
|
||||||
|
for expr in exprs {
|
||||||
|
let result = find_captures_in_expr(working_set, expr, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Filepath(_) => {}
|
||||||
|
Expr::Float(_) => {}
|
||||||
|
Expr::FullCellPath(cell_path) => {
|
||||||
|
let result = find_captures_in_expr(working_set, &cell_path.head, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
Expr::Garbage => {}
|
||||||
|
Expr::GlobPattern(_) => {}
|
||||||
|
Expr::Int(_) => {}
|
||||||
|
Expr::Keyword(_, _, expr) => {
|
||||||
|
let result = find_captures_in_expr(working_set, expr, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
Expr::List(exprs) => {
|
||||||
|
for expr in exprs {
|
||||||
|
let result = find_captures_in_expr(working_set, expr, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Operator(_) => {}
|
||||||
|
Expr::Range(expr1, expr2, expr3, _) => {
|
||||||
|
if let Some(expr) = expr1 {
|
||||||
|
let result = find_captures_in_expr(working_set, expr, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
if let Some(expr) = expr2 {
|
||||||
|
let result = find_captures_in_expr(working_set, expr, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
if let Some(expr) = expr3 {
|
||||||
|
let result = find_captures_in_expr(working_set, expr, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::RowCondition(var_id, expr) => {
|
||||||
|
seen.push(*var_id);
|
||||||
|
|
||||||
|
let result = find_captures_in_expr(working_set, expr, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
Expr::Signature(_) => {}
|
||||||
|
Expr::String(_) => {}
|
||||||
|
Expr::Subexpression(block_id) => {
|
||||||
|
let block = working_set.get_block(*block_id);
|
||||||
|
let result = find_captures_in_block(working_set, block, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
Expr::Table(headers, values) => {
|
||||||
|
for header in headers {
|
||||||
|
let result = find_captures_in_expr(working_set, header, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
for row in values {
|
||||||
|
for cell in row {
|
||||||
|
let result = find_captures_in_expr(working_set, cell, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::ValueWithUnit(expr, _) => {
|
||||||
|
let result = find_captures_in_expr(working_set, expr, seen);
|
||||||
|
output.extend(&result);
|
||||||
|
}
|
||||||
|
Expr::Var(var_id) => {
|
||||||
|
if !seen.contains(var_id) {
|
||||||
|
output.push(*var_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::VarDecl(var_id) => {
|
||||||
|
seen.push(*var_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
// Parses a vector of u8 to create an AST Block. If a file name is given, then
|
// Parses a vector of u8 to create an AST Block. If a file name is given, then
|
||||||
// the name is stored in the working set. When parsing a source without a file
|
// the name is stored in the working set. When parsing a source without a file
|
||||||
// name, the source of bytes is stored as "source"
|
// name, the source of bytes is stored as "source"
|
||||||
|
|
|
@ -2,11 +2,12 @@ use nu_parser::ParseError;
|
||||||
use nu_parser::*;
|
use nu_parser::*;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Expr, Expression, Pipeline, Statement},
|
ast::{Expr, Expression, Pipeline, Statement},
|
||||||
engine::{Command, EngineState, StateWorkingSet},
|
engine::{Command, EngineState, Stack, StateWorkingSet},
|
||||||
Signature, SyntaxShape,
|
Signature, SyntaxShape,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct Let;
|
pub struct Let;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -31,10 +32,11 @@ impl Command for Let {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &nu_protocol::engine::EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
_call: &nu_protocol::ast::Call,
|
_call: &nu_protocol::ast::Call,
|
||||||
_input: nu_protocol::Value,
|
_input: nu_protocol::PipelineData,
|
||||||
) -> Result<nu_protocol::Value, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,3 +12,4 @@ serde = {version = "1.0.130", features = ["derive"]}
|
||||||
chrono = { version="0.4.19", features=["serde"] }
|
chrono = { version="0.4.19", features=["serde"] }
|
||||||
chrono-humanize = "0.2.1"
|
chrono-humanize = "0.2.1"
|
||||||
byte-unit = "4.0.9"
|
byte-unit = "4.0.9"
|
||||||
|
im = "15.0.0"
|
|
@ -1,6 +1,6 @@
|
||||||
use std::ops::{Index, IndexMut};
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
use crate::{DeclId, Signature};
|
use crate::{DeclId, Signature, VarId};
|
||||||
|
|
||||||
use super::Statement;
|
use super::Statement;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ pub struct Block {
|
||||||
pub signature: Box<Signature>,
|
pub signature: Box<Signature>,
|
||||||
pub stmts: Vec<Statement>,
|
pub stmts: Vec<Statement>,
|
||||||
pub exports: Vec<(Vec<u8>, DeclId)>, // Assuming just defs for now
|
pub exports: Vec<(Vec<u8>, DeclId)>, // Assuming just defs for now
|
||||||
|
pub captures: Vec<VarId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Block {
|
impl Block {
|
||||||
|
@ -47,6 +48,7 @@ impl Block {
|
||||||
signature: Box::new(Signature::new("")),
|
signature: Box::new(Signature::new("")),
|
||||||
stmts: vec![],
|
stmts: vec![],
|
||||||
exports: vec![],
|
exports: vec![],
|
||||||
|
captures: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +57,7 @@ impl Block {
|
||||||
signature: self.signature,
|
signature: self.signature,
|
||||||
stmts: self.stmts,
|
stmts: self.stmts,
|
||||||
exports,
|
exports,
|
||||||
|
captures: self.captures,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,6 +71,7 @@ where
|
||||||
signature: Box::new(Signature::new("")),
|
signature: Box::new(Signature::new("")),
|
||||||
stmts: stmts.collect(),
|
stmts: stmts.collect(),
|
||||||
exports: vec![],
|
exports: vec![],
|
||||||
|
captures: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ pub enum Expr {
|
||||||
RangeOperator,
|
RangeOperator,
|
||||||
),
|
),
|
||||||
Var(VarId),
|
Var(VarId),
|
||||||
|
VarDecl(VarId),
|
||||||
Call(Box<Call>),
|
Call(Box<Call>),
|
||||||
ExternalCall(String, Span, Vec<Expression>),
|
ExternalCall(String, Span, Vec<Expression>),
|
||||||
Operator(Operator),
|
Operator(Operator),
|
||||||
|
|
|
@ -77,6 +77,7 @@ impl Expression {
|
||||||
pub fn as_var(&self) -> Option<VarId> {
|
pub fn as_var(&self) -> Option<VarId> {
|
||||||
match self.expr {
|
match self.expr {
|
||||||
Expr::Var(var_id) => Some(var_id),
|
Expr::Var(var_id) => Some(var_id),
|
||||||
|
Expr::VarDecl(var_id) => Some(var_id),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{ast::Call, value::Value, BlockId, Example, ShellError, Signature};
|
use crate::{ast::Call, BlockId, Example, PipelineData, ShellError, Signature};
|
||||||
|
|
||||||
use super::EvaluationContext;
|
use super::{EngineState, Stack};
|
||||||
|
|
||||||
pub trait Command: Send + Sync {
|
pub trait Command: Send + Sync + CommandClone {
|
||||||
fn name(&self) -> &str;
|
fn name(&self) -> &str;
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
|
@ -17,10 +17,11 @@ pub trait Command: Send + Sync {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
context: &EvaluationContext,
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: Value,
|
input: PipelineData,
|
||||||
) -> Result<Value, ShellError>;
|
) -> Result<PipelineData, ShellError>;
|
||||||
|
|
||||||
fn is_binary(&self) -> bool {
|
fn is_binary(&self) -> bool {
|
||||||
false
|
false
|
||||||
|
@ -55,3 +56,22 @@ pub trait Command: Send + Sync {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait CommandClone {
|
||||||
|
fn clone_box(&self) -> Box<dyn Command>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> CommandClone for T
|
||||||
|
where
|
||||||
|
T: 'static + Command + Clone,
|
||||||
|
{
|
||||||
|
fn clone_box(&self) -> Box<dyn Command> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Box<dyn Command> {
|
||||||
|
fn clone(&self) -> Box<dyn Command> {
|
||||||
|
self.clone_box()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +1,20 @@
|
||||||
use super::Command;
|
use super::Command;
|
||||||
use crate::{ast::Block, BlockId, DeclId, Example, Signature, Span, Type, VarId};
|
use crate::{ast::Block, BlockId, DeclId, Example, Signature, Span, Type, VarId};
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use std::{collections::HashMap, slice::Iter};
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
sync::{atomic::AtomicBool, Arc},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct EngineState {
|
pub struct EngineState {
|
||||||
files: Vec<(String, usize, usize)>,
|
files: im::Vector<(String, usize, usize)>,
|
||||||
file_contents: Vec<u8>,
|
file_contents: im::Vector<(Vec<u8>, usize, usize)>,
|
||||||
vars: Vec<Type>,
|
vars: im::Vector<Type>,
|
||||||
decls: Vec<Box<dyn Command>>,
|
decls: im::Vector<Box<dyn Command + 'static>>,
|
||||||
blocks: Vec<Block>,
|
blocks: im::Vector<Block>,
|
||||||
pub scope: Vec<ScopeFrame>,
|
pub scope: im::Vector<ScopeFrame>,
|
||||||
|
pub ctrlc: Option<Arc<AtomicBool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tells whether a decl etc. is visible or not
|
// Tells whether a decl etc. is visible or not
|
||||||
|
@ -53,7 +58,7 @@ impl Visibility {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ScopeFrame {
|
pub struct ScopeFrame {
|
||||||
pub vars: HashMap<Vec<u8>, VarId>,
|
pub vars: HashMap<Vec<u8>, VarId>,
|
||||||
predecls: HashMap<Vec<u8>, DeclId>, // temporary storage for predeclarations
|
predecls: HashMap<Vec<u8>, DeclId>, // temporary storage for predeclarations
|
||||||
|
@ -95,12 +100,13 @@ impl Default for EngineState {
|
||||||
impl EngineState {
|
impl EngineState {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
files: vec![],
|
files: im::vector![],
|
||||||
file_contents: vec![],
|
file_contents: im::vector![],
|
||||||
vars: vec![],
|
vars: im::vector![],
|
||||||
decls: vec![],
|
decls: im::vector![],
|
||||||
blocks: vec![],
|
blocks: im::vector![],
|
||||||
scope: vec![ScopeFrame::new()],
|
scope: im::vector![ScopeFrame::new()],
|
||||||
|
ctrlc: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +118,7 @@ impl EngineState {
|
||||||
this.vars.extend(delta.vars);
|
this.vars.extend(delta.vars);
|
||||||
this.blocks.extend(delta.blocks);
|
this.blocks.extend(delta.blocks);
|
||||||
|
|
||||||
if let Some(last) = this.scope.last_mut() {
|
if let Some(last) = this.scope.back_mut() {
|
||||||
let first = delta.scope.remove(0);
|
let first = delta.scope.remove(0);
|
||||||
for item in first.decls.into_iter() {
|
for item in first.decls.into_iter() {
|
||||||
last.decls.insert(item.0, item.1);
|
last.decls.insert(item.0, item.1);
|
||||||
|
@ -165,9 +171,11 @@ impl EngineState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_contents(&self) {
|
pub fn print_contents(&self) {
|
||||||
let string = String::from_utf8_lossy(&self.file_contents);
|
for (contents, _, _) in self.file_contents.iter() {
|
||||||
|
let string = String::from_utf8_lossy(contents);
|
||||||
println!("{}", string);
|
println!("{}", string);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
|
pub fn find_decl(&self, name: &[u8]) -> Option<DeclId> {
|
||||||
let mut visibility: Visibility = Visibility::new();
|
let mut visibility: Visibility = Visibility::new();
|
||||||
|
@ -200,7 +208,13 @@ impl EngineState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_span_contents(&self, span: &Span) -> &[u8] {
|
pub fn get_span_contents(&self, span: &Span) -> &[u8] {
|
||||||
&self.file_contents[span.start..span.end]
|
for (contents, start, finish) in &self.file_contents {
|
||||||
|
if span.start >= *start && span.end <= *finish {
|
||||||
|
return &contents[(span.start - start)..(span.end - start)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("internal error: span missing in file contents cache")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_var(&self, var_id: VarId) -> &Type {
|
pub fn get_var(&self, var_id: VarId) -> &Type {
|
||||||
|
@ -253,10 +267,14 @@ impl EngineState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_span_start(&self) -> usize {
|
pub fn next_span_start(&self) -> usize {
|
||||||
self.file_contents.len()
|
if let Some((_, _, last)) = self.file_contents.last() {
|
||||||
|
*last
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn files(&self) -> Iter<(String, usize, usize)> {
|
pub fn files(&self) -> impl Iterator<Item = &(String, usize, usize)> {
|
||||||
self.files.iter()
|
self.files.iter()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -273,8 +291,11 @@ impl EngineState {
|
||||||
pub fn get_file_source(&self, file_id: usize) -> String {
|
pub fn get_file_source(&self, file_id: usize) -> String {
|
||||||
for file in self.files.iter().enumerate() {
|
for file in self.files.iter().enumerate() {
|
||||||
if file.0 == file_id {
|
if file.0 == file_id {
|
||||||
let output =
|
let contents = self.get_span_contents(&Span {
|
||||||
String::from_utf8_lossy(&self.file_contents[file.1 .1..file.1 .2]).to_string();
|
start: file.1 .1,
|
||||||
|
end: file.1 .2,
|
||||||
|
});
|
||||||
|
let output = String::from_utf8_lossy(contents).to_string();
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@ -286,12 +307,13 @@ impl EngineState {
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn add_file(&mut self, filename: String, contents: Vec<u8>) -> usize {
|
pub(crate) fn add_file(&mut self, filename: String, contents: Vec<u8>) -> usize {
|
||||||
let next_span_start = self.next_span_start();
|
let next_span_start = self.next_span_start();
|
||||||
|
let next_span_end = next_span_start + contents.len();
|
||||||
|
|
||||||
self.file_contents.extend(&contents);
|
self.file_contents
|
||||||
|
.push_back((contents, next_span_start, next_span_end));
|
||||||
|
|
||||||
let next_span_end = self.next_span_start();
|
self.files
|
||||||
|
.push_back((filename, next_span_start, next_span_end));
|
||||||
self.files.push((filename, next_span_start, next_span_end));
|
|
||||||
|
|
||||||
self.num_files() - 1
|
self.num_files() - 1
|
||||||
}
|
}
|
||||||
|
@ -304,7 +326,7 @@ pub struct StateWorkingSet<'a> {
|
||||||
|
|
||||||
pub struct StateDelta {
|
pub struct StateDelta {
|
||||||
files: Vec<(String, usize, usize)>,
|
files: Vec<(String, usize, usize)>,
|
||||||
pub(crate) file_contents: Vec<u8>,
|
pub(crate) file_contents: Vec<(Vec<u8>, usize, usize)>,
|
||||||
vars: Vec<Type>, // indexed by VarId
|
vars: Vec<Type>, // indexed by VarId
|
||||||
decls: Vec<Box<dyn Command>>, // indexed by DeclId
|
decls: Vec<Box<dyn Command>>, // indexed by DeclId
|
||||||
blocks: Vec<Block>, // indexed by BlockId
|
blocks: Vec<Block>, // indexed by BlockId
|
||||||
|
@ -481,7 +503,13 @@ impl<'a> StateWorkingSet<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_span_start(&self) -> usize {
|
pub fn next_span_start(&self) -> usize {
|
||||||
self.permanent_state.next_span_start() + self.delta.file_contents.len()
|
let permanent_span_start = self.permanent_state.next_span_start();
|
||||||
|
|
||||||
|
if let Some((_, _, last)) = self.delta.file_contents.last() {
|
||||||
|
permanent_span_start + *last
|
||||||
|
} else {
|
||||||
|
permanent_span_start
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn global_span_offset(&self) -> usize {
|
pub fn global_span_offset(&self) -> usize {
|
||||||
|
@ -520,10 +548,11 @@ impl<'a> StateWorkingSet<'a> {
|
||||||
|
|
||||||
pub fn add_file(&mut self, filename: String, contents: &[u8]) -> usize {
|
pub fn add_file(&mut self, filename: String, contents: &[u8]) -> usize {
|
||||||
let next_span_start = self.next_span_start();
|
let next_span_start = self.next_span_start();
|
||||||
|
let next_span_end = next_span_start + contents.len();
|
||||||
|
|
||||||
self.delta.file_contents.extend(contents);
|
self.delta
|
||||||
|
.file_contents
|
||||||
let next_span_end = self.next_span_start();
|
.push((contents.to_vec(), next_span_start, next_span_end));
|
||||||
|
|
||||||
self.delta
|
self.delta
|
||||||
.files
|
.files
|
||||||
|
@ -535,11 +564,17 @@ impl<'a> StateWorkingSet<'a> {
|
||||||
pub fn get_span_contents(&self, span: Span) -> &[u8] {
|
pub fn get_span_contents(&self, span: Span) -> &[u8] {
|
||||||
let permanent_end = self.permanent_state.next_span_start();
|
let permanent_end = self.permanent_state.next_span_start();
|
||||||
if permanent_end <= span.start {
|
if permanent_end <= span.start {
|
||||||
&self.delta.file_contents[(span.start - permanent_end)..(span.end - permanent_end)]
|
for (contents, start, finish) in &self.delta.file_contents {
|
||||||
} else {
|
if (span.start >= *start) && (span.end <= *finish) {
|
||||||
&self.permanent_state.file_contents[span.start..span.end]
|
return &contents[(span.start - permanent_end)..(span.end - permanent_end)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return self.permanent_state.get_span_contents(&span);
|
||||||
|
}
|
||||||
|
|
||||||
|
panic!("internal error: missing span contents in file cache")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn enter_scope(&mut self) {
|
pub fn enter_scope(&mut self) {
|
||||||
self.delta.enter_scope();
|
self.delta.enter_scope();
|
||||||
|
|
|
@ -1,137 +0,0 @@
|
||||||
use super::EngineState;
|
|
||||||
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
|
||||||
|
|
||||||
use crate::{Example, ShellError, Signature, Value, VarId};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct EvaluationContext {
|
|
||||||
pub engine_state: Rc<RefCell<EngineState>>,
|
|
||||||
pub stack: Stack,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EvaluationContext {
|
|
||||||
pub fn get_var(&self, var_id: VarId) -> Result<Value, ShellError> {
|
|
||||||
self.stack.get_var(var_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enter_scope(&self) -> EvaluationContext {
|
|
||||||
Self {
|
|
||||||
engine_state: self.engine_state.clone(),
|
|
||||||
stack: self.stack.clone().enter_scope(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_var(&self, var_id: VarId, value: Value) {
|
|
||||||
// We need to make values concreate before we assign them to variables, as stream values
|
|
||||||
// will drain and remain drained.
|
|
||||||
//
|
|
||||||
// TODO: find a good home for converting a stream->list when storing into a variable
|
|
||||||
// TODO: add ctrl-c support when setting a var
|
|
||||||
|
|
||||||
let value = match value {
|
|
||||||
Value::Stream { stream, span } => Value::List {
|
|
||||||
vals: stream.collect(),
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
x => x,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.stack.add_var(var_id, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_env_var(&self, var: String, value: String) {
|
|
||||||
self.stack.add_env_var(var, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_stack(&self) {
|
|
||||||
self.stack.print_stack();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_signatures(&self) -> Vec<Signature> {
|
|
||||||
self.engine_state.borrow().get_signatures()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_signatures_with_examples(&self) -> Vec<(Signature, Vec<Example>)> {
|
|
||||||
self.engine_state.borrow().get_signatures_with_examples()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct StackFrame {
|
|
||||||
pub vars: HashMap<VarId, Value>,
|
|
||||||
pub env_vars: HashMap<String, String>,
|
|
||||||
pub parent: Option<Stack>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Stack(Rc<RefCell<StackFrame>>);
|
|
||||||
|
|
||||||
impl Default for Stack {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stack {
|
|
||||||
pub fn new() -> Stack {
|
|
||||||
Stack(Rc::new(RefCell::new(StackFrame {
|
|
||||||
vars: HashMap::new(),
|
|
||||||
env_vars: HashMap::new(),
|
|
||||||
parent: None,
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
pub fn get_var(&self, var_id: VarId) -> Result<Value, ShellError> {
|
|
||||||
let this = self.0.borrow();
|
|
||||||
match this.vars.get(&var_id) {
|
|
||||||
Some(v) => Ok(v.clone()),
|
|
||||||
_ => {
|
|
||||||
if let Some(parent) = &this.parent {
|
|
||||||
parent.get_var(var_id)
|
|
||||||
} else {
|
|
||||||
Err(ShellError::InternalError("variable not found".into()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_var(&self, var_id: VarId, value: Value) {
|
|
||||||
let mut this = self.0.borrow_mut();
|
|
||||||
this.vars.insert(var_id, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_env_var(&self, var: String, value: String) {
|
|
||||||
let mut this = self.0.borrow_mut();
|
|
||||||
this.env_vars.insert(var, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn enter_scope(self) -> Stack {
|
|
||||||
Stack(Rc::new(RefCell::new(StackFrame {
|
|
||||||
vars: HashMap::new(),
|
|
||||||
env_vars: HashMap::new(),
|
|
||||||
parent: Some(self),
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_env_vars(&self) -> HashMap<String, String> {
|
|
||||||
self.0.borrow().env_vars.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_env_var(&self, name: &str) -> Option<String> {
|
|
||||||
self.0.borrow().env_vars.get(name).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn print_stack(&self) {
|
|
||||||
println!("===frame===");
|
|
||||||
println!("vars:");
|
|
||||||
for (var, val) in &self.0.borrow().vars {
|
|
||||||
println!(" {}: {:?}", var, val);
|
|
||||||
}
|
|
||||||
println!("env vars:");
|
|
||||||
for (var, val) in &self.0.borrow().env_vars {
|
|
||||||
println!(" {}: {:?}", var, val);
|
|
||||||
}
|
|
||||||
if let Some(parent) = &self.0.borrow().parent {
|
|
||||||
parent.print_stack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +1,9 @@
|
||||||
mod call_info;
|
mod call_info;
|
||||||
mod command;
|
mod command;
|
||||||
mod engine_state;
|
mod engine_state;
|
||||||
mod evaluation_context;
|
mod stack;
|
||||||
|
|
||||||
pub use call_info::*;
|
pub use call_info::*;
|
||||||
pub use command::*;
|
pub use command::*;
|
||||||
pub use engine_state::*;
|
pub use engine_state::*;
|
||||||
pub use evaluation_context::*;
|
pub use stack::*;
|
||||||
|
|
97
crates/nu-protocol/src/engine/stack.rs
Normal file
97
crates/nu-protocol/src/engine/stack.rs
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::{ShellError, Value, VarId};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Stack {
|
||||||
|
pub vars: HashMap<VarId, Value>,
|
||||||
|
pub env_vars: HashMap<String, String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Stack {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stack {
|
||||||
|
pub fn new() -> Stack {
|
||||||
|
Stack {
|
||||||
|
vars: HashMap::new(),
|
||||||
|
env_vars: HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn get_var(&self, var_id: VarId) -> Result<Value, ShellError> {
|
||||||
|
if let Some(v) = self.vars.get(&var_id) {
|
||||||
|
return Ok(v.clone());
|
||||||
|
}
|
||||||
|
Err(ShellError::InternalError("variable not found".into()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_var(&mut self, var_id: VarId, value: Value) {
|
||||||
|
self.vars.insert(var_id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_env_var(&mut self, var: String, value: String) {
|
||||||
|
self.env_vars.insert(var, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_captures(&self, captures: &[VarId]) -> Stack {
|
||||||
|
let mut output = Stack::new();
|
||||||
|
|
||||||
|
for capture in captures {
|
||||||
|
// Note: this assumes we have calculated captures correctly and that commands
|
||||||
|
// that take in a var decl will manually set this into scope when running the blocks
|
||||||
|
if let Ok(value) = self.get_var(*capture) {
|
||||||
|
output.vars.insert(*capture, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
|
// pub fn enter_scope(&self) -> Stack {
|
||||||
|
// // FIXME: VERY EXPENSIVE to clone entire stack
|
||||||
|
// let mut output = self.clone();
|
||||||
|
// output.0.push(StackFrame {
|
||||||
|
// vars: HashMap::new(),
|
||||||
|
// env_vars: HashMap::new(),
|
||||||
|
// });
|
||||||
|
|
||||||
|
// output
|
||||||
|
// }
|
||||||
|
|
||||||
|
pub fn get_env_vars(&self) -> HashMap<String, String> {
|
||||||
|
// let mut output = HashMap::new();
|
||||||
|
|
||||||
|
// for frame in &self.0 {
|
||||||
|
// output.extend(frame.env_vars.clone().into_iter());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// output
|
||||||
|
self.env_vars.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_env_var(&self, name: &str) -> Option<String> {
|
||||||
|
// for frame in self.0.iter().rev() {
|
||||||
|
if let Some(v) = self.env_vars.get(name) {
|
||||||
|
return Some(v.to_string());
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_stack(&self) {
|
||||||
|
// for frame in self.0.iter().rev() {
|
||||||
|
// println!("===frame===");
|
||||||
|
println!("vars:");
|
||||||
|
for (var, val) in &self.vars {
|
||||||
|
println!(" {}: {:?}", var, val);
|
||||||
|
}
|
||||||
|
println!("env vars:");
|
||||||
|
for (var, val) in &self.env_vars {
|
||||||
|
println!(" {}: {:?}", var, val);
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ pub mod ast;
|
||||||
pub mod engine;
|
pub mod engine;
|
||||||
mod example;
|
mod example;
|
||||||
mod id;
|
mod id;
|
||||||
|
mod pipeline_data;
|
||||||
mod shell_error;
|
mod shell_error;
|
||||||
mod signature;
|
mod signature;
|
||||||
mod span;
|
mod span;
|
||||||
|
@ -12,6 +13,7 @@ pub use value::Value;
|
||||||
|
|
||||||
pub use example::*;
|
pub use example::*;
|
||||||
pub use id::*;
|
pub use id::*;
|
||||||
|
pub use pipeline_data::*;
|
||||||
pub use shell_error::*;
|
pub use shell_error::*;
|
||||||
pub use signature::*;
|
pub use signature::*;
|
||||||
pub use span::*;
|
pub use span::*;
|
||||||
|
|
163
crates/nu-protocol/src/pipeline_data.rs
Normal file
163
crates/nu-protocol/src/pipeline_data.rs
Normal file
|
@ -0,0 +1,163 @@
|
||||||
|
use std::sync::{atomic::AtomicBool, Arc};
|
||||||
|
|
||||||
|
use crate::{ast::PathMember, ShellError, Span, Value, ValueStream};
|
||||||
|
|
||||||
|
pub enum PipelineData {
|
||||||
|
Value(Value),
|
||||||
|
Stream(ValueStream),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PipelineData {
|
||||||
|
pub fn new() -> PipelineData {
|
||||||
|
PipelineData::Value(Value::nothing())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_value(self) -> Value {
|
||||||
|
match self {
|
||||||
|
PipelineData::Value(v) => v,
|
||||||
|
PipelineData::Stream(s) => Value::List {
|
||||||
|
vals: s.collect(),
|
||||||
|
span: Span::unknown(), // FIXME?
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn collect_string(self) -> String {
|
||||||
|
match self {
|
||||||
|
PipelineData::Value(v) => v.collect_string(),
|
||||||
|
PipelineData::Stream(s) => s.collect_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn follow_cell_path(self, cell_path: &[PathMember]) -> Result<Value, ShellError> {
|
||||||
|
match self {
|
||||||
|
// FIXME: there are probably better ways of doing this
|
||||||
|
PipelineData::Stream(stream) => Value::List {
|
||||||
|
vals: stream.collect(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
}
|
||||||
|
.follow_cell_path(cell_path),
|
||||||
|
PipelineData::Value(v) => v.follow_cell_path(cell_path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simplified mapper to help with simple values also. For full iterator support use `.into_iter()` instead
|
||||||
|
pub fn map<F>(
|
||||||
|
self,
|
||||||
|
mut f: F,
|
||||||
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
|
) -> Result<PipelineData, ShellError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
F: FnMut(Value) -> Value + 'static + Send,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
PipelineData::Value(Value::List { vals, .. }) => {
|
||||||
|
Ok(vals.into_iter().map(f).into_pipeline_data(ctrlc))
|
||||||
|
}
|
||||||
|
PipelineData::Stream(stream) => Ok(stream.map(f).into_pipeline_data(ctrlc)),
|
||||||
|
PipelineData::Value(Value::Range { val, .. }) => {
|
||||||
|
Ok(val.into_range_iter()?.map(f).into_pipeline_data(ctrlc))
|
||||||
|
}
|
||||||
|
PipelineData::Value(v) => {
|
||||||
|
let output = f(v);
|
||||||
|
match output {
|
||||||
|
Value::Error { error } => Err(error),
|
||||||
|
v => Ok(v.into_pipeline_data()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simplified flatmapper. For full iterator support use `.into_iter()` instead
|
||||||
|
pub fn flat_map<U, F>(
|
||||||
|
self,
|
||||||
|
mut f: F,
|
||||||
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
|
) -> Result<PipelineData, ShellError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
U: IntoIterator<Item = Value>,
|
||||||
|
<U as IntoIterator>::IntoIter: 'static + Send,
|
||||||
|
F: FnMut(Value) -> U + 'static + Send,
|
||||||
|
{
|
||||||
|
match self {
|
||||||
|
PipelineData::Value(Value::List { vals, .. }) => {
|
||||||
|
Ok(vals.into_iter().map(f).flatten().into_pipeline_data(ctrlc))
|
||||||
|
}
|
||||||
|
PipelineData::Stream(stream) => Ok(stream.map(f).flatten().into_pipeline_data(ctrlc)),
|
||||||
|
PipelineData::Value(Value::Range { val, .. }) => match val.into_range_iter() {
|
||||||
|
Ok(iter) => Ok(iter.map(f).flatten().into_pipeline_data(ctrlc)),
|
||||||
|
Err(error) => Err(error),
|
||||||
|
},
|
||||||
|
PipelineData::Value(v) => Ok(f(v).into_iter().into_pipeline_data(ctrlc)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for PipelineData {
|
||||||
|
fn default() -> Self {
|
||||||
|
PipelineData::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct PipelineIterator(PipelineData);
|
||||||
|
|
||||||
|
impl IntoIterator for PipelineData {
|
||||||
|
type Item = Value;
|
||||||
|
|
||||||
|
type IntoIter = PipelineIterator;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
match self {
|
||||||
|
PipelineData::Value(Value::List { vals, .. }) => {
|
||||||
|
PipelineIterator(PipelineData::Stream(ValueStream {
|
||||||
|
stream: Box::new(vals.into_iter()),
|
||||||
|
ctrlc: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
x => PipelineIterator(x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for PipelineIterator {
|
||||||
|
type Item = Value;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
match &mut self.0 {
|
||||||
|
PipelineData::Value(Value::Nothing { .. }) => None,
|
||||||
|
PipelineData::Value(v) => {
|
||||||
|
let prev = std::mem::take(v);
|
||||||
|
Some(prev)
|
||||||
|
}
|
||||||
|
PipelineData::Stream(stream) => stream.next(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IntoPipelineData {
|
||||||
|
fn into_pipeline_data(self) -> PipelineData;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntoPipelineData for Value {
|
||||||
|
fn into_pipeline_data(self) -> PipelineData {
|
||||||
|
PipelineData::Value(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IntoInterruptiblePipelineData {
|
||||||
|
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> IntoInterruptiblePipelineData for T
|
||||||
|
where
|
||||||
|
T: Iterator<Item = Value> + Send + 'static,
|
||||||
|
{
|
||||||
|
fn into_pipeline_data(self, ctrlc: Option<Arc<AtomicBool>>) -> PipelineData {
|
||||||
|
PipelineData::Stream(ValueStream {
|
||||||
|
stream: Box::new(self),
|
||||||
|
ctrlc,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::ast::Call;
|
use crate::ast::Call;
|
||||||
use crate::engine::Command;
|
use crate::engine::Command;
|
||||||
use crate::engine::EvaluationContext;
|
use crate::engine::EngineState;
|
||||||
|
use crate::engine::Stack;
|
||||||
use crate::BlockId;
|
use crate::BlockId;
|
||||||
|
use crate::PipelineData;
|
||||||
use crate::SyntaxShape;
|
use crate::SyntaxShape;
|
||||||
use crate::Value;
|
|
||||||
use crate::VarId;
|
use crate::VarId;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -335,6 +336,7 @@ impl Signature {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct Predeclaration {
|
struct Predeclaration {
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
}
|
}
|
||||||
|
@ -354,14 +356,16 @@ impl Command for Predeclaration {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
_call: &Call,
|
_call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<crate::Value, crate::ShellError> {
|
) -> Result<PipelineData, crate::ShellError> {
|
||||||
panic!("Internal error: can't run a predeclaration without a body")
|
panic!("Internal error: can't run a predeclaration without a body")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
struct BlockCommand {
|
struct BlockCommand {
|
||||||
signature: Signature,
|
signature: Signature,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
|
@ -382,10 +386,11 @@ impl Command for BlockCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_context: &EvaluationContext,
|
_engine_state: &EngineState,
|
||||||
|
_stack: &mut Stack,
|
||||||
_call: &Call,
|
_call: &Call,
|
||||||
_input: Value,
|
_input: PipelineData,
|
||||||
) -> Result<crate::Value, crate::ShellError> {
|
) -> Result<crate::PipelineData, crate::ShellError> {
|
||||||
panic!("Internal error: can't run custom command with 'run', use block_id");
|
panic!("Internal error: can't run custom command with 'run', use block_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,10 +59,6 @@ pub enum Value {
|
||||||
vals: Vec<Value>,
|
vals: Vec<Value>,
|
||||||
span: Span,
|
span: Span,
|
||||||
},
|
},
|
||||||
Stream {
|
|
||||||
stream: ValueStream,
|
|
||||||
span: Span,
|
|
||||||
},
|
|
||||||
List {
|
List {
|
||||||
vals: Vec<Value>,
|
vals: Vec<Value>,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
@ -110,7 +106,6 @@ impl Value {
|
||||||
Value::Record { span, .. } => Ok(*span),
|
Value::Record { span, .. } => Ok(*span),
|
||||||
Value::List { span, .. } => Ok(*span),
|
Value::List { span, .. } => Ok(*span),
|
||||||
Value::Block { span, .. } => Ok(*span),
|
Value::Block { span, .. } => Ok(*span),
|
||||||
Value::Stream { span, .. } => Ok(*span),
|
|
||||||
Value::Nothing { span, .. } => Ok(*span),
|
Value::Nothing { span, .. } => Ok(*span),
|
||||||
Value::Binary { span, .. } => Ok(*span),
|
Value::Binary { span, .. } => Ok(*span),
|
||||||
Value::CellPath { span, .. } => Ok(*span),
|
Value::CellPath { span, .. } => Ok(*span),
|
||||||
|
@ -129,7 +124,6 @@ impl Value {
|
||||||
Value::Range { span, .. } => *span = new_span,
|
Value::Range { span, .. } => *span = new_span,
|
||||||
Value::String { span, .. } => *span = new_span,
|
Value::String { span, .. } => *span = new_span,
|
||||||
Value::Record { span, .. } => *span = new_span,
|
Value::Record { span, .. } => *span = new_span,
|
||||||
Value::Stream { span, .. } => *span = new_span,
|
|
||||||
Value::List { span, .. } => *span = new_span,
|
Value::List { span, .. } => *span = new_span,
|
||||||
Value::Block { span, .. } => *span = new_span,
|
Value::Block { span, .. } => *span = new_span,
|
||||||
Value::Nothing { span, .. } => *span = new_span,
|
Value::Nothing { span, .. } => *span = new_span,
|
||||||
|
@ -158,7 +152,6 @@ impl Value {
|
||||||
Value::List { .. } => Type::List(Box::new(Type::Unknown)), // FIXME
|
Value::List { .. } => Type::List(Box::new(Type::Unknown)), // FIXME
|
||||||
Value::Nothing { .. } => Type::Nothing,
|
Value::Nothing { .. } => Type::Nothing,
|
||||||
Value::Block { .. } => Type::Block,
|
Value::Block { .. } => Type::Block,
|
||||||
Value::Stream { .. } => Type::ValueStream,
|
|
||||||
Value::Error { .. } => Type::Error,
|
Value::Error { .. } => Type::Error,
|
||||||
Value::Binary { .. } => Type::Binary,
|
Value::Binary { .. } => Type::Binary,
|
||||||
Value::CellPath { .. } => Type::CellPath,
|
Value::CellPath { .. } => Type::CellPath,
|
||||||
|
@ -174,19 +167,10 @@ impl Value {
|
||||||
Value::Filesize { val, .. } => format_filesize(val),
|
Value::Filesize { val, .. } => format_filesize(val),
|
||||||
Value::Duration { val, .. } => format_duration(val),
|
Value::Duration { val, .. } => format_duration(val),
|
||||||
Value::Date { val, .. } => HumanTime::from(val).to_string(),
|
Value::Date { val, .. } => HumanTime::from(val).to_string(),
|
||||||
Value::Range { val, .. } => match val.into_range_iter() {
|
Value::Range { val, .. } => {
|
||||||
Ok(iter) => {
|
format!("{}..{}", val.from.into_string(), val.to.into_string())
|
||||||
format!(
|
|
||||||
"range: [{}]",
|
|
||||||
iter.map(|x| x.into_string())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(", ")
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
Err(error) => format!("{:?}", error),
|
|
||||||
},
|
|
||||||
Value::String { val, .. } => val,
|
Value::String { val, .. } => val,
|
||||||
Value::Stream { stream, .. } => stream.into_string(),
|
|
||||||
Value::List { vals: val, .. } => format!(
|
Value::List { vals: val, .. } => format!(
|
||||||
"[{}]",
|
"[{}]",
|
||||||
val.into_iter()
|
val.into_iter()
|
||||||
|
@ -218,17 +202,10 @@ impl Value {
|
||||||
Value::Filesize { val, .. } => format!("{} bytes", val),
|
Value::Filesize { val, .. } => format!("{} bytes", val),
|
||||||
Value::Duration { val, .. } => format!("{} ns", val),
|
Value::Duration { val, .. } => format!("{} ns", val),
|
||||||
Value::Date { val, .. } => format!("{:?}", val),
|
Value::Date { val, .. } => format!("{:?}", val),
|
||||||
Value::Range { val, .. } => match val.into_range_iter() {
|
Value::Range { val, .. } => {
|
||||||
Ok(iter) => iter
|
format!("{}..{}", val.from.into_string(), val.to.into_string())
|
||||||
.map(|x| x.into_string())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(", "),
|
|
||||||
Err(error) => {
|
|
||||||
format!("{:?}", error)
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Value::String { val, .. } => val,
|
Value::String { val, .. } => val,
|
||||||
Value::Stream { stream, .. } => stream.collect_string(),
|
|
||||||
Value::List { vals: val, .. } => val
|
Value::List { vals: val, .. } => val
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| x.collect_string())
|
.map(|x| x.collect_string())
|
||||||
|
@ -274,13 +251,6 @@ impl Value {
|
||||||
return Err(ShellError::AccessBeyondEnd(val.len(), *origin_span));
|
return Err(ShellError::AccessBeyondEnd(val.len(), *origin_span));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Stream { stream, .. } => {
|
|
||||||
if let Some(item) = stream.nth(*count) {
|
|
||||||
current = item;
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::AccessBeyondEndOfStream(*origin_span));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x => {
|
x => {
|
||||||
return Err(ShellError::IncompatiblePathAccess(
|
return Err(ShellError::IncompatiblePathAccess(
|
||||||
format!("{}", x.get_type()),
|
format!("{}", x.get_type()),
|
||||||
|
@ -329,27 +299,6 @@ impl Value {
|
||||||
span: *span,
|
span: *span,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Value::Stream { stream, span } => {
|
|
||||||
let mut output = vec![];
|
|
||||||
for val in stream {
|
|
||||||
output.push(val.clone().follow_cell_path(&[PathMember::String {
|
|
||||||
val: column_name.clone(),
|
|
||||||
span: *origin_span,
|
|
||||||
}])?);
|
|
||||||
// if let Value::Record { cols, vals, .. } = val {
|
|
||||||
// for col in cols.iter().enumerate() {
|
|
||||||
// if col.1 == column_name {
|
|
||||||
// output.push(vals[col.0].clone());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
current = Value::List {
|
|
||||||
vals: output,
|
|
||||||
span: *span,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
x => {
|
x => {
|
||||||
return Err(ShellError::IncompatiblePathAccess(
|
return Err(ShellError::IncompatiblePathAccess(
|
||||||
format!("{}", x.get_type()),
|
format!("{}", x.get_type()),
|
||||||
|
@ -374,64 +323,6 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn map<F>(self, span: Span, mut f: F) -> Result<Value, ShellError>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
F: FnMut(Self) -> Value + 'static,
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
Value::List { vals, .. } => Ok(Value::Stream {
|
|
||||||
stream: vals.into_iter().map(f).into_value_stream(),
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
Value::Stream { stream, .. } => Ok(Value::Stream {
|
|
||||||
stream: stream.map(f).into_value_stream(),
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
Value::Range { val, .. } => Ok(Value::Stream {
|
|
||||||
stream: val.into_range_iter()?.map(f).into_value_stream(),
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
v => {
|
|
||||||
let output = f(v);
|
|
||||||
match output {
|
|
||||||
Value::Error { error } => Err(error),
|
|
||||||
v => Ok(v),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn flat_map<U, F>(self, span: Span, mut f: F) -> Value
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
U: IntoIterator<Item = Value>,
|
|
||||||
<U as IntoIterator>::IntoIter: 'static,
|
|
||||||
F: FnMut(Self) -> U + 'static,
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
Value::List { vals, .. } => Value::Stream {
|
|
||||||
stream: vals.into_iter().map(f).flatten().into_value_stream(),
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
Value::Stream { stream, .. } => Value::Stream {
|
|
||||||
stream: stream.map(f).flatten().into_value_stream(),
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
Value::Range { val, .. } => match val.into_range_iter() {
|
|
||||||
Ok(iter) => Value::Stream {
|
|
||||||
stream: iter.map(f).flatten().into_value_stream(),
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
Err(error) => Value::Error { error },
|
|
||||||
},
|
|
||||||
v => Value::Stream {
|
|
||||||
stream: f(v).into_iter().into_value_stream(),
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn string(val: impl Into<String>, span: Span) -> Value {
|
pub fn string(val: impl Into<String>, span: Span) -> Value {
|
||||||
Value::String {
|
Value::String {
|
||||||
val: val.into(),
|
val: val.into(),
|
||||||
|
@ -460,6 +351,12 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Value {
|
||||||
|
fn default() -> Self {
|
||||||
|
Value::nothing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialOrd for Value {
|
impl PartialOrd for Value {
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
// Compare two floating point numbers. The decision interval for equality is dynamically
|
// Compare two floating point numbers. The decision interval for equality is dynamically
|
||||||
|
@ -511,24 +408,6 @@ impl PartialOrd for Value {
|
||||||
..
|
..
|
||||||
},
|
},
|
||||||
) if lhs_headers == rhs_headers && lhs == rhs => Some(Ordering::Equal),
|
) if lhs_headers == rhs_headers && lhs == rhs => Some(Ordering::Equal),
|
||||||
(Value::Stream { stream: lhs, .. }, Value::Stream { stream: rhs, .. }) => {
|
|
||||||
lhs.clone().partial_cmp(rhs.clone())
|
|
||||||
}
|
|
||||||
(Value::Stream { stream: lhs, .. }, Value::String { val: rhs, .. }) => {
|
|
||||||
lhs.clone().collect_string().partial_cmp(rhs)
|
|
||||||
}
|
|
||||||
(Value::String { val: lhs, .. }, Value::Stream { stream: rhs, .. }) => {
|
|
||||||
lhs.partial_cmp(&rhs.clone().collect_string())
|
|
||||||
}
|
|
||||||
// NOTE: This may look a bit strange, but a `Stream` is still just a `List`, it just
|
|
||||||
// happens to be in an iterator form instead of a concrete form. The contained values
|
|
||||||
// can be compared.
|
|
||||||
(Value::Stream { stream: lhs, .. }, Value::List { vals: rhs, .. }) => {
|
|
||||||
lhs.clone().collect::<Vec<Value>>().partial_cmp(rhs)
|
|
||||||
}
|
|
||||||
(Value::List { vals: lhs, .. }, Value::Stream { stream: rhs, .. }) => {
|
|
||||||
lhs.partial_cmp(&rhs.clone().collect::<Vec<Value>>())
|
|
||||||
}
|
|
||||||
(Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => {
|
(Value::Binary { val: lhs, .. }, Value::Binary { val: rhs, .. }) => {
|
||||||
lhs.partial_cmp(rhs)
|
lhs.partial_cmp(rhs)
|
||||||
}
|
}
|
||||||
|
@ -852,10 +731,6 @@ impl Value {
|
||||||
val: rhs.contains(lhs),
|
val: rhs.contains(lhs),
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
(lhs, Value::Stream { stream: rhs, .. }) => Ok(Value::Bool {
|
|
||||||
val: rhs.clone().any(|x| lhs == &x),
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: op,
|
op_span: op,
|
||||||
lhs_ty: self.get_type(),
|
lhs_ty: self.get_type(),
|
||||||
|
@ -886,10 +761,6 @@ impl Value {
|
||||||
val: !rhs.contains(lhs),
|
val: !rhs.contains(lhs),
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
(lhs, Value::Stream { stream: rhs, .. }) => Ok(Value::Bool {
|
|
||||||
val: rhs.clone().all(|x| lhs != &x),
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
_ => Err(ShellError::OperatorMismatch {
|
_ => Err(ShellError::OperatorMismatch {
|
||||||
op_span: op,
|
op_span: op,
|
||||||
lhs_ty: self.get_type(),
|
lhs_ty: self.get_type(),
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
use crate::*;
|
use crate::*;
|
||||||
use std::{cell::RefCell, fmt::Debug, rc::Rc};
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use serde::{ser::SerializeSeq, Deserialize, Serialize};
|
pub struct ValueStream {
|
||||||
|
pub stream: Box<dyn Iterator<Item = Value> + Send + 'static>,
|
||||||
#[derive(Clone)]
|
pub ctrlc: Option<Arc<AtomicBool>>,
|
||||||
pub struct ValueStream(pub Rc<RefCell<dyn Iterator<Item = Value>>>);
|
}
|
||||||
|
|
||||||
impl ValueStream {
|
impl ValueStream {
|
||||||
pub fn into_string(self) -> String {
|
pub fn into_string(self) -> String {
|
||||||
|
@ -22,8 +28,14 @@ impl ValueStream {
|
||||||
.join("\n")
|
.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_stream(input: impl Iterator<Item = Value> + 'static) -> ValueStream {
|
pub fn from_stream(
|
||||||
ValueStream(Rc::new(RefCell::new(input)))
|
input: impl Iterator<Item = Value> + Send + 'static,
|
||||||
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
|
) -> ValueStream {
|
||||||
|
ValueStream {
|
||||||
|
stream: Box::new(input),
|
||||||
|
ctrlc,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,67 +49,73 @@ impl Iterator for ValueStream {
|
||||||
type Item = Value;
|
type Item = Value;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
{
|
if let Some(ctrlc) = &self.ctrlc {
|
||||||
self.0.borrow_mut().next()
|
if ctrlc.load(Ordering::SeqCst) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.stream.next()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.stream.next()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Serialize for ValueStream {
|
// impl Serialize for ValueStream {
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
// fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
where
|
// where
|
||||||
S: serde::Serializer,
|
// S: serde::Serializer,
|
||||||
{
|
// {
|
||||||
let mut seq = serializer.serialize_seq(None)?;
|
// let mut seq = serializer.serialize_seq(None)?;
|
||||||
|
|
||||||
for element in self.0.borrow_mut().into_iter() {
|
// for element in self.0.borrow_mut().into_iter() {
|
||||||
seq.serialize_element(&element)?;
|
// seq.serialize_element(&element)?;
|
||||||
}
|
// }
|
||||||
seq.end()
|
// seq.end()
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for ValueStream {
|
// impl<'de> Deserialize<'de> for ValueStream {
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
// fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
where
|
// where
|
||||||
D: serde::Deserializer<'de>,
|
// D: serde::Deserializer<'de>,
|
||||||
{
|
// {
|
||||||
deserializer.deserialize_seq(MySeqVisitor)
|
// deserializer.deserialize_seq(MySeqVisitor)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
struct MySeqVisitor;
|
// struct MySeqVisitor;
|
||||||
|
|
||||||
impl<'a> serde::de::Visitor<'a> for MySeqVisitor {
|
// impl<'a> serde::de::Visitor<'a> for MySeqVisitor {
|
||||||
type Value = ValueStream;
|
// type Value = ValueStream;
|
||||||
|
|
||||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
// fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
formatter.write_str("a value stream")
|
// formatter.write_str("a value stream")
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
// fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
|
||||||
where
|
// where
|
||||||
A: serde::de::SeqAccess<'a>,
|
// A: serde::de::SeqAccess<'a>,
|
||||||
{
|
// {
|
||||||
let mut output: Vec<Value> = vec![];
|
// let mut output: Vec<Value> = vec![];
|
||||||
|
|
||||||
while let Some(value) = seq.next_element()? {
|
// while let Some(value) = seq.next_element()? {
|
||||||
output.push(value);
|
// output.push(value);
|
||||||
}
|
// }
|
||||||
|
|
||||||
Ok(ValueStream(Rc::new(RefCell::new(output.into_iter()))))
|
// Ok(ValueStream(Rc::new(RefCell::new(output.into_iter()))))
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub trait IntoValueStream {
|
// pub trait IntoValueStream {
|
||||||
fn into_value_stream(self) -> ValueStream;
|
// fn into_value_stream(self) -> ValueStream;
|
||||||
}
|
// }
|
||||||
|
|
||||||
impl<T> IntoValueStream for T
|
// impl<T> IntoValueStream for T
|
||||||
where
|
// where
|
||||||
T: Iterator<Item = Value> + 'static,
|
// T: Iterator<Item = Value> + 'static,
|
||||||
{
|
// {
|
||||||
fn into_value_stream(self) -> ValueStream {
|
// fn into_value_stream(self) -> ValueStream {
|
||||||
ValueStream::from_stream(self)
|
// ValueStream::from_stream(self)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
194
src/main.rs
194
src/main.rs
|
@ -1,4 +1,10 @@
|
||||||
use std::{cell::RefCell, io::Write, rc::Rc};
|
use std::{
|
||||||
|
io::Write,
|
||||||
|
sync::{
|
||||||
|
atomic::{AtomicBool, Ordering},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use dialoguer::{
|
use dialoguer::{
|
||||||
console::{Style, Term},
|
console::{Style, Term},
|
||||||
|
@ -12,8 +18,8 @@ use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::Call,
|
ast::Call,
|
||||||
engine::{EngineState, EvaluationContext, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
ShellError, Value,
|
IntoPipelineData, PipelineData, ShellError, Value,
|
||||||
};
|
};
|
||||||
use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt};
|
use reedline::{Completer, CompletionActionHandler, DefaultPrompt, LineBuffer, Prompt};
|
||||||
|
|
||||||
|
@ -82,14 +88,27 @@ fn main() -> Result<()> {
|
||||||
miette_hook(x);
|
miette_hook(x);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let engine_state = create_default_context();
|
let mut engine_state = create_default_context();
|
||||||
|
|
||||||
|
// TODO: make this conditional in the future
|
||||||
|
// Ctrl-c protection section
|
||||||
|
let ctrlc = Arc::new(AtomicBool::new(false));
|
||||||
|
let handler_ctrlc = ctrlc.clone();
|
||||||
|
let engine_state_ctrlc = ctrlc.clone();
|
||||||
|
|
||||||
|
ctrlc::set_handler(move || {
|
||||||
|
handler_ctrlc.store(true, Ordering::SeqCst);
|
||||||
|
})
|
||||||
|
.expect("Error setting Ctrl-C handler");
|
||||||
|
|
||||||
|
engine_state.ctrlc = Some(engine_state_ctrlc);
|
||||||
|
// End ctrl-c protection section
|
||||||
|
|
||||||
if let Some(path) = std::env::args().nth(1) {
|
if let Some(path) = std::env::args().nth(1) {
|
||||||
let file = std::fs::read(&path).into_diagnostic()?;
|
let file = std::fs::read(&path).into_diagnostic()?;
|
||||||
|
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let engine_state = engine_state.borrow();
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let mut working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
let (output, err) = parse(&mut working_set, Some(&path), &file, false);
|
let (output, err) = parse(&mut working_set, Some(&path), &file, false);
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
|
@ -99,20 +118,16 @@ fn main() -> Result<()> {
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
};
|
};
|
||||||
|
|
||||||
EngineState::merge_delta(&mut *engine_state.borrow_mut(), delta);
|
EngineState::merge_delta(&mut engine_state, delta);
|
||||||
|
|
||||||
let state = EvaluationContext {
|
let mut stack = nu_protocol::engine::Stack::new();
|
||||||
engine_state: engine_state.clone(),
|
|
||||||
stack: nu_protocol::engine::Stack::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
match eval_block(&state, &block, Value::nothing()) {
|
match eval_block(&engine_state, &mut stack, &block, PipelineData::new()) {
|
||||||
Ok(value) => {
|
Ok(pipeline_data) => {
|
||||||
println!("{}", value.into_string());
|
println!("{}", pipeline_data.collect_string());
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let engine_state = engine_state.borrow();
|
let working_set = StateWorkingSet::new(&engine_state);
|
||||||
let working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
|
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
|
|
||||||
|
@ -127,28 +142,9 @@ fn main() -> Result<()> {
|
||||||
let completer = NuCompleter::new(engine_state.clone());
|
let completer = NuCompleter::new(engine_state.clone());
|
||||||
let mut entry_num = 0;
|
let mut entry_num = 0;
|
||||||
|
|
||||||
let mut line_editor = Reedline::create()
|
|
||||||
.into_diagnostic()?
|
|
||||||
.with_history(Box::new(
|
|
||||||
FileBackedHistory::with_file(1000, "history.txt".into()).into_diagnostic()?,
|
|
||||||
))
|
|
||||||
.into_diagnostic()?
|
|
||||||
.with_highlighter(Box::new(NuHighlighter {
|
|
||||||
engine_state: engine_state.clone(),
|
|
||||||
}))
|
|
||||||
.with_completion_action_handler(Box::new(FuzzyCompletion {
|
|
||||||
completer: Box::new(completer),
|
|
||||||
}))
|
|
||||||
// .with_completion_action_handler(Box::new(
|
|
||||||
// ListCompletionHandler::default().with_completer(Box::new(completer)),
|
|
||||||
// ))
|
|
||||||
.with_validator(Box::new(NuValidator {
|
|
||||||
engine_state: engine_state.clone(),
|
|
||||||
}));
|
|
||||||
|
|
||||||
let default_prompt = DefaultPrompt::new(1);
|
let default_prompt = DefaultPrompt::new(1);
|
||||||
let mut nu_prompt = NushellPrompt::new();
|
let mut nu_prompt = NushellPrompt::new();
|
||||||
let stack = nu_protocol::engine::Stack::new();
|
let mut stack = nu_protocol::engine::Stack::new();
|
||||||
|
|
||||||
// Load config startup file
|
// Load config startup file
|
||||||
if let Some(mut config_path) = nu_path::config_dir() {
|
if let Some(mut config_path) = nu_path::config_dir() {
|
||||||
|
@ -162,15 +158,53 @@ fn main() -> Result<()> {
|
||||||
let config_filename = config_path.to_string_lossy().to_owned();
|
let config_filename = config_path.to_string_lossy().to_owned();
|
||||||
|
|
||||||
if let Ok(contents) = std::fs::read_to_string(&config_path) {
|
if let Ok(contents) = std::fs::read_to_string(&config_path) {
|
||||||
eval_source(engine_state.clone(), &stack, &contents, &config_filename);
|
eval_source(&mut engine_state, &mut stack, &contents, &config_filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let history_path = if let Some(mut history_path) = nu_path::config_dir() {
|
||||||
|
history_path.push("nushell");
|
||||||
|
history_path.push("history.txt");
|
||||||
|
|
||||||
|
Some(history_path)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
//Reset the ctrl-c handler
|
||||||
|
ctrlc.store(false, Ordering::SeqCst);
|
||||||
|
|
||||||
|
let line_editor = Reedline::create()
|
||||||
|
.into_diagnostic()?
|
||||||
|
.with_completion_action_handler(Box::new(FuzzyCompletion {
|
||||||
|
completer: Box::new(completer.clone()),
|
||||||
|
}))
|
||||||
|
.with_highlighter(Box::new(NuHighlighter {
|
||||||
|
engine_state: engine_state.clone(),
|
||||||
|
}))
|
||||||
|
// .with_completion_action_handler(Box::new(
|
||||||
|
// ListCompletionHandler::default().with_completer(Box::new(completer)),
|
||||||
|
// ))
|
||||||
|
.with_validator(Box::new(NuValidator {
|
||||||
|
engine_state: engine_state.clone(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
let mut line_editor = if let Some(history_path) = history_path.clone() {
|
||||||
|
line_editor
|
||||||
|
.with_history(Box::new(
|
||||||
|
FileBackedHistory::with_file(1000, history_path.clone())
|
||||||
|
.into_diagnostic()?,
|
||||||
|
))
|
||||||
|
.into_diagnostic()?
|
||||||
|
} else {
|
||||||
|
line_editor
|
||||||
|
};
|
||||||
|
|
||||||
let prompt = update_prompt(
|
let prompt = update_prompt(
|
||||||
PROMPT_COMMAND,
|
PROMPT_COMMAND,
|
||||||
engine_state.clone(),
|
&engine_state,
|
||||||
&stack,
|
&stack,
|
||||||
&mut nu_prompt,
|
&mut nu_prompt,
|
||||||
&default_prompt,
|
&default_prompt,
|
||||||
|
@ -184,25 +218,25 @@ fn main() -> Result<()> {
|
||||||
if s.trim() == "exit" {
|
if s.trim() == "exit" {
|
||||||
break;
|
break;
|
||||||
} else if s.trim() == "vars" {
|
} else if s.trim() == "vars" {
|
||||||
engine_state.borrow().print_vars();
|
engine_state.print_vars();
|
||||||
continue;
|
continue;
|
||||||
} else if s.trim() == "decls" {
|
} else if s.trim() == "decls" {
|
||||||
engine_state.borrow().print_decls();
|
engine_state.print_decls();
|
||||||
continue;
|
continue;
|
||||||
} else if s.trim() == "blocks" {
|
} else if s.trim() == "blocks" {
|
||||||
engine_state.borrow().print_blocks();
|
engine_state.print_blocks();
|
||||||
continue;
|
continue;
|
||||||
} else if s.trim() == "stack" {
|
} else if s.trim() == "stack" {
|
||||||
stack.print_stack();
|
stack.print_stack();
|
||||||
continue;
|
continue;
|
||||||
} else if s.trim() == "contents" {
|
} else if s.trim() == "contents" {
|
||||||
engine_state.borrow().print_contents();
|
engine_state.print_contents();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state.clone(),
|
&mut engine_state,
|
||||||
&stack,
|
&mut stack,
|
||||||
&s,
|
&s,
|
||||||
&format!("entry #{}", entry_num),
|
&format!("entry #{}", entry_num),
|
||||||
);
|
);
|
||||||
|
@ -229,16 +263,19 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_value(value: Value, state: &EvaluationContext) -> Result<(), ShellError> {
|
fn print_value(value: Value, engine_state: &EngineState) -> Result<(), ShellError> {
|
||||||
// If the table function is in the declarations, then we can use it
|
// If the table function is in the declarations, then we can use it
|
||||||
// to create the table value that will be printed in the terminal
|
// to create the table value that will be printed in the terminal
|
||||||
let engine_state = state.engine_state.borrow();
|
|
||||||
let output = match engine_state.find_decl("table".as_bytes()) {
|
let output = match engine_state.find_decl("table".as_bytes()) {
|
||||||
Some(decl_id) => {
|
Some(decl_id) => {
|
||||||
let table = engine_state
|
let mut stack = Stack::new();
|
||||||
.get_decl(decl_id)
|
let table = engine_state.get_decl(decl_id).run(
|
||||||
.run(state, &Call::new(), value)?;
|
engine_state,
|
||||||
table.into_string()
|
&mut stack,
|
||||||
|
&Call::new(),
|
||||||
|
value.into_pipeline_data(),
|
||||||
|
)?;
|
||||||
|
table.collect_string()
|
||||||
}
|
}
|
||||||
None => value.into_string(),
|
None => value.into_string(),
|
||||||
};
|
};
|
||||||
|
@ -254,7 +291,7 @@ fn print_value(value: Value, state: &EvaluationContext) -> Result<(), ShellError
|
||||||
|
|
||||||
fn update_prompt<'prompt>(
|
fn update_prompt<'prompt>(
|
||||||
env_variable: &str,
|
env_variable: &str,
|
||||||
engine_state: Rc<RefCell<EngineState>>,
|
engine_state: &EngineState,
|
||||||
stack: &Stack,
|
stack: &Stack,
|
||||||
nu_prompt: &'prompt mut NushellPrompt,
|
nu_prompt: &'prompt mut NushellPrompt,
|
||||||
default_prompt: &'prompt DefaultPrompt,
|
default_prompt: &'prompt DefaultPrompt,
|
||||||
|
@ -270,37 +307,22 @@ fn update_prompt<'prompt>(
|
||||||
return nu_prompt as &dyn Prompt;
|
return nu_prompt as &dyn Prompt;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (block, delta) = {
|
let block = {
|
||||||
let ref_engine_state = engine_state.borrow();
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
let mut working_set = StateWorkingSet::new(&ref_engine_state);
|
|
||||||
let (output, err) = parse(&mut working_set, None, prompt_command.as_bytes(), false);
|
let (output, err) = parse(&mut working_set, None, prompt_command.as_bytes(), false);
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
return default_prompt as &dyn Prompt;
|
return default_prompt as &dyn Prompt;
|
||||||
}
|
}
|
||||||
(output, working_set.render())
|
output
|
||||||
};
|
};
|
||||||
|
|
||||||
EngineState::merge_delta(&mut *engine_state.borrow_mut(), delta);
|
let mut stack = stack.clone();
|
||||||
|
|
||||||
let state = nu_protocol::engine::EvaluationContext {
|
let evaluated_prompt = match eval_block(engine_state, &mut stack, &block, PipelineData::new()) {
|
||||||
engine_state: engine_state.clone(),
|
Ok(pipeline_data) => pipeline_data.collect_string(),
|
||||||
stack: stack.clone(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let evaluated_prompt = match eval_block(&state, &block, Value::nothing()) {
|
|
||||||
Ok(value) => match value.as_string() {
|
|
||||||
Ok(prompt) => prompt,
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let engine_state = engine_state.borrow();
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
let working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
report_error(&working_set, &err);
|
|
||||||
return default_prompt as &dyn Prompt;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(err) => {
|
|
||||||
let engine_state = engine_state.borrow();
|
|
||||||
let working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
return default_prompt as &dyn Prompt;
|
return default_prompt as &dyn Prompt;
|
||||||
}
|
}
|
||||||
|
@ -312,14 +334,13 @@ fn update_prompt<'prompt>(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_source(
|
fn eval_source(
|
||||||
engine_state: Rc<RefCell<EngineState>>,
|
engine_state: &mut EngineState,
|
||||||
stack: &Stack,
|
stack: &mut Stack,
|
||||||
source: &str,
|
source: &str,
|
||||||
fname: &str,
|
fname: &str,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let engine_state = engine_state.borrow();
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
let mut working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
let (output, err) = parse(
|
let (output, err) = parse(
|
||||||
&mut working_set,
|
&mut working_set,
|
||||||
Some(fname), // format!("entry #{}", entry_num)
|
Some(fname), // format!("entry #{}", entry_num)
|
||||||
|
@ -333,26 +354,19 @@ fn eval_source(
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
};
|
};
|
||||||
|
|
||||||
EngineState::merge_delta(&mut *engine_state.borrow_mut(), delta);
|
EngineState::merge_delta(engine_state, delta);
|
||||||
|
|
||||||
let state = nu_protocol::engine::EvaluationContext {
|
match eval_block(engine_state, stack, &block, PipelineData::new()) {
|
||||||
engine_state: engine_state.clone(),
|
Ok(pipeline_data) => {
|
||||||
stack: stack.clone(),
|
if let Err(err) = print_value(pipeline_data.into_value(), engine_state) {
|
||||||
};
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
match eval_block(&state, &block, Value::nothing()) {
|
|
||||||
Ok(value) => {
|
|
||||||
if let Err(err) = print_value(value, &state) {
|
|
||||||
let engine_state = engine_state.borrow();
|
|
||||||
let working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
|
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let engine_state = engine_state.borrow();
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
let working_set = StateWorkingSet::new(&*engine_state);
|
|
||||||
|
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
return false;
|
return false;
|
||||||
|
|
42
src/tests.rs
42
src/tests.rs
|
@ -249,32 +249,32 @@ fn alias_2() -> TestResult {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_param1() -> TestResult {
|
fn block_param1() -> TestResult {
|
||||||
run_test("[3] | each { $it + 10 }", "[13]")
|
run_test("[3] | each { $it + 10 } | get 0", "13")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_param2() -> TestResult {
|
fn block_param2() -> TestResult {
|
||||||
run_test("[3] | each { |y| $y + 10 }", "[13]")
|
run_test("[3] | each { |y| $y + 10 } | get 0", "13")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_param3_list_iteration() -> TestResult {
|
fn block_param3_list_iteration() -> TestResult {
|
||||||
run_test("[1,2,3] | each { $it + 10 }", "[11, 12, 13]")
|
run_test("[1,2,3] | each { $it + 10 } | get 1", "12")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn block_param4_list_iteration() -> TestResult {
|
fn block_param4_list_iteration() -> TestResult {
|
||||||
run_test("[1,2,3] | each { |y| $y + 10 }", "[11, 12, 13]")
|
run_test("[1,2,3] | each { |y| $y + 10 } | get 2", "13")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn range_iteration1() -> TestResult {
|
fn range_iteration1() -> TestResult {
|
||||||
run_test("1..4 | each { |y| $y + 10 }", "[11, 12, 13, 14]")
|
run_test("1..4 | each { |y| $y + 10 } | get 0", "11")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn range_iteration2() -> TestResult {
|
fn range_iteration2() -> TestResult {
|
||||||
run_test("4..1 | each { |y| $y + 100 }", "[104, 103, 102, 101]")
|
run_test("4..1 | each { |y| $y + 100 } | get 3", "101")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -311,22 +311,22 @@ fn build_string3() -> TestResult {
|
||||||
#[test]
|
#[test]
|
||||||
fn build_string4() -> TestResult {
|
fn build_string4() -> TestResult {
|
||||||
run_test(
|
run_test(
|
||||||
"['sam','rick','pete'] | each { build-string $it ' is studying'}",
|
"['sam','rick','pete'] | each { build-string $it ' is studying'} | get 2",
|
||||||
"[sam is studying, rick is studying, pete is studying]",
|
"pete is studying",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn build_string5() -> TestResult {
|
fn build_string5() -> TestResult {
|
||||||
run_test(
|
run_test(
|
||||||
"['sam','rick','pete'] | each { |x| build-string $x ' is studying'}",
|
"['sam','rick','pete'] | each { |x| build-string $x ' is studying'} | get 1",
|
||||||
"[sam is studying, rick is studying, pete is studying]",
|
"rick is studying",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cell_path_subexpr1() -> TestResult {
|
fn cell_path_subexpr1() -> TestResult {
|
||||||
run_test("([[lang, gems]; [nu, 100]]).lang", "[nu]")
|
run_test("([[lang, gems]; [nu, 100]]).lang | get 0", "nu")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -336,7 +336,7 @@ fn cell_path_subexpr2() -> TestResult {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cell_path_var1() -> TestResult {
|
fn cell_path_var1() -> TestResult {
|
||||||
run_test("let x = [[lang, gems]; [nu, 100]]; $x.lang", "[nu]")
|
run_test("let x = [[lang, gems]; [nu, 100]]; $x.lang | get 0", "nu")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -352,21 +352,21 @@ fn custom_rest_var() -> TestResult {
|
||||||
#[test]
|
#[test]
|
||||||
fn row_iteration() -> TestResult {
|
fn row_iteration() -> TestResult {
|
||||||
run_test(
|
run_test(
|
||||||
"[[name, size]; [tj, 100], [rl, 200]] | each { $it.size * 8 }",
|
"[[name, size]; [tj, 100], [rl, 200]] | each { $it.size * 8 } | get 1",
|
||||||
"[800, 1600]",
|
"1600",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn record_iteration() -> TestResult {
|
fn record_iteration() -> TestResult {
|
||||||
run_test("([[name, level]; [aa, 100], [bb, 200]] | each { $it | each { |x| if $x.column == \"level\" { $x.value + 100 } else { $x.value } } }).level", "[200, 300]")
|
run_test("([[name, level]; [aa, 100], [bb, 200]] | each { $it | each { |x| if $x.column == \"level\" { $x.value + 100 } else { $x.value } } }).level | get 1", "300")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn row_condition1() -> TestResult {
|
fn row_condition1() -> TestResult {
|
||||||
run_test(
|
run_test(
|
||||||
"([[name, size]; [a, 1], [b, 2], [c, 3]] | where size < 3).name",
|
"([[name, size]; [a, 1], [b, 2], [c, 3]] | where size < 3).name | get 1",
|
||||||
"[a, b]",
|
"b",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,6 +621,14 @@ fn for_loops() -> TestResult {
|
||||||
run_test(r#"(for x in [1, 2, 3] { $x + 10 }).1"#, "12")
|
run_test(r#"(for x in [1, 2, 3] { $x + 10 }).1"#, "12")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn par_each() -> TestResult {
|
||||||
|
run_test(
|
||||||
|
r#"1..10 | par-each --numbered { ([[index, item]; [$it.index, ($it.item > 5)]]).0 } | where index == 4 | get item.0"#,
|
||||||
|
"false",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn type_in_list_of_this_type() -> TestResult {
|
fn type_in_list_of_this_type() -> TestResult {
|
||||||
run_test(r#"42 in [41 42 43]"#, "true")
|
run_test(r#"42 in [41 42 43]"#, "true")
|
||||||
|
|
Loading…
Reference in a new issue