mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Compare commits
62 Commits
Author | SHA1 | Date | |
---|---|---|---|
388973e9ab | |||
2129ec7558 | |||
82f122525c | |||
7c4c00f1e6 | |||
fe6c7dc10a | |||
9bc24e3b12 | |||
833baca66e | |||
9fd92512a2 | |||
b692ca7896 | |||
52dc04a35a | |||
42b1287759 | |||
5a471aa1d0 | |||
a4b8d4a098 | |||
a3be6affa4 | |||
71b99edd48 | |||
64553ddcb7 | |||
2a42482ae9 | |||
11f345a8ae | |||
fec50d8cfe | |||
05e42381df | |||
b435075e09 | |||
430da53f0b | |||
2e6d836dd1 | |||
899d324a9c | |||
576ed6a906 | |||
d744cf8437 | |||
088e662285 | |||
f9b0b81eb2 | |||
c5485c6501 | |||
d8ed01400f | |||
ebc4694e05 | |||
a9441d670e | |||
495d2ebd70 | |||
ad26adc3e3 | |||
63a62e19f9 | |||
4f2ae34df9 | |||
a636f161a4 | |||
dfb1e22559 | |||
dff85a7f70 | |||
3be198d2f5 | |||
d19314fe3a | |||
d06f457b2a | |||
7d07881d96 | |||
3e6e3a207c | |||
481c6d4511 | |||
231a445809 | |||
93e8f6c05e | |||
9de2144fc4 | |||
363dc51ba0 | |||
99117ff2ef | |||
5356cb9fbd | |||
0e13d9fbaa | |||
2dcb16870b | |||
ac9909112f | |||
eb3c2c9e76 | |||
3d29e3efbf | |||
f410fb6689 | |||
eb62fd466e | |||
b50cdd6de8 | |||
f38e2b5c6d | |||
455915ec9e | |||
2333158256 |
@ -4,7 +4,7 @@ tasks:
|
||||
- name: Clippy
|
||||
init: cargo clippy --all --features=stable -- -D clippy::result_unwrap_used -D clippy::option_unwrap_used
|
||||
- name: Testing
|
||||
init: cargo test --all --features=stable,test-bins
|
||||
init: cargo test --all --features=stable
|
||||
- name: Build
|
||||
init: cargo build --features=stable
|
||||
- name: Nu
|
||||
|
1963
Cargo.lock
generated
1963
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
85
Cargo.toml
85
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
||||
name = "nu"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
version = "0.25.1"
|
||||
version = "0.26.0"
|
||||
|
||||
[workspace]
|
||||
members = ["crates/*/"]
|
||||
@ -18,66 +18,64 @@ members = ["crates/*/"]
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-cli = {version = "0.25.1", path = "./crates/nu-cli"}
|
||||
nu-data = {version = "0.25.1", path = "./crates/nu-data"}
|
||||
nu-errors = {version = "0.25.1", path = "./crates/nu-errors"}
|
||||
nu-parser = {version = "0.25.1", path = "./crates/nu-parser"}
|
||||
nu-plugin = {version = "0.25.1", path = "./crates/nu-plugin"}
|
||||
nu-protocol = {version = "0.25.1", path = "./crates/nu-protocol"}
|
||||
nu-source = {version = "0.25.1", path = "./crates/nu-source"}
|
||||
nu-value-ext = {version = "0.25.1", path = "./crates/nu-value-ext"}
|
||||
nu-cli = {version = "0.26.0", path = "./crates/nu-cli"}
|
||||
nu-command = {version = "0.26.0", path = "./crates/nu-command"}
|
||||
nu-data = {version = "0.26.0", path = "./crates/nu-data"}
|
||||
nu-engine = {version = "0.26.0", path = "./crates/nu-engine"}
|
||||
nu-errors = {version = "0.26.0", path = "./crates/nu-errors"}
|
||||
nu-parser = {version = "0.26.0", path = "./crates/nu-parser"}
|
||||
nu-plugin = {version = "0.26.0", path = "./crates/nu-plugin"}
|
||||
nu-protocol = {version = "0.26.0", path = "./crates/nu-protocol"}
|
||||
nu-source = {version = "0.26.0", path = "./crates/nu-source"}
|
||||
nu-value-ext = {version = "0.26.0", path = "./crates/nu-value-ext"}
|
||||
|
||||
nu_plugin_binaryview = {version = "0.25.1", path = "./crates/nu_plugin_binaryview", optional = true}
|
||||
nu_plugin_chart = {version = "0.25.1", path = "./crates/nu_plugin_chart", optional = true}
|
||||
nu_plugin_fetch = {version = "0.25.1", path = "./crates/nu_plugin_fetch", optional = true}
|
||||
nu_plugin_from_bson = {version = "0.25.1", path = "./crates/nu_plugin_from_bson", optional = true}
|
||||
nu_plugin_from_sqlite = {version = "0.25.1", path = "./crates/nu_plugin_from_sqlite", optional = true}
|
||||
nu_plugin_inc = {version = "0.25.1", path = "./crates/nu_plugin_inc", optional = true}
|
||||
nu_plugin_match = {version = "0.25.1", path = "./crates/nu_plugin_match", optional = true}
|
||||
nu_plugin_post = {version = "0.25.1", path = "./crates/nu_plugin_post", optional = true}
|
||||
nu_plugin_ps = {version = "0.25.1", path = "./crates/nu_plugin_ps", optional = true}
|
||||
nu_plugin_s3 = {version = "0.25.1", path = "./crates/nu_plugin_s3", optional = true}
|
||||
nu_plugin_selector = {version = "0.25.1", path = "./crates/nu_plugin_selector", optional = true}
|
||||
nu_plugin_start = {version = "0.25.1", path = "./crates/nu_plugin_start", optional = true}
|
||||
nu_plugin_sys = {version = "0.25.1", path = "./crates/nu_plugin_sys", optional = true}
|
||||
nu_plugin_textview = {version = "0.25.1", path = "./crates/nu_plugin_textview", optional = true}
|
||||
nu_plugin_to_bson = {version = "0.25.1", path = "./crates/nu_plugin_to_bson", optional = true}
|
||||
nu_plugin_to_sqlite = {version = "0.25.1", path = "./crates/nu_plugin_to_sqlite", optional = true}
|
||||
nu_plugin_tree = {version = "0.25.1", path = "./crates/nu_plugin_tree", optional = true}
|
||||
nu_plugin_xpath = {version = "0.25.1", path = "./crates/nu_plugin_xpath", optional = true}
|
||||
nu_plugin_binaryview = {version = "0.26.0", path = "./crates/nu_plugin_binaryview", optional = true}
|
||||
nu_plugin_chart = {version = "0.26.0", path = "./crates/nu_plugin_chart", optional = true}
|
||||
nu_plugin_fetch = {version = "0.26.0", path = "./crates/nu_plugin_fetch", optional = true}
|
||||
nu_plugin_from_bson = {version = "0.26.0", path = "./crates/nu_plugin_from_bson", optional = true}
|
||||
nu_plugin_from_sqlite = {version = "0.26.0", path = "./crates/nu_plugin_from_sqlite", optional = true}
|
||||
nu_plugin_inc = {version = "0.26.0", path = "./crates/nu_plugin_inc", optional = true}
|
||||
nu_plugin_match = {version = "0.26.0", path = "./crates/nu_plugin_match", optional = true}
|
||||
nu_plugin_post = {version = "0.26.0", path = "./crates/nu_plugin_post", optional = true}
|
||||
nu_plugin_ps = {version = "0.26.0", path = "./crates/nu_plugin_ps", optional = true}
|
||||
nu_plugin_s3 = {version = "0.26.0", path = "./crates/nu_plugin_s3", optional = true}
|
||||
nu_plugin_selector = {version = "0.26.0", path = "./crates/nu_plugin_selector", optional = true}
|
||||
nu_plugin_start = {version = "0.26.0", path = "./crates/nu_plugin_start", optional = true}
|
||||
nu_plugin_sys = {version = "0.26.0", path = "./crates/nu_plugin_sys", optional = true}
|
||||
nu_plugin_textview = {version = "0.26.0", path = "./crates/nu_plugin_textview", optional = true}
|
||||
nu_plugin_to_bson = {version = "0.26.0", path = "./crates/nu_plugin_to_bson", optional = true}
|
||||
nu_plugin_to_sqlite = {version = "0.26.0", path = "./crates/nu_plugin_to_sqlite", optional = true}
|
||||
nu_plugin_tree = {version = "0.26.0", path = "./crates/nu_plugin_tree", optional = true}
|
||||
nu_plugin_xpath = {version = "0.26.0", path = "./crates/nu_plugin_xpath", optional = true}
|
||||
|
||||
# Required to bootstrap the main binary
|
||||
clap = "2.33.3"
|
||||
ctrlc = {version = "3.1.6", optional = true}
|
||||
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
|
||||
itertools = "0.9.0"
|
||||
itertools = "0.10.0"
|
||||
log = "0.4.11"
|
||||
pretty_env_logger = "0.4.0"
|
||||
syn = "=1.0.57"
|
||||
|
||||
[dev-dependencies]
|
||||
dunce = "1.0.1"
|
||||
nu-test-support = {version = "0.25.1", path = "./crates/nu-test-support"}
|
||||
nu-test-support = {version = "0.26.0", path = "./crates/nu-test-support"}
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
[features]
|
||||
ctrlc-support = ["nu-cli/ctrlc"]
|
||||
directories-support = ["nu-cli/directories", "nu-cli/dirs", "nu-data/directories", "nu-data/dirs"]
|
||||
git-support = ["nu-cli/git2"]
|
||||
ptree-support = ["nu-cli/ptree"]
|
||||
rich-benchmark = ["nu-cli/rich-benchmark"]
|
||||
rustyline-support = ["nu-cli/rustyline-support"]
|
||||
term-support = ["nu-cli/term"]
|
||||
uuid-support = ["nu-cli/uuid_crate"]
|
||||
which-support = ["nu-cli/ichwh", "nu-cli/which"]
|
||||
ctrlc-support = ["nu-cli/ctrlc", "nu-command/ctrlc"]
|
||||
directories-support = ["nu-cli/directories", "nu-cli/dirs", "nu-command/directories", "nu-command/dirs", "nu-data/directories", "nu-data/dirs", "nu-engine/dirs"]
|
||||
ptree-support = ["nu-cli/ptree", "nu-command/ptree"]
|
||||
rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"]
|
||||
term-support = ["nu-cli/term", "nu-command/term"]
|
||||
uuid-support = ["nu-cli/uuid_crate", "nu-command/uuid_crate"]
|
||||
which-support = ["nu-cli/ichwh", "nu-cli/which", "nu-command/ichwh", "nu-command/which"]
|
||||
|
||||
default = [
|
||||
"sys",
|
||||
"ps",
|
||||
"textview",
|
||||
"inc",
|
||||
"git-support",
|
||||
"directories-support",
|
||||
"ctrlc-support",
|
||||
"which-support",
|
||||
@ -88,7 +86,6 @@ default = [
|
||||
"match",
|
||||
"post",
|
||||
"fetch",
|
||||
"rich-benchmark",
|
||||
"zip-support",
|
||||
]
|
||||
extra = ["default", "binaryview", "tree", "clipboard-cli", "trash-support", "start", "bson", "sqlite", "s3", "chart", "xpath", "selector"]
|
||||
@ -106,18 +103,18 @@ post = ["nu_plugin_post"]
|
||||
ps = ["nu_plugin_ps"]
|
||||
sys = ["nu_plugin_sys"]
|
||||
textview = ["nu_plugin_textview"]
|
||||
zip-support = ["nu-cli/zip"]
|
||||
zip-support = ["nu-cli/zip", "nu-command/zip"]
|
||||
|
||||
# Extra
|
||||
binaryview = ["nu_plugin_binaryview"]
|
||||
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
|
||||
chart = ["nu_plugin_chart"]
|
||||
clipboard-cli = ["nu-cli/clipboard-cli"]
|
||||
clipboard-cli = ["nu-cli/clipboard-cli", "nu-command/clipboard-cli"]
|
||||
s3 = ["nu_plugin_s3"]
|
||||
selector = ["nu_plugin_selector"]
|
||||
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
|
||||
start = ["nu_plugin_start"]
|
||||
trash-support = ["nu-cli/trash-support"]
|
||||
trash-support = ["nu-cli/trash-support", "nu-command/trash-support"]
|
||||
tree = ["nu_plugin_tree"]
|
||||
xpath = ["nu_plugin_xpath"]
|
||||
|
||||
|
@ -11,7 +11,7 @@
|
||||
|
||||
A new type of shell.
|
||||
|
||||

|
||||

|
||||
|
||||
## Status
|
||||
|
||||
@ -300,8 +300,8 @@ Nu is in heavy development, and will naturally change as it matures and people u
|
||||
| Errors | | | X | | | Error reporting works, but could use usability polish
|
||||
| Documentation | | X | | | | Book and related are barebones and lack task-based lessons
|
||||
| Paging | | X | | | | Textview has paging, but we'd like paging for tables
|
||||
| Functions| X | | | | | No functions, yet, only aliases
|
||||
| Variables| X | | | | | Nu doesn't yet support variables
|
||||
| Functions | | X | | | | No functions, yet, only aliases
|
||||
| Variables| | X | | | | Nu doesn't yet support variables
|
||||
| Completions | | X | | | | Completions are currently barebones, at best
|
||||
| Type-checking | | X | | | | Commands check basic types, but input/output isn't checked
|
||||
|
||||
|
@ -5,80 +5,80 @@ description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.25.1"
|
||||
version = "0.26.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-data = {version = "0.25.1", path = "../nu-data"}
|
||||
nu-errors = {version = "0.25.1", path = "../nu-errors"}
|
||||
nu-json = {version = "0.25.1", path = "../nu-json"}
|
||||
nu-parser = {version = "0.25.1", path = "../nu-parser"}
|
||||
nu-plugin = {version = "0.25.1", path = "../nu-plugin"}
|
||||
nu-protocol = {version = "0.25.1", path = "../nu-protocol"}
|
||||
nu-source = {version = "0.25.1", path = "../nu-source"}
|
||||
nu-stream = {version = "0.25.1", path = "../nu-stream"}
|
||||
nu-table = {version = "0.25.1", path = "../nu-table"}
|
||||
nu-test-support = {version = "0.25.1", path = "../nu-test-support"}
|
||||
nu-value-ext = {version = "0.25.1", path = "../nu-value-ext"}
|
||||
nu-command = { version = "0.26.0", path = "../nu-command" }
|
||||
nu-data = { version = "0.26.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.26.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.26.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.26.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.26.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.26.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.26.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.26.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.26.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.26.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.26.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.26.0", path = "../nu-value-ext" }
|
||||
|
||||
Inflector = "0.11"
|
||||
ansi_term = "0.12.1"
|
||||
arboard = {version = "1.1.0", optional = true}
|
||||
arboard = { version = "1.1.0", optional = true }
|
||||
async-recursion = "0.3.1"
|
||||
async-trait = "0.1.40"
|
||||
base64 = "0.13.0"
|
||||
bigdecimal = {version = "0.2.0", features = ["serde"]}
|
||||
bigdecimal = { version = "0.2.0", features = ["serde"] }
|
||||
byte-unit = "4.0.9"
|
||||
bytes = "0.5.6"
|
||||
calamine = "0.16.1"
|
||||
chrono = {version = "0.4.15", features = ["serde"]}
|
||||
chrono = { version = "0.4.15", features = ["serde"] }
|
||||
chrono-tz = "0.5.3"
|
||||
clap = "2.33.3"
|
||||
codespan-reporting = "0.9.5"
|
||||
codespan-reporting = "0.11.0"
|
||||
csv = "1.1.3"
|
||||
ctrlc = {version = "3.1.6", optional = true}
|
||||
ctrlc = { version = "3.1.6", optional = true }
|
||||
derive-new = "0.5.8"
|
||||
directories = {version = "3.0.1", optional = true}
|
||||
dirs = {version = "3.0.1", optional = true}
|
||||
directories-next = { version = "2.0.0", optional = true }
|
||||
dirs-next = { version = "2.0.0", optional = true }
|
||||
dtparse = "1.2.0"
|
||||
dunce = "1.0.1"
|
||||
eml-parser = "0.1.0"
|
||||
encoding_rs = "0.8.24"
|
||||
filesize = "0.2.0"
|
||||
fs_extra = "1.2.0"
|
||||
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
|
||||
futures-util = "0.3.5"
|
||||
futures = { version = "0.3.5", features = ["compat", "io-compat"] }
|
||||
futures-util = "0.3.8"
|
||||
futures_codec = "0.4.1"
|
||||
getset = "0.1.1"
|
||||
git2 = {version = "0.13.11", default_features = false, optional = true}
|
||||
glob = "0.3.0"
|
||||
heim = {version = "0.1.0-rc.1", optional = true}
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.6.0"
|
||||
ichwh = {version = "0.3.4", optional = true}
|
||||
indexmap = {version = "1.6.0", features = ["serde-1"]}
|
||||
itertools = "0.9.0"
|
||||
ical = "0.7.0"
|
||||
ichwh = { version = "0.3.4", optional = true }
|
||||
indexmap = { version = "1.6.0", features = ["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.*"
|
||||
log = "0.4.11"
|
||||
meval = "0.2.0"
|
||||
num-bigint = {version = "0.3.0", features = ["serde"]}
|
||||
num-format = {version = "0.4.0", features = ["with-num-bigint"]}
|
||||
num-bigint = { version = "0.3.0", features = ["serde"] }
|
||||
num-format = { version = "0.4.0", features = ["with-num-bigint"] }
|
||||
num-traits = "0.2.12"
|
||||
parking_lot = "0.11.0"
|
||||
pin-utils = "0.1.0"
|
||||
pretty-hex = "0.2.0"
|
||||
ptree = {version = "0.3.0", optional = true}
|
||||
ptree = { version = "0.3.0", optional = true }
|
||||
query_interface = "0.3.5"
|
||||
quick-xml = "0.18.1"
|
||||
quick-xml = "0.20.0"
|
||||
rand = "0.7.3"
|
||||
rayon = "1.4.0"
|
||||
regex = "1.3.9"
|
||||
roxmltree = "0.13.0"
|
||||
rust-embed = "5.6.0"
|
||||
rustyline = {version = "6.3.0", optional = true}
|
||||
serde = {version = "1.0.115", features = ["derive"]}
|
||||
roxmltree = "0.14.0"
|
||||
rust-embed = "5.8.0"
|
||||
rustyline = { version = "6.3.0", optional = true }
|
||||
serde = { version = "1.0.115", features = ["derive"] }
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
serde_json = "1.0.57"
|
||||
@ -90,18 +90,18 @@ strip-ansi-escapes = "0.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
sxd-xpath = "0.4.2"
|
||||
tempfile = "3.1.0"
|
||||
term = {version = "0.6.1", optional = true}
|
||||
term = { version = "0.6.1", optional = true }
|
||||
term_size = "0.3.2"
|
||||
termcolor = "1.1.0"
|
||||
titlecase = "1.0"
|
||||
toml = "0.5.6"
|
||||
trash = {version = "1.2.0", optional = true}
|
||||
trash = { version = "1.2.0", optional = true }
|
||||
unicode-segmentation = "1.6.0"
|
||||
uom = {version = "0.30.0", features = ["f64", "try-from"]}
|
||||
url = "2.1.1"
|
||||
uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"], optional = true}
|
||||
which = {version = "4.0.2", optional = true}
|
||||
zip = {version = "0.5.7", optional = true}
|
||||
uuid_crate = { package = "uuid", version = "0.8.1", features = ["v4"], optional = true }
|
||||
which = { version = "4.0.2", optional = true }
|
||||
zip = { version = "0.5.7", optional = true }
|
||||
shadow-rs = { version = "0.5", default-features = false, optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
umask = "1.0.0"
|
||||
@ -126,8 +126,10 @@ quickcheck = "0.9.2"
|
||||
quickcheck_macros = "0.9.1"
|
||||
|
||||
[features]
|
||||
default = ["shadow-rs"]
|
||||
clipboard-cli = ["arboard"]
|
||||
rich-benchmark = ["heim"]
|
||||
rustyline-support = ["rustyline"]
|
||||
rustyline-support = ["rustyline", "nu-engine/rustyline-support"]
|
||||
stable = []
|
||||
trash-support = ["trash"]
|
||||
dirs = ["dirs-next"]
|
||||
directories = ["directories-next"]
|
||||
|
@ -1,27 +1,36 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::default_context::create_default_context;
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use crate::prelude::*;
|
||||
use crate::script::{print_err, run_script_standalone};
|
||||
use crate::line_editor::configure_ctrl_c;
|
||||
use nu_command::commands::default_context::create_default_context;
|
||||
#[allow(unused_imports)]
|
||||
use nu_command::maybe_print_errors;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::EvaluationContext;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use crate::script::{process_script, LineResult};
|
||||
pub(crate) use nu_command::script::{process_script, LineResult};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use crate::shell::Helper;
|
||||
use crate::line_editor::{
|
||||
configure_rustyline_editor, convert_rustyline_result_to_string,
|
||||
default_rustyline_editor_configuration, nu_line_editor_helper,
|
||||
};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use nu_data::config;
|
||||
use nu_source::{Tag, Text};
|
||||
use nu_stream::InputStream;
|
||||
#[allow(unused_imports)]
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use nu_command::script::{print_err, run_script_standalone};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use rustyline::{self, error::ReadlineError};
|
||||
|
||||
use crate::EnvironmentSyncer;
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_protocol::{UntaggedValue, Value};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use rustyline::{
|
||||
self,
|
||||
config::Configurer,
|
||||
config::{ColorMode, CompletionType, Config},
|
||||
error::ReadlineError,
|
||||
At, Cmd, Editor, KeyPress, Movement, Word,
|
||||
};
|
||||
use std::error::Error;
|
||||
use std::iter::Iterator;
|
||||
use std::path::PathBuf;
|
||||
@ -85,20 +94,6 @@ pub async fn run_script_file(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
fn convert_rustyline_result_to_string(input: Result<String, ReadlineError>) -> LineResult {
|
||||
match input {
|
||||
Ok(s) if s == "history -c" || s == "history --clear" => LineResult::ClearHistory,
|
||||
Ok(s) => LineResult::Success(s),
|
||||
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
|
||||
Err(ReadlineError::Eof) => LineResult::CtrlD,
|
||||
Err(err) => {
|
||||
outln!("Error: {:?}", err);
|
||||
LineResult::Break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The entry point for the CLI. Will register all known internal commands, load experimental commands, load plugins, then prepare the prompt and line reader for input.
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
@ -128,7 +123,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
// Give ourselves a scope to work in
|
||||
context.scope.enter_scope();
|
||||
|
||||
let history_path = crate::commands::history::history_path(&configuration);
|
||||
let history_path = nu_engine::history_path(&configuration);
|
||||
let _ = rl.load_history(&history_path);
|
||||
|
||||
let mut session_text = String::new();
|
||||
@ -168,17 +163,9 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
let (prompt_block, err) = nu_parser::parse(&prompt_line, 0, &context.scope);
|
||||
|
||||
if err.is_some() {
|
||||
use crate::git::current_branch;
|
||||
context.scope.exit_scope();
|
||||
|
||||
format!(
|
||||
"\x1b[32m{}{}\x1b[m> ",
|
||||
cwd,
|
||||
match current_branch() {
|
||||
Some(s) => format!("({})", s),
|
||||
None => "".to_string(),
|
||||
}
|
||||
)
|
||||
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
|
||||
} else {
|
||||
// let env = context.get_env();
|
||||
|
||||
@ -189,7 +176,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
Ok(result) => match result.collect_string(Tag::unknown()).await {
|
||||
Ok(string_result) => {
|
||||
let errors = context.get_errors();
|
||||
context.maybe_print_errors(Text::from(prompt_line));
|
||||
maybe_print_errors(&context, Text::from(prompt_line));
|
||||
context.clear_errors();
|
||||
|
||||
if !errors.is_empty() {
|
||||
@ -214,15 +201,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
use crate::git::current_branch;
|
||||
format!(
|
||||
"\x1b[32m{}{}\x1b[m> ",
|
||||
cwd,
|
||||
match current_branch() {
|
||||
Some(s) => format!("({})", s),
|
||||
None => "".to_string(),
|
||||
}
|
||||
)
|
||||
format!("\x1b[32m{}{}\x1b[m> ", cwd, current_branch())
|
||||
}
|
||||
};
|
||||
|
||||
@ -284,7 +263,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
LineResult::Success(line) => {
|
||||
rl.add_history_entry(&line);
|
||||
let _ = rl.save_history(&history_path);
|
||||
context.maybe_print_errors(Text::from(session_text.clone()));
|
||||
maybe_print_errors(&context, Text::from(session_text.clone()));
|
||||
}
|
||||
|
||||
LineResult::ClearHistory => {
|
||||
@ -300,7 +279,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
print_err(err, &Text::from(session_text.clone()));
|
||||
});
|
||||
|
||||
context.maybe_print_errors(Text::from(session_text.clone()));
|
||||
maybe_print_errors(&context, Text::from(session_text.clone()));
|
||||
}
|
||||
|
||||
LineResult::CtrlC => {
|
||||
@ -344,7 +323,7 @@ pub async fn cli(mut context: EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
}
|
||||
|
||||
pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellError> {
|
||||
if let Ok(plugins) = crate::plugin::scan(search_paths()) {
|
||||
if let Ok(plugins) = nu_engine::plugin::build_plugin::scan(search_paths()) {
|
||||
context.add_commands(
|
||||
plugins
|
||||
.into_iter()
|
||||
@ -356,23 +335,6 @@ pub fn register_plugins(context: &mut EvaluationContext) -> Result<(), ShellErro
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_ctrl_c(_context: &mut EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
#[cfg(feature = "ctrlc")]
|
||||
{
|
||||
let cc = _context.ctrl_c.clone();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
cc.store(true, Ordering::SeqCst);
|
||||
})?;
|
||||
|
||||
if _context.ctrl_c.load(Ordering::SeqCst) {
|
||||
_context.ctrl_c.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_startup_commands(
|
||||
context: &mut EvaluationContext,
|
||||
config: &dyn nu_data::config::Conf,
|
||||
@ -383,16 +345,17 @@ async fn run_startup_commands(
|
||||
value: UntaggedValue::Table(pipelines),
|
||||
..
|
||||
} => {
|
||||
let mut script_file = String::new();
|
||||
for pipeline in pipelines {
|
||||
if let Ok(pipeline_string) = pipeline.as_string() {
|
||||
let _ = run_script_standalone(pipeline_string, false, context, false).await;
|
||||
}
|
||||
script_file.push_str(&pipeline.as_string()?);
|
||||
script_file.push('\n');
|
||||
}
|
||||
let _ = run_script_standalone(script_file, false, context, false).await;
|
||||
}
|
||||
_ => {
|
||||
return Err(ShellError::untagged_runtime_error(
|
||||
"expected a table of pipeline strings as startup commands",
|
||||
))
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -400,187 +363,6 @@ async fn run_startup_commands(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
fn default_rustyline_editor_configuration() -> Editor<Helper> {
|
||||
#[cfg(windows)]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
|
||||
#[cfg(not(windows))]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
|
||||
|
||||
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
||||
let mut rl: Editor<_> = Editor::with_config(config);
|
||||
|
||||
// add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight
|
||||
rl.bind_sequence(
|
||||
KeyPress::ControlLeft,
|
||||
Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
|
||||
);
|
||||
rl.bind_sequence(
|
||||
KeyPress::ControlRight,
|
||||
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
|
||||
);
|
||||
|
||||
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
|
||||
rl.bind_sequence(KeyPress::BracketedPasteStart, rustyline::Cmd::Noop);
|
||||
|
||||
// Let's set the defaults up front and then override them later if the user indicates
|
||||
// defaults taken from here https://github.com/kkawakam/rustyline/blob/2fe886c9576c1ea13ca0e5808053ad491a6fe049/src/config.rs#L150-L167
|
||||
rl.set_max_history_size(100);
|
||||
rl.set_history_ignore_dups(true);
|
||||
rl.set_history_ignore_space(false);
|
||||
rl.set_completion_type(DEFAULT_COMPLETION_MODE);
|
||||
rl.set_completion_prompt_limit(100);
|
||||
rl.set_keyseq_timeout(-1);
|
||||
rl.set_edit_mode(rustyline::config::EditMode::Emacs);
|
||||
rl.set_auto_add_history(false);
|
||||
rl.set_bell_style(rustyline::config::BellStyle::default());
|
||||
rl.set_color_mode(rustyline::ColorMode::Enabled);
|
||||
rl.set_tab_stop(8);
|
||||
|
||||
if let Err(e) = crate::keybinding::load_keybindings(&mut rl) {
|
||||
println!("Error loading keybindings: {:?}", e);
|
||||
}
|
||||
|
||||
rl
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
fn configure_rustyline_editor(
|
||||
rl: &mut Editor<Helper>,
|
||||
config: &dyn nu_data::config::Conf,
|
||||
) -> Result<(), ShellError> {
|
||||
#[cfg(windows)]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
|
||||
#[cfg(not(windows))]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
|
||||
|
||||
if let Some(line_editor_vars) = config.var("line_editor") {
|
||||
for (idx, value) in line_editor_vars.row_entries() {
|
||||
match idx.as_ref() {
|
||||
"max_history_size" => {
|
||||
if let Ok(max_history_size) = value.as_u64() {
|
||||
rl.set_max_history_size(max_history_size as usize);
|
||||
}
|
||||
}
|
||||
"history_duplicates" => {
|
||||
// history_duplicates = match value.as_string() {
|
||||
// Ok(s) if s.to_lowercase() == "alwaysadd" => {
|
||||
// rustyline::config::HistoryDuplicates::AlwaysAdd
|
||||
// }
|
||||
// Ok(s) if s.to_lowercase() == "ignoreconsecutive" => {
|
||||
// rustyline::config::HistoryDuplicates::IgnoreConsecutive
|
||||
// }
|
||||
// _ => rustyline::config::HistoryDuplicates::AlwaysAdd,
|
||||
// };
|
||||
if let Ok(history_duplicates) = value.as_bool() {
|
||||
rl.set_history_ignore_dups(history_duplicates);
|
||||
}
|
||||
}
|
||||
"history_ignore_space" => {
|
||||
if let Ok(history_ignore_space) = value.as_bool() {
|
||||
rl.set_history_ignore_space(history_ignore_space);
|
||||
}
|
||||
}
|
||||
"completion_type" => {
|
||||
let completion_type = match value.as_string() {
|
||||
Ok(s) if s.to_lowercase() == "circular" => {
|
||||
rustyline::config::CompletionType::Circular
|
||||
}
|
||||
Ok(s) if s.to_lowercase() == "list" => {
|
||||
rustyline::config::CompletionType::List
|
||||
}
|
||||
#[cfg(all(unix, feature = "with-fuzzy"))]
|
||||
Ok(s) if s.to_lowercase() == "fuzzy" => {
|
||||
rustyline::config::CompletionType::Fuzzy
|
||||
}
|
||||
_ => DEFAULT_COMPLETION_MODE,
|
||||
};
|
||||
rl.set_completion_type(completion_type);
|
||||
}
|
||||
"completion_prompt_limit" => {
|
||||
if let Ok(completion_prompt_limit) = value.as_u64() {
|
||||
rl.set_completion_prompt_limit(completion_prompt_limit as usize);
|
||||
}
|
||||
}
|
||||
"keyseq_timeout_ms" => {
|
||||
if let Ok(keyseq_timeout_ms) = value.as_u64() {
|
||||
rl.set_keyseq_timeout(keyseq_timeout_ms as i32);
|
||||
}
|
||||
}
|
||||
"edit_mode" => {
|
||||
let edit_mode = match value.as_string() {
|
||||
Ok(s) if s.to_lowercase() == "vi" => rustyline::config::EditMode::Vi,
|
||||
Ok(s) if s.to_lowercase() == "emacs" => rustyline::config::EditMode::Emacs,
|
||||
_ => rustyline::config::EditMode::Emacs,
|
||||
};
|
||||
rl.set_edit_mode(edit_mode);
|
||||
// Note: When edit_mode is Emacs, the keyseq_timeout_ms is set to -1
|
||||
// no matter what you may have configured. This is so that key chords
|
||||
// can be applied without having to do them in a given timeout. So,
|
||||
// it essentially turns off the keyseq timeout.
|
||||
}
|
||||
"auto_add_history" => {
|
||||
if let Ok(auto_add_history) = value.as_bool() {
|
||||
rl.set_auto_add_history(auto_add_history);
|
||||
}
|
||||
}
|
||||
"bell_style" => {
|
||||
let bell_style = match value.as_string() {
|
||||
Ok(s) if s.to_lowercase() == "audible" => {
|
||||
rustyline::config::BellStyle::Audible
|
||||
}
|
||||
Ok(s) if s.to_lowercase() == "none" => rustyline::config::BellStyle::None,
|
||||
Ok(s) if s.to_lowercase() == "visible" => {
|
||||
rustyline::config::BellStyle::Visible
|
||||
}
|
||||
_ => rustyline::config::BellStyle::default(),
|
||||
};
|
||||
rl.set_bell_style(bell_style);
|
||||
}
|
||||
"color_mode" => {
|
||||
let color_mode = match value.as_string() {
|
||||
Ok(s) if s.to_lowercase() == "enabled" => rustyline::ColorMode::Enabled,
|
||||
Ok(s) if s.to_lowercase() == "forced" => rustyline::ColorMode::Forced,
|
||||
Ok(s) if s.to_lowercase() == "disabled" => rustyline::ColorMode::Disabled,
|
||||
_ => rustyline::ColorMode::Enabled,
|
||||
};
|
||||
rl.set_color_mode(color_mode);
|
||||
}
|
||||
"tab_stop" => {
|
||||
if let Ok(tab_stop) = value.as_u64() {
|
||||
rl.set_tab_stop(tab_stop as usize);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
fn nu_line_editor_helper(
|
||||
context: &mut EvaluationContext,
|
||||
config: &dyn nu_data::config::Conf,
|
||||
) -> crate::shell::Helper {
|
||||
let hinter = rustyline_hinter(config);
|
||||
crate::shell::Helper::new(context.clone(), hinter)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
fn rustyline_hinter(config: &dyn nu_data::config::Conf) -> Option<rustyline::hint::HistoryHinter> {
|
||||
if let Some(line_editor_vars) = config.var("line_editor") {
|
||||
for (idx, value) in line_editor_vars.row_entries() {
|
||||
if idx == "show_hints" && value.expect_string() == "false" {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(rustyline::hint::HistoryHinter {})
|
||||
}
|
||||
|
||||
pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<String, ShellError> {
|
||||
// FIXME: do we still need this?
|
||||
let line = if let Some(s) = line.strip_suffix('\n') {
|
||||
@ -607,15 +389,32 @@ pub async fn parse_and_eval(line: &str, ctx: &EvaluationContext) -> Result<Strin
|
||||
result?.collect_string(Tag::unknown()).await.map(|x| x.item)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn current_branch() -> String {
|
||||
#[cfg(feature = "shadow-rs")]
|
||||
{
|
||||
Some(shadow_rs::branch())
|
||||
.map(|x| x.trim().to_string())
|
||||
.filter(|x| !x.is_empty())
|
||||
.map(|x| format!("({})", x))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
#[cfg(not(feature = "shadow-rs"))]
|
||||
{
|
||||
"".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nu_engine::basic_evaluation_context;
|
||||
|
||||
#[quickcheck]
|
||||
fn quickcheck_parse(data: String) -> bool {
|
||||
let (tokens, err) = nu_parser::lex(&data, 0);
|
||||
let (lite_block, err2) = nu_parser::group(tokens);
|
||||
let (lite_block, err2) = nu_parser::block(tokens);
|
||||
if err.is_none() && err2.is_none() {
|
||||
let context = crate::evaluation_context::EvaluationContext::basic().unwrap();
|
||||
let context = basic_evaluation_context().unwrap();
|
||||
let _ = nu_parser::classify_block(&lite_block, &context.scope);
|
||||
}
|
||||
true
|
||||
|
@ -1,10 +0,0 @@
|
||||
pub(crate) mod block;
|
||||
mod dynamic;
|
||||
pub(crate) mod expr;
|
||||
pub(crate) mod external;
|
||||
pub(crate) mod internal;
|
||||
pub(crate) mod maybe_text_codec;
|
||||
pub(crate) mod plugin;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use dynamic::Command as DynamicCommand;
|
@ -1,395 +0,0 @@
|
||||
use crate::deserializer::ConfigDeserializer;
|
||||
use crate::evaluate::evaluate_args::evaluate_args;
|
||||
use crate::prelude::*;
|
||||
use crate::{commands::help::get_help, run_block};
|
||||
use derive_new::new;
|
||||
use getset::Getters;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::{self, Block};
|
||||
use nu_protocol::{CallInfo, EvaluatedArgs, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||
use parking_lot::Mutex;
|
||||
use serde::Deserialize;
|
||||
use std::ops::Deref;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UnevaluatedCallInfo {
|
||||
pub args: hir::Call,
|
||||
pub name_tag: Tag,
|
||||
}
|
||||
|
||||
impl UnevaluatedCallInfo {
|
||||
pub async fn evaluate(self, ctx: &EvaluationContext) -> Result<CallInfo, ShellError> {
|
||||
let args = evaluate_args(&self.args, ctx).await?;
|
||||
|
||||
Ok(CallInfo {
|
||||
args,
|
||||
name_tag: self.name_tag,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn switch_present(&self, switch: &str) -> bool {
|
||||
self.args.switch_preset(switch)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Getters)]
|
||||
#[get = "pub(crate)"]
|
||||
pub struct CommandArgs {
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: UnevaluatedCallInfo,
|
||||
pub scope: Scope,
|
||||
pub input: InputStream,
|
||||
}
|
||||
|
||||
#[derive(Getters, Clone)]
|
||||
#[get = "pub(crate)"]
|
||||
pub struct RawCommandArgs {
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub scope: Scope,
|
||||
pub call_info: UnevaluatedCallInfo,
|
||||
}
|
||||
|
||||
impl RawCommandArgs {
|
||||
pub fn with_input(self, input: impl Into<InputStream>) -> CommandArgs {
|
||||
CommandArgs {
|
||||
host: self.host,
|
||||
ctrl_c: self.ctrl_c,
|
||||
current_errors: self.current_errors,
|
||||
shell_manager: self.shell_manager,
|
||||
call_info: self.call_info,
|
||||
scope: self.scope,
|
||||
input: input.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for CommandArgs {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.call_info.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl CommandArgs {
|
||||
pub async fn evaluate_once(self) -> Result<EvaluatedWholeStreamCommandArgs, ShellError> {
|
||||
let ctx = EvaluationContext::from_args(&self);
|
||||
let host = self.host.clone();
|
||||
let ctrl_c = self.ctrl_c.clone();
|
||||
let shell_manager = self.shell_manager.clone();
|
||||
let input = self.input;
|
||||
let call_info = self.call_info.evaluate(&ctx).await?;
|
||||
let scope = self.scope.clone();
|
||||
|
||||
Ok(EvaluatedWholeStreamCommandArgs::new(
|
||||
host,
|
||||
ctrl_c,
|
||||
shell_manager,
|
||||
call_info,
|
||||
input,
|
||||
scope,
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn process<'de, T: Deserialize<'de>>(self) -> Result<(T, InputStream), ShellError> {
|
||||
let args = self.evaluate_once().await?;
|
||||
let call_info = args.call_info.clone();
|
||||
|
||||
let mut deserializer = ConfigDeserializer::from_call_info(call_info);
|
||||
|
||||
Ok((T::deserialize(&mut deserializer)?, args.input))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct RunnableContext {
|
||||
pub input: InputStream,
|
||||
pub shell_manager: ShellManager,
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub scope: Scope,
|
||||
pub name: Tag,
|
||||
}
|
||||
|
||||
impl RunnableContext {
|
||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
||||
self.scope.get_command(name)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EvaluatedWholeStreamCommandArgs {
|
||||
pub args: EvaluatedCommandArgs,
|
||||
pub input: InputStream,
|
||||
}
|
||||
|
||||
impl Deref for EvaluatedWholeStreamCommandArgs {
|
||||
type Target = EvaluatedCommandArgs;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.args
|
||||
}
|
||||
}
|
||||
|
||||
impl EvaluatedWholeStreamCommandArgs {
|
||||
pub fn new(
|
||||
host: Arc<parking_lot::Mutex<dyn Host>>,
|
||||
ctrl_c: Arc<AtomicBool>,
|
||||
shell_manager: ShellManager,
|
||||
call_info: CallInfo,
|
||||
input: impl Into<InputStream>,
|
||||
scope: Scope,
|
||||
) -> EvaluatedWholeStreamCommandArgs {
|
||||
EvaluatedWholeStreamCommandArgs {
|
||||
args: EvaluatedCommandArgs {
|
||||
host,
|
||||
ctrl_c,
|
||||
shell_manager,
|
||||
call_info,
|
||||
scope,
|
||||
},
|
||||
input: input.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name_tag(&self) -> Tag {
|
||||
self.args.call_info.name_tag.clone()
|
||||
}
|
||||
|
||||
pub fn parts(self) -> (InputStream, EvaluatedArgs) {
|
||||
let EvaluatedWholeStreamCommandArgs { args, input } = self;
|
||||
|
||||
(input, args.call_info.args)
|
||||
}
|
||||
|
||||
pub fn split(self) -> (InputStream, EvaluatedCommandArgs) {
|
||||
let EvaluatedWholeStreamCommandArgs { args, input } = self;
|
||||
|
||||
(input, args)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Getters, new)]
|
||||
#[get = "pub(crate)"]
|
||||
pub struct EvaluatedCommandArgs {
|
||||
pub host: Arc<parking_lot::Mutex<dyn Host>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub shell_manager: ShellManager,
|
||||
pub call_info: CallInfo,
|
||||
pub scope: Scope,
|
||||
}
|
||||
|
||||
impl EvaluatedCommandArgs {
|
||||
pub fn nth(&self, pos: usize) -> Option<&Value> {
|
||||
self.call_info.args.nth(pos)
|
||||
}
|
||||
|
||||
/// Get the nth positional argument, error if not possible
|
||||
pub fn expect_nth(&self, pos: usize) -> Result<&Value, ShellError> {
|
||||
self.call_info
|
||||
.args
|
||||
.nth(pos)
|
||||
.ok_or_else(|| ShellError::unimplemented("Better error: expect_nth"))
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &str) -> Option<&Value> {
|
||||
self.call_info.args.get(name)
|
||||
}
|
||||
|
||||
pub fn has(&self, name: &str) -> bool {
|
||||
self.call_info.args.has(name)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Example {
|
||||
pub example: &'static str,
|
||||
pub description: &'static str,
|
||||
pub result: Option<Vec<Value>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait WholeStreamCommand: Send + Sync {
|
||||
fn name(&self) -> &str;
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::new(self.name()).desc(self.usage()).filter()
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str;
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError>;
|
||||
|
||||
fn is_binary(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
// Commands that are not meant to be run by users
|
||||
fn is_internal(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
|
||||
// Custom commands are blocks, so we can use the information in the block to also
|
||||
// implement a WholeStreamCommand
|
||||
#[allow(clippy::suspicious_else_formatting)]
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Block {
|
||||
fn name(&self) -> &str {
|
||||
&self.params.name
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
self.params.clone()
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
""
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let call_info = args.call_info.clone();
|
||||
|
||||
let mut block = self.clone();
|
||||
block.set_redirect(call_info.args.external_redirection);
|
||||
|
||||
let ctx = EvaluationContext::from_args(&args);
|
||||
let evaluated = call_info.evaluate(&ctx).await?;
|
||||
|
||||
let input = args.input;
|
||||
ctx.scope.enter_scope();
|
||||
if let Some(args) = evaluated.args.positional {
|
||||
// FIXME: do not do this
|
||||
for arg in args.into_iter().zip(self.params.positional.iter()) {
|
||||
let name = arg.1 .0.name();
|
||||
|
||||
if name.starts_with('$') {
|
||||
ctx.scope.add_var(name, arg.0);
|
||||
} else {
|
||||
ctx.scope.add_var(format!("${}", name), arg.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(args) = evaluated.args.named {
|
||||
for named in &block.params.named {
|
||||
let name = named.0;
|
||||
if let Some(value) = args.get(name) {
|
||||
if name.starts_with('$') {
|
||||
ctx.scope.add_var(name, value.clone());
|
||||
} else {
|
||||
ctx.scope.add_var(format!("${}", name), value.clone());
|
||||
}
|
||||
} else if name.starts_with('$') {
|
||||
ctx.scope
|
||||
.add_var(name, UntaggedValue::nothing().into_untagged_value());
|
||||
} else {
|
||||
ctx.scope.add_var(
|
||||
format!("${}", name),
|
||||
UntaggedValue::nothing().into_untagged_value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for named in &block.params.named {
|
||||
let name = named.0;
|
||||
if name.starts_with('$') {
|
||||
ctx.scope
|
||||
.add_var(name, UntaggedValue::nothing().into_untagged_value());
|
||||
} else {
|
||||
ctx.scope.add_var(
|
||||
format!("${}", name),
|
||||
UntaggedValue::nothing().into_untagged_value(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
let result = run_block(&block, &ctx, input).await;
|
||||
ctx.scope.exit_scope();
|
||||
result.map(|x| x.to_output_stream())
|
||||
}
|
||||
|
||||
fn is_binary(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn is_internal(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Command(Arc<dyn WholeStreamCommand>);
|
||||
|
||||
impl PrettyDebugWithSource for Command {
|
||||
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
|
||||
b::typed(
|
||||
"whole stream command",
|
||||
b::description(self.name())
|
||||
+ b::space()
|
||||
+ b::equals()
|
||||
+ b::space()
|
||||
+ self.signature().pretty_debug(source),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Command {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Command({})", self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn name(&self) -> &str {
|
||||
self.0.name()
|
||||
}
|
||||
|
||||
pub fn signature(&self) -> Signature {
|
||||
self.0.signature()
|
||||
}
|
||||
|
||||
pub fn usage(&self) -> &str {
|
||||
self.0.usage()
|
||||
}
|
||||
|
||||
pub fn examples(&self) -> Vec<Example> {
|
||||
self.0.examples()
|
||||
}
|
||||
|
||||
pub async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
if args.call_info.switch_present("help") {
|
||||
let cl = self.0.clone();
|
||||
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(get_help(&*cl, &args.scope)).into_value(Tag::unknown()),
|
||||
))))
|
||||
} else {
|
||||
self.0.run(args).await
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_binary(&self) -> bool {
|
||||
self.0.is_binary()
|
||||
}
|
||||
|
||||
pub fn is_internal(&self) -> bool {
|
||||
self.0.is_internal()
|
||||
}
|
||||
|
||||
pub fn stream_command(&self) -> &dyn WholeStreamCommand {
|
||||
&*self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Command {
|
||||
Command(Arc::new(command))
|
||||
}
|
@ -1,62 +0,0 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str length"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("str length")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"outputs the lengths of the strings in the pipeline"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |x| match x.as_string() {
|
||||
Ok(s) => ReturnSuccess::value(UntaggedValue::int(s.len()).into_untagged_value()),
|
||||
Err(err) => Err(err),
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Return the lengths of multiple strings",
|
||||
example: "echo 'hello' | str length",
|
||||
result: Some(vec![UntaggedValue::int(5).into_untagged_value()]),
|
||||
},
|
||||
Example {
|
||||
description: "Return the lengths of multiple strings",
|
||||
example: "echo 'hi' 'there' | str length",
|
||||
result: Some(vec![
|
||||
UntaggedValue::int(2).into_untagged_value(),
|
||||
UntaggedValue::int(5).into_untagged_value(),
|
||||
]),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
@ -1,55 +0,0 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str reverse"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("str reverse")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"outputs the reversals of the strings in the pipeline"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(args
|
||||
.input
|
||||
.map(move |x| match x.as_string() {
|
||||
Ok(s) => ReturnSuccess::value(
|
||||
UntaggedValue::string(s.chars().rev().collect::<String>())
|
||||
.into_untagged_value(),
|
||||
),
|
||||
Err(err) => Err(err),
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Return the reversals of multiple strings",
|
||||
example: "echo 'Nushell' | str reverse",
|
||||
result: Some(vec![UntaggedValue::string("llehsuN").into_untagged_value()]),
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::SubCommand;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::{Tag, Tagged};
|
||||
use nu_value_ext::ValueExt;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Arguments {
|
||||
replace: Tagged<String>,
|
||||
rest: Vec<ColumnPath>,
|
||||
}
|
||||
|
||||
pub struct SubCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str set"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("str set")
|
||||
.required("set", SyntaxShape::String, "the new string to set")
|
||||
.rest(
|
||||
SyntaxShape::ColumnPath,
|
||||
"optionally set text by column paths",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"sets text"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
operate(args).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Set contents with preferred string",
|
||||
example: "echo 'good day' | str set 'good bye'",
|
||||
result: Some(vec![Value::from("good bye")]),
|
||||
},
|
||||
Example {
|
||||
description: "Set the contents on preferred column paths",
|
||||
example: "open Cargo.toml | str set '255' package.version",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Replace(String);
|
||||
|
||||
async fn operate(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (Arguments { replace, rest }, input) = args.process().await?;
|
||||
let options = Replace(replace.item);
|
||||
|
||||
let column_paths: Vec<_> = rest;
|
||||
|
||||
Ok(input
|
||||
.map(move |v| {
|
||||
if column_paths.is_empty() {
|
||||
ReturnSuccess::value(action(&v, &options, v.tag())?)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
|
||||
for path in &column_paths {
|
||||
let options = options.clone();
|
||||
|
||||
ret = ret.swap_data_by_column_path(
|
||||
path,
|
||||
Box::new(move |old| action(old, &options, old.tag())),
|
||||
)?;
|
||||
}
|
||||
|
||||
ReturnSuccess::value(ret)
|
||||
}
|
||||
})
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
fn action(_input: &Value, options: &Replace, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
||||
let replacement = &options.0;
|
||||
Ok(UntaggedValue::string(replacement.as_str()).into_value(tag))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::{action, Replace, SubCommand};
|
||||
use nu_source::Tag;
|
||||
use nu_test_support::value::string;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(SubCommand {})?)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sets() {
|
||||
let word = string("andres");
|
||||
let expected = string("robalino");
|
||||
|
||||
let set_options = Replace(String::from("robalino"));
|
||||
|
||||
let actual = action(&word, &set_options, Tag::unknown()).unwrap();
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
}
|
@ -1,136 +0,0 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use indexmap::map::IndexMap;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
|
||||
pub struct Which;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Which {
|
||||
fn name(&self) -> &str {
|
||||
"which"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("which")
|
||||
.required("application", SyntaxShape::String, "application")
|
||||
.switch("all", "list all executables", Some('a'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Finds a program file, alias or custom command."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
which(args).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Shortcuts for creating an entry to the output table
|
||||
fn entry(arg: impl Into<String>, path: Value, builtin: bool, tag: Tag) -> Value {
|
||||
let mut map = IndexMap::new();
|
||||
map.insert(
|
||||
"arg".to_string(),
|
||||
UntaggedValue::Primitive(Primitive::String(arg.into())).into_value(tag.clone()),
|
||||
);
|
||||
map.insert("path".to_string(), path);
|
||||
map.insert(
|
||||
"builtin".to_string(),
|
||||
UntaggedValue::boolean(builtin).into_value(tag.clone()),
|
||||
);
|
||||
|
||||
UntaggedValue::row(map).into_value(tag)
|
||||
}
|
||||
|
||||
macro_rules! create_entry {
|
||||
($arg:expr, $path:expr, $tag:expr, $is_builtin:expr) => {
|
||||
entry(
|
||||
$arg.clone(),
|
||||
UntaggedValue::Primitive(Primitive::String($path.to_string())).into_value($tag.clone()),
|
||||
$is_builtin,
|
||||
$tag,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! entry_path {
|
||||
($arg:expr, $path:expr, $tag:expr) => {
|
||||
entry(
|
||||
$arg.clone(),
|
||||
UntaggedValue::Primitive(Primitive::Path($path)).into_value($tag.clone()),
|
||||
false,
|
||||
$tag,
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Debug)]
|
||||
struct WhichArgs {
|
||||
application: Tagged<String>,
|
||||
all: bool,
|
||||
}
|
||||
|
||||
async fn which(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let mut output = vec![];
|
||||
let scope = args.scope.clone();
|
||||
|
||||
let (WhichArgs { application, all }, _) = args.process().await?;
|
||||
let external = application.starts_with('^');
|
||||
let item = if external {
|
||||
application.item[1..].to_string()
|
||||
} else {
|
||||
application.item.clone()
|
||||
};
|
||||
if !external {
|
||||
if let Some(entry) = entry_for(&scope, &item, application.tag.clone()) {
|
||||
output.push(ReturnSuccess::value(entry));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ichwh")]
|
||||
{
|
||||
if let Ok(paths) = ichwh::which_all(&item).await {
|
||||
for path in paths {
|
||||
output.push(ReturnSuccess::value(entry_path!(
|
||||
item,
|
||||
path.into(),
|
||||
application.tag.clone()
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if all {
|
||||
Ok(futures::stream::iter(output.into_iter()).to_output_stream())
|
||||
} else {
|
||||
Ok(futures::stream::iter(output.into_iter().take(1)).to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
fn entry_for(scope: &Scope, name: &str, tag: Tag) -> Option<Value> {
|
||||
if scope.has_custom_command(name) {
|
||||
Some(create_entry!(name, "Nushell custom command", tag, false))
|
||||
} else if scope.has_command(name) {
|
||||
Some(create_entry!(name, "Nushell built-in command", tag, true))
|
||||
} else if scope.has_alias(name) {
|
||||
Some(create_entry!(name, "Nushell alias", tag, false))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ShellError;
|
||||
use super::Which;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Which {})?)
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ use indexmap::set::IndexSet;
|
||||
|
||||
use super::matchers::Matcher;
|
||||
use crate::completion::{Completer, CompletionContext, Suggestion};
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use nu_engine::EvaluationContext;
|
||||
|
||||
pub struct CommandCompleter;
|
||||
|
||||
|
@ -256,7 +256,7 @@ pub fn completion_location(line: &str, block: &Block, pos: usize) -> Vec<Complet
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use nu_parser::{classify_block, group, lex, ParserScope};
|
||||
use nu_parser::{block, classify_block, lex, ParserScope};
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
@ -307,7 +307,7 @@ mod tests {
|
||||
pos: usize,
|
||||
) -> Vec<LocationType> {
|
||||
let (tokens, _) = lex(line, 0);
|
||||
let (lite_block, _) = group(tokens);
|
||||
let (lite_block, _) = block(tokens);
|
||||
|
||||
scope.enter_scope();
|
||||
let (block, _) = classify_block(&lite_block, scope);
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::matchers::Matcher;
|
||||
use crate::completion::{Completer, CompletionContext, Suggestion};
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use nu_engine::EvaluationContext;
|
||||
|
||||
pub struct FlagCompleter {
|
||||
pub(crate) cmd: String,
|
||||
|
@ -4,8 +4,8 @@ pub(crate) mod flag;
|
||||
pub(crate) mod matchers;
|
||||
pub(crate) mod path;
|
||||
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use matchers::Matcher;
|
||||
use nu_engine::EvaluationContext;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub struct Suggestion {
|
||||
|
@ -29,7 +29,7 @@ impl PathCompleter {
|
||||
{
|
||||
let home_prefix = format!("~{}", SEP);
|
||||
if base_dir_name.starts_with(&home_prefix) {
|
||||
let mut home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("~"));
|
||||
let mut home_dir = dirs_next::home_dir().unwrap_or_else(|| PathBuf::from("~"));
|
||||
home_dir.push(&base_dir_name[2..]);
|
||||
home_dir
|
||||
} else {
|
||||
|
@ -1,6 +1,3 @@
|
||||
pub(crate) mod directory_specific_environment;
|
||||
pub(crate) mod environment;
|
||||
pub(crate) mod environment_syncer;
|
||||
pub(crate) mod host;
|
||||
|
||||
pub(crate) use self::host::Host;
|
||||
|
@ -1,6 +1,5 @@
|
||||
use crate::commands;
|
||||
use commands::autoenv;
|
||||
use indexmap::{IndexMap, IndexSet};
|
||||
use nu_command::commands::autoenv;
|
||||
use nu_errors::ShellError;
|
||||
use serde::Deserialize;
|
||||
use std::env::*;
|
||||
|
33
crates/nu-cli/src/env/environment.rs
vendored
33
crates/nu-cli/src/env/environment.rs
vendored
@ -1,38 +1,9 @@
|
||||
use crate::env::directory_specific_environment::*;
|
||||
use indexmap::{indexmap, IndexSet};
|
||||
use nu_data::config::Conf;
|
||||
use nu_engine::Env;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{UntaggedValue, Value};
|
||||
use std::env::*;
|
||||
use std::ffi::OsString;
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub trait Env: Debug + Send {
|
||||
fn env(&self) -> Option<Value>;
|
||||
fn path(&self) -> Option<Value>;
|
||||
|
||||
fn add_env(&mut self, key: &str, value: &str);
|
||||
fn add_path(&mut self, new_path: OsString);
|
||||
}
|
||||
|
||||
impl Env for Box<dyn Env> {
|
||||
fn env(&self) -> Option<Value> {
|
||||
(**self).env()
|
||||
}
|
||||
|
||||
fn path(&self) -> Option<Value> {
|
||||
(**self).path()
|
||||
}
|
||||
|
||||
fn add_env(&mut self, key: &str, value: &str) {
|
||||
(**self).add_env(key, value);
|
||||
}
|
||||
|
||||
fn add_path(&mut self, new_path: OsString) {
|
||||
(**self).add_path(new_path);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Environment {
|
||||
@ -128,7 +99,7 @@ impl Env for Environment {
|
||||
{
|
||||
let mut new_paths = current_paths.clone();
|
||||
|
||||
let new_path_candidates = split_paths(&paths).map(|path| {
|
||||
let new_path_candidates = std::env::split_paths(&paths).map(|path| {
|
||||
UntaggedValue::string(path.to_string_lossy()).into_value(tag.clone())
|
||||
});
|
||||
|
||||
|
31
crates/nu-cli/src/env/environment_syncer.rs
vendored
31
crates/nu-cli/src/env/environment_syncer.rs
vendored
@ -1,6 +1,7 @@
|
||||
use crate::env::environment::{Env, Environment};
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use crate::env::environment::Environment;
|
||||
use nu_data::config::{Conf, NuConfig};
|
||||
use nu_engine::Env;
|
||||
use nu_engine::EvaluationContext;
|
||||
use nu_errors::ShellError;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::{atomic::Ordering, Arc};
|
||||
@ -152,10 +153,10 @@ impl EnvironmentSyncer {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::EnvironmentSyncer;
|
||||
use crate::env::environment::Env;
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use indexmap::IndexMap;
|
||||
use nu_data::config::tests::FakeConfig;
|
||||
use nu_engine::basic_evaluation_context;
|
||||
use nu_engine::Env;
|
||||
use nu_errors::ShellError;
|
||||
use nu_test_support::fs::Stub::FileWithContent;
|
||||
use nu_test_support::playground::Playground;
|
||||
@ -170,8 +171,8 @@ mod tests {
|
||||
#[test]
|
||||
fn syncs_env_if_new_env_entry_is_added_to_an_existing_configuration() -> Result<(), ShellError>
|
||||
{
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let mut expected = IndexMap::new();
|
||||
expected.insert(
|
||||
@ -202,7 +203,7 @@ mod tests {
|
||||
let new_file = dirs.test().join("updated_configuration.toml");
|
||||
|
||||
let fake_config = FakeConfig::new(&file);
|
||||
let mut actual = EnvironmentSyncer::with_config(Box::new(fake_config.clone()));
|
||||
let mut actual = EnvironmentSyncer::with_config(Box::new(fake_config));
|
||||
|
||||
// Here, the environment variables from the current session
|
||||
// are cleared since we will load and set them from the
|
||||
@ -273,8 +274,8 @@ mod tests {
|
||||
#[test]
|
||||
fn syncs_env_if_new_env_entry_in_session_is_not_in_configuration_file() -> Result<(), ShellError>
|
||||
{
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let mut expected = IndexMap::new();
|
||||
expected.insert(
|
||||
@ -372,8 +373,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn nu_envs_have_higher_priority_and_does_not_get_overwritten() -> Result<(), ShellError> {
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let mut expected = IndexMap::new();
|
||||
expected.insert(
|
||||
@ -448,8 +449,8 @@ mod tests {
|
||||
#[test]
|
||||
fn syncs_path_if_new_path_entry_in_session_is_not_in_configuration_file(
|
||||
) -> Result<(), ShellError> {
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let expected = std::env::join_paths(vec![
|
||||
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
||||
@ -535,8 +536,8 @@ mod tests {
|
||||
#[test]
|
||||
fn nu_paths_have_higher_priority_and_new_paths_get_appended_to_the_end(
|
||||
) -> Result<(), ShellError> {
|
||||
let mut ctx = EvaluationContext::basic()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
||||
let mut ctx = basic_evaluation_context()?;
|
||||
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||
|
||||
let expected = std::env::join_paths(vec![
|
||||
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
||||
|
@ -1,493 +0,0 @@
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_protocol::hir::ClassifiedBlock;
|
||||
use nu_protocol::{
|
||||
Primitive, ReturnSuccess, ShellTypeName, Signature, SyntaxShape, UntaggedValue, Value,
|
||||
};
|
||||
use nu_source::{AnchorLocation, TaggedItem};
|
||||
|
||||
use crate::prelude::*;
|
||||
|
||||
use num_bigint::BigInt;
|
||||
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::command::CommandArgs;
|
||||
use crate::commands::{
|
||||
whole_stream_command, BuildString, Command, Each, Echo, First, Get, Keep, Last, Let, Nth,
|
||||
StrCollect, WholeStreamCommand, Wrap,
|
||||
};
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use nu_stream::{InputStream, OutputStream};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::executor::block_on;
|
||||
use serde::Deserialize;
|
||||
|
||||
pub fn test_examples(cmd: Command) -> Result<(), ShellError> {
|
||||
let examples = cmd.examples();
|
||||
|
||||
let base_context = EvaluationContext::basic()?;
|
||||
|
||||
base_context.add_commands(vec![
|
||||
// Mocks
|
||||
whole_stream_command(MockLs {}),
|
||||
// Minimal restricted commands to aid in testing
|
||||
whole_stream_command(Echo {}),
|
||||
whole_stream_command(BuildString {}),
|
||||
whole_stream_command(First {}),
|
||||
whole_stream_command(Get {}),
|
||||
whole_stream_command(Keep {}),
|
||||
whole_stream_command(Each {}),
|
||||
whole_stream_command(Last {}),
|
||||
whole_stream_command(Nth {}),
|
||||
whole_stream_command(Let {}),
|
||||
whole_stream_command(StrCollect),
|
||||
whole_stream_command(Wrap),
|
||||
cmd,
|
||||
]);
|
||||
|
||||
for sample_pipeline in examples {
|
||||
let mut ctx = base_context.clone();
|
||||
|
||||
let block = parse_line(sample_pipeline.example, &ctx)?;
|
||||
|
||||
println!("{:#?}", block);
|
||||
|
||||
if let Some(expected) = &sample_pipeline.result {
|
||||
let result = block_on(evaluate_block(block, &mut ctx))?;
|
||||
|
||||
ctx.with_errors(|reasons| reasons.iter().cloned().take(1).next())
|
||||
.map_or(Ok(()), Err)?;
|
||||
|
||||
if expected.len() != result.len() {
|
||||
let rows_returned =
|
||||
format!("expected: {}\nactual: {}", expected.len(), result.len());
|
||||
let failed_call = format!("command: {}\n", sample_pipeline.example);
|
||||
|
||||
panic!(
|
||||
"example command produced unexpected number of results.\n {} {}",
|
||||
failed_call, rows_returned
|
||||
);
|
||||
}
|
||||
|
||||
for (e, a) in expected.iter().zip(result.iter()) {
|
||||
if !values_equal(e, a) {
|
||||
let row_errored = format!("expected: {:#?}\nactual: {:#?}", e, a);
|
||||
let failed_call = format!("command: {}\n", sample_pipeline.example);
|
||||
|
||||
panic!(
|
||||
"example command produced unexpected result.\n {} {}",
|
||||
failed_call, row_errored
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
|
||||
let examples = cmd.examples();
|
||||
|
||||
let base_context = EvaluationContext::basic()?;
|
||||
|
||||
base_context.add_commands(vec![
|
||||
whole_stream_command(Echo {}),
|
||||
whole_stream_command(BuildString {}),
|
||||
whole_stream_command(Get {}),
|
||||
whole_stream_command(Keep {}),
|
||||
whole_stream_command(Each {}),
|
||||
whole_stream_command(Let {}),
|
||||
whole_stream_command(cmd),
|
||||
whole_stream_command(StrCollect),
|
||||
whole_stream_command(Wrap),
|
||||
]);
|
||||
|
||||
for sample_pipeline in examples {
|
||||
let mut ctx = base_context.clone();
|
||||
|
||||
let block = parse_line(sample_pipeline.example, &ctx)?;
|
||||
|
||||
if let Some(expected) = &sample_pipeline.result {
|
||||
let result = block_on(evaluate_block(block, &mut ctx))?;
|
||||
|
||||
ctx.with_errors(|reasons| reasons.iter().cloned().take(1).next())
|
||||
.map_or(Ok(()), Err)?;
|
||||
|
||||
if expected.len() != result.len() {
|
||||
let rows_returned =
|
||||
format!("expected: {}\nactual: {}", expected.len(), result.len());
|
||||
let failed_call = format!("command: {}\n", sample_pipeline.example);
|
||||
|
||||
panic!(
|
||||
"example command produced unexpected number of results.\n {} {}",
|
||||
failed_call, rows_returned
|
||||
);
|
||||
}
|
||||
|
||||
for (e, a) in expected.iter().zip(result.iter()) {
|
||||
if !values_equal(e, a) {
|
||||
let row_errored = format!("expected: {:#?}\nactual: {:#?}", e, a);
|
||||
let failed_call = format!("command: {}\n", sample_pipeline.example);
|
||||
|
||||
panic!(
|
||||
"example command produced unexpected result.\n {} {}",
|
||||
failed_call, row_errored
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn test_anchors(cmd: Command) -> Result<(), ShellError> {
|
||||
let examples = cmd.examples();
|
||||
|
||||
let base_context = EvaluationContext::basic()?;
|
||||
|
||||
base_context.add_commands(vec![
|
||||
// Minimal restricted commands to aid in testing
|
||||
whole_stream_command(MockCommand {}),
|
||||
whole_stream_command(MockEcho {}),
|
||||
whole_stream_command(MockLs {}),
|
||||
whole_stream_command(BuildString {}),
|
||||
whole_stream_command(First {}),
|
||||
whole_stream_command(Get {}),
|
||||
whole_stream_command(Keep {}),
|
||||
whole_stream_command(Each {}),
|
||||
whole_stream_command(Last {}),
|
||||
whole_stream_command(Nth {}),
|
||||
whole_stream_command(Let {}),
|
||||
whole_stream_command(StrCollect),
|
||||
whole_stream_command(Wrap),
|
||||
cmd,
|
||||
]);
|
||||
|
||||
for sample_pipeline in examples {
|
||||
let pipeline_with_anchor = format!("mock --open --path | {}", sample_pipeline.example);
|
||||
|
||||
let mut ctx = base_context.clone();
|
||||
|
||||
let block = parse_line(&pipeline_with_anchor, &ctx)?;
|
||||
let result = block_on(evaluate_block(block, &mut ctx))?;
|
||||
|
||||
ctx.with_errors(|reasons| reasons.iter().cloned().take(1).next())
|
||||
.map_or(Ok(()), Err)?;
|
||||
|
||||
for actual in result.iter() {
|
||||
if !is_anchor_carried(actual, mock_path()) {
|
||||
let failed_call = format!("command: {}\n", pipeline_with_anchor);
|
||||
|
||||
panic!(
|
||||
"example command didn't carry anchor tag correctly.\n {} {:#?} {:#?}",
|
||||
failed_call,
|
||||
actual,
|
||||
mock_path()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Parse and run a nushell pipeline
|
||||
fn parse_line(line: &str, ctx: &EvaluationContext) -> Result<ClassifiedBlock, ShellError> {
|
||||
//FIXME: do we still need this?
|
||||
let line = if let Some(line) = line.strip_suffix('\n') {
|
||||
line
|
||||
} else {
|
||||
line
|
||||
};
|
||||
|
||||
let (lite_result, err) = nu_parser::lex(&line, 0);
|
||||
if let Some(err) = err {
|
||||
return Err(err.into());
|
||||
}
|
||||
let (lite_result, err) = nu_parser::group(lite_result);
|
||||
if let Some(err) = err {
|
||||
return Err(err.into());
|
||||
}
|
||||
|
||||
// TODO ensure the command whose examples we're testing is actually in the pipeline
|
||||
let (block, err) = nu_parser::classify_block(&lite_result, &ctx.scope);
|
||||
Ok(ClassifiedBlock { block, failed: err })
|
||||
}
|
||||
|
||||
async fn evaluate_block(
|
||||
block: ClassifiedBlock,
|
||||
ctx: &mut EvaluationContext,
|
||||
) -> Result<Vec<Value>, ShellError> {
|
||||
let input_stream = InputStream::empty();
|
||||
let env = ctx.get_env();
|
||||
|
||||
ctx.scope.enter_scope();
|
||||
ctx.scope.add_env(env);
|
||||
|
||||
let result = run_block(&block.block, ctx, input_stream).await;
|
||||
|
||||
ctx.scope.exit_scope();
|
||||
|
||||
let result = result?.drain_vec().await;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
// TODO probably something already available to do this
|
||||
// TODO perhaps better panic messages when things don't compare
|
||||
|
||||
// Deep value comparisons that ignore tags
|
||||
fn values_equal(expected: &Value, actual: &Value) -> bool {
|
||||
use nu_protocol::UntaggedValue::*;
|
||||
|
||||
match (&expected.value, &actual.value) {
|
||||
(Primitive(e), Primitive(a)) => e == a,
|
||||
(Row(e), Row(a)) => {
|
||||
if e.entries.len() != a.entries.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
e.entries
|
||||
.iter()
|
||||
.zip(a.entries.iter())
|
||||
.all(|((ek, ev), (ak, av))| ek == ak && values_equal(ev, av))
|
||||
}
|
||||
(Table(e), Table(a)) => e.iter().zip(a.iter()).all(|(e, a)| values_equal(e, a)),
|
||||
(e, a) => unimplemented!("{} {}", e.type_name(), a.type_name()),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_anchor_carried(actual: &Value, anchor: AnchorLocation) -> bool {
|
||||
actual.tag.anchor() == Some(anchor)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Arguments {
|
||||
path: Option<bool>,
|
||||
open: bool,
|
||||
}
|
||||
|
||||
struct MockCommand;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for MockCommand {
|
||||
fn name(&self) -> &str {
|
||||
"mock"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("mock")
|
||||
.switch("open", "fake opening sources", Some('o'))
|
||||
.switch("path", "file open", Some('p'))
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Generates tables and metadata that mimics behavior of real commands in controlled ways."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
|
||||
let (
|
||||
Arguments {
|
||||
path: mocked_path,
|
||||
open: open_mock,
|
||||
},
|
||||
_input,
|
||||
) = args.process().await?;
|
||||
|
||||
let out = UntaggedValue::string("Yehuda Katz in Ecuador");
|
||||
|
||||
if open_mock {
|
||||
if let Some(true) = mocked_path {
|
||||
return Ok(OutputStream::one(Ok(ReturnSuccess::Value(Value {
|
||||
value: out,
|
||||
tag: Tag {
|
||||
anchor: Some(mock_path()),
|
||||
span: name_tag.span,
|
||||
},
|
||||
}))));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
||||
out.into_value(name_tag),
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
struct MockEcho;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct MockEchoArgs {
|
||||
pub rest: Vec<Value>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for MockEcho {
|
||||
fn name(&self) -> &str {
|
||||
"echo"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("echo").rest(SyntaxShape::Any, "the values to echo")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Mock echo."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
let (MockEchoArgs { rest }, input) = args.process().await?;
|
||||
|
||||
let mut base_value = UntaggedValue::string("Yehuda Katz in Ecuador").into_value(name_tag);
|
||||
let input: Vec<Value> = input.collect().await;
|
||||
|
||||
if let Some(first) = input.get(0) {
|
||||
base_value = first.clone()
|
||||
}
|
||||
|
||||
let stream = rest.into_iter().map(move |i| {
|
||||
let base_value = base_value.clone();
|
||||
match i.as_string() {
|
||||
Ok(s) => OutputStream::one(Ok(ReturnSuccess::Value(Value {
|
||||
value: UntaggedValue::Primitive(Primitive::String(s)),
|
||||
tag: base_value.tag,
|
||||
}))),
|
||||
_ => match i {
|
||||
Value {
|
||||
value: UntaggedValue::Table(table),
|
||||
..
|
||||
} => {
|
||||
if table.len() == 1 && table[0].is_table() {
|
||||
let mut values: Vec<Value> =
|
||||
table[0].table_entries().map(Clone::clone).collect();
|
||||
|
||||
for v in values.iter_mut() {
|
||||
v.tag = base_value.tag();
|
||||
}
|
||||
|
||||
let subtable =
|
||||
vec![UntaggedValue::Table(values).into_value(base_value.tag())];
|
||||
|
||||
futures::stream::iter(subtable.into_iter().map(ReturnSuccess::value))
|
||||
.to_output_stream()
|
||||
} else {
|
||||
futures::stream::iter(
|
||||
table
|
||||
.into_iter()
|
||||
.map(move |mut v| {
|
||||
v.tag = base_value.tag();
|
||||
v
|
||||
})
|
||||
.map(ReturnSuccess::value),
|
||||
)
|
||||
.to_output_stream()
|
||||
}
|
||||
}
|
||||
_ => OutputStream::one(Ok(ReturnSuccess::Value(Value {
|
||||
value: i.value.clone(),
|
||||
tag: base_value.tag,
|
||||
}))),
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
Ok(futures::stream::iter(stream).flatten().to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
struct MockLs;
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for MockLs {
|
||||
fn name(&self) -> &str {
|
||||
"ls"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("ls")
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Mock ls."
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let name_tag = args.call_info.name_tag.clone();
|
||||
|
||||
let mut base_value =
|
||||
UntaggedValue::string("Andrés N. Robalino in Portland").into_value(name_tag);
|
||||
let input: Vec<Value> = args.input.collect().await;
|
||||
|
||||
if let Some(first) = input.get(0) {
|
||||
base_value = first.clone()
|
||||
}
|
||||
|
||||
Ok(futures::stream::iter(
|
||||
file_listing()
|
||||
.iter()
|
||||
.map(|row| Value {
|
||||
value: row.value.clone(),
|
||||
tag: base_value.tag.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.into_iter()
|
||||
.map(ReturnSuccess::value),
|
||||
)
|
||||
.to_output_stream())
|
||||
}
|
||||
}
|
||||
|
||||
fn int(s: impl Into<BigInt>) -> Value {
|
||||
UntaggedValue::int(s).into_untagged_value()
|
||||
}
|
||||
|
||||
fn string(input: impl Into<String>) -> Value {
|
||||
UntaggedValue::string(input.into()).into_untagged_value()
|
||||
}
|
||||
|
||||
fn date(input: impl Into<String>) -> Value {
|
||||
let key = input.into().tagged_unknown();
|
||||
crate::value::Date::naive_from_str(key.borrow_tagged())
|
||||
.expect("date from string failed")
|
||||
.into_untagged_value()
|
||||
}
|
||||
|
||||
fn file_listing() -> Vec<Value> {
|
||||
vec![
|
||||
row! {
|
||||
"name".to_string() => string("Andrés.txt"),
|
||||
"type".to_string() => string("File"),
|
||||
"chickens".to_string() => int(10),
|
||||
"modified".to_string() => date("2019-07-23")
|
||||
},
|
||||
row! {
|
||||
"name".to_string() => string("Jonathan"),
|
||||
"type".to_string() => string("Dir"),
|
||||
"chickens".to_string() => int(5),
|
||||
"modified".to_string() => date("2019-07-23")
|
||||
},
|
||||
row! {
|
||||
"name".to_string() => string("Andrés.txt"),
|
||||
"type".to_string() => string("File"),
|
||||
"chickens".to_string() => int(20),
|
||||
"modified".to_string() => date("2019-09-24")
|
||||
},
|
||||
row! {
|
||||
"name".to_string() => string("Yehuda"),
|
||||
"type".to_string() => string("Dir"),
|
||||
"chickens".to_string() => int(4),
|
||||
"modified".to_string() => date("2019-09-24")
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn mock_path() -> AnchorLocation {
|
||||
let path = String::from("path/to/las_best_arepas_in_the_world.txt");
|
||||
|
||||
AnchorLocation::File(path)
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
pub fn current_branch() -> Option<String> {
|
||||
#[cfg(feature = "git2")]
|
||||
{
|
||||
use git2::{Repository, RepositoryOpenFlags};
|
||||
use std::ffi::OsString;
|
||||
|
||||
let v: Vec<OsString> = vec![];
|
||||
match Repository::open_ext(".", RepositoryOpenFlags::empty(), v) {
|
||||
Ok(repo) => {
|
||||
let r = repo.head();
|
||||
match r {
|
||||
Ok(r) => match r.shorthand() {
|
||||
Some(s) => Some(s.to_string()),
|
||||
None => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "git2"))]
|
||||
{
|
||||
None
|
||||
}
|
||||
}
|
@ -405,14 +405,10 @@ pub struct Keybinding {
|
||||
|
||||
type Keybindings = Vec<Keybinding>;
|
||||
|
||||
pub(crate) fn keybinding_path() -> Result<std::path::PathBuf, nu_errors::ShellError> {
|
||||
nu_data::config::default_path_for(&Some(std::path::PathBuf::from("keybindings.yml")))
|
||||
}
|
||||
|
||||
pub(crate) fn load_keybindings(
|
||||
rl: &mut rustyline::Editor<crate::shell::Helper>,
|
||||
) -> Result<(), nu_errors::ShellError> {
|
||||
let filename = keybinding_path()?;
|
||||
let filename = nu_data::keybinding::keybinding_path()?;
|
||||
let contents = std::fs::read_to_string(filename);
|
||||
|
||||
// Silently fail if there is no file there
|
||||
|
@ -14,45 +14,23 @@ extern crate quickcheck;
|
||||
extern crate quickcheck_macros;
|
||||
|
||||
mod cli;
|
||||
mod commands;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
mod completion;
|
||||
mod deserializer;
|
||||
mod documentation;
|
||||
mod env;
|
||||
mod evaluate;
|
||||
mod evaluation_context;
|
||||
mod format;
|
||||
mod futures;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
mod git;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
mod keybinding;
|
||||
mod path;
|
||||
mod plugin;
|
||||
pub mod script;
|
||||
mod line_editor;
|
||||
mod shell;
|
||||
pub mod types;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(test)]
|
||||
mod examples;
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub use crate::cli::cli;
|
||||
|
||||
pub use crate::cli::{parse_and_eval, register_plugins, run_script_file};
|
||||
pub use crate::commands::classified::block::run_block;
|
||||
pub use crate::commands::command::{
|
||||
whole_stream_command, CommandArgs, EvaluatedWholeStreamCommandArgs, Example, WholeStreamCommand,
|
||||
};
|
||||
|
||||
pub use crate::commands::default_context::create_default_context;
|
||||
pub use crate::commands::help::get_help;
|
||||
pub use crate::env::environment_syncer::EnvironmentSyncer;
|
||||
pub use crate::env::host::BasicHost;
|
||||
pub use crate::evaluation_context::EvaluationContext;
|
||||
pub use crate::prelude::ToOutputStream;
|
||||
pub use nu_command::commands::default_context::create_default_context;
|
||||
pub use nu_data::config;
|
||||
pub use nu_data::dict::TaggedListBuilder;
|
||||
pub use nu_data::primitive;
|
||||
|
234
crates/nu-cli/src/line_editor.rs
Normal file
234
crates/nu-cli/src/line_editor.rs
Normal file
@ -0,0 +1,234 @@
|
||||
use nu_engine::EvaluationContext;
|
||||
use std::error::Error;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use crate::prelude::*;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use nu_command::script::LineResult;
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use crate::shell::Helper;
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
use rustyline::{
|
||||
self,
|
||||
config::Configurer,
|
||||
config::{ColorMode, CompletionType, Config},
|
||||
error::ReadlineError,
|
||||
At, Cmd, Editor, KeyPress, Movement, Word,
|
||||
};
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn convert_rustyline_result_to_string(input: Result<String, ReadlineError>) -> LineResult {
|
||||
match input {
|
||||
Ok(s) if s == "history -c" || s == "history --clear" => LineResult::ClearHistory,
|
||||
Ok(s) => LineResult::Success(s),
|
||||
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
|
||||
Err(ReadlineError::Eof) => LineResult::CtrlD,
|
||||
Err(err) => {
|
||||
outln!("Error: {:?}", err);
|
||||
LineResult::Break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
|
||||
#[cfg(windows)]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
|
||||
#[cfg(not(windows))]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
|
||||
|
||||
let config = Config::builder().color_mode(ColorMode::Forced).build();
|
||||
let mut rl: Editor<_> = Editor::with_config(config);
|
||||
|
||||
// add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight
|
||||
rl.bind_sequence(
|
||||
KeyPress::ControlLeft,
|
||||
Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
|
||||
);
|
||||
rl.bind_sequence(
|
||||
KeyPress::ControlRight,
|
||||
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
|
||||
);
|
||||
|
||||
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
|
||||
rl.bind_sequence(KeyPress::BracketedPasteStart, rustyline::Cmd::Noop);
|
||||
|
||||
// Let's set the defaults up front and then override them later if the user indicates
|
||||
// defaults taken from here https://github.com/kkawakam/rustyline/blob/2fe886c9576c1ea13ca0e5808053ad491a6fe049/src/config.rs#L150-L167
|
||||
rl.set_max_history_size(100);
|
||||
rl.set_history_ignore_dups(true);
|
||||
rl.set_history_ignore_space(false);
|
||||
rl.set_completion_type(DEFAULT_COMPLETION_MODE);
|
||||
rl.set_completion_prompt_limit(100);
|
||||
rl.set_keyseq_timeout(-1);
|
||||
rl.set_edit_mode(rustyline::config::EditMode::Emacs);
|
||||
rl.set_auto_add_history(false);
|
||||
rl.set_bell_style(rustyline::config::BellStyle::default());
|
||||
rl.set_color_mode(rustyline::ColorMode::Enabled);
|
||||
rl.set_tab_stop(8);
|
||||
|
||||
if let Err(e) = crate::keybinding::load_keybindings(&mut rl) {
|
||||
println!("Error loading keybindings: {:?}", e);
|
||||
}
|
||||
|
||||
rl
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn configure_rustyline_editor(
|
||||
rl: &mut Editor<Helper>,
|
||||
config: &dyn nu_data::config::Conf,
|
||||
) -> Result<(), ShellError> {
|
||||
#[cfg(windows)]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
|
||||
#[cfg(not(windows))]
|
||||
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
|
||||
|
||||
if let Some(line_editor_vars) = config.var("line_editor") {
|
||||
for (idx, value) in line_editor_vars.row_entries() {
|
||||
match idx.as_ref() {
|
||||
"max_history_size" => {
|
||||
if let Ok(max_history_size) = value.as_u64() {
|
||||
rl.set_max_history_size(max_history_size as usize);
|
||||
}
|
||||
}
|
||||
"history_duplicates" => {
|
||||
// history_duplicates = match value.as_string() {
|
||||
// Ok(s) if s.to_lowercase() == "alwaysadd" => {
|
||||
// rustyline::config::HistoryDuplicates::AlwaysAdd
|
||||
// }
|
||||
// Ok(s) if s.to_lowercase() == "ignoreconsecutive" => {
|
||||
// rustyline::config::HistoryDuplicates::IgnoreConsecutive
|
||||
// }
|
||||
// _ => rustyline::config::HistoryDuplicates::AlwaysAdd,
|
||||
// };
|
||||
if let Ok(history_duplicates) = value.as_bool() {
|
||||
rl.set_history_ignore_dups(history_duplicates);
|
||||
}
|
||||
}
|
||||
"history_ignore_space" => {
|
||||
if let Ok(history_ignore_space) = value.as_bool() {
|
||||
rl.set_history_ignore_space(history_ignore_space);
|
||||
}
|
||||
}
|
||||
"completion_type" => {
|
||||
let completion_type = match value.as_string() {
|
||||
Ok(s) if s.to_lowercase() == "circular" => {
|
||||
rustyline::config::CompletionType::Circular
|
||||
}
|
||||
Ok(s) if s.to_lowercase() == "list" => {
|
||||
rustyline::config::CompletionType::List
|
||||
}
|
||||
#[cfg(all(unix, feature = "with-fuzzy"))]
|
||||
Ok(s) if s.to_lowercase() == "fuzzy" => {
|
||||
rustyline::config::CompletionType::Fuzzy
|
||||
}
|
||||
_ => DEFAULT_COMPLETION_MODE,
|
||||
};
|
||||
rl.set_completion_type(completion_type);
|
||||
}
|
||||
"completion_prompt_limit" => {
|
||||
if let Ok(completion_prompt_limit) = value.as_u64() {
|
||||
rl.set_completion_prompt_limit(completion_prompt_limit as usize);
|
||||
}
|
||||
}
|
||||
"keyseq_timeout_ms" => {
|
||||
if let Ok(keyseq_timeout_ms) = value.as_u64() {
|
||||
rl.set_keyseq_timeout(keyseq_timeout_ms as i32);
|
||||
}
|
||||
}
|
||||
"edit_mode" => {
|
||||
let edit_mode = match value.as_string() {
|
||||
Ok(s) if s.to_lowercase() == "vi" => rustyline::config::EditMode::Vi,
|
||||
Ok(s) if s.to_lowercase() == "emacs" => rustyline::config::EditMode::Emacs,
|
||||
_ => rustyline::config::EditMode::Emacs,
|
||||
};
|
||||
rl.set_edit_mode(edit_mode);
|
||||
// Note: When edit_mode is Emacs, the keyseq_timeout_ms is set to -1
|
||||
// no matter what you may have configured. This is so that key chords
|
||||
// can be applied without having to do them in a given timeout. So,
|
||||
// it essentially turns off the keyseq timeout.
|
||||
}
|
||||
"auto_add_history" => {
|
||||
if let Ok(auto_add_history) = value.as_bool() {
|
||||
rl.set_auto_add_history(auto_add_history);
|
||||
}
|
||||
}
|
||||
"bell_style" => {
|
||||
let bell_style = match value.as_string() {
|
||||
Ok(s) if s.to_lowercase() == "audible" => {
|
||||
rustyline::config::BellStyle::Audible
|
||||
}
|
||||
Ok(s) if s.to_lowercase() == "none" => rustyline::config::BellStyle::None,
|
||||
Ok(s) if s.to_lowercase() == "visible" => {
|
||||
rustyline::config::BellStyle::Visible
|
||||
}
|
||||
_ => rustyline::config::BellStyle::default(),
|
||||
};
|
||||
rl.set_bell_style(bell_style);
|
||||
}
|
||||
"color_mode" => {
|
||||
let color_mode = match value.as_string() {
|
||||
Ok(s) if s.to_lowercase() == "enabled" => rustyline::ColorMode::Enabled,
|
||||
Ok(s) if s.to_lowercase() == "forced" => rustyline::ColorMode::Forced,
|
||||
Ok(s) if s.to_lowercase() == "disabled" => rustyline::ColorMode::Disabled,
|
||||
_ => rustyline::ColorMode::Enabled,
|
||||
};
|
||||
rl.set_color_mode(color_mode);
|
||||
}
|
||||
"tab_stop" => {
|
||||
if let Ok(tab_stop) = value.as_u64() {
|
||||
rl.set_tab_stop(tab_stop as usize);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn nu_line_editor_helper(
|
||||
context: &mut EvaluationContext,
|
||||
config: &dyn nu_data::config::Conf,
|
||||
) -> crate::shell::Helper {
|
||||
let hinter = rustyline_hinter(config);
|
||||
crate::shell::Helper::new(context.clone(), hinter)
|
||||
}
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub fn rustyline_hinter(
|
||||
config: &dyn nu_data::config::Conf,
|
||||
) -> Option<rustyline::hint::HistoryHinter> {
|
||||
if let Some(line_editor_vars) = config.var("line_editor") {
|
||||
for (idx, value) in line_editor_vars.row_entries() {
|
||||
if idx == "show_hints" && value.expect_string() == "false" {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(rustyline::hint::HistoryHinter {})
|
||||
}
|
||||
|
||||
pub fn configure_ctrl_c(_context: &mut EvaluationContext) -> Result<(), Box<dyn Error>> {
|
||||
#[cfg(feature = "ctrlc")]
|
||||
{
|
||||
let cc = _context.ctrl_c.clone();
|
||||
|
||||
ctrlc::set_handler(move || {
|
||||
cc.store(true, Ordering::SeqCst);
|
||||
})?;
|
||||
|
||||
if _context.ctrl_c.load(Ordering::SeqCst) {
|
||||
_context.ctrl_c.store(false, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -21,28 +21,6 @@ macro_rules! stream {
|
||||
}}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace_stream {
|
||||
(target: $target:tt, $desc:tt = $expr:expr) => {{
|
||||
if log::log_enabled!(target: $target, log::Level::Trace) {
|
||||
use futures::stream::StreamExt;
|
||||
|
||||
let objects = $expr.inspect(move |o| {
|
||||
trace!(
|
||||
target: $target,
|
||||
"{} = {}",
|
||||
$desc,
|
||||
nu_source::PrettyDebug::plain_string(o, 70)
|
||||
);
|
||||
});
|
||||
|
||||
nu_stream::InputStream::from_stream(objects.boxed())
|
||||
} else {
|
||||
$expr
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! trace_out_stream {
|
||||
(target: $target:tt, $desc:tt = $expr:expr) => {{
|
||||
@ -68,44 +46,17 @@ macro_rules! trace_out_stream {
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) use nu_protocol::{errln, out, outln, row};
|
||||
use nu_source::HasFallibleSpan;
|
||||
|
||||
pub(crate) use crate::commands::command::{CommandArgs, RawCommandArgs, RunnableContext};
|
||||
pub(crate) use crate::commands::Example;
|
||||
pub(crate) use crate::evaluation_context::EvaluationContext;
|
||||
pub(crate) use nu_data::config;
|
||||
pub(crate) use nu_data::value;
|
||||
// pub(crate) use crate::env::host::handle_unexpected;
|
||||
pub(crate) use crate::env::Host;
|
||||
pub(crate) use crate::evaluate::scope::Scope;
|
||||
|
||||
pub(crate) use crate::shell::filesystem_shell::FilesystemShell;
|
||||
pub(crate) use crate::shell::help_shell::HelpShell;
|
||||
pub(crate) use crate::shell::shell_manager::ShellManager;
|
||||
pub(crate) use crate::shell::value_shell::ValueShell;
|
||||
pub(crate) use bigdecimal::BigDecimal;
|
||||
pub(crate) use futures::stream::BoxStream;
|
||||
pub(crate) use futures::{Stream, StreamExt};
|
||||
pub(crate) use nu_engine::Host;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_errors::ShellError;
|
||||
pub(crate) use nu_parser::ParserScope;
|
||||
pub(crate) use nu_source::{
|
||||
b, AnchorLocation, DebugDocBuilder, PrettyDebug, PrettyDebugWithSource, Span, SpannedItem, Tag,
|
||||
TaggedItem, Text,
|
||||
};
|
||||
pub(crate) use nu_stream::{InputStream, InterruptibleStream, OutputStream};
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_protocol::outln;
|
||||
pub(crate) use nu_stream::OutputStream;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use nu_value_ext::ValueExt;
|
||||
pub(crate) use num_bigint::BigInt;
|
||||
pub(crate) use num_traits::cast::ToPrimitive;
|
||||
pub(crate) use serde::Deserialize;
|
||||
pub(crate) use std::collections::VecDeque;
|
||||
pub(crate) use std::future::Future;
|
||||
pub(crate) use std::sync::atomic::{AtomicBool, Ordering};
|
||||
pub(crate) use std::sync::Arc;
|
||||
|
||||
pub(crate) use async_trait::async_trait;
|
||||
pub(crate) use indexmap::{indexmap, IndexMap};
|
||||
pub(crate) use itertools::Itertools;
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use std::sync::atomic::Ordering;
|
||||
|
||||
pub trait FromInputStream {
|
||||
fn from_input_stream(self) -> OutputStream;
|
||||
@ -122,26 +73,6 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToInputStream {
|
||||
fn to_input_stream(self) -> InputStream;
|
||||
}
|
||||
|
||||
impl<T, U> ToInputStream for T
|
||||
where
|
||||
T: Stream<Item = U> + Send + 'static,
|
||||
U: Into<Result<nu_protocol::Value, nu_errors::ShellError>>,
|
||||
{
|
||||
fn to_input_stream(self) -> InputStream {
|
||||
InputStream::from_stream(self.map(|item| match item.into() {
|
||||
Ok(result) => result,
|
||||
Err(err) => match HasFallibleSpan::maybe_span(&err) {
|
||||
Some(span) => nu_protocol::UntaggedValue::Error(err).into_value(span),
|
||||
None => nu_protocol::UntaggedValue::Error(err).into_untagged_value(),
|
||||
},
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToOutputStream {
|
||||
fn to_output_stream(self) -> OutputStream;
|
||||
}
|
||||
@ -157,16 +88,3 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Interruptible<V> {
|
||||
fn interruptible(self, ctrl_c: Arc<AtomicBool>) -> InterruptibleStream<V>;
|
||||
}
|
||||
|
||||
impl<S, V> Interruptible<V> for S
|
||||
where
|
||||
S: Stream<Item = V> + Send + 'static,
|
||||
{
|
||||
fn interruptible(self, ctrl_c: Arc<AtomicBool>) -> InterruptibleStream<V> {
|
||||
InterruptibleStream::new(self, ctrl_c)
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,8 @@
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub(crate) mod completer;
|
||||
pub(crate) mod filesystem_shell;
|
||||
pub(crate) mod help_shell;
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub(crate) mod helper;
|
||||
pub(crate) mod painter;
|
||||
pub(crate) mod palette;
|
||||
pub(crate) mod shell;
|
||||
pub(crate) mod shell_manager;
|
||||
pub(crate) mod value_shell;
|
||||
|
||||
#[cfg(feature = "rustyline-support")]
|
||||
pub(crate) use helper::Helper;
|
||||
|
@ -4,7 +4,7 @@ use crate::completion::matchers;
|
||||
use crate::completion::matchers::Matcher;
|
||||
use crate::completion::path::{PathCompleter, PathSuggestion};
|
||||
use crate::completion::{self, Completer, Suggestion};
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use nu_engine::EvaluationContext;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_source::Tag;
|
||||
|
||||
|
@ -1,12 +1,8 @@
|
||||
use std::borrow::Cow::{self, Owned};
|
||||
|
||||
use nu_source::{Tag, Tagged};
|
||||
|
||||
use crate::completion;
|
||||
use crate::evaluation_context::EvaluationContext;
|
||||
use crate::shell::completer::NuCompleter;
|
||||
use crate::shell::painter::Painter;
|
||||
use crate::shell::palette::DefaultPalette;
|
||||
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
|
||||
use nu_source::{Tag, Tagged};
|
||||
use std::borrow::Cow::{self, Owned};
|
||||
|
||||
pub struct Helper {
|
||||
completer: NuCompleter,
|
||||
@ -123,7 +119,7 @@ impl rustyline::validate::Validator for NuValidator {
|
||||
}
|
||||
}
|
||||
|
||||
let (_, err) = nu_parser::group(tokens);
|
||||
let (_, err) = nu_parser::block(tokens);
|
||||
|
||||
if let Some(err) = err {
|
||||
if let nu_errors::ParseErrorReason::Eof { .. } = err.reason() {
|
||||
@ -148,3 +144,51 @@ fn vec_tag<T>(input: Vec<Tagged<T>>) -> Option<Tag> {
|
||||
}
|
||||
|
||||
impl rustyline::Helper for Helper {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nu_engine::basic_evaluation_context;
|
||||
use rustyline::completion::Completer;
|
||||
use rustyline::line_buffer::LineBuffer;
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn closing_quote_should_replaced() {
|
||||
let text = "cd \"folder with spaces\\subdirectory\\\"";
|
||||
let replacement = "\"folder with spaces\\subdirectory\\subsubdirectory\\\"";
|
||||
|
||||
let mut buffer = LineBuffer::with_capacity(256);
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 1);
|
||||
|
||||
let helper = Helper::new(basic_evaluation_context().unwrap(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), &replacement);
|
||||
|
||||
assert_eq!(
|
||||
buffer.as_str(),
|
||||
"cd \"folder with spaces\\subdirectory\\subsubdirectory\\\""
|
||||
);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn replacement_with_cursor_in_text() {
|
||||
let text = "cd \"folder with spaces\\subdirectory\\\"";
|
||||
let replacement = "\"folder with spaces\\subdirectory\\subsubdirectory\\\"";
|
||||
|
||||
let mut buffer = LineBuffer::with_capacity(256);
|
||||
buffer.insert_str(0, text);
|
||||
buffer.set_pos(text.len() - 30);
|
||||
|
||||
let helper = Helper::new(basic_evaluation_context().unwrap(), None);
|
||||
|
||||
helper.update(&mut buffer, "cd ".len(), &replacement);
|
||||
|
||||
assert_eq!(
|
||||
buffer.as_str(),
|
||||
"cd \"folder with spaces\\subdirectory\\subsubdirectory\\\""
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
#![allow(dead_code)]
|
||||
use crate::prelude::*;
|
||||
use itertools::{merge_join_by, EitherOrBoth, Itertools};
|
||||
use lazy_static::lazy_static;
|
||||
use log::trace;
|
||||
use nu_engine::Scope;
|
||||
use nu_errors::ShellError;
|
||||
use nu_parser::ParserScope;
|
||||
use nu_protocol::{
|
||||
@ -14,9 +16,6 @@ use nu_source::Span;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{collections::HashMap, hash::Hash};
|
||||
|
||||
use itertools::{merge_join_by, EitherOrBoth, Itertools};
|
||||
use log::trace;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct VarDeclaration {
|
||||
pub name: String,
|
||||
@ -296,7 +295,7 @@ fn get_shape_of_expr(expr: &SpannedExpression) -> Option<SyntaxShape> {
|
||||
Expression::Boolean(_) => Some(SyntaxShape::String),
|
||||
|
||||
Expression::Path(_) => Some(SyntaxShape::ColumnPath),
|
||||
Expression::FilePath(_) => Some(SyntaxShape::Path),
|
||||
Expression::FilePath(_) => Some(SyntaxShape::FilePath),
|
||||
Expression::Block(_) => Some(SyntaxShape::Block),
|
||||
Expression::ExternalCommand(_) => Some(SyntaxShape::String),
|
||||
Expression::Table(_, _) => Some(SyntaxShape::Table),
|
||||
|
@ -1,13 +0,0 @@
|
||||
use nu_test_support::{nu, pipeline};
|
||||
|
||||
#[test]
|
||||
fn echo_range_is_lazy() {
|
||||
let actual = nu!(
|
||||
cwd: "tests/fixtures/formats", pipeline(
|
||||
r#"
|
||||
echo 1..10000000000 | first 3 | to json
|
||||
"#
|
||||
));
|
||||
|
||||
assert_eq!(actual.out, "[1,2,3]");
|
||||
}
|
132
crates/nu-command/Cargo.toml
Normal file
132
crates/nu-command/Cargo.toml
Normal file
@ -0,0 +1,132 @@
|
||||
[package]
|
||||
authors = ["The Nu Project Contributors"]
|
||||
build = "build.rs"
|
||||
description = "CLI for nushell"
|
||||
edition = "2018"
|
||||
license = "MIT"
|
||||
name = "nu-command"
|
||||
version = "0.26.0"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
nu-data = { version = "0.26.0", path = "../nu-data" }
|
||||
nu-engine = { version = "0.26.0", path = "../nu-engine" }
|
||||
nu-errors = { version = "0.26.0", path = "../nu-errors" }
|
||||
nu-json = { version = "0.26.0", path = "../nu-json" }
|
||||
nu-parser = { version = "0.26.0", path = "../nu-parser" }
|
||||
nu-plugin = { version = "0.26.0", path = "../nu-plugin" }
|
||||
nu-protocol = { version = "0.26.0", path = "../nu-protocol" }
|
||||
nu-source = { version = "0.26.0", path = "../nu-source" }
|
||||
nu-stream = { version = "0.26.0", path = "../nu-stream" }
|
||||
nu-table = { version = "0.26.0", path = "../nu-table" }
|
||||
nu-test-support = { version = "0.26.0", path = "../nu-test-support" }
|
||||
nu-value-ext = { version = "0.26.0", path = "../nu-value-ext" }
|
||||
|
||||
Inflector = "0.11"
|
||||
ansi_term = "0.12.1"
|
||||
arboard = { version = "1.1.0", optional = true }
|
||||
async-recursion = "0.3.1"
|
||||
async-trait = "0.1.40"
|
||||
base64 = "0.13.0"
|
||||
bigdecimal = { version = "0.2.0", features = ["serde"] }
|
||||
byte-unit = "4.0.9"
|
||||
bytes = "0.5.6"
|
||||
calamine = "0.16.1"
|
||||
chrono = { version = "0.4.15", features = ["serde"] }
|
||||
chrono-tz = "0.5.3"
|
||||
clap = "2.33.3"
|
||||
codespan-reporting = "0.11.0"
|
||||
csv = "1.1.3"
|
||||
ctrlc = { version = "3.1.6", optional = true }
|
||||
derive-new = "0.5.8"
|
||||
directories-next = { version = "2.0.0", optional = true }
|
||||
dirs-next = { version = "2.0.0", optional = true }
|
||||
dtparse = "1.2.0"
|
||||
dunce = "1.0.1"
|
||||
eml-parser = "0.1.0"
|
||||
encoding_rs = "0.8.24"
|
||||
filesize = "0.2.0"
|
||||
fs_extra = "1.2.0"
|
||||
futures = { version = "0.3.5", features = ["compat", "io-compat"] }
|
||||
futures-util = "0.3.8"
|
||||
futures_codec = "0.4.1"
|
||||
getset = "0.1.1"
|
||||
glob = "0.3.0"
|
||||
htmlescape = "0.3.1"
|
||||
ical = "0.7.0"
|
||||
ichwh = { version = "0.3.4", optional = true }
|
||||
indexmap = { version = "1.6.0", features = ["serde-1"] }
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.*"
|
||||
log = "0.4.11"
|
||||
meval = "0.2.0"
|
||||
num-bigint = { version = "0.3.0", features = ["serde"] }
|
||||
num-format = { version = "0.4.0", features = ["with-num-bigint"] }
|
||||
num-traits = "0.2.12"
|
||||
parking_lot = "0.11.0"
|
||||
pin-utils = "0.1.0"
|
||||
pretty-hex = "0.2.0"
|
||||
ptree = { version = "0.3.0", optional = true }
|
||||
query_interface = "0.3.5"
|
||||
quick-xml = "0.20.0"
|
||||
rand = "0.7.3"
|
||||
rayon = "1.4.0"
|
||||
regex = "1.3.9"
|
||||
roxmltree = "0.14.0"
|
||||
rust-embed = "5.8.0"
|
||||
rustyline = { version = "6.3.0", optional = true }
|
||||
serde = { version = "1.0.115", features = ["derive"] }
|
||||
serde_bytes = "0.11.5"
|
||||
serde_ini = "0.2.0"
|
||||
serde_json = "1.0.57"
|
||||
serde_urlencoded = "0.7.0"
|
||||
serde_yaml = "0.8.13"
|
||||
sha2 = "0.9.1"
|
||||
shellexpand = "2.0.0"
|
||||
strip-ansi-escapes = "0.1.0"
|
||||
sxd-document = "0.3.2"
|
||||
sxd-xpath = "0.4.2"
|
||||
tempfile = "3.1.0"
|
||||
term = { version = "0.6.1", optional = true }
|
||||
term_size = "0.3.2"
|
||||
termcolor = "1.1.0"
|
||||
titlecase = "1.0"
|
||||
toml = "0.5.6"
|
||||
trash = { version = "1.2.0", optional = true }
|
||||
unicode-segmentation = "1.6.0"
|
||||
url = "2.1.1"
|
||||
uuid_crate = { package = "uuid", version = "0.8.1", features = ["v4"], optional = true }
|
||||
which = { version = "4.0.2", optional = true }
|
||||
zip = { version = "0.5.7", optional = true }
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
umask = "1.0.0"
|
||||
users = "0.10.0"
|
||||
|
||||
# TODO this will be possible with new dependency resolver
|
||||
# (currently on nightly behind -Zfeatures=itarget):
|
||||
# https://github.com/rust-lang/cargo/issues/7914
|
||||
#[target.'cfg(not(windows))'.dependencies]
|
||||
#num-format = {version = "0.4", features = ["with-system-locale"]}
|
||||
|
||||
[dependencies.rusqlite]
|
||||
features = ["bundled", "blob"]
|
||||
optional = true
|
||||
version = "0.24.2"
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.5"
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = "0.9.2"
|
||||
quickcheck_macros = "0.9.1"
|
||||
|
||||
[features]
|
||||
clipboard-cli = ["arboard"]
|
||||
rustyline-support = ["rustyline"]
|
||||
stable = []
|
||||
trash-support = ["trash"]
|
||||
directories = ["directories-next"]
|
||||
dirs = ["dirs-next"]
|
BIN
crates/nu-command/assets/228_themes.zip
Normal file
BIN
crates/nu-command/assets/228_themes.zip
Normal file
Binary file not shown.
3
crates/nu-command/build.rs
Normal file
3
crates/nu-command/build.rs
Normal file
@ -0,0 +1,3 @@
|
||||
fn main() -> shadow_rs::SdResult<()> {
|
||||
shadow_rs::new()
|
||||
}
|
@ -7,7 +7,7 @@ mod to_delimited_data;
|
||||
pub(crate) mod ansi;
|
||||
pub(crate) mod append;
|
||||
pub(crate) mod args;
|
||||
pub(crate) mod autoenv;
|
||||
pub mod autoenv;
|
||||
pub(crate) mod autoenv_trust;
|
||||
pub(crate) mod autoenv_untrust;
|
||||
pub(crate) mod autoview;
|
||||
@ -20,7 +20,7 @@ pub(crate) mod chart;
|
||||
pub(crate) mod classified;
|
||||
#[cfg(feature = "clipboard-cli")]
|
||||
pub(crate) mod clip;
|
||||
pub(crate) mod command;
|
||||
pub mod command;
|
||||
pub(crate) mod compact;
|
||||
pub(crate) mod config;
|
||||
pub(crate) mod constants;
|
||||
@ -30,7 +30,7 @@ pub(crate) mod date;
|
||||
pub(crate) mod debug;
|
||||
pub(crate) mod def;
|
||||
pub(crate) mod default;
|
||||
pub(crate) mod default_context;
|
||||
pub mod default_context;
|
||||
pub(crate) mod describe;
|
||||
pub(crate) mod do_;
|
||||
pub(crate) mod drop;
|
||||
@ -136,9 +136,6 @@ pub(crate) mod wrap;
|
||||
|
||||
pub(crate) use autoview::Autoview;
|
||||
pub(crate) use cd::Cd;
|
||||
pub(crate) use command::{
|
||||
whole_stream_command, Command, Example, UnevaluatedCallInfo, WholeStreamCommand,
|
||||
};
|
||||
|
||||
pub(crate) use ansi::Ansi;
|
||||
pub(crate) use append::Command as Append;
|
||||
@ -263,8 +260,8 @@ pub(crate) use split_by::SplitBy;
|
||||
pub(crate) use str_::{
|
||||
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
|
||||
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad,
|
||||
StrReverse, StrScreamingSnakeCase, StrSet, StrSnakeCase, StrStartsWith, StrSubstring,
|
||||
StrToDatetime, StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
||||
StrReverse, StrScreamingSnakeCase, StrSnakeCase, StrStartsWith, StrSubstring, StrToDatetime,
|
||||
StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
||||
};
|
||||
pub(crate) use table::Table;
|
||||
pub(crate) use tags::Tags;
|
||||
@ -290,8 +287,8 @@ pub(crate) use wrap::Wrap;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::commands::whole_stream_command;
|
||||
use crate::examples::{test_anchors, test_examples};
|
||||
use nu_engine::{whole_stream_command, Command};
|
||||
use nu_errors::ShellError;
|
||||
|
||||
fn full_tests() -> Vec<Command> {
|
||||
@ -302,6 +299,34 @@ mod tests {
|
||||
whole_stream_command(Move),
|
||||
whole_stream_command(Update),
|
||||
whole_stream_command(Empty),
|
||||
// Str Command Suite
|
||||
whole_stream_command(Str),
|
||||
whole_stream_command(StrToDecimal),
|
||||
whole_stream_command(StrToInteger),
|
||||
whole_stream_command(StrDowncase),
|
||||
whole_stream_command(StrUpcase),
|
||||
whole_stream_command(StrCapitalize),
|
||||
whole_stream_command(StrFindReplace),
|
||||
whole_stream_command(StrFrom),
|
||||
whole_stream_command(StrSubstring),
|
||||
whole_stream_command(StrToDatetime),
|
||||
whole_stream_command(StrContains),
|
||||
whole_stream_command(StrIndexOf),
|
||||
whole_stream_command(StrTrim),
|
||||
whole_stream_command(StrTrimLeft),
|
||||
whole_stream_command(StrTrimRight),
|
||||
whole_stream_command(StrStartsWith),
|
||||
whole_stream_command(StrEndsWith),
|
||||
//whole_stream_command(StrCollect),
|
||||
whole_stream_command(StrLength),
|
||||
whole_stream_command(StrLPad),
|
||||
whole_stream_command(StrReverse),
|
||||
whole_stream_command(StrRPad),
|
||||
whole_stream_command(StrCamelCase),
|
||||
whole_stream_command(StrPascalCase),
|
||||
whole_stream_command(StrKebabCase),
|
||||
whole_stream_command(StrSnakeCase),
|
||||
whole_stream_command(StrScreamingSnakeCase),
|
||||
]
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use ansi_term::Color;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
@ -9,7 +9,7 @@ pub struct Ansi;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct AnsiArgs {
|
||||
color: Value,
|
||||
code: Value,
|
||||
escape: Option<Tagged<String>>,
|
||||
osc: Option<Tagged<String>>,
|
||||
}
|
||||
@ -23,18 +23,18 @@ impl WholeStreamCommand for Ansi {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("ansi")
|
||||
.optional(
|
||||
"color",
|
||||
"code",
|
||||
SyntaxShape::Any,
|
||||
"the name of the color to use or 'reset' to reset the color",
|
||||
"the name of the code to use like 'green' or 'reset' to reset the color",
|
||||
)
|
||||
.named(
|
||||
"escape", // \x1b
|
||||
"escape", // \x1b[
|
||||
SyntaxShape::Any,
|
||||
"escape sequence without the escape character(s)",
|
||||
Some('e'),
|
||||
)
|
||||
.named(
|
||||
"osc",
|
||||
"osc", // \x1b]
|
||||
SyntaxShape::Any,
|
||||
"operating system command (ocs) escape sequence without the escape character(s)",
|
||||
Some('o'),
|
||||
@ -119,7 +119,7 @@ Format: #
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let (AnsiArgs { color, escape, osc }, _) = args.process().await?;
|
||||
let (AnsiArgs { code, escape, osc }, _) = args.process().await?;
|
||||
|
||||
if let Some(e) = escape {
|
||||
let esc_vec: Vec<char> = e.item.chars().collect();
|
||||
@ -145,6 +145,7 @@ Format: #
|
||||
o.tag(),
|
||||
));
|
||||
}
|
||||
|
||||
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
|
||||
// OCS's need to end with a bell '\x07' char
|
||||
let output = format!("\x1b]{};", o.item);
|
||||
@ -153,25 +154,24 @@ Format: #
|
||||
)));
|
||||
}
|
||||
|
||||
let color_string = color.as_string()?;
|
||||
let ansi_code = str_to_ansi_color(color_string);
|
||||
let code_string = code.as_string()?;
|
||||
let ansi_code = str_to_ansi(code_string);
|
||||
|
||||
if let Some(output) = ansi_code {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(output).into_value(color.tag()),
|
||||
UntaggedValue::string(output).into_value(code.tag()),
|
||||
)))
|
||||
} else {
|
||||
Err(ShellError::labeled_error(
|
||||
"Unknown color",
|
||||
"unknown color",
|
||||
color.tag(),
|
||||
"Unknown ansi code",
|
||||
"unknown ansi code",
|
||||
code.tag(),
|
||||
))
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn str_to_ansi_color(s: String) -> Option<String> {
|
||||
pub fn str_to_ansi(s: String) -> Option<String> {
|
||||
match s.as_str() {
|
||||
"g" | "green" => Some(Color::Green.prefix().to_string()),
|
||||
"gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()),
|
||||
@ -222,6 +222,57 @@ pub fn str_to_ansi_color(s: String) -> Option<String> {
|
||||
"wd" | "white_dimmed" => Some(Color::White.dimmed().prefix().to_string()),
|
||||
"wr" | "white_reverse" => Some(Color::White.reverse().prefix().to_string()),
|
||||
"reset" => Some("\x1b[0m".to_owned()),
|
||||
|
||||
// Reference for ansi codes https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
|
||||
// Another good reference http://ascii-table.com/ansi-escape-sequences.php
|
||||
|
||||
// For setting title like `echo [$(char title) $(pwd) $(char bel)] | str collect`
|
||||
"title" => Some("\x1b]2;".to_string()), // ESC]2; xterm sets window title using OSC syntax escapes
|
||||
"bel" => Some('\x07'.to_string()), // Terminal Bell
|
||||
"backspace" => Some('\x08'.to_string()), // Backspace
|
||||
|
||||
// Ansi Erase Sequences
|
||||
"clear_screen" => Some("\x1b[J".to_string()), // clears the screen
|
||||
"clear_screen_from_cursor_to_end" => Some("\x1b[0J".to_string()), // clears from cursor until end of screen
|
||||
"clear_screen_from_cursor_to_beginning" => Some("\x1b[1J".to_string()), // clears from cursor to beginning of screen
|
||||
"cls" | "clear_entire_screen" => Some("\x1b[2J".to_string()), // clears the entire screen
|
||||
"erase_line" => Some("\x1b[K".to_string()), // clears the current line
|
||||
"erase_line_from_cursor_to_end" => Some("\x1b[0K".to_string()), // clears from cursor to end of line
|
||||
"erase_line_from_cursor_to_beginning" => Some("\x1b[1K".to_string()), // clears from cursor to start of line
|
||||
"erase_entire_line" => Some("\x1b[2K".to_string()), // clears entire line
|
||||
|
||||
// Turn on/off cursor
|
||||
"cursor_off" => Some("\x1b[?25l".to_string()),
|
||||
"cursor_on" => Some("\x1b[?25h".to_string()),
|
||||
|
||||
// Turn on/off blinking
|
||||
"cursor_blink_off" => Some("\x1b[?12l".to_string()),
|
||||
"cursor_blink_on" => Some("\x1b[?12h".to_string()),
|
||||
|
||||
// Cursor position in ESC [ <r>;<c>R where r = row and c = column
|
||||
"cursor_position" => Some("\x1b[6n".to_string()),
|
||||
|
||||
// Report Terminal Identity
|
||||
"identity" => Some("\x1b[0c".to_string()),
|
||||
|
||||
// Ansi escape only - CSI command
|
||||
"csi" | "escape" | "escape_left" => Some("\x1b[".to_string()),
|
||||
// OSC escape (Operating system command)
|
||||
"osc" | "escape_right" => Some("\x1b]".to_string()),
|
||||
|
||||
// Ansi RGB - Needs to be 32;2;r;g;b or 48;2;r;g;b
|
||||
// assuming the rgb will be passed via command and no here
|
||||
"rgb_fg" => Some("\x1b[32;2;".to_string()),
|
||||
"rgb_bg" => Some("\x1b[48;2;".to_string()),
|
||||
|
||||
// Ansi color index - Needs 38;5;idx or 48;5;idx where idx = 0 to 255
|
||||
"idx_fg" | "color_idx_fg" => Some("\x1b[38;5;".to_string()),
|
||||
"idx_bg" | "color_idx_bg" => Some("\x1b[48;5;".to_string()),
|
||||
|
||||
// Returns terminal size like "[<r>;<c>R" where r is rows and c is columns
|
||||
// This should work assuming your terminal is not greater than 999x999
|
||||
"size" => Some("\x1b[s\x1b[999;999H\x1b[6n\x1b[u".to_string()),
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use serde::Deserialize;
|
||||
@ -63,8 +63,7 @@ The file can contain several optional sections:
|
||||
}
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(crate::commands::help::get_help(&Autoenv, &args.scope))
|
||||
.into_value(Tag::unknown()),
|
||||
UntaggedValue::string(get_help(&Autoenv, &args.scope)).into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use super::autoenv::read_trusted;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::SyntaxShape;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
@ -1,6 +1,6 @@
|
||||
use super::autoenv::Trusted;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::SyntaxShape;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
@ -1,8 +1,8 @@
|
||||
use crate::commands::autoview::options::{ConfigExtensions, NuConfig as AutoViewConfiguration};
|
||||
use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand};
|
||||
use crate::prelude::*;
|
||||
use crate::primitive::get_color_config;
|
||||
use nu_data::value::format_leaf;
|
||||
use nu_engine::{UnevaluatedCallInfo, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
|
||||
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||
@ -134,30 +134,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
||||
out!("{}", s);
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Line(ref s)),
|
||||
tag: Tag { anchor, span },
|
||||
} if anchor.is_some() => {
|
||||
if let Some(text) = text {
|
||||
let mut stream = VecDeque::new();
|
||||
stream.push_back(
|
||||
UntaggedValue::string(s).into_value(Tag { anchor, span }),
|
||||
);
|
||||
let command_args =
|
||||
create_default_command_args(&context).with_input(stream);
|
||||
let result = text.run(command_args).await?;
|
||||
result.collect::<Vec<_>>().await;
|
||||
} else {
|
||||
out!("{}\n", s);
|
||||
}
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Line(s)),
|
||||
..
|
||||
} => {
|
||||
out!("{}\n", s);
|
||||
}
|
||||
Value {
|
||||
value: UntaggedValue::Primitive(Primitive::Path(s)),
|
||||
value: UntaggedValue::Primitive(Primitive::FilePath(s)),
|
||||
..
|
||||
} => {
|
||||
out!("{}", s.display());
|
@ -1,8 +1,8 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
#[cfg(feature = "rich-benchmark")]
|
||||
use heim::cpu::time;
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
// use heim::cpu::time;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::{Block, CapturedBlock, ClassifiedCommand, Group, InternalCommand, Pipeline},
|
||||
@ -81,22 +81,22 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
|
||||
let start_time = Instant::now();
|
||||
|
||||
#[cfg(feature = "rich-benchmark")]
|
||||
let start = time().await;
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
// let start = time().await;
|
||||
|
||||
context.scope.enter_scope();
|
||||
let result = run_block(&block.block, &context, input).await;
|
||||
context.scope.exit_scope();
|
||||
let output = result?.into_vec().await;
|
||||
|
||||
#[cfg(feature = "rich-benchmark")]
|
||||
let end = time().await;
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
// let end = time().await;
|
||||
|
||||
let end_time = Instant::now();
|
||||
context.clear_errors();
|
||||
|
||||
// return basic runtime
|
||||
#[cfg(not(feature = "rich-benchmark"))]
|
||||
//#[cfg(not(feature = "rich-benchmark"))]
|
||||
{
|
||||
let mut indexmap = IndexMap::with_capacity(1);
|
||||
|
||||
@ -105,28 +105,28 @@ async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
|
||||
}
|
||||
// return advanced stats
|
||||
#[cfg(feature = "rich-benchmark")]
|
||||
if let (Ok(start), Ok(end)) = (start, end) {
|
||||
let mut indexmap = IndexMap::with_capacity(4);
|
||||
// #[cfg(feature = "rich-benchmark")]
|
||||
// if let (Ok(start), Ok(end)) = (start, end) {
|
||||
// let mut indexmap = IndexMap::with_capacity(4);
|
||||
|
||||
let real_time = into_big_int(end_time - start_time);
|
||||
indexmap.insert("real time".to_string(), real_time);
|
||||
// let real_time = into_big_int(end_time - start_time);
|
||||
// indexmap.insert("real time".to_string(), real_time);
|
||||
|
||||
let user_time = into_big_int(end.user() - start.user());
|
||||
indexmap.insert("user time".to_string(), user_time);
|
||||
// let user_time = into_big_int(end.user() - start.user());
|
||||
// indexmap.insert("user time".to_string(), user_time);
|
||||
|
||||
let system_time = into_big_int(end.system() - start.system());
|
||||
indexmap.insert("system time".to_string(), system_time);
|
||||
// let system_time = into_big_int(end.system() - start.system());
|
||||
// indexmap.insert("system time".to_string(), system_time);
|
||||
|
||||
let idle_time = into_big_int(end.idle() - start.idle());
|
||||
indexmap.insert("idle time".to_string(), idle_time);
|
||||
// let idle_time = into_big_int(end.idle() - start.idle());
|
||||
// indexmap.insert("idle time".to_string(), idle_time);
|
||||
|
||||
benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
|
||||
} else {
|
||||
Err(ShellError::untagged_runtime_error(
|
||||
"Could not retrieve CPU time",
|
||||
))
|
||||
}
|
||||
// benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
|
||||
// } else {
|
||||
// Err(ShellError::untagged_runtime_error(
|
||||
// "Could not retrieve CPU time",
|
||||
// ))
|
||||
// }
|
||||
}
|
||||
|
||||
async fn benchmark_output<T, Output>(
|
@ -1,8 +1,8 @@
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use nu_data::value::format_leaf;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
||||
#[derive(Deserialize)]
|
@ -1,7 +1,7 @@
|
||||
use crate::commands::{command::EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
|
||||
use crate::prelude::*;
|
||||
use chrono::{Datelike, Local, NaiveDate};
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::{EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
@ -1,16 +1,9 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use nu_engine::shell::CdArgs;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CdArgs {
|
||||
pub(crate) path: Option<Tagged<PathBuf>>,
|
||||
}
|
||||
|
||||
pub struct Cd;
|
||||
|
||||
@ -23,7 +16,7 @@ impl WholeStreamCommand for Cd {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("cd").optional(
|
||||
"directory",
|
||||
SyntaxShape::Path,
|
||||
SyntaxShape::FilePath,
|
||||
"the directory to change to",
|
||||
)
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
||||
@ -19,7 +19,7 @@ impl WholeStreamCommand for Char {
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("ansi")
|
||||
Signature::build("char")
|
||||
.required(
|
||||
"character",
|
||||
SyntaxShape::Any,
|
||||
@ -130,24 +130,6 @@ fn str_to_character(s: &str) -> Option<String> {
|
||||
"snowy" | "snow" => Some("❄️".to_string()),
|
||||
"thunderstorm" | "thunder" => Some("🌩️".to_string()),
|
||||
|
||||
// Reference for ansi codes https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
|
||||
// Another good reference http://ascii-table.com/ansi-escape-sequences.php
|
||||
|
||||
// For setting title like `echo [$(char title) $(pwd) $(char bel)] | str collect`
|
||||
"title" => Some("\x1b]2;".to_string()), // ESC]2; xterm sets window title
|
||||
"bel" => Some('\x07'.to_string()), // Terminal Bell
|
||||
"backspace" => Some('\x08'.to_string()), // Backspace
|
||||
|
||||
// Ansi Erase Sequences
|
||||
"clear_screen" => Some("\x1b[J".to_string()), // clears the screen
|
||||
"clear_screen_from_cursor_to_end" => Some("\x1b[0J".to_string()), // clears from cursor until end of screen
|
||||
"clear_screen_from_cursor_to_beginning" => Some("\x1b[1J".to_string()), // clears from cursor to beginning of screen
|
||||
"cls" | "clear_entire_screen" => Some("\x1b[2J".to_string()), // clears the entire screen
|
||||
"erase_line" => Some("\x1b[K".to_string()), // clears the current line
|
||||
"erase_line_from_cursor_to_end" => Some("\x1b[0K".to_string()), // clears from cursor to end of line
|
||||
"erase_line_from_cursor_to_beginning" => Some("\x1b[1K".to_string()), // clears from cursor to start of line
|
||||
"erase_entire_line" => Some("\x1b[2K".to_string()), // clears entire line
|
||||
|
||||
_ => None,
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
@ -28,8 +28,7 @@ impl WholeStreamCommand for Chart {
|
||||
}
|
||||
|
||||
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
||||
UntaggedValue::string(crate::commands::help::get_help(&Chart, &args.scope))
|
||||
.into_value(Tag::unknown()),
|
||||
UntaggedValue::string(get_help(&Chart, &args.scope)).into_value(Tag::unknown()),
|
||||
))))
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use crate::commands::classified::maybe_text_codec::{MaybeTextCodec, StringOrBinary};
|
||||
use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::futures::ThreadedReceiver;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::{MaybeTextCodec, StringOrBinary};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::io::Write;
|
||||
@ -18,6 +18,7 @@ use nu_protocol::hir::Expression;
|
||||
use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
|
||||
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
|
||||
use nu_source::Tag;
|
||||
use nu_stream::trace_stream;
|
||||
|
||||
pub(crate) async fn run_external_command(
|
||||
command: ExternalCommand,
|
||||
@ -95,7 +96,7 @@ async fn run_with_stdin(
|
||||
|
||||
#[cfg(feature = "dirs")]
|
||||
{
|
||||
home_dir = dirs::home_dir;
|
||||
home_dir = dirs_next::home_dir;
|
||||
}
|
||||
#[cfg(not(feature = "dirs"))]
|
||||
{
|
||||
@ -221,8 +222,7 @@ fn spawn(
|
||||
for value in block_on_stream(input) {
|
||||
match &value.value {
|
||||
UntaggedValue::Primitive(Primitive::Nothing) => continue,
|
||||
UntaggedValue::Primitive(Primitive::String(s))
|
||||
| UntaggedValue::Primitive(Primitive::Line(s)) => {
|
||||
UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||
if stdin_write.write(s.as_bytes()).is_err() {
|
||||
// Other side has closed, so exit
|
||||
return Ok(());
|
||||
@ -464,7 +464,7 @@ pub fn did_find_command(#[allow(unused)] name: &str) -> bool {
|
||||
let cmd_builtins = [
|
||||
"assoc", "break", "color", "copy", "date", "del", "dir", "dpath", "echo", "erase",
|
||||
"for", "ftype", "md", "mkdir", "mklink", "move", "path", "ren", "rename", "rd",
|
||||
"rmdir", "set", "start", "time", "title", "type", "ver", "verify", "vol",
|
||||
"rmdir", "start", "time", "title", "type", "ver", "verify", "vol",
|
||||
];
|
||||
|
||||
cmd_builtins.contains(&name)
|
||||
@ -533,15 +533,16 @@ mod tests {
|
||||
add_double_quotes, argument_is_quoted, escape_double_quotes, expand_tilde, remove_quotes,
|
||||
};
|
||||
#[cfg(feature = "which")]
|
||||
use super::{run_external_command, EvaluationContext, InputStream};
|
||||
use super::{run_external_command, InputStream};
|
||||
|
||||
#[cfg(feature = "which")]
|
||||
use futures::executor::block_on;
|
||||
#[cfg(feature = "which")]
|
||||
use nu_engine::basic_evaluation_context;
|
||||
#[cfg(feature = "which")]
|
||||
use nu_errors::ShellError;
|
||||
#[cfg(feature = "which")]
|
||||
use nu_test_support::commands::ExternalBuilder;
|
||||
|
||||
// async fn read(mut stream: OutputStream) -> Option<Value> {
|
||||
// match stream.try_next().await {
|
||||
// Ok(val) => {
|
||||
@ -562,7 +563,7 @@ mod tests {
|
||||
|
||||
let input = InputStream::empty();
|
||||
let mut ctx =
|
||||
EvaluationContext::basic().expect("There was a problem creating a basic context.");
|
||||
basic_evaluation_context().expect("There was a problem creating a basic context.");
|
||||
|
||||
assert!(
|
||||
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
|
||||
@ -576,7 +577,7 @@ mod tests {
|
||||
// async fn failure_run() -> Result<(), ShellError> {
|
||||
// let cmd = ExternalBuilder::for_name("fail").build();
|
||||
|
||||
// let mut ctx = EvaluationContext::basic().expect("There was a problem creating a basic context.");
|
||||
// let mut ctx = crate::cli::basic_evaluation_context().expect("There was a problem creating a basic context.");
|
||||
// let stream = run_external_command(cmd, &mut ctx, None, false)
|
||||
// .await?
|
||||
// .expect("There was a problem running the external command.");
|
5
crates/nu-command/src/commands/classified/mod.rs
Normal file
5
crates/nu-command/src/commands/classified/mod.rs
Normal file
@ -0,0 +1,5 @@
|
||||
mod dynamic;
|
||||
pub(crate) mod external;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
pub(crate) use dynamic::Command as DynamicCommand;
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::Signature;
|
||||
use std::process::Command;
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use futures::stream::StreamExt;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, Value};
|
||||
|
21
crates/nu-command/src/commands/command.rs
Normal file
21
crates/nu-command/src/commands/command.rs
Normal file
@ -0,0 +1,21 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::Command;
|
||||
use nu_errors::ShellError;
|
||||
use parking_lot::Mutex;
|
||||
use std::sync::atomic::AtomicBool;
|
||||
|
||||
pub struct RunnableContext {
|
||||
pub input: InputStream,
|
||||
pub shell_manager: ShellManager,
|
||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||
pub ctrl_c: Arc<AtomicBool>,
|
||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||
pub scope: Scope,
|
||||
pub name: Tag,
|
||||
}
|
||||
|
||||
impl RunnableContext {
|
||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
||||
self.scope.get_command(name)
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use futures::future;
|
||||
use futures::stream::StreamExt;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
@ -1,8 +1,9 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use crate::{CommandArgs, OutputStream};
|
||||
use nu_engine::CommandArgs;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
use nu_stream::OutputStream;
|
||||
|
||||
pub struct Command;
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
||||
@ -21,7 +21,7 @@ impl WholeStreamCommand for SubCommand {
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("config load").required(
|
||||
"load",
|
||||
SyntaxShape::Path,
|
||||
SyntaxShape::FilePath,
|
||||
"Path to load the config from",
|
||||
)
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
@ -36,6 +36,6 @@ pub async fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let path = config::default_path()?;
|
||||
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::Primitive(Primitive::Path(path)).into_value(args.call_info.name_tag),
|
||||
UntaggedValue::Primitive(Primitive::FilePath(path)).into_value(args.call_info.name_tag),
|
||||
)))
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use futures::stream::StreamExt;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue, Value};
|
||||
|
@ -1,19 +1,10 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub struct Cpy;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct CopyArgs {
|
||||
pub src: Tagged<PathBuf>,
|
||||
pub dst: Tagged<PathBuf>,
|
||||
pub recursive: Tagged<bool>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Cpy {
|
||||
fn name(&self) -> &str {
|
||||
@ -22,8 +13,8 @@ impl WholeStreamCommand for Cpy {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("cp")
|
||||
.required("src", SyntaxShape::Pattern, "the place to copy from")
|
||||
.required("dst", SyntaxShape::Path, "the place to copy to")
|
||||
.required("src", SyntaxShape::GlobPattern, "the place to copy from")
|
||||
.required("dst", SyntaxShape::FilePath, "the place to copy to")
|
||||
.switch(
|
||||
"recursive",
|
||||
"copy recursively through subdirectories",
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
||||
@ -21,8 +21,7 @@ impl WholeStreamCommand for Command {
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
Ok(OutputStream::one(ReturnSuccess::value(
|
||||
UntaggedValue::string(crate::commands::help::get_help(&Command, &args.scope))
|
||||
.into_value(Tag::unknown()),
|
||||
UntaggedValue::string(get_help(&Command, &args.scope)).into_value(Tag::unknown()),
|
||||
)))
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
Dictionary, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
@ -1,7 +1,7 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use chrono_tz::TZ_VARIANTS;
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, ReturnSuccess, Signature, UntaggedValue};
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use chrono::{DateTime, Local};
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, UntaggedValue};
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use chrono::{Datelike, Timelike};
|
||||
use indexmap::IndexMap;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Dictionary, Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::date::parser::{datetime_in_timezone, ParseErrorKind};
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
||||
use nu_errors::ShellError;
|
||||
|
||||
use crate::commands::date::utils::date_to_value;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_protocol::Signature;
|
||||
|
||||
pub struct Date;
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::CapturedBlock, Signature, SyntaxShape, Value};
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||
use nu_source::Tagged;
|
@ -1,8 +1,10 @@
|
||||
use crate::prelude::*;
|
||||
use nu_engine::basic_evaluation_context;
|
||||
use nu_engine::whole_stream_command;
|
||||
use std::error::Error;
|
||||
|
||||
pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Box<dyn Error>> {
|
||||
let context = EvaluationContext::basic()?;
|
||||
let context = basic_evaluation_context()?;
|
||||
|
||||
{
|
||||
use crate::commands::*;
|
||||
@ -89,7 +91,6 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
|
||||
whole_stream_command(StrFindReplace),
|
||||
whole_stream_command(StrFrom),
|
||||
whole_stream_command(StrSubstring),
|
||||
whole_stream_command(StrSet),
|
||||
whole_stream_command(StrToDatetime),
|
||||
whole_stream_command(StrContains),
|
||||
whole_stream_command(StrIndexOf),
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::CapturedBlock, hir::ExternalRedirection, Signature, SyntaxShape, Value};
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
173
crates/nu-command/src/commands/du.rs
Normal file
173
crates/nu-command/src/commands/du.rs
Normal file
@ -0,0 +1,173 @@
|
||||
use crate::prelude::*;
|
||||
use glob::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_engine::{DirBuilder, DirInfo, FileInfo};
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const NAME: &str = "du";
|
||||
const GLOB_PARAMS: MatchOptions = MatchOptions {
|
||||
case_sensitive: true,
|
||||
require_literal_separator: true,
|
||||
require_literal_leading_dot: false,
|
||||
};
|
||||
|
||||
pub struct Du;
|
||||
|
||||
#[derive(Deserialize, Clone)]
|
||||
pub struct DuArgs {
|
||||
path: Option<Tagged<PathBuf>>,
|
||||
all: bool,
|
||||
deref: bool,
|
||||
exclude: Option<Tagged<String>>,
|
||||
#[serde(rename = "max-depth")]
|
||||
max_depth: Option<Tagged<u64>>,
|
||||
#[serde(rename = "min-size")]
|
||||
min_size: Option<Tagged<u64>>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl WholeStreamCommand for Du {
|
||||
fn name(&self) -> &str {
|
||||
NAME
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(NAME)
|
||||
.optional("path", SyntaxShape::GlobPattern, "starting directory")
|
||||
.switch(
|
||||
"all",
|
||||
"Output file sizes as well as directory sizes",
|
||||
Some('a'),
|
||||
)
|
||||
.switch(
|
||||
"deref",
|
||||
"Dereference symlinks to their targets for size",
|
||||
Some('r'),
|
||||
)
|
||||
.named(
|
||||
"exclude",
|
||||
SyntaxShape::GlobPattern,
|
||||
"Exclude these file names",
|
||||
Some('x'),
|
||||
)
|
||||
.named(
|
||||
"max-depth",
|
||||
SyntaxShape::Int,
|
||||
"Directory recursion limit",
|
||||
Some('d'),
|
||||
)
|
||||
.named(
|
||||
"min-size",
|
||||
SyntaxShape::Int,
|
||||
"Exclude files below this size",
|
||||
Some('m'),
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Find disk usage sizes of specified items"
|
||||
}
|
||||
|
||||
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
du(args).await
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Disk usage of the current directory",
|
||||
example: "du",
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
async fn du(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||
let tag = args.call_info.name_tag.clone();
|
||||
let ctrl_c = args.ctrl_c.clone();
|
||||
let ctrl_c_copy = ctrl_c.clone();
|
||||
|
||||
let (args, _): (DuArgs, _) = args.process().await?;
|
||||
let exclude = args.exclude.map_or(Ok(None), move |x| {
|
||||
Pattern::new(&x.item)
|
||||
.map(Option::Some)
|
||||
.map_err(|e| ShellError::labeled_error(e.msg, "glob error", x.tag.clone()))
|
||||
})?;
|
||||
|
||||
let include_files = args.all;
|
||||
let paths = match args.path {
|
||||
Some(p) => {
|
||||
let p = p.item.to_str().expect("Why isn't this encoded properly?");
|
||||
glob::glob_with(p, GLOB_PARAMS)
|
||||
}
|
||||
None => glob::glob_with("*", GLOB_PARAMS),
|
||||
}
|
||||
.map_err(|e| ShellError::labeled_error(e.msg, "glob error", tag.clone()))?
|
||||
.filter(move |p| {
|
||||
if include_files {
|
||||
true
|
||||
} else {
|
||||
match p {
|
||||
Ok(f) if f.is_dir() => true,
|
||||
Err(e) if e.path().is_dir() => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
})
|
||||
.map(|v| v.map_err(glob_err_into));
|
||||
|
||||
let all = args.all;
|
||||
let deref = args.deref;
|
||||
let max_depth = args.max_depth.map(|f| f.item);
|
||||
let min_size = args.min_size.map(|f| f.item);
|
||||
|
||||
let params = DirBuilder {
|
||||
tag: tag.clone(),
|
||||
min: min_size,
|
||||
deref,
|
||||
exclude,
|
||||
all,
|
||||
};
|
||||
|
||||
let inp = futures::stream::iter(paths);
|
||||
|
||||
Ok(inp
|
||||
.flat_map(move |path| match path {
|
||||
Ok(p) => {
|
||||
let mut output = vec![];
|
||||
if p.is_dir() {
|
||||
output.push(Ok(ReturnSuccess::Value(
|
||||
DirInfo::new(p, ¶ms, max_depth, ctrl_c.clone()).into(),
|
||||
)));
|
||||
} else {
|
||||
for v in FileInfo::new(p, deref, tag.clone()).into_iter() {
|
||||
output.push(Ok(ReturnSuccess::Value(v.into())));
|
||||
}
|
||||
}
|
||||
futures::stream::iter(output)
|
||||
}
|
||||
Err(e) => futures::stream::iter(vec![Err(e)]),
|
||||
})
|
||||
.interruptible(ctrl_c_copy)
|
||||
.to_output_stream())
|
||||
}
|
||||
|
||||
fn glob_err_into(e: GlobError) -> ShellError {
|
||||
let e = e.into_error();
|
||||
ShellError::from(e)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Du;
|
||||
use super::ShellError;
|
||||
|
||||
#[test]
|
||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||
use crate::examples::test as test_examples;
|
||||
|
||||
Ok(test_examples(Du {})?)
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
|
||||
use futures::stream::once;
|
||||
use nu_errors::ShellError;
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::each::process_row;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::each::group::run_block_on_vec;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
//use itertools::Itertools;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{hir::CapturedBlock, Primitive, Signature, SyntaxShape, UntaggedValue};
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::Operator;
|
||||
use nu_protocol::{
|
||||
@ -76,6 +76,7 @@ struct RangeIterator {
|
||||
end: Primitive,
|
||||
tag: Tag,
|
||||
is_end_inclusive: bool,
|
||||
moves_up: bool,
|
||||
}
|
||||
|
||||
impl RangeIterator {
|
||||
@ -85,9 +86,15 @@ impl RangeIterator {
|
||||
x => x,
|
||||
};
|
||||
|
||||
let end = match range.to.0.item {
|
||||
Primitive::Nothing => Primitive::Int(u64::MAX.into()),
|
||||
x => x,
|
||||
};
|
||||
|
||||
RangeIterator {
|
||||
moves_up: start <= end,
|
||||
curr: start,
|
||||
end: range.to.0.item,
|
||||
end,
|
||||
tag,
|
||||
is_end_inclusive: matches!(range.to.1, RangeInclusion::Inclusive),
|
||||
}
|
||||
@ -121,7 +128,9 @@ impl Iterator for RangeIterator {
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
if (ordering == Ordering::Less) || (self.is_end_inclusive && ordering == Ordering::Equal) {
|
||||
if self.moves_up
|
||||
&& (ordering == Ordering::Less || self.is_end_inclusive && ordering == Ordering::Equal)
|
||||
{
|
||||
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
|
||||
|
||||
let next_value = nu_data::value::compute_values(
|
||||
@ -130,6 +139,35 @@ impl Iterator for RangeIterator {
|
||||
&UntaggedValue::int(1),
|
||||
);
|
||||
|
||||
self.curr = match next_value {
|
||||
Ok(result) => match result {
|
||||
UntaggedValue::Primitive(p) => p,
|
||||
_ => {
|
||||
return Some(Err(ShellError::unimplemented(
|
||||
"Internal error: expected a primitive result from increment",
|
||||
)));
|
||||
}
|
||||
},
|
||||
Err((left_type, right_type)) => {
|
||||
return Some(Err(ShellError::coerce_error(
|
||||
left_type.spanned(self.tag.span),
|
||||
right_type.spanned(self.tag.span),
|
||||
)));
|
||||
}
|
||||
};
|
||||
Some(ReturnSuccess::value(output))
|
||||
} else if !self.moves_up
|
||||
&& (ordering == Ordering::Greater
|
||||
|| self.is_end_inclusive && ordering == Ordering::Equal)
|
||||
{
|
||||
let output = UntaggedValue::Primitive(self.curr.clone()).into_value(self.tag.clone());
|
||||
|
||||
let next_value = nu_data::value::compute_values(
|
||||
Operator::Plus,
|
||||
&UntaggedValue::Primitive(self.curr.clone()),
|
||||
&UntaggedValue::int(-1),
|
||||
);
|
||||
|
||||
self.curr = match next_value {
|
||||
Ok(result) => match result {
|
||||
UntaggedValue::Primitive(p) => p,
|
||||
@ -148,7 +186,6 @@ impl Iterator for RangeIterator {
|
||||
};
|
||||
Some(ReturnSuccess::value(output))
|
||||
} else {
|
||||
// TODO: add inclusive/exclusive ranges
|
||||
None
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::classified::block::run_block;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::run_block;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
hir::CapturedBlock, ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape,
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::UnevaluatedCallInfo;
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::UnevaluatedCallInfo;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::hir::ExternalRedirection;
|
||||
use nu_protocol::{
|
||||
@ -27,7 +27,7 @@ impl WholeStreamCommand for Enter {
|
||||
Signature::build("enter")
|
||||
.required(
|
||||
"location",
|
||||
SyntaxShape::Path,
|
||||
SyntaxShape::FilePath,
|
||||
"the location to create a new shell from",
|
||||
)
|
||||
.named(
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape};
|
||||
use nu_source::Tagged;
|
||||
@ -21,8 +21,11 @@ impl WholeStreamCommand for Exec {
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("exec")
|
||||
.required("command", SyntaxShape::Path, "the command to execute")
|
||||
.rest(SyntaxShape::Pattern, "any additional arguments for command")
|
||||
.required("command", SyntaxShape::FilePath, "the command to execute")
|
||||
.rest(
|
||||
SyntaxShape::GlobPattern,
|
||||
"any additional arguments for command",
|
||||
)
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
@ -1,4 +1,3 @@
|
||||
use crate::commands::command::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{CommandAction, ReturnSuccess, Signature};
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
@ -1,5 +1,5 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{
|
||||
Dictionary, ReturnSuccess, Signature, SyntaxShape, TaggedDictBuilder, UntaggedValue, Value,
|
@ -1,6 +1,6 @@
|
||||
use crate::commands::WholeStreamCommand;
|
||||
use crate::evaluate::evaluate_baseline_expr;
|
||||
use crate::prelude::*;
|
||||
use nu_engine::evaluate_baseline_expr;
|
||||
use nu_engine::WholeStreamCommand;
|
||||
use nu_errors::ShellError;
|
||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||
use nu_source::Tagged;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user