forked from extern/nushell
Compare commits
227 Commits
Author | SHA1 | Date | |
---|---|---|---|
7dc1d6a350 | |||
deff1aa63b | |||
08e7d0dfb6 | |||
892aae267d | |||
11a9144e84 | |||
039f223b53 | |||
e1cb026184 | |||
2a96152a43 | |||
0795d56c1c | |||
48a90fea70 | |||
b202951c1d | |||
c3d2c61729 | |||
d7b707939f | |||
991ac6eb77 | |||
011b7c4a07 | |||
617341f8d5 | |||
abd2632977 | |||
5481db4079 | |||
041086d22a | |||
aa564f5072 | |||
8367f2001c | |||
1cfb228924 | |||
b403fb1275 | |||
3443ca40c5 | |||
96f95653a6 | |||
7f7e8465da | |||
e3a273cf73 | |||
233161d56e | |||
d883ab250a | |||
ef4e3f907c | |||
debeadbf3f | |||
d66baaceb9 | |||
85329f9a81 | |||
a5fefaf78b | |||
6f17662a4e | |||
c83aea3c89 | |||
67aaf4cb2d | |||
3083346884 | |||
d07789677f | |||
fb1846120d | |||
da1e1295ea | |||
ecaea57263 | |||
fa928bd25d | |||
c1981dfc26 | |||
fd41fa31d5 | |||
2c52144f41 | |||
87c7898b65 | |||
44e088c6fe | |||
7b4cbd7ce9 | |||
b052d524da | |||
47c4b8e88a | |||
d0a2a02eea | |||
b1e1dab4cb | |||
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 | |||
3ffa804088 | |||
98810d22b1 | |||
5e72b2a797 | |||
7e4e7fa4a6 | |||
d297199d7c | |||
17a433996e | |||
b9bb4692a4 | |||
d05dcdda02 | |||
27fe356214 | |||
fc44df1e45 | |||
77f915befe | |||
a5f7600f6f | |||
7eb8634ad7 | |||
452d8c06e9 | |||
48f535f02e | |||
43c10b0625 | |||
328b09fe04 | |||
15d49e4096 | |||
3ef53fe2cd | |||
7d8e759e98 | |||
69b3be61a4 | |||
79476a5cb2 | |||
f449baf8de | |||
5ff4bcfb7a | |||
98537ce8b7 | |||
d2a00a2daa | |||
f22938fc4a | |||
1e67ae8e94 | |||
c012d648fb | |||
67acaae53c | |||
e3da546e23 | |||
e5b136f70d | |||
058ef69da3 | |||
2a483531a4 | |||
05202671db | |||
8509873043 | |||
57a2d695e2 | |||
0b5ab1ef22 | |||
2eac79569c | |||
ac578b8491 | |||
5183fd25bb | |||
10f5a8ef78 | |||
a30837298d | |||
f377a3a7b4 | |||
83c874666a | |||
e000ed47cd | |||
af2f064f42 | |||
9c7b25134b | |||
2d15df9e6c | |||
d2ab287756 | |||
e73278990c | |||
12bc92df35 | |||
f19a801022 | |||
b193303aa3 | |||
e299e76fcf | |||
c857e18c4a | |||
5fb3df4054 | |||
8b597187fc | |||
930f9f0063 | |||
63d4df9810 | |||
13ba533fc4 | |||
6d60bab2fd | |||
5be774b2e5 | |||
b412ff92c0 | |||
5a75e11b0e | |||
e66bf70589 | |||
3924e9d50a | |||
8df748463d | |||
0113661c81 | |||
0ee054b14d | |||
80b39454ff | |||
97f3671e2c | |||
b674cee9d2 | |||
cb8491cfee | |||
8196b031f8 | |||
50dd56d3c4 | |||
0f7e1d4d01 | |||
ec77c572b9 | |||
f97561c416 | |||
5faa82e323 | |||
4e17292a12 | |||
666fbbb0d1 | |||
c6fe58467b | |||
46d1938f5c | |||
8229af7591 | |||
ee76523507 | |||
c283db373b | |||
1b0ed30516 | |||
a6fdee4a51 | |||
6951fb440c | |||
502c9ea706 | |||
22f67be461 | |||
77ffd06715 | |||
1d833ef972 | |||
0d8064ed2d | |||
cc06ea4d87 | |||
3cf7652e86 | |||
1eb28c6cb6 | |||
db590369a8 | |||
f4d654d2a2 | |||
5725e55abb | |||
b6d19cc9fa | |||
bc6c884a14 | |||
cb78bf8fd6 | |||
400bc97e35 | |||
2fd464bf7b | |||
e626522b3a | |||
791e07650d | |||
bf2363947b | |||
a2cc2259e7 | |||
808fe496a6 | |||
2fb48bd6ac |
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
31
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -12,9 +12,9 @@ A clear and concise description of what the bug is.
|
|||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
1.
|
1.
|
||||||
2.
|
2.
|
||||||
3.
|
3.
|
||||||
|
|
||||||
**Expected behavior**
|
**Expected behavior**
|
||||||
A clear and concise description of what you expected to happen.
|
A clear and concise description of what you expected to happen.
|
||||||
@ -23,8 +23,25 @@ A clear and concise description of what you expected to happen.
|
|||||||
If applicable, add screenshots to help explain your problem.
|
If applicable, add screenshots to help explain your problem.
|
||||||
|
|
||||||
**Configuration (please complete the following information):**
|
**Configuration (please complete the following information):**
|
||||||
- OS (e.g. Windows):
|
|
||||||
- Nu version (you can use the `version` command to find out):
|
|
||||||
- Optional features (if any):
|
|
||||||
|
|
||||||
Add any other context about the problem here.
|
Run `version | pivot` and paste the output to show OS, features, etc.
|
||||||
|
|
||||||
|
```
|
||||||
|
> version | pivot
|
||||||
|
╭───┬────────────────────┬───────────────────────────────────────────────────────────────────────╮
|
||||||
|
│ # │ Column0 │ Column1 │
|
||||||
|
├───┼────────────────────┼───────────────────────────────────────────────────────────────────────┤
|
||||||
|
│ 0 │ version │ 0.24.1 │
|
||||||
|
│ 1 │ build_os │ macos-x86_64 │
|
||||||
|
│ 2 │ rust_version │ rustc 1.48.0 │
|
||||||
|
│ 3 │ cargo_version │ cargo 1.48.0 │
|
||||||
|
│ 4 │ pkg_version │ 0.24.1 │
|
||||||
|
│ 5 │ build_time │ 2020-12-18 09:54:09 │
|
||||||
|
│ 6 │ build_rust_channel │ release │
|
||||||
|
│ 7 │ features │ ctrlc, default, directories, dirs, git, ichwh, ptree, rich-benchmark, │
|
||||||
|
│ │ │ rustyline, term, uuid, which, zip │
|
||||||
|
╰───┴────────────────────┴───────────────────────────────────────────────────────────────────────╯
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
**Add any other context about the problem here.**
|
||||||
|
6
.gitpod.Dockerfile
vendored
6
.gitpod.Dockerfile
vendored
@ -1,5 +1,9 @@
|
|||||||
FROM gitpod/workspace-full
|
FROM gitpod/workspace-full
|
||||||
|
|
||||||
|
# Gitpod will not rebuild Nushell's dev image unless *some* change is made to this Dockerfile.
|
||||||
|
# To force a rebuild, simply increase this counter:
|
||||||
|
ENV TRIGGER_REBUILD 1
|
||||||
|
|
||||||
USER gitpod
|
USER gitpod
|
||||||
|
|
||||||
RUN sudo apt-get update && \
|
RUN sudo apt-get update && \
|
||||||
@ -11,4 +15,4 @@ RUN sudo apt-get update && \
|
|||||||
rust-lldb \
|
rust-lldb \
|
||||||
&& sudo rm -rf /var/lib/apt/lists/*
|
&& sudo rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
ENV RUST_LLDB=/usr/bin/lldb-8
|
ENV RUST_LLDB=/usr/bin/lldb-11
|
||||||
|
@ -4,7 +4,7 @@ tasks:
|
|||||||
- name: Clippy
|
- name: Clippy
|
||||||
init: cargo clippy --all --features=stable -- -D clippy::result_unwrap_used -D clippy::option_unwrap_used
|
init: cargo clippy --all --features=stable -- -D clippy::result_unwrap_used -D clippy::option_unwrap_used
|
||||||
- name: Testing
|
- name: Testing
|
||||||
init: cargo test --all --features=stable,test-bins
|
init: cargo test --all --features=stable
|
||||||
- name: Build
|
- name: Build
|
||||||
init: cargo build --features=stable
|
init: cargo build --features=stable
|
||||||
- name: Nu
|
- name: Nu
|
||||||
|
3626
Cargo.lock
generated
3626
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
184
Cargo.toml
184
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
|||||||
name = "nu"
|
name = "nu"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
version = "0.21.0"
|
version = "0.27.1"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["crates/*/"]
|
members = ["crates/*/"]
|
||||||
@ -18,79 +18,110 @@ members = ["crates/*/"]
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cli = {version = "0.21.0", path = "./crates/nu-cli"}
|
nu-cli = { version = "0.27.1", path = "./crates/nu-cli", default-features = false }
|
||||||
nu-data = {version = "0.21.0", path = "./crates/nu-data"}
|
nu-command = { version = "0.27.1", path = "./crates/nu-command" }
|
||||||
nu-errors = {version = "0.21.0", path = "./crates/nu-errors"}
|
nu-data = { version = "0.27.1", path = "./crates/nu-data" }
|
||||||
nu-parser = {version = "0.21.0", path = "./crates/nu-parser"}
|
nu-engine = { version = "0.27.1", path = "./crates/nu-engine" }
|
||||||
nu-plugin = {version = "0.21.0", path = "./crates/nu-plugin"}
|
nu-errors = { version = "0.27.1", path = "./crates/nu-errors" }
|
||||||
nu-protocol = {version = "0.21.0", path = "./crates/nu-protocol"}
|
nu-parser = { version = "0.27.1", path = "./crates/nu-parser" }
|
||||||
nu-source = {version = "0.21.0", path = "./crates/nu-source"}
|
nu-plugin = { version = "0.27.1", path = "./crates/nu-plugin" }
|
||||||
nu-value-ext = {version = "0.21.0", path = "./crates/nu-value-ext"}
|
nu-protocol = { version = "0.27.1", path = "./crates/nu-protocol" }
|
||||||
|
nu-source = { version = "0.27.1", path = "./crates/nu-source" }
|
||||||
|
nu-value-ext = { version = "0.27.1", path = "./crates/nu-value-ext" }
|
||||||
|
|
||||||
nu_plugin_binaryview = {version = "0.21.0", path = "./crates/nu_plugin_binaryview", optional = true}
|
nu_plugin_binaryview = { version = "0.27.1", path = "./crates/nu_plugin_binaryview", optional = true }
|
||||||
nu_plugin_chart = {version = "0.21.0", path = "./crates/nu_plugin_chart", optional = true}
|
nu_plugin_chart = { version = "0.27.1", path = "./crates/nu_plugin_chart", optional = true }
|
||||||
nu_plugin_fetch = {version = "0.21.0", path = "./crates/nu_plugin_fetch", optional = true}
|
nu_plugin_fetch = { version = "0.27.1", path = "./crates/nu_plugin_fetch", optional = true }
|
||||||
nu_plugin_from_bson = {version = "0.21.0", path = "./crates/nu_plugin_from_bson", optional = true}
|
nu_plugin_from_bson = { version = "0.27.1", path = "./crates/nu_plugin_from_bson", optional = true }
|
||||||
nu_plugin_from_sqlite = {version = "0.21.0", path = "./crates/nu_plugin_from_sqlite", optional = true}
|
nu_plugin_from_sqlite = { version = "0.27.1", path = "./crates/nu_plugin_from_sqlite", optional = true }
|
||||||
nu_plugin_inc = {version = "0.21.0", path = "./crates/nu_plugin_inc", optional = true}
|
nu_plugin_inc = { version = "0.27.1", path = "./crates/nu_plugin_inc", optional = true }
|
||||||
nu_plugin_match = {version = "0.21.0", path = "./crates/nu_plugin_match", optional = true}
|
nu_plugin_match = { version = "0.27.1", path = "./crates/nu_plugin_match", optional = true }
|
||||||
nu_plugin_post = {version = "0.21.0", path = "./crates/nu_plugin_post", optional = true}
|
nu_plugin_post = { version = "0.27.1", path = "./crates/nu_plugin_post", optional = true }
|
||||||
nu_plugin_ps = {version = "0.21.0", path = "./crates/nu_plugin_ps", optional = true}
|
nu_plugin_ps = { version = "0.27.1", path = "./crates/nu_plugin_ps", optional = true }
|
||||||
nu_plugin_s3 = {version = "0.21.0", path = "./crates/nu_plugin_s3", optional = true}
|
nu_plugin_s3 = { version = "0.27.1", path = "./crates/nu_plugin_s3", optional = true }
|
||||||
nu_plugin_start = {version = "0.21.0", path = "./crates/nu_plugin_start", optional = true}
|
nu_plugin_selector = { version = "0.27.1", path = "./crates/nu_plugin_selector", optional = true }
|
||||||
nu_plugin_sys = {version = "0.21.0", path = "./crates/nu_plugin_sys", optional = true}
|
nu_plugin_start = { version = "0.27.1", path = "./crates/nu_plugin_start", optional = true }
|
||||||
nu_plugin_textview = {version = "0.21.0", path = "./crates/nu_plugin_textview", optional = true}
|
nu_plugin_sys = { version = "0.27.1", path = "./crates/nu_plugin_sys", optional = true }
|
||||||
nu_plugin_to_bson = {version = "0.21.0", path = "./crates/nu_plugin_to_bson", optional = true}
|
nu_plugin_textview = { version = "0.27.1", path = "./crates/nu_plugin_textview", optional = true }
|
||||||
nu_plugin_to_sqlite = {version = "0.21.0", path = "./crates/nu_plugin_to_sqlite", optional = true}
|
nu_plugin_to_bson = { version = "0.27.1", path = "./crates/nu_plugin_to_bson", optional = true }
|
||||||
nu_plugin_tree = {version = "0.21.0", path = "./crates/nu_plugin_tree", optional = true}
|
nu_plugin_to_sqlite = { version = "0.27.1", path = "./crates/nu_plugin_to_sqlite", optional = true }
|
||||||
nu_plugin_xpath = {version = "0.21.0", path = "./crates/nu_plugin_xpath", optional = true}
|
nu_plugin_tree = { version = "0.27.1", path = "./crates/nu_plugin_tree", optional = true }
|
||||||
|
nu_plugin_xpath = { version = "0.27.1", path = "./crates/nu_plugin_xpath", optional = true }
|
||||||
|
|
||||||
# Required to bootstrap the main binary
|
# Required to bootstrap the main binary
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
ctrlc = {version = "3.1.6", optional = true}
|
ctrlc = { version = "3.1.7", optional = true }
|
||||||
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
|
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
|
||||||
log = "0.4.11"
|
itertools = "0.10.0"
|
||||||
|
log = "0.4.14"
|
||||||
pretty_env_logger = "0.4.0"
|
pretty_env_logger = "0.4.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
nu-test-support = { version = "0.27.1", path = "./crates/nu-test-support" }
|
||||||
dunce = "1.0.1"
|
dunce = "1.0.1"
|
||||||
nu-test-support = {version = "0.21.0", path = "./crates/nu-test-support"}
|
serial_test = "0.5.1"
|
||||||
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
ctrlc-support = ["nu-cli/ctrlc"]
|
ctrlc-support = ["nu-cli/ctrlc", "nu-command/ctrlc"]
|
||||||
directories-support = ["nu-cli/directories", "nu-cli/dirs", "nu-data/directories", "nu-data/dirs"]
|
directories-support = [
|
||||||
git-support = ["nu-cli/git2"]
|
"nu-cli/directories",
|
||||||
ptree-support = ["nu-cli/ptree"]
|
"nu-cli/dirs",
|
||||||
rich-benchmark = ["nu-cli/rich-benchmark"]
|
"nu-command/directories",
|
||||||
rustyline-support = ["nu-cli/rustyline-support"]
|
"nu-command/dirs",
|
||||||
term-support = ["nu-cli/term"]
|
"nu-data/directories",
|
||||||
uuid-support = ["nu-cli/uuid_crate"]
|
"nu-data/dirs",
|
||||||
which-support = ["nu-cli/ichwh", "nu-cli/which"]
|
"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 = [
|
default = [
|
||||||
"sys",
|
"nu-cli/shadow-rs",
|
||||||
"ps",
|
"sys",
|
||||||
"textview",
|
"ps",
|
||||||
"inc",
|
"textview",
|
||||||
"git-support",
|
"inc",
|
||||||
"directories-support",
|
"directories-support",
|
||||||
"ctrlc-support",
|
"ctrlc-support",
|
||||||
"which-support",
|
"which-support",
|
||||||
"ptree-support",
|
"ptree-support",
|
||||||
"term-support",
|
"term-support",
|
||||||
"uuid-support",
|
"uuid-support",
|
||||||
"rustyline-support",
|
"rustyline-support",
|
||||||
"match",
|
"match",
|
||||||
"post",
|
"post",
|
||||||
"fetch",
|
"fetch",
|
||||||
"rich-benchmark",
|
"zip-support",
|
||||||
]
|
]
|
||||||
extra = ["default", "binaryview", "tree", "clipboard-cli", "trash-support", "start", "bson", "sqlite", "s3", "chart", "xpath"]
|
|
||||||
stable = ["default"]
|
|
||||||
|
|
||||||
wasi = ["inc", "match", "directories-support", "ptree-support", "match", "tree", "rustyline-support"]
|
stable = ["default"]
|
||||||
|
extra = [
|
||||||
|
"default",
|
||||||
|
"binaryview",
|
||||||
|
"tree",
|
||||||
|
"clipboard-cli",
|
||||||
|
"trash-support",
|
||||||
|
"start",
|
||||||
|
"bson",
|
||||||
|
"sqlite",
|
||||||
|
"s3",
|
||||||
|
"chart",
|
||||||
|
"xpath",
|
||||||
|
"selector",
|
||||||
|
]
|
||||||
|
|
||||||
|
wasi = ["inc", "match", "ptree-support", "match", "tree", "rustyline-support"]
|
||||||
|
|
||||||
trace = ["nu-parser/trace"]
|
trace = ["nu-parser/trace"]
|
||||||
|
|
||||||
@ -102,19 +133,27 @@ post = ["nu_plugin_post"]
|
|||||||
ps = ["nu_plugin_ps"]
|
ps = ["nu_plugin_ps"]
|
||||||
sys = ["nu_plugin_sys"]
|
sys = ["nu_plugin_sys"]
|
||||||
textview = ["nu_plugin_textview"]
|
textview = ["nu_plugin_textview"]
|
||||||
|
zip-support = ["nu-cli/zip", "nu-command/zip"]
|
||||||
|
|
||||||
# Extra
|
# Extra
|
||||||
binaryview = ["nu_plugin_binaryview"]
|
binaryview = ["nu_plugin_binaryview"]
|
||||||
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
|
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
|
||||||
chart = ["nu_plugin_chart"]
|
chart = ["nu_plugin_chart"]
|
||||||
clipboard-cli = ["nu-cli/clipboard-cli"]
|
clipboard-cli = ["nu-cli/clipboard-cli", "nu-command/clipboard-cli"]
|
||||||
s3 = ["nu_plugin_s3"]
|
s3 = ["nu_plugin_s3"]
|
||||||
|
selector = ["nu_plugin_selector"]
|
||||||
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
|
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
|
||||||
start = ["nu_plugin_start"]
|
start = ["nu_plugin_start"]
|
||||||
trash-support = ["nu-cli/trash-support"]
|
trash-support = ["nu-cli/trash-support", "nu-command/trash-support"]
|
||||||
tree = ["nu_plugin_tree"]
|
tree = ["nu_plugin_tree"]
|
||||||
xpath = ["nu_plugin_xpath"]
|
xpath = ["nu_plugin_xpath"]
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
#strip = "symbols" #Couldn't get working +nightly
|
||||||
|
codegen-units = 1 #Reduce parallel codegen units
|
||||||
|
lto = true #Link Time Optimization
|
||||||
|
opt-level = 'z' #Optimize for size
|
||||||
|
|
||||||
# Core plugins that ship with `cargo install nu` by default
|
# Core plugins that ship with `cargo install nu` by default
|
||||||
# Currently, Cargo limits us to installing only one binary
|
# Currently, Cargo limits us to installing only one binary
|
||||||
# unless we use [[bin]], so we use this as a workaround
|
# unless we use [[bin]], so we use this as a workaround
|
||||||
@ -190,6 +229,31 @@ name = "nu_plugin_extra_xpath"
|
|||||||
path = "src/plugins/nu_plugin_extra_xpath.rs"
|
path = "src/plugins/nu_plugin_extra_xpath.rs"
|
||||||
required-features = ["xpath"]
|
required-features = ["xpath"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nu_plugin_extra_selector"
|
||||||
|
path = "src/plugins/nu_plugin_extra_selector.rs"
|
||||||
|
required-features = ["selector"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nu_plugin_extra_from_bson"
|
||||||
|
path = "src/plugins/nu_plugin_extra_from_bson.rs"
|
||||||
|
required-features = ["bson"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nu_plugin_extra_to_bson"
|
||||||
|
path = "src/plugins/nu_plugin_extra_to_bson.rs"
|
||||||
|
required-features = ["bson"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nu_plugin_extra_from_sqlite"
|
||||||
|
path = "src/plugins/nu_plugin_extra_from_sqlite.rs"
|
||||||
|
required-features = ["sqlite"]
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "nu_plugin_extra_to_sqlite"
|
||||||
|
path = "src/plugins/nu_plugin_extra_to_sqlite.rs"
|
||||||
|
required-features = ["sqlite"]
|
||||||
|
|
||||||
# Main nu binary
|
# Main nu binary
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "nu"
|
name = "nu"
|
||||||
|
88
README.md
88
README.md
@ -7,11 +7,11 @@
|
|||||||
[](https://changelog.com/podcast/363)
|
[](https://changelog.com/podcast/363)
|
||||||
[](https://twitter.com/nu_shell)
|
[](https://twitter.com/nu_shell)
|
||||||
|
|
||||||
## Nu Shell
|
## Nushell
|
||||||
|
|
||||||
A new type of shell.
|
A new type of shell.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ There are also [good first issues](https://github.com/nushell/nushell/issues?q=i
|
|||||||
|
|
||||||
We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come and chat with us.
|
We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come and chat with us.
|
||||||
|
|
||||||
You can also find more learning resources in our [documentation](https://www.nushell.sh/documentation.html) site.
|
You can also find information on more specific topics in our [cookbook](https://www.nushell.sh/cookbook/).
|
||||||
|
|
||||||
Try it in Gitpod.
|
Try it in Gitpod.
|
||||||
|
|
||||||
@ -44,19 +44,19 @@ Try it in Gitpod.
|
|||||||
|
|
||||||
### Local
|
### Local
|
||||||
|
|
||||||
Up-to-date installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/en/installation.html). **Windows users**: please note that Nu works on Windows 10 and does not currently have Windows 7/8.1 support.
|
Up-to-date installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). **Windows users**: please note that Nu works on Windows 10 and does not currently have Windows 7/8.1 support.
|
||||||
|
|
||||||
To build Nu, you will need to use the **latest stable (1.41 or later)** version of the compiler.
|
To build Nu, you will need to use the **latest stable (1.47 or later)** version of the compiler.
|
||||||
|
|
||||||
Required dependencies:
|
Required dependencies:
|
||||||
|
|
||||||
* pkg-config and libssl (only needed on Linux)
|
- pkg-config and libssl (only needed on Linux)
|
||||||
* On Debian/Ubuntu: `apt install pkg-config libssl-dev`
|
- On Debian/Ubuntu: `apt install pkg-config libssl-dev`
|
||||||
|
|
||||||
Optional dependencies:
|
Optional dependencies:
|
||||||
|
|
||||||
* To use Nu with all possible optional features enabled, you'll also need the following:
|
- To use Nu with all possible optional features enabled, you'll also need the following:
|
||||||
* On Linux (on Debian/Ubuntu): `apt install libxcb-composite0-dev libx11-dev`
|
- On Linux (on Debian/Ubuntu): `apt install libxcb-composite0-dev libx11-dev`
|
||||||
|
|
||||||
To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs/) and the latest stable compiler via `rustup install stable`):
|
To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs/) and the latest stable compiler via `rustup install stable`):
|
||||||
|
|
||||||
@ -64,7 +64,7 @@ To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs
|
|||||||
cargo install nu
|
cargo install nu
|
||||||
```
|
```
|
||||||
|
|
||||||
You can also build Nu yourself with all the bells and whistles (be sure to have installed the [dependencies](https://www.nushell.sh/book/en/installation.html#dependencies) for your platform), once you have checked out this repo with git:
|
You can also build Nu yourself with all the bells and whistles (be sure to have installed the [dependencies](https://www.nushell.sh/book/installation.html#dependencies) for your platform), once you have checked out this repo with git:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo build --workspace --features=extra
|
cargo build --workspace --features=extra
|
||||||
@ -139,9 +139,9 @@ Just as the Unix philosophy, Nu allows commands to output from stdout and read f
|
|||||||
Additionally, commands can output structured data (you can think of this as a third kind of stream).
|
Additionally, commands can output structured data (you can think of this as a third kind of stream).
|
||||||
Commands that work in the pipeline fit into one of three categories:
|
Commands that work in the pipeline fit into one of three categories:
|
||||||
|
|
||||||
* Commands that produce a stream (eg, `ls`)
|
- Commands that produce a stream (eg, `ls`)
|
||||||
* Commands that filter a stream (eg, `where type == "Dir"`)
|
- Commands that filter a stream (eg, `where type == "Dir"`)
|
||||||
* Commands that consume the output of the pipeline (eg, `autoview`)
|
- Commands that consume the output of the pipeline (eg, `autoview`)
|
||||||
|
|
||||||
Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right.
|
Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right.
|
||||||
|
|
||||||
@ -219,22 +219,22 @@ We can pipeline this into a command that gets the contents of one of the columns
|
|||||||
name │ nu
|
name │ nu
|
||||||
readme │ README.md
|
readme │ README.md
|
||||||
repository │ https://github.com/nushell/nushell
|
repository │ https://github.com/nushell/nushell
|
||||||
version │ 0.15.1
|
version │ 0.21.0
|
||||||
───────────────┴────────────────────────────────────
|
───────────────┴────────────────────────────────────
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, we can use commands outside of Nu once we have the data we want:
|
Finally, we can use commands outside of Nu once we have the data we want:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
> open Cargo.toml | get package.version | echo $it
|
> open Cargo.toml | get package.version
|
||||||
0.15.1
|
0.21.0
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we use the variable `$it` to refer to the value being piped to the external command.
|
Here we use the variable `$it` to refer to the value being piped to the external command.
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
Nu has early support for configuring the shell. You can refer to the book for a list of [all supported variables](https://www.nushell.sh/book/en/configuration.html).
|
Nu has early support for configuring the shell. You can refer to the book for a list of [all supported variables](https://www.nushell.sh/book/configuration.html).
|
||||||
|
|
||||||
To set one of these variables, you can use `config set`. For example:
|
To set one of these variables, you can use `config set`. For example:
|
||||||
|
|
||||||
@ -270,49 +270,55 @@ If the plugin is a sink, it is given the full vector of final data and is given
|
|||||||
|
|
||||||
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
|
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
|
||||||
|
|
||||||
* First and foremost, Nu is cross-platform. Commands and techniques should carry between platforms and offer first-class consistent support for Windows, macOS, and Linux.
|
- First and foremost, Nu is cross-platform. Commands and techniques should carry between platforms and offer first-class consistent support for Windows, macOS, and Linux.
|
||||||
|
|
||||||
* Nu ensures direct compatibility with existing platform-specific executables that make up people's workflows.
|
- Nu ensures direct compatibility with existing platform-specific executables that make up people's workflows.
|
||||||
|
|
||||||
* Nu's workflow and tools should have the usability in day-to-day experience of using a shell in 2019 (and beyond).
|
- Nu's workflow and tools should have the usability in day-to-day experience of using a shell in 2019 (and beyond).
|
||||||
|
|
||||||
* Nu views data as both structured and unstructured. It is a structured shell like PowerShell.
|
- Nu views data as both structured and unstructured. It is a structured shell like PowerShell.
|
||||||
|
|
||||||
* Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state.
|
- Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state.
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
You can find a list of Nu commands, complete with documentation, in [quick command references](https://www.nushell.sh/documentation.html#quick-command-references).
|
You can find a list of Nu commands, complete with documentation, in [quick command references](https://www.nushell.sh/book/command_reference.html).
|
||||||
|
|
||||||
## Progress
|
## Progress
|
||||||
|
|
||||||
Nu is in heavy development, and will naturally change as it matures and people use it. The chart below isn't meant to be exhaustive, but rather helps give an idea for some of the areas of development and their relative completion:
|
Nu is in heavy development, and will naturally change as it matures and people use it. The chart below isn't meant to be exhaustive, but rather helps give an idea for some of the areas of development and their relative completion:
|
||||||
|
|
||||||
| Features | Not started | Prototype | MVP | Preview | Mature | Notes
|
| Features | Not started | Prototype | MVP | Preview | Mature | Notes |
|
||||||
| -------- |:-----------:|:---------:|:---:|:-------:|:------:| -----
|
| ------------- | :---------: | :-------: | :-: | :-----: | :----: | -------------------------------------------------------------------- |
|
||||||
| Aliases | | X | | | | Initial implementation but lacks necessary features
|
| Aliases | | X | | | | Initial implementation but lacks necessary features |
|
||||||
| Notebook | | X | | | | Initial jupyter support, but it loses state and lacks features
|
| Notebook | | X | | | | Initial jupyter support, but it loses state and lacks features |
|
||||||
| File ops | | | X | | | cp, mv, rm, mkdir have some support, but lacking others
|
| File ops | | | X | | | cp, mv, rm, mkdir have some support, but lacking others |
|
||||||
| Environment | | X | | | | Temporary environment, but no session-wide env variables
|
| Environment | | X | | | | Temporary environment, but no session-wide env variables |
|
||||||
| Shells | | X | | | | Basic value and file shells, but no opt-in/opt-out for commands
|
| Shells | | X | | | | Basic value and file shells, but no opt-in/opt-out for commands |
|
||||||
| Protocol | | | X | | | Streaming protocol is serviceable
|
| Protocol | | | X | | | Streaming protocol is serviceable |
|
||||||
| Plugins | | X | | | | Plugins work on one row at a time, lack batching and expression eval
|
| Plugins | | X | | | | Plugins work on one row at a time, lack batching and expression eval |
|
||||||
| Errors | | | X | | | Error reporting works, but could use usability polish
|
| Errors | | | X | | | Error reporting works, but could use usability polish |
|
||||||
| Documentation | | X | | | | Book and related are barebones and lack task-based lessons
|
| Documentation | | X | | | | Book and related are barebones and lack task-based lessons |
|
||||||
| Paging | | X | | | | Textview has paging, but we'd like paging for tables
|
| Paging | | X | | | | Textview has paging, but we'd like paging for tables |
|
||||||
| Functions| X | | | | | No functions, yet, only aliases
|
| Functions | | X | | | | No functions, yet, only aliases |
|
||||||
| Variables| X | | | | | Nu doesn't yet support variables
|
| Variables | | X | | | | Nu doesn't yet support variables |
|
||||||
| Completions | | X | | | | Completions are currently barebones, at best
|
| Completions | | X | | | | Completions are currently barebones, at best |
|
||||||
| Type-checking | | X | | | | Commands check basic types, but input/output isn't checked
|
| Type-checking | | X | | | | Commands check basic types, but input/output isn't checked |
|
||||||
|
|
||||||
## Current Roadmap
|
## Current Roadmap
|
||||||
|
|
||||||
We've added a `Roadmap Board` to help collaboratively capture the direction we're going for the current release as well as capture some important issues we'd like to see in NuShell. You can find the Roadmap [here](https://github.com/nushell/nushell/projects/2).
|
We've added a `Roadmap Board` to help collaboratively capture the direction we're going for the current release as well as capture some important issues we'd like to see in Nushell. You can find the Roadmap [here](https://github.com/nushell/nushell/projects/2).
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
See [Contributing](CONTRIBUTING.md) for details.
|
See [Contributing](CONTRIBUTING.md) for details.
|
||||||
|
|
||||||
|
Thanks to all the people who already contributed!
|
||||||
|
|
||||||
|
<a href="https://github.com/nushell/nushell/graphs/contributors">
|
||||||
|
<img src="https://contributors-img.web.app/image?repo=nushell/nushell" />
|
||||||
|
</a>
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
The project is made available under the MIT license. See the `LICENSE` file for more information.
|
The project is made available under the MIT license. See the `LICENSE` file for more information.
|
||||||
|
@ -1,107 +1,111 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["The Nu Project Contributors"]
|
authors = ["The Nu Project Contributors"]
|
||||||
|
build = "build.rs"
|
||||||
description = "CLI for nushell"
|
description = "CLI for nushell"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.21.0"
|
version = "0.27.1"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
doctest = false
|
doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-data = {version = "0.21.0", path = "../nu-data"}
|
nu-command = { version = "0.27.1", path = "../nu-command" }
|
||||||
nu-errors = {version = "0.21.0", path = "../nu-errors"}
|
nu-data = { version = "0.27.1", path = "../nu-data" }
|
||||||
nu-parser = {version = "0.21.0", path = "../nu-parser"}
|
nu-engine = { version = "0.27.1", path = "../nu-engine" }
|
||||||
nu-plugin = {version = "0.21.0", path = "../nu-plugin"}
|
nu-errors = { version = "0.27.1", path = "../nu-errors" }
|
||||||
nu-protocol = {version = "0.21.0", path = "../nu-protocol"}
|
nu-json = { version = "0.27.1", path = "../nu-json" }
|
||||||
nu-source = {version = "0.21.0", path = "../nu-source"}
|
nu-parser = { version = "0.27.1", path = "../nu-parser" }
|
||||||
nu-table = {version = "0.21.0", path = "../nu-table"}
|
nu-plugin = { version = "0.27.1", path = "../nu-plugin" }
|
||||||
nu-test-support = {version = "0.21.0", path = "../nu-test-support"}
|
nu-protocol = { version = "0.27.1", path = "../nu-protocol" }
|
||||||
nu-value-ext = {version = "0.21.0", path = "../nu-value-ext"}
|
nu-source = { version = "0.27.1", path = "../nu-source" }
|
||||||
|
nu-stream = { version = "0.27.1", path = "../nu-stream" }
|
||||||
|
nu-table = { version = "0.27.1", path = "../nu-table" }
|
||||||
|
nu-test-support = { version = "0.27.1", path = "../nu-test-support" }
|
||||||
|
nu-value-ext = { version = "0.27.1", path = "../nu-value-ext" }
|
||||||
|
|
||||||
|
Inflector = "0.11"
|
||||||
ansi_term = "0.12.1"
|
ansi_term = "0.12.1"
|
||||||
async-recursion = "0.3.1"
|
arboard = { version = "1.1.0", optional = true }
|
||||||
async-trait = "0.1.40"
|
async-recursion = "0.3.2"
|
||||||
base64 = "0.12.3"
|
async-trait = "0.1.42"
|
||||||
bigdecimal = {version = "0.2.0", features = ["serde"]}
|
base64 = "0.13.0"
|
||||||
|
bigdecimal = { version = "0.2.0", features = ["serde"] }
|
||||||
byte-unit = "4.0.9"
|
byte-unit = "4.0.9"
|
||||||
bytes = "0.5.6"
|
bytes = "1.0.1"
|
||||||
calamine = "0.16.1"
|
calamine = "0.17.0"
|
||||||
chrono = {version = "0.4.15", features = ["serde"]}
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
|
chrono-tz = "0.5.3"
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
codespan-reporting = "0.9.5"
|
codespan-reporting = "0.11.0"
|
||||||
csv = "1.1.3"
|
csv = "1.1.5"
|
||||||
ctrlc = {version = "3.1.6", optional = true}
|
ctrlc = { version = "3.1.7", optional = true }
|
||||||
derive-new = "0.5.8"
|
derive-new = "0.5.8"
|
||||||
directories = {version = "3.0.1", optional = true}
|
directories-next = { version = "2.0.0", optional = true }
|
||||||
dirs = {version = "3.0.1", optional = true}
|
dirs-next = { version = "2.0.0", optional = true }
|
||||||
dtparse = "1.2.0"
|
dtparse = "1.2.0"
|
||||||
dunce = "1.0.1"
|
dunce = "1.0.1"
|
||||||
eml-parser = "0.1.0"
|
eml-parser = "0.1.0"
|
||||||
|
encoding_rs = "0.8.28"
|
||||||
filesize = "0.2.0"
|
filesize = "0.2.0"
|
||||||
fs_extra = "1.2.0"
|
fs_extra = "1.2.0"
|
||||||
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
|
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
|
||||||
futures-util = "0.3.5"
|
futures-util = "0.3.12"
|
||||||
futures_codec = "0.4.1"
|
futures_codec = "0.4.1"
|
||||||
getset = "0.1.1"
|
getset = "0.1.1"
|
||||||
git2 = {version = "0.13.11", default_features = false, optional = true}
|
|
||||||
glob = "0.3.0"
|
glob = "0.3.0"
|
||||||
heim = {version = "0.1.0-beta.3", optional = true}
|
|
||||||
htmlescape = "0.3.1"
|
htmlescape = "0.3.1"
|
||||||
ical = "0.6.0"
|
ical = "0.7.0"
|
||||||
ichwh = {version = "0.3.4", optional = true}
|
ichwh = { version = "0.3.4", optional = true }
|
||||||
indexmap = {version = "1.6.0", features = ["serde-1"]}
|
indexmap = { version = "1.6.1", features = ["serde-1"] }
|
||||||
itertools = "0.9.0"
|
itertools = "0.10.0"
|
||||||
log = "0.4.11"
|
lazy_static = "1.*"
|
||||||
|
log = "0.4.14"
|
||||||
meval = "0.2.0"
|
meval = "0.2.0"
|
||||||
num-bigint = {version = "0.3.0", features = ["serde"]}
|
num-bigint = { version = "0.3.1", features = ["serde"] }
|
||||||
num-format = {version = "0.4.0", features = ["with-num-bigint"]}
|
num-format = { version = "0.4.0", features = ["with-num-bigint"] }
|
||||||
num-traits = "0.2.12"
|
num-traits = "0.2.14"
|
||||||
parking_lot = "0.11.0"
|
parking_lot = "0.11.1"
|
||||||
pin-utils = "0.1.0"
|
pin-utils = "0.1.0"
|
||||||
pretty-hex = "0.2.0"
|
pretty-hex = "0.2.1"
|
||||||
ptree = {version = "0.3.0", optional = true}
|
ptree = { version = "0.3.1", optional = true }
|
||||||
query_interface = "0.3.5"
|
query_interface = "0.3.5"
|
||||||
quick-xml = "0.18.1"
|
quick-xml = "0.21.0"
|
||||||
rand = "0.7.3"
|
rand = "0.8.3"
|
||||||
regex = "1.3.9"
|
rayon = "1.5.0"
|
||||||
roxmltree = "0.13.0"
|
regex = "1.4.3"
|
||||||
rust-embed = "5.6.0"
|
roxmltree = "0.14.0"
|
||||||
rustyline = {version = "6.3.0", optional = true}
|
rust-embed = "5.9.0"
|
||||||
serde = {version = "1.0.115", features = ["derive"]}
|
rustyline = { version = "6.3.0", optional = true }
|
||||||
serde-hjson = "0.9.1"
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
serde_bytes = "0.11.5"
|
serde_bytes = "0.11.5"
|
||||||
serde_ini = "0.2.0"
|
serde_ini = "0.2.0"
|
||||||
serde_json = "1.0.57"
|
serde_json = "1.0.61"
|
||||||
serde_urlencoded = "0.7.0"
|
serde_urlencoded = "0.7.0"
|
||||||
serde_yaml = "0.8.13"
|
serde_yaml = "0.8.16"
|
||||||
sha2 = "0.9.1"
|
sha2 = "0.9.3"
|
||||||
shellexpand = "2.0.0"
|
shellexpand = "2.1.0"
|
||||||
strip-ansi-escapes = "0.1.0"
|
strip-ansi-escapes = "0.1.0"
|
||||||
sxd-document = "0.3.2"
|
sxd-document = "0.3.2"
|
||||||
sxd-xpath = "0.4.2"
|
sxd-xpath = "0.4.2"
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.2.0"
|
||||||
term = {version = "0.6.1", optional = true}
|
term = { version = "0.7.0", optional = true }
|
||||||
term_size = "0.3.2"
|
term_size = "0.3.2"
|
||||||
termcolor = "1.1.0"
|
termcolor = "1.1.2"
|
||||||
toml = "0.5.6"
|
titlecase = "1.1.0"
|
||||||
unicode-segmentation = "1.6.0"
|
toml = "0.5.8"
|
||||||
uom = {version = "0.28.0", features = ["f64", "try-from"]}
|
trash = { version = "1.3.0", optional = true }
|
||||||
uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"], optional = true}
|
unicode-segmentation = "1.7.1"
|
||||||
which = {version = "4.0.2", optional = true}
|
|
||||||
zip = {version = "0.5.7", optional = true}
|
|
||||||
|
|
||||||
Inflector = "0.11"
|
|
||||||
clipboard = {version = "0.5.0", optional = true}
|
|
||||||
encoding_rs = "0.8.24"
|
|
||||||
rayon = "1.4.0"
|
|
||||||
trash = {version = "1.1.1", optional = true}
|
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
|
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
|
||||||
|
which = { version = "4.0.2", optional = true }
|
||||||
|
zip = { version = "0.5.9", optional = true }
|
||||||
|
shadow-rs = { version = "0.5", default-features = false, optional = true }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
umask = "1.0.0"
|
umask = "1.0.0"
|
||||||
users = "0.10.0"
|
users = "0.11.0"
|
||||||
|
|
||||||
# TODO this will be possible with new dependency resolver
|
# TODO this will be possible with new dependency resolver
|
||||||
# (currently on nightly behind -Zfeatures=itarget):
|
# (currently on nightly behind -Zfeatures=itarget):
|
||||||
@ -112,18 +116,20 @@ users = "0.10.0"
|
|||||||
[dependencies.rusqlite]
|
[dependencies.rusqlite]
|
||||||
features = ["bundled", "blob"]
|
features = ["bundled", "blob"]
|
||||||
optional = true
|
optional = true
|
||||||
version = "0.24.0"
|
version = "0.24.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
git2 = {version = "0.13.11", optional = true}
|
shadow-rs = "0.5"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = "0.9.2"
|
quickcheck = "1.0.3"
|
||||||
quickcheck_macros = "0.9.1"
|
quickcheck_macros = "1.0.0"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
clipboard-cli = ["clipboard"]
|
default = ["shadow-rs"]
|
||||||
rich-benchmark = ["heim"]
|
clipboard-cli = ["arboard"]
|
||||||
rustyline-support = ["rustyline"]
|
rustyline-support = ["rustyline", "nu-engine/rustyline-support"]
|
||||||
stable = []
|
stable = []
|
||||||
trash-support = ["trash"]
|
trash-support = ["trash"]
|
||||||
|
dirs = ["dirs-next"]
|
||||||
|
directories = ["directories-next"]
|
||||||
|
@ -1,36 +1,3 @@
|
|||||||
use std::path::Path;
|
fn main() -> shadow_rs::SdResult<()> {
|
||||||
use std::{env, fs, io};
|
shadow_rs::new()
|
||||||
|
|
||||||
fn main() -> Result<(), io::Error> {
|
|
||||||
let out_dir = env::var_os("OUT_DIR").expect(
|
|
||||||
"\
|
|
||||||
OUT_DIR environment variable not found. \
|
|
||||||
OUT_DIR is guaranteed to to exist in a build script by cargo - see \
|
|
||||||
https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts\
|
|
||||||
");
|
|
||||||
|
|
||||||
let latest_commit_hash = latest_commit_hash(env::current_dir()?).unwrap_or_default();
|
|
||||||
|
|
||||||
let commit_hash_path = Path::new(&out_dir).join("git_commit_hash");
|
|
||||||
fs::write(commit_hash_path, latest_commit_hash)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn latest_commit_hash<P: AsRef<Path>>(dir: P) -> Result<String, Box<dyn std::error::Error>> {
|
|
||||||
#[cfg(feature = "git2")]
|
|
||||||
{
|
|
||||||
use git2::Repository;
|
|
||||||
let dir = dir.as_ref();
|
|
||||||
Ok(Repository::discover(dir)?
|
|
||||||
.head()?
|
|
||||||
.peel_to_commit()?
|
|
||||||
.id()
|
|
||||||
.to_string())
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "git2"))]
|
|
||||||
{
|
|
||||||
Ok(String::new())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,64 +0,0 @@
|
|||||||
use crate::commands::Command;
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_parser::SignatureRegistry;
|
|
||||||
use nu_protocol::Signature;
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default)]
|
|
||||||
pub struct CommandRegistry {
|
|
||||||
registry: Arc<Mutex<IndexMap<String, Command>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SignatureRegistry for CommandRegistry {
|
|
||||||
fn has(&self, name: &str) -> bool {
|
|
||||||
let registry = self.registry.lock();
|
|
||||||
registry.contains_key(name)
|
|
||||||
}
|
|
||||||
fn get(&self, name: &str) -> Option<Signature> {
|
|
||||||
let registry = self.registry.lock();
|
|
||||||
registry.get(name).map(|command| command.signature())
|
|
||||||
}
|
|
||||||
fn clone_box(&self) -> Box<dyn SignatureRegistry> {
|
|
||||||
Box::new(self.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CommandRegistry {
|
|
||||||
pub fn new() -> CommandRegistry {
|
|
||||||
CommandRegistry {
|
|
||||||
registry: Arc::new(Mutex::new(IndexMap::default())),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CommandRegistry {
|
|
||||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
|
||||||
let registry = self.registry.lock();
|
|
||||||
|
|
||||||
registry.get(name).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn expect_command(&self, name: &str) -> Result<Command, ShellError> {
|
|
||||||
self.get_command(name).ok_or_else(|| {
|
|
||||||
ShellError::untagged_runtime_error(format!("Could not load command: {}", name))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn has(&self, name: &str) -> bool {
|
|
||||||
let registry = self.registry.lock();
|
|
||||||
|
|
||||||
registry.contains_key(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn insert(&mut self, name: impl Into<String>, command: Command) {
|
|
||||||
let mut registry = self.registry.lock();
|
|
||||||
registry.insert(name.into(), command);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn names(&self) -> Vec<String> {
|
|
||||||
let registry = self.registry.lock();
|
|
||||||
registry.keys().cloned().collect()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,351 +0,0 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_data::config;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_parser::SignatureRegistry;
|
|
||||||
use nu_protocol::hir::{ClassifiedCommand, Expression, NamedValue, SpannedExpression, Variable};
|
|
||||||
use nu_protocol::{
|
|
||||||
hir::Block, CommandAction, NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape,
|
|
||||||
UntaggedValue, Value,
|
|
||||||
};
|
|
||||||
use nu_source::Tagged;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
|
|
||||||
pub struct Alias;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct AliasArgs {
|
|
||||||
pub name: Tagged<String>,
|
|
||||||
pub args: Vec<Value>,
|
|
||||||
pub block: Block,
|
|
||||||
pub infer: Option<bool>,
|
|
||||||
pub save: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for Alias {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"alias"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("alias")
|
|
||||||
.required("name", SyntaxShape::String, "the name of the alias")
|
|
||||||
.required("args", SyntaxShape::Table, "the arguments to the alias")
|
|
||||||
.required(
|
|
||||||
"block",
|
|
||||||
SyntaxShape::Block,
|
|
||||||
"the block to run as the body of the alias",
|
|
||||||
)
|
|
||||||
.switch("infer", "infer argument types (experimental)", Some('i'))
|
|
||||||
.switch("save", "save the alias to your config", Some('s'))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Define a shortcut for another command."
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
alias(args, registry).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "An alias without parameters",
|
|
||||||
example: "alias say-hi [] { echo 'Hello!' }",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "An alias with a single parameter",
|
|
||||||
example: "alias l [x] { ls $x }",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn alias(
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
let mut raw_input = args.raw_input.clone();
|
|
||||||
let (
|
|
||||||
AliasArgs {
|
|
||||||
name,
|
|
||||||
args: list,
|
|
||||||
block,
|
|
||||||
infer,
|
|
||||||
save,
|
|
||||||
},
|
|
||||||
_ctx,
|
|
||||||
) = args.process(®istry).await?;
|
|
||||||
let mut processed_args: Vec<String> = vec![];
|
|
||||||
|
|
||||||
if let Some(true) = save {
|
|
||||||
let mut result = nu_data::config::read(name.clone().tag, &None)?;
|
|
||||||
|
|
||||||
// process the alias to remove the --save flag
|
|
||||||
let left_brace = raw_input.find('{').unwrap_or(0);
|
|
||||||
let right_brace = raw_input.rfind('}').unwrap_or_else(|| raw_input.len());
|
|
||||||
let left = raw_input[..left_brace]
|
|
||||||
.replace("--save", "") // TODO using regex (or reconstruct string from AST?)
|
|
||||||
.replace("-si", "-i")
|
|
||||||
.replace("-s ", "")
|
|
||||||
.replace("-is", "-i");
|
|
||||||
let right = raw_input[right_brace..]
|
|
||||||
.replace("--save", "")
|
|
||||||
.replace("-si", "-i")
|
|
||||||
.replace("-s ", "")
|
|
||||||
.replace("-is", "-i");
|
|
||||||
raw_input = format!("{}{}{}", left, &raw_input[left_brace..right_brace], right);
|
|
||||||
|
|
||||||
// create a value from raw_input alias
|
|
||||||
let alias: Value = raw_input.trim().to_string().into();
|
|
||||||
let alias_start = raw_input.find('[').unwrap_or(0); // used to check if the same alias already exists
|
|
||||||
|
|
||||||
// add to startup if alias doesn't exist and replce if it does
|
|
||||||
match result.get_mut("startup") {
|
|
||||||
Some(startup) => {
|
|
||||||
if let UntaggedValue::Table(ref mut commands) = startup.value {
|
|
||||||
if let Some(command) = commands.iter_mut().find(|command| {
|
|
||||||
let cmd_str = command.as_string().unwrap_or_default();
|
|
||||||
cmd_str.starts_with(&raw_input[..alias_start])
|
|
||||||
}) {
|
|
||||||
*command = alias;
|
|
||||||
} else {
|
|
||||||
commands.push(alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
let table = UntaggedValue::table(&[alias]);
|
|
||||||
result.insert("startup".to_string(), table.into_value(Tag::default()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config::write(&result, &None)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
for item in list.iter() {
|
|
||||||
if let Ok(string) = item.as_string() {
|
|
||||||
processed_args.push(format!("${}", string));
|
|
||||||
} else {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
"Expected a string",
|
|
||||||
"expected a string",
|
|
||||||
item.tag(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(true) = infer {
|
|
||||||
Ok(OutputStream::one(ReturnSuccess::action(
|
|
||||||
CommandAction::AddAlias(
|
|
||||||
name.to_string(),
|
|
||||||
to_arg_shapes(processed_args, &block, ®istry)?,
|
|
||||||
block,
|
|
||||||
),
|
|
||||||
)))
|
|
||||||
} else {
|
|
||||||
Ok(OutputStream::one(ReturnSuccess::action(
|
|
||||||
CommandAction::AddAlias(
|
|
||||||
name.to_string(),
|
|
||||||
processed_args
|
|
||||||
.into_iter()
|
|
||||||
.map(|arg| (arg, SyntaxShape::Any))
|
|
||||||
.collect(),
|
|
||||||
block,
|
|
||||||
),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_arg_shapes(
|
|
||||||
args: Vec<String>,
|
|
||||||
block: &Block,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<Vec<(String, SyntaxShape)>, ShellError> {
|
|
||||||
match find_block_shapes(block, registry) {
|
|
||||||
Ok(found) => Ok(args
|
|
||||||
.iter()
|
|
||||||
.map(|arg| {
|
|
||||||
(
|
|
||||||
arg.clone(),
|
|
||||||
match found.get(arg) {
|
|
||||||
None | Some((_, None)) => SyntaxShape::Any,
|
|
||||||
Some((_, Some(shape))) => *shape,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect()),
|
|
||||||
Err(err) => Err(err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ShapeMap = HashMap<String, (Span, Option<SyntaxShape>)>;
|
|
||||||
|
|
||||||
fn check_insert(
|
|
||||||
existing: &mut ShapeMap,
|
|
||||||
to_add: (String, (Span, Option<SyntaxShape>)),
|
|
||||||
) -> Result<(), ShellError> {
|
|
||||||
match (to_add.1).1 {
|
|
||||||
None => match existing.get(&to_add.0) {
|
|
||||||
None => {
|
|
||||||
existing.insert(to_add.0, to_add.1);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
Some(_) => Ok(()),
|
|
||||||
},
|
|
||||||
Some(new) => match existing.insert(to_add.0.clone(), ((to_add.1).0, Some(new))) {
|
|
||||||
None => Ok(()),
|
|
||||||
Some(exist) => match exist.1 {
|
|
||||||
None => Ok(()),
|
|
||||||
Some(shape) => match shape {
|
|
||||||
SyntaxShape::Any => Ok(()),
|
|
||||||
shape if shape == new => Ok(()),
|
|
||||||
_ => Err(ShellError::labeled_error_with_secondary(
|
|
||||||
"Type conflict in alias variable use",
|
|
||||||
format!("{:?}", new),
|
|
||||||
(to_add.1).0,
|
|
||||||
format!("{:?}", shape),
|
|
||||||
exist.0,
|
|
||||||
)),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn check_merge(existing: &mut ShapeMap, new: &ShapeMap) -> Result<(), ShellError> {
|
|
||||||
for (k, v) in new.iter() {
|
|
||||||
check_insert(existing, (k.clone(), *v))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_expr_shapes(
|
|
||||||
spanned_expr: &SpannedExpression,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<ShapeMap, ShellError> {
|
|
||||||
match &spanned_expr.expr {
|
|
||||||
// TODO range will need similar if/when invocations can be parsed within range expression
|
|
||||||
Expression::Binary(bin) => find_expr_shapes(&bin.left, registry).and_then(|mut left| {
|
|
||||||
find_expr_shapes(&bin.right, registry)
|
|
||||||
.and_then(|right| check_merge(&mut left, &right).map(|()| left))
|
|
||||||
}),
|
|
||||||
Expression::Block(b) => find_block_shapes(&b, registry),
|
|
||||||
Expression::Path(path) => match &path.head.expr {
|
|
||||||
Expression::Invocation(b) => find_block_shapes(&b, registry),
|
|
||||||
Expression::Variable(Variable::Other(var, _)) => {
|
|
||||||
let mut result = HashMap::new();
|
|
||||||
result.insert(var.to_string(), (spanned_expr.span, None));
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
_ => Ok(HashMap::new()),
|
|
||||||
},
|
|
||||||
_ => Ok(HashMap::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_block_shapes(block: &Block, registry: &CommandRegistry) -> Result<ShapeMap, ShellError> {
|
|
||||||
let apply_shape = |found: ShapeMap, sig_shape: SyntaxShape| -> ShapeMap {
|
|
||||||
found
|
|
||||||
.iter()
|
|
||||||
.map(|(v, sh)| match sh.1 {
|
|
||||||
None => (v.clone(), (sh.0, Some(sig_shape))),
|
|
||||||
Some(shape) => (v.clone(), (sh.0, Some(shape))),
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut arg_shapes = HashMap::new();
|
|
||||||
for pipeline in &block.block {
|
|
||||||
for classified in &pipeline.list {
|
|
||||||
match classified {
|
|
||||||
ClassifiedCommand::Expr(spanned_expr) => {
|
|
||||||
let found = find_expr_shapes(&spanned_expr, registry)?;
|
|
||||||
check_merge(&mut arg_shapes, &found)?
|
|
||||||
}
|
|
||||||
ClassifiedCommand::Internal(internal) => {
|
|
||||||
if let Some(signature) = registry.get(&internal.name) {
|
|
||||||
if let Some(positional) = &internal.args.positional {
|
|
||||||
for (i, spanned_expr) in positional.iter().enumerate() {
|
|
||||||
let found = find_expr_shapes(&spanned_expr, registry)?;
|
|
||||||
if i >= signature.positional.len() {
|
|
||||||
if let Some((sig_shape, _)) = &signature.rest_positional {
|
|
||||||
check_merge(
|
|
||||||
&mut arg_shapes,
|
|
||||||
&apply_shape(found, *sig_shape),
|
|
||||||
)?;
|
|
||||||
} else {
|
|
||||||
unreachable!("should have error'd in parsing");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let (pos_type, _) = &signature.positional[i];
|
|
||||||
match pos_type {
|
|
||||||
// TODO pass on mandatory/optional?
|
|
||||||
PositionalType::Mandatory(_, sig_shape)
|
|
||||||
| PositionalType::Optional(_, sig_shape) => {
|
|
||||||
check_merge(
|
|
||||||
&mut arg_shapes,
|
|
||||||
&apply_shape(found, *sig_shape),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(named) = &internal.args.named {
|
|
||||||
for (name, val) in named.iter() {
|
|
||||||
if let NamedValue::Value(_, spanned_expr) = val {
|
|
||||||
let found = find_expr_shapes(&spanned_expr, registry)?;
|
|
||||||
match signature.named.get(name) {
|
|
||||||
None => {
|
|
||||||
unreachable!("should have error'd in parsing");
|
|
||||||
}
|
|
||||||
Some((named_type, _)) => {
|
|
||||||
if let NamedType::Mandatory(_, sig_shape)
|
|
||||||
| NamedType::Optional(_, sig_shape) = named_type
|
|
||||||
{
|
|
||||||
check_merge(
|
|
||||||
&mut arg_shapes,
|
|
||||||
&apply_shape(found, *sig_shape),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!("registry has lost name it provided");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ClassifiedCommand::Dynamic(_) | ClassifiedCommand::Error(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(arg_shapes)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::Alias;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(Alias {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,146 +0,0 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use ansi_term::Color;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
|
|
||||||
pub struct Ansi;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct AnsiArgs {
|
|
||||||
color: Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for Ansi {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"ansi"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("ansi").required(
|
|
||||||
"color",
|
|
||||||
SyntaxShape::Any,
|
|
||||||
"the name of the color to use or 'reset' to reset the color",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Output ANSI codes to change color"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Change color to green",
|
|
||||||
example: r#"ansi green"#,
|
|
||||||
result: Some(vec![Value::from("\u{1b}[32m")]),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Reset the color",
|
|
||||||
example: r#"ansi reset"#,
|
|
||||||
result: Some(vec![Value::from("\u{1b}[0m")]),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description:
|
|
||||||
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
|
|
||||||
example: r#"echo [$(ansi rb) Hello " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#,
|
|
||||||
result: Some(vec![Value::from(
|
|
||||||
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld",
|
|
||||||
)]),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let (AnsiArgs { color }, _) = args.process(®istry).await?;
|
|
||||||
|
|
||||||
let color_string = color.as_string()?;
|
|
||||||
|
|
||||||
let ansi_code = str_to_ansi_color(color_string);
|
|
||||||
|
|
||||||
if let Some(output) = ansi_code {
|
|
||||||
Ok(OutputStream::one(ReturnSuccess::value(
|
|
||||||
UntaggedValue::string(output).into_value(color.tag()),
|
|
||||||
)))
|
|
||||||
} else {
|
|
||||||
Err(ShellError::labeled_error(
|
|
||||||
"Unknown color",
|
|
||||||
"unknown color",
|
|
||||||
color.tag(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn str_to_ansi_color(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()),
|
|
||||||
"gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()),
|
|
||||||
"gi" | "green_italic" => Some(Color::Green.italic().prefix().to_string()),
|
|
||||||
"gd" | "green_dimmed" => Some(Color::Green.dimmed().prefix().to_string()),
|
|
||||||
"gr" | "green_reverse" => Some(Color::Green.reverse().prefix().to_string()),
|
|
||||||
"r" | "red" => Some(Color::Red.prefix().to_string()),
|
|
||||||
"rb" | "red_bold" => Some(Color::Red.bold().prefix().to_string()),
|
|
||||||
"ru" | "red_underline" => Some(Color::Red.underline().prefix().to_string()),
|
|
||||||
"ri" | "red_italic" => Some(Color::Red.italic().prefix().to_string()),
|
|
||||||
"rd" | "red_dimmed" => Some(Color::Red.dimmed().prefix().to_string()),
|
|
||||||
"rr" | "red_reverse" => Some(Color::Red.reverse().prefix().to_string()),
|
|
||||||
"u" | "blue" => Some(Color::Blue.prefix().to_string()),
|
|
||||||
"ub" | "blue_bold" => Some(Color::Blue.bold().prefix().to_string()),
|
|
||||||
"uu" | "blue_underline" => Some(Color::Blue.underline().prefix().to_string()),
|
|
||||||
"ui" | "blue_italic" => Some(Color::Blue.italic().prefix().to_string()),
|
|
||||||
"ud" | "blue_dimmed" => Some(Color::Blue.dimmed().prefix().to_string()),
|
|
||||||
"ur" | "blue_reverse" => Some(Color::Blue.reverse().prefix().to_string()),
|
|
||||||
"b" | "black" => Some(Color::Black.prefix().to_string()),
|
|
||||||
"bb" | "black_bold" => Some(Color::Black.bold().prefix().to_string()),
|
|
||||||
"bu" | "black_underline" => Some(Color::Black.underline().prefix().to_string()),
|
|
||||||
"bi" | "black_italic" => Some(Color::Black.italic().prefix().to_string()),
|
|
||||||
"bd" | "black_dimmed" => Some(Color::Black.dimmed().prefix().to_string()),
|
|
||||||
"br" | "black_reverse" => Some(Color::Black.reverse().prefix().to_string()),
|
|
||||||
"y" | "yellow" => Some(Color::Yellow.prefix().to_string()),
|
|
||||||
"yb" | "yellow_bold" => Some(Color::Yellow.bold().prefix().to_string()),
|
|
||||||
"yu" | "yellow_underline" => Some(Color::Yellow.underline().prefix().to_string()),
|
|
||||||
"yi" | "yellow_italic" => Some(Color::Yellow.italic().prefix().to_string()),
|
|
||||||
"yd" | "yellow_dimmed" => Some(Color::Yellow.dimmed().prefix().to_string()),
|
|
||||||
"yr" | "yellow_reverse" => Some(Color::Yellow.reverse().prefix().to_string()),
|
|
||||||
"p" | "purple" => Some(Color::Purple.prefix().to_string()),
|
|
||||||
"pb" | "purple_bold" => Some(Color::Purple.bold().prefix().to_string()),
|
|
||||||
"pu" | "purple_underline" => Some(Color::Purple.underline().prefix().to_string()),
|
|
||||||
"pi" | "purple_italic" => Some(Color::Purple.italic().prefix().to_string()),
|
|
||||||
"pd" | "purple_dimmed" => Some(Color::Purple.dimmed().prefix().to_string()),
|
|
||||||
"pr" | "purple_reverse" => Some(Color::Purple.reverse().prefix().to_string()),
|
|
||||||
"c" | "cyan" => Some(Color::Cyan.prefix().to_string()),
|
|
||||||
"cb" | "cyan_bold" => Some(Color::Cyan.bold().prefix().to_string()),
|
|
||||||
"cu" | "cyan_underline" => Some(Color::Cyan.underline().prefix().to_string()),
|
|
||||||
"ci" | "cyan_italic" => Some(Color::Cyan.italic().prefix().to_string()),
|
|
||||||
"cd" | "cyan_dimmed" => Some(Color::Cyan.dimmed().prefix().to_string()),
|
|
||||||
"cr" | "cyan_reverse" => Some(Color::Cyan.reverse().prefix().to_string()),
|
|
||||||
"w" | "white" => Some(Color::White.prefix().to_string()),
|
|
||||||
"wb" | "white_bold" => Some(Color::White.bold().prefix().to_string()),
|
|
||||||
"wu" | "white_underline" => Some(Color::White.underline().prefix().to_string()),
|
|
||||||
"wi" | "white_italic" => Some(Color::White.italic().prefix().to_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()),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::Ansi;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(Ansi {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Arguments {
|
|
||||||
row: Value,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Command;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for Command {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"append"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("append").required(
|
|
||||||
"row value",
|
|
||||||
SyntaxShape::Any,
|
|
||||||
"the value of the row to append to the table",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Append the given row to the table"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let (Arguments { mut row }, input) = args.process(registry).await?;
|
|
||||||
|
|
||||||
let input: Vec<Value> = input.collect().await;
|
|
||||||
|
|
||||||
if let Some(first) = input.get(0) {
|
|
||||||
row.tag = first.tag();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(
|
|
||||||
futures::stream::iter(input.into_iter().chain(vec![row]).map(ReturnSuccess::value))
|
|
||||||
.to_output_stream(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Add something to the end of a list or table",
|
|
||||||
example: "echo [1 2 3] | append 4",
|
|
||||||
result: Some(vec![
|
|
||||||
UntaggedValue::int(1).into(),
|
|
||||||
UntaggedValue::int(2).into(),
|
|
||||||
UntaggedValue::int(3).into(),
|
|
||||||
UntaggedValue::int(4).into(),
|
|
||||||
]),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,141 +0,0 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
use nu_source::Tagged;
|
|
||||||
|
|
||||||
pub struct Char;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct CharArgs {
|
|
||||||
name: Tagged<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for Char {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"char"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("ansi").required(
|
|
||||||
"character",
|
|
||||||
SyntaxShape::Any,
|
|
||||||
"the name of the character to output",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Output special characters (eg. 'newline')"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Output newline",
|
|
||||||
example: r#"char newline"#,
|
|
||||||
result: Some(vec![Value::from("\n")]),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Output prompt character, newline and a hamburger character",
|
|
||||||
example: r#"echo $(char prompt) $(char newline) $(char hamburger)"#,
|
|
||||||
result: Some(vec![
|
|
||||||
UntaggedValue::string("\u{25b6}").into(),
|
|
||||||
UntaggedValue::string("\n").into(),
|
|
||||||
UntaggedValue::string("\u{2261}").into(),
|
|
||||||
]),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let (CharArgs { name }, _) = args.process(®istry).await?;
|
|
||||||
|
|
||||||
let special_character = str_to_character(&name.item);
|
|
||||||
|
|
||||||
if let Some(output) = special_character {
|
|
||||||
Ok(OutputStream::one(ReturnSuccess::value(
|
|
||||||
UntaggedValue::string(output).into_value(name.tag()),
|
|
||||||
)))
|
|
||||||
} else {
|
|
||||||
Err(ShellError::labeled_error(
|
|
||||||
"Unknown character",
|
|
||||||
"unknown character",
|
|
||||||
name.tag(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn str_to_character(s: &str) -> Option<String> {
|
|
||||||
match s {
|
|
||||||
"newline" | "enter" | "nl" => Some("\n".into()),
|
|
||||||
"tab" => Some("\t".into()),
|
|
||||||
"sp" | "space" => Some(" ".into()),
|
|
||||||
// Unicode names came from https://www.compart.com/en/unicode
|
|
||||||
// Private Use Area (U+E000-U+F8FF)
|
|
||||||
"branch" => Some('\u{e0a0}'.to_string()), //
|
|
||||||
"segment" => Some('\u{e0b0}'.to_string()), //
|
|
||||||
|
|
||||||
"identical_to" | "hamburger" => Some('\u{2261}'.to_string()), // ≡
|
|
||||||
"not_identical_to" | "branch_untracked" => Some('\u{2262}'.to_string()), // ≢
|
|
||||||
"strictly_equivalent_to" | "branch_identical" => Some('\u{2263}'.to_string()), // ≣
|
|
||||||
|
|
||||||
"upwards_arrow" | "branch_ahead" => Some('\u{2191}'.to_string()), // ↑
|
|
||||||
"downwards_arrow" | "branch_behind" => Some('\u{2193}'.to_string()), // ↓
|
|
||||||
"up_down_arrow" | "branch_ahead_behind" => Some('\u{2195}'.to_string()), // ↕
|
|
||||||
|
|
||||||
"black_right_pointing_triangle" | "prompt" => Some('\u{25b6}'.to_string()), // ▶
|
|
||||||
"vector_or_cross_product" | "failed" => Some('\u{2a2f}'.to_string()), // ⨯
|
|
||||||
"high_voltage_sign" | "elevated" => Some('\u{26a1}'.to_string()), // ⚡
|
|
||||||
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
|
|
||||||
"hash" | "hashtag" | "pound_sign" | "sharp" | "root" => Some("#".into()), // #
|
|
||||||
|
|
||||||
// Weather symbols
|
|
||||||
"sun" => Some("\x1b[33;1m\u{2600}\x1b[0m".to_string()), // Yellow Bold ☀
|
|
||||||
"moon" => Some("\x1b[36m\u{263d}\x1b[0m".to_string()), // Cyan ☽
|
|
||||||
"clouds" => Some("\x1b[37;1m\u{2601}\x1b[0m".to_string()), // White Bold ☁
|
|
||||||
"rain" => Some("\x1b[37;1m\u{2614}\x1b[0m".to_string()), // White Bold ☔
|
|
||||||
"fog" => Some("\x1b[37;1m\u{2592}\x1b[0m".to_string()), // White Bold ▒
|
|
||||||
"mist" => Some("\x1b[34m\u{2591}\x1b[0m".to_string()), // Blue ░
|
|
||||||
"haze" => Some("\x1b[33m\u{2591}\x1b[0m".to_string()), // Yellow ░
|
|
||||||
"snow" => Some("\x1b[37;1m\u{2744}\x1b[0m".to_string()), // White Bold ❄
|
|
||||||
"thunderstorm" => Some("\x1b[33;1m\u{26a1}\x1b[0m".to_string()), // Yellow Bold ⚡
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::Char;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(Char {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
use crate::commands::classified::expr::run_expression_block;
|
|
||||||
use crate::commands::classified::internal::run_internal_command;
|
|
||||||
use crate::evaluation_context::EvaluationContext;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use crate::stream::InputStream;
|
|
||||||
use futures::stream::TryStreamExt;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::hir::{Block, ClassifiedCommand, Commands};
|
|
||||||
use nu_protocol::{ReturnSuccess, Scope, UntaggedValue, Value};
|
|
||||||
use std::sync::atomic::Ordering;
|
|
||||||
|
|
||||||
pub(crate) async fn run_block(
|
|
||||||
block: &Block,
|
|
||||||
ctx: &mut EvaluationContext,
|
|
||||||
mut input: InputStream,
|
|
||||||
scope: Arc<Scope>,
|
|
||||||
) -> Result<InputStream, ShellError> {
|
|
||||||
let mut output: Result<InputStream, ShellError> = Ok(InputStream::empty());
|
|
||||||
for pipeline in &block.block {
|
|
||||||
match output {
|
|
||||||
Ok(inp) if inp.is_empty() => {}
|
|
||||||
Ok(inp) => {
|
|
||||||
let mut output_stream = inp.to_output_stream();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match output_stream.try_next().await {
|
|
||||||
Ok(Some(ReturnSuccess::Value(Value {
|
|
||||||
value: UntaggedValue::Error(e),
|
|
||||||
..
|
|
||||||
}))) => return Err(e),
|
|
||||||
Ok(Some(_item)) => {
|
|
||||||
if let Some(err) = ctx.get_errors().get(0) {
|
|
||||||
ctx.clear_errors();
|
|
||||||
return Err(err.clone());
|
|
||||||
}
|
|
||||||
if ctx.ctrl_c.load(Ordering::SeqCst) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(None) => {
|
|
||||||
if let Some(err) = ctx.get_errors().get(0) {
|
|
||||||
ctx.clear_errors();
|
|
||||||
return Err(err.clone());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
Err(e) => return Err(e),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
output = run_pipeline(pipeline, ctx, input, scope.clone()).await;
|
|
||||||
|
|
||||||
input = InputStream::empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run_pipeline(
|
|
||||||
commands: &Commands,
|
|
||||||
ctx: &mut EvaluationContext,
|
|
||||||
mut input: InputStream,
|
|
||||||
scope: Arc<Scope>,
|
|
||||||
) -> Result<InputStream, ShellError> {
|
|
||||||
for item in commands.list.clone() {
|
|
||||||
input = match item {
|
|
||||||
ClassifiedCommand::Dynamic(_) => {
|
|
||||||
return Err(ShellError::unimplemented("Dynamic commands"))
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassifiedCommand::Expr(expr) => {
|
|
||||||
run_expression_block(*expr, ctx, scope.clone()).await?
|
|
||||||
}
|
|
||||||
|
|
||||||
ClassifiedCommand::Error(err) => return Err(err.into()),
|
|
||||||
|
|
||||||
ClassifiedCommand::Internal(left) => {
|
|
||||||
run_internal_command(left, ctx, input, scope.clone()).await?
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(input)
|
|
||||||
}
|
|
@ -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,340 +0,0 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::help::get_help;
|
|
||||||
use crate::deserializer::ConfigDeserializer;
|
|
||||||
use crate::evaluate::evaluate_args::evaluate_args;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use derive_new::new;
|
|
||||||
use getset::Getters;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::hir;
|
|
||||||
use nu_protocol::{CallInfo, EvaluatedArgs, ReturnSuccess, Scope, Signature, UntaggedValue, Value};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::ops::Deref;
|
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct UnevaluatedCallInfo {
|
|
||||||
pub args: hir::Call,
|
|
||||||
pub name_tag: Tag,
|
|
||||||
pub scope: Arc<Scope>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UnevaluatedCallInfo {
|
|
||||||
pub async fn evaluate(self, registry: &CommandRegistry) -> Result<CallInfo, ShellError> {
|
|
||||||
let args = evaluate_args(&self.args, registry, self.scope.clone()).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 input: InputStream,
|
|
||||||
pub raw_input: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 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,
|
|
||||||
input: input.into(),
|
|
||||||
raw_input: String::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<EvaluatedWholeStreamCommandArgs, ShellError> {
|
|
||||||
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(registry).await?;
|
|
||||||
|
|
||||||
Ok(EvaluatedWholeStreamCommandArgs::new(
|
|
||||||
host,
|
|
||||||
ctrl_c,
|
|
||||||
shell_manager,
|
|
||||||
call_info,
|
|
||||||
input,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn evaluate_once_with_scope(
|
|
||||||
self,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
scope: Arc<Scope>,
|
|
||||||
) -> Result<EvaluatedWholeStreamCommandArgs, ShellError> {
|
|
||||||
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 = UnevaluatedCallInfo {
|
|
||||||
name_tag: self.call_info.name_tag,
|
|
||||||
args: self.call_info.args,
|
|
||||||
scope: scope.clone(),
|
|
||||||
};
|
|
||||||
let call_info = call_info.evaluate(registry).await?;
|
|
||||||
|
|
||||||
Ok(EvaluatedWholeStreamCommandArgs::new(
|
|
||||||
host,
|
|
||||||
ctrl_c,
|
|
||||||
shell_manager,
|
|
||||||
call_info,
|
|
||||||
input,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn process<'de, T: Deserialize<'de>>(
|
|
||||||
self,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<(T, InputStream), ShellError> {
|
|
||||||
let args = self.evaluate_once(registry).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 registry: CommandRegistry,
|
|
||||||
pub name: Tag,
|
|
||||||
pub raw_input: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RunnableContext {
|
|
||||||
pub fn get_command(&self, name: &str) -> Option<Command> {
|
|
||||||
self.registry.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>,
|
|
||||||
) -> EvaluatedWholeStreamCommandArgs {
|
|
||||||
EvaluatedWholeStreamCommandArgs {
|
|
||||||
args: EvaluatedCommandArgs {
|
|
||||||
host,
|
|
||||||
ctrl_c,
|
|
||||||
shell_manager,
|
|
||||||
call_info,
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
if args.call_info.switch_present("help") {
|
|
||||||
let cl = self.0.clone();
|
|
||||||
let registry = registry.clone();
|
|
||||||
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
|
||||||
UntaggedValue::string(get_help(&*cl, ®istry)).into_value(Tag::unknown()),
|
|
||||||
))))
|
|
||||||
} else {
|
|
||||||
self.0.run(args, registry).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,76 +0,0 @@
|
|||||||
use crate::prelude::*;
|
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
|
|
||||||
use crate::commands::date::utils::{date_to_value, date_to_value_raw};
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
|
||||||
use nu_source::Tagged;
|
|
||||||
|
|
||||||
pub struct Date;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct FormatArgs {
|
|
||||||
format: Tagged<String>,
|
|
||||||
raw: Option<bool>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for Date {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"date format"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("date format")
|
|
||||||
.required("format", SyntaxShape::String, "strftime format")
|
|
||||||
.switch("raw", "print date without tables", Some('r'))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"format the current date using the given format string."
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
format(args, registry).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn format(
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
let (FormatArgs { format, raw }, _) = args.process(®istry).await?;
|
|
||||||
|
|
||||||
let dt_fmt = format.to_string();
|
|
||||||
|
|
||||||
let value = {
|
|
||||||
let local: DateTime<Local> = Local::now();
|
|
||||||
if let Some(true) = raw {
|
|
||||||
UntaggedValue::string(date_to_value_raw(local, dt_fmt)).into_untagged_value()
|
|
||||||
} else {
|
|
||||||
date_to_value(local, tag, dt_fmt)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(OutputStream::one(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::Date;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(Date {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
pub mod command;
|
|
||||||
pub mod format;
|
|
||||||
pub mod now;
|
|
||||||
pub mod utc;
|
|
||||||
|
|
||||||
mod utils;
|
|
||||||
|
|
||||||
pub use command::Command as Date;
|
|
||||||
pub use format::Date as DateFormat;
|
|
||||||
pub use now::Date as DateNow;
|
|
||||||
pub use utc::Date as DateUTC;
|
|
@ -1,63 +0,0 @@
|
|||||||
use crate::prelude::*;
|
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
|
|
||||||
use crate::commands::date::utils::date_to_value;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use nu_protocol::Signature;
|
|
||||||
|
|
||||||
pub struct Date;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for Date {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"date now"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("date now")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"return the current date."
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
now(args, registry).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn now(
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
let args = args.evaluate_once(®istry).await?;
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
|
|
||||||
let no_fmt = "".to_string();
|
|
||||||
|
|
||||||
let value = {
|
|
||||||
let local: DateTime<Local> = Local::now();
|
|
||||||
date_to_value(local, tag, no_fmt)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(OutputStream::one(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::Date;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(Date {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
use crate::prelude::*;
|
|
||||||
use chrono::DateTime;
|
|
||||||
use nu_protocol::{Dictionary, Value};
|
|
||||||
|
|
||||||
use chrono::{Datelike, TimeZone, Timelike};
|
|
||||||
use core::fmt::Display;
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use nu_protocol::UntaggedValue;
|
|
||||||
|
|
||||||
pub fn date_to_value_raw<T: TimeZone>(dt: DateTime<T>, dt_format: String) -> String
|
|
||||||
where
|
|
||||||
T::Offset: Display,
|
|
||||||
{
|
|
||||||
let result = dt.format(&dt_format);
|
|
||||||
format!("{}", result)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn date_to_value<T: TimeZone>(dt: DateTime<T>, tag: Tag, dt_format: String) -> Value
|
|
||||||
where
|
|
||||||
T::Offset: Display,
|
|
||||||
{
|
|
||||||
let mut indexmap = IndexMap::new();
|
|
||||||
|
|
||||||
if dt_format.is_empty() {
|
|
||||||
indexmap.insert(
|
|
||||||
"year".to_string(),
|
|
||||||
UntaggedValue::int(dt.year()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"month".to_string(),
|
|
||||||
UntaggedValue::int(dt.month()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"day".to_string(),
|
|
||||||
UntaggedValue::int(dt.day()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"hour".to_string(),
|
|
||||||
UntaggedValue::int(dt.hour()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"minute".to_string(),
|
|
||||||
UntaggedValue::int(dt.minute()).into_value(&tag),
|
|
||||||
);
|
|
||||||
indexmap.insert(
|
|
||||||
"second".to_string(),
|
|
||||||
UntaggedValue::int(dt.second()).into_value(&tag),
|
|
||||||
);
|
|
||||||
|
|
||||||
let tz = dt.offset();
|
|
||||||
indexmap.insert(
|
|
||||||
"timezone".to_string(),
|
|
||||||
UntaggedValue::string(format!("{}", tz)).into_value(&tag),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let result = dt.format(&dt_format);
|
|
||||||
indexmap.insert(
|
|
||||||
"formatted".to_string(),
|
|
||||||
UntaggedValue::string(format!("{}", result)).into_value(&tag),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag)
|
|
||||||
}
|
|
@ -1,90 +0,0 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_data::config::{Conf, NuConfig};
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::{BufRead, BufReader};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
const DEFAULT_LOCATION: &str = "history.txt";
|
|
||||||
|
|
||||||
pub fn history_path(config: &dyn Conf) -> PathBuf {
|
|
||||||
let default_path = nu_data::config::user_data()
|
|
||||||
.map(|mut p| {
|
|
||||||
p.push(DEFAULT_LOCATION);
|
|
||||||
p
|
|
||||||
})
|
|
||||||
.unwrap_or_else(|_| PathBuf::from(DEFAULT_LOCATION));
|
|
||||||
|
|
||||||
config
|
|
||||||
.var("history-path")
|
|
||||||
.map_or(default_path.clone(), |custom_path| {
|
|
||||||
match custom_path.as_string() {
|
|
||||||
Ok(path) => PathBuf::from(path),
|
|
||||||
Err(_) => default_path,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct History;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for History {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"history"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("history")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Display command history."
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
history(args, registry)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn history(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
|
||||||
let config: Box<dyn Conf> = Box::new(NuConfig::new());
|
|
||||||
let tag = args.call_info.name_tag;
|
|
||||||
let path = history_path(&config);
|
|
||||||
let file = File::open(path);
|
|
||||||
if let Ok(file) = file {
|
|
||||||
let reader = BufReader::new(file);
|
|
||||||
let output = reader.lines().filter_map(move |line| match line {
|
|
||||||
Ok(line) => Some(ReturnSuccess::value(
|
|
||||||
UntaggedValue::string(line).into_value(tag.clone()),
|
|
||||||
)),
|
|
||||||
Err(_) => None,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(futures::stream::iter(output).to_output_stream())
|
|
||||||
} else {
|
|
||||||
Err(ShellError::labeled_error(
|
|
||||||
"Could not open history",
|
|
||||||
"history file could not be opened",
|
|
||||||
tag,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::History;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(History {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,185 +0,0 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::classified::block::run_block;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::evaluate::evaluate_baseline_expr;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{
|
|
||||||
hir::Block, hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub struct If;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct IfArgs {
|
|
||||||
condition: Block,
|
|
||||||
then_case: Block,
|
|
||||||
else_case: Block,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for If {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"if"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("if")
|
|
||||||
.required(
|
|
||||||
"condition",
|
|
||||||
SyntaxShape::Math,
|
|
||||||
"the condition that must match",
|
|
||||||
)
|
|
||||||
.required(
|
|
||||||
"then_case",
|
|
||||||
SyntaxShape::Block,
|
|
||||||
"block to run if condition is true",
|
|
||||||
)
|
|
||||||
.required(
|
|
||||||
"else_case",
|
|
||||||
SyntaxShape::Block,
|
|
||||||
"block to run if condition is false",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Run blocks if a condition is true or false."
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
if_command(args, registry).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Run a block if a condition is true",
|
|
||||||
example: "echo 10 | if $it > 5 { echo 'greater than 5' } { echo 'less than or equal to 5' }",
|
|
||||||
result: Some(vec![UntaggedValue::string("greater than 5").into()]),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Run a block if a condition is false",
|
|
||||||
example: "echo 1 | if $it > 5 { echo 'greater than 5' } { echo 'less than or equal to 5' }",
|
|
||||||
result: Some(vec![UntaggedValue::string("less than or equal to 5").into()]),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
async fn if_command(
|
|
||||||
raw_args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = Arc::new(registry.clone());
|
|
||||||
let scope = raw_args.call_info.scope.clone();
|
|
||||||
let tag = raw_args.call_info.name_tag.clone();
|
|
||||||
let context = Arc::new(EvaluationContext::from_raw(&raw_args, ®istry));
|
|
||||||
|
|
||||||
let (
|
|
||||||
IfArgs {
|
|
||||||
condition,
|
|
||||||
then_case,
|
|
||||||
else_case,
|
|
||||||
},
|
|
||||||
input,
|
|
||||||
) = raw_args.process(®istry).await?;
|
|
||||||
let condition = {
|
|
||||||
if condition.block.len() != 1 {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
"Expected a condition",
|
|
||||||
"expected a condition",
|
|
||||||
tag,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
match condition.block[0].list.get(0) {
|
|
||||||
Some(item) => match item {
|
|
||||||
ClassifiedCommand::Expr(expr) => expr.clone(),
|
|
||||||
_ => {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
"Expected a condition",
|
|
||||||
"expected a condition",
|
|
||||||
tag,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
None => {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
"Expected a condition",
|
|
||||||
"expected a condition",
|
|
||||||
tag,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(input
|
|
||||||
.then(move |input| {
|
|
||||||
let condition = condition.clone();
|
|
||||||
let then_case = then_case.clone();
|
|
||||||
let else_case = else_case.clone();
|
|
||||||
let registry = registry.clone();
|
|
||||||
let scope = Scope::append_it(scope.clone(), input);
|
|
||||||
let mut context = context.clone();
|
|
||||||
|
|
||||||
async move {
|
|
||||||
//FIXME: should we use the scope that's brought in as well?
|
|
||||||
let condition = evaluate_baseline_expr(&condition, &*registry, scope.clone()).await;
|
|
||||||
|
|
||||||
match condition {
|
|
||||||
Ok(condition) => match condition.as_bool() {
|
|
||||||
Ok(b) => {
|
|
||||||
if b {
|
|
||||||
match run_block(
|
|
||||||
&then_case,
|
|
||||||
Arc::make_mut(&mut context),
|
|
||||||
InputStream::empty(),
|
|
||||||
scope,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(stream) => stream.to_output_stream(),
|
|
||||||
Err(e) => futures::stream::iter(vec![Err(e)].into_iter())
|
|
||||||
.to_output_stream(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
match run_block(
|
|
||||||
&else_case,
|
|
||||||
Arc::make_mut(&mut context),
|
|
||||||
InputStream::empty(),
|
|
||||||
scope,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
|
||||||
Ok(stream) => stream.to_output_stream(),
|
|
||||||
Err(e) => futures::stream::iter(vec![Err(e)].into_iter())
|
|
||||||
.to_output_stream(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
futures::stream::iter(vec![Err(e)].into_iter()).to_output_stream()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => futures::stream::iter(vec![Err(e)].into_iter()).to_output_stream(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
.to_output_stream())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::If;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(If {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
use nu_source::Tagged;
|
|
||||||
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct SubCommandArgs {
|
|
||||||
expression: Option<Tagged<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"math eval"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Evaluate a math expression into a number"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("math eval").desc(self.usage()).optional(
|
|
||||||
"math expression",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the math expression to evaluate",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
eval(args, registry).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Evalulate math in the pipeline",
|
|
||||||
example: "echo '10 / 4' | math eval",
|
|
||||||
result: Some(vec![UntaggedValue::decimal_from_float(
|
|
||||||
2.5,
|
|
||||||
Span::unknown(),
|
|
||||||
)
|
|
||||||
.into()]),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn eval(
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name = args.call_info.name_tag.span;
|
|
||||||
let (SubCommandArgs { expression }, input) = args.process(registry).await?;
|
|
||||||
|
|
||||||
Ok(input
|
|
||||||
.map(move |x| {
|
|
||||||
if let Some(Tagged {
|
|
||||||
tag,
|
|
||||||
item: expression,
|
|
||||||
}) = &expression
|
|
||||||
{
|
|
||||||
UntaggedValue::string(expression).into_value(tag)
|
|
||||||
} else {
|
|
||||||
x
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.map(move |input| {
|
|
||||||
if let Ok(string) = input.as_string() {
|
|
||||||
match parse(&string, &input.tag) {
|
|
||||||
Ok(value) => ReturnSuccess::value(value),
|
|
||||||
Err(err) => Err(ShellError::labeled_error(
|
|
||||||
"Math evaluation error",
|
|
||||||
err,
|
|
||||||
&input.tag.span,
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(ShellError::labeled_error(
|
|
||||||
"Expected a string from pipeline",
|
|
||||||
"requires string input",
|
|
||||||
name,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.to_output_stream())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse<T: Into<Tag>>(math_expression: &str, tag: T) -> Result<Value, String> {
|
|
||||||
match meval::eval_str(math_expression) {
|
|
||||||
Ok(num) if num.is_infinite() || num.is_nan() => Err("cannot represent result".to_string()),
|
|
||||||
Ok(num) => Ok(UntaggedValue::from(Primitive::from(num)).into_value(tag)),
|
|
||||||
Err(error) => Err(error.to_string().to_lowercase()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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,47 +0,0 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Command;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for Command {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"move"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("move")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Moves across desired subcommand."
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
_args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
|
||||||
UntaggedValue::string(crate::commands::help::get_help(&Command, ®istry))
|
|
||||||
.into_value(Tag::unknown()),
|
|
||||||
))))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::Command;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(Command {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
use super::{operate, DefaultArguments};
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub struct PathBasename;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for PathBasename {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"path basename"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("path basename")
|
|
||||||
.rest(SyntaxShape::ColumnPath, "optionally operate by path")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"gets the filename of a path"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
let (DefaultArguments { rest }, input) = args.process(®istry).await?;
|
|
||||||
operate(input, rest, &action, tag.span).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Get basename of a path",
|
|
||||||
example: "echo '/home/joe/test.txt' | path basename",
|
|
||||||
result: Some(vec![Value::from("test.txt")]),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(path: &Path) -> UntaggedValue {
|
|
||||||
UntaggedValue::string(match path.file_name() {
|
|
||||||
Some(filename) => filename.to_string_lossy().to_string(),
|
|
||||||
_ => "".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::PathBasename;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(PathBasename {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
use super::{operate, DefaultArguments};
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub struct PathDirname;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for PathDirname {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"path dirname"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("path dirname").rest(SyntaxShape::ColumnPath, "optionally operate by path")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"gets the dirname of a path"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
let (DefaultArguments { rest }, input) = args.process(®istry).await?;
|
|
||||||
operate(input, rest, &action, tag.span).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Get dirname of a path",
|
|
||||||
example: "echo '/home/joe/test.txt' | path dirname",
|
|
||||||
result: Some(vec![Value::from("/home/joe")]),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(path: &Path) -> UntaggedValue {
|
|
||||||
UntaggedValue::string(match path.parent() {
|
|
||||||
Some(dirname) => dirname.to_string_lossy().to_string(),
|
|
||||||
_ => "".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::PathDirname;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(PathDirname {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
use super::{operate, DefaultArguments};
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub struct PathExists;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for PathExists {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"path exists"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("path exists").rest(SyntaxShape::ColumnPath, "optionally operate by path")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"checks whether the path exists"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
let (DefaultArguments { rest }, input) = args.process(®istry).await?;
|
|
||||||
operate(input, rest, &action, tag.span).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Check if file exists",
|
|
||||||
example: "echo '/home/joe/todo.txt' | path exists",
|
|
||||||
result: Some(vec![Value::from(UntaggedValue::boolean(false))]),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(path: &Path) -> UntaggedValue {
|
|
||||||
UntaggedValue::boolean(path.exists())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::PathExists;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(PathExists {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
use super::{operate, DefaultArguments};
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub struct PathExpand;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for PathExpand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"path expand"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("path expand").rest(SyntaxShape::ColumnPath, "optionally operate by path")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"expands the path to its absolute form"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
let (DefaultArguments { rest }, input) = args.process(®istry).await?;
|
|
||||||
operate(input, rest, &action, tag.span).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Expand relative directories",
|
|
||||||
example: "echo '/home/joe/foo/../bar' | path expand",
|
|
||||||
result: None,
|
|
||||||
//Some(vec![Value::from("/home/joe/bar")]),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(path: &Path) -> UntaggedValue {
|
|
||||||
let ps = path.to_string_lossy();
|
|
||||||
let expanded = shellexpand::tilde(&ps);
|
|
||||||
let path: &Path = expanded.as_ref().as_ref();
|
|
||||||
UntaggedValue::string(match path.canonicalize() {
|
|
||||||
Ok(p) => p.to_string_lossy().to_string(),
|
|
||||||
Err(_) => ps.to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::PathExpand;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(PathExpand {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
use super::{operate, DefaultArguments};
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub struct PathExtension;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for PathExtension {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"path extension"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("path extension")
|
|
||||||
.rest(SyntaxShape::ColumnPath, "optionally operate by path")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"gets the extension of a path"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
let (DefaultArguments { rest }, input) = args.process(®istry).await?;
|
|
||||||
operate(input, rest, &action, tag.span).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
Example {
|
|
||||||
description: "Get extension of a path",
|
|
||||||
example: "echo 'test.txt' | path extension",
|
|
||||||
result: Some(vec![Value::from("txt")]),
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "You get an empty string if there is no extension",
|
|
||||||
example: "echo 'test' | path extension",
|
|
||||||
result: Some(vec![Value::from("")]),
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(path: &Path) -> UntaggedValue {
|
|
||||||
UntaggedValue::string(match path.extension() {
|
|
||||||
Some(ext) => ext.to_string_lossy().to_string(),
|
|
||||||
_ => "".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::PathExtension;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(PathExtension {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
use super::{operate, DefaultArguments};
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
pub struct PathFilestem;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for PathFilestem {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"path filestem"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("path filestem")
|
|
||||||
.rest(SyntaxShape::ColumnPath, "optionally operate by path")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"gets the filestem of a path"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
let (DefaultArguments { rest }, input) = args.process(®istry).await?;
|
|
||||||
operate(input, rest, &action, tag.span).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Get filestem of a path",
|
|
||||||
example: "echo '/home/joe/test.txt' | path filestem",
|
|
||||||
result: Some(vec![Value::from("test")]),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(path: &Path) -> UntaggedValue {
|
|
||||||
UntaggedValue::string(match path.file_stem() {
|
|
||||||
Some(stem) => stem.to_string_lossy().to_string(),
|
|
||||||
_ => "".to_string(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::PathFilestem;
|
|
||||||
use super::ShellError;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(PathFilestem {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,79 +0,0 @@
|
|||||||
use crate::commands::classified::block::run_block;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use derive_new::new;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{hir::Block, Scope, Signature, SyntaxShape, UntaggedValue};
|
|
||||||
|
|
||||||
#[derive(new, Clone)]
|
|
||||||
pub struct AliasCommand {
|
|
||||||
name: String,
|
|
||||||
args: Vec<(String, SyntaxShape)>,
|
|
||||||
block: Block,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for AliasCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
let mut alias = Signature::build(&self.name);
|
|
||||||
|
|
||||||
for (arg, shape) in &self.args {
|
|
||||||
alias = alias.optional(arg, *shape, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
alias
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
""
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let call_info = args.call_info.clone();
|
|
||||||
let registry = registry.clone();
|
|
||||||
let mut block = self.block.clone();
|
|
||||||
block.set_redirect(call_info.args.external_redirection);
|
|
||||||
|
|
||||||
let alias_command = self.clone();
|
|
||||||
let mut context = EvaluationContext::from_args(&args, ®istry);
|
|
||||||
let input = args.input;
|
|
||||||
|
|
||||||
let scope = call_info.scope.clone();
|
|
||||||
let evaluated = call_info.evaluate(®istry).await?;
|
|
||||||
|
|
||||||
let mut vars = IndexMap::new();
|
|
||||||
|
|
||||||
let mut num_positionals = 0;
|
|
||||||
if let Some(positional) = &evaluated.args.positional {
|
|
||||||
num_positionals = positional.len();
|
|
||||||
for (pos, arg) in positional.iter().enumerate() {
|
|
||||||
vars.insert(alias_command.args[pos].0.to_string(), arg.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if alias_command.args.len() > num_positionals {
|
|
||||||
for idx in 0..(alias_command.args.len() - num_positionals) {
|
|
||||||
vars.insert(
|
|
||||||
alias_command.args[idx + num_positionals].0.to_string(),
|
|
||||||
UntaggedValue::nothing().into_untagged_value(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let scope = Scope::append_vars(scope, vars);
|
|
||||||
|
|
||||||
// FIXME: we need to patch up the spans to point at the top-level error
|
|
||||||
Ok(run_block(&block, &mut context, input, scope)
|
|
||||||
.await?
|
|
||||||
.to_output_stream())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
|
||||||
use nu_source::Tagged;
|
|
||||||
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct SubCommandArgs {
|
|
||||||
separator: Option<Tagged<String>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"str collect"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("str collect").desc(self.usage()).optional(
|
|
||||||
"separator",
|
|
||||||
SyntaxShape::String,
|
|
||||||
"the separator to put between the different values",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"collects a list of strings into a string"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
collect(args, registry).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Collect a list of string",
|
|
||||||
example: "echo ['a' 'b' 'c'] | str collect",
|
|
||||||
result: Some(vec![Value::from("abc")]),
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn collect(
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
|
||||||
let (SubCommandArgs { separator }, input) = args.process(registry).await?;
|
|
||||||
let separator = separator.map(|tagged| tagged.item).unwrap_or_default();
|
|
||||||
|
|
||||||
let strings: Vec<Result<String, ShellError>> =
|
|
||||||
input.map(|value| value.as_string()).collect().await;
|
|
||||||
let strings: Vec<String> = strings.into_iter().collect::<Result<_, _>>()?;
|
|
||||||
let output = strings.join(&separator);
|
|
||||||
|
|
||||||
Ok(OutputStream::one(ReturnSuccess::value(
|
|
||||||
UntaggedValue::string(output).into_value(tag),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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,66 +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,
|
|
||||||
_registry: &CommandRegistry,
|
|
||||||
) -> 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,59 +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,
|
|
||||||
_registry: &CommandRegistry,
|
|
||||||
) -> 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,124 +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,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
operate(args, registry).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,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
|
|
||||||
let (Arguments { replace, rest }, input) = args.process(®istry).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_plugin::test_helpers::value::string;
|
|
||||||
use nu_source::Tag;
|
|
||||||
|
|
||||||
#[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,144 +0,0 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::ShellTypeName;
|
|
||||||
use nu_protocol::{
|
|
||||||
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
|
|
||||||
};
|
|
||||||
use nu_source::Tag;
|
|
||||||
use nu_value_ext::ValueExt;
|
|
||||||
|
|
||||||
use num_bigint::BigInt;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
struct Arguments {
|
|
||||||
rest: Vec<ColumnPath>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct SubCommand;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for SubCommand {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"str to-int"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("str to-int").rest(
|
|
||||||
SyntaxShape::ColumnPath,
|
|
||||||
"optionally convert text into integer by column paths",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"converts text into integer"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
operate(args, registry).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Convert to an integer",
|
|
||||||
example: "echo '255' | str to-int",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn operate(
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
|
|
||||||
let (Arguments { rest }, input) = args.process(®istry).await?;
|
|
||||||
|
|
||||||
let column_paths: Vec<_> = rest;
|
|
||||||
|
|
||||||
Ok(input
|
|
||||||
.map(move |v| {
|
|
||||||
if column_paths.is_empty() {
|
|
||||||
ReturnSuccess::value(action(&v, v.tag())?)
|
|
||||||
} else {
|
|
||||||
let mut ret = v;
|
|
||||||
|
|
||||||
for path in &column_paths {
|
|
||||||
ret = ret.swap_data_by_column_path(
|
|
||||||
path,
|
|
||||||
Box::new(move |old| action(old, old.tag())),
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReturnSuccess::value(ret)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.to_output_stream())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
|
|
||||||
match &input.value {
|
|
||||||
UntaggedValue::Primitive(Primitive::Line(s))
|
|
||||||
| UntaggedValue::Primitive(Primitive::String(s)) => {
|
|
||||||
let other = s.trim();
|
|
||||||
let out = match BigInt::from_str(other) {
|
|
||||||
Ok(v) => UntaggedValue::int(v),
|
|
||||||
Err(reason) => {
|
|
||||||
return Err(ShellError::labeled_error(
|
|
||||||
"could not parse as an integer",
|
|
||||||
reason.to_string(),
|
|
||||||
tag.into().span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Ok(out.into_value(tag))
|
|
||||||
}
|
|
||||||
other => {
|
|
||||||
let got = format!("got {}", other.type_name());
|
|
||||||
Err(ShellError::labeled_error(
|
|
||||||
"value is not string",
|
|
||||||
got,
|
|
||||||
tag.into().span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::ShellError;
|
|
||||||
use super::{action, SubCommand};
|
|
||||||
use nu_plugin::test_helpers::value::{int, string};
|
|
||||||
use nu_source::Tag;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(SubCommand {})?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn turns_to_integer() {
|
|
||||||
let word = string("10");
|
|
||||||
let expected = int(10);
|
|
||||||
|
|
||||||
let actual = action(&word, Tag::unknown()).unwrap();
|
|
||||||
assert_eq!(actual, expected);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
|
|
||||||
let integer_str = string("36anra");
|
|
||||||
|
|
||||||
let actual = action(&integer_str, Tag::unknown());
|
|
||||||
|
|
||||||
assert!(actual.is_err());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,91 +0,0 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use futures::StreamExt;
|
|
||||||
use nu_data::value::format_leaf;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue, Value};
|
|
||||||
|
|
||||||
pub struct ToMarkdown;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for ToMarkdown {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"to md"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("to md")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Convert table into simple Markdown"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
to_md(args, registry).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn to_md(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
let args = args.evaluate_once(®istry).await?;
|
|
||||||
let name_tag = args.name_tag();
|
|
||||||
let input: Vec<Value> = args.input.collect().await;
|
|
||||||
let headers = nu_protocol::merge_descriptors(&input);
|
|
||||||
let mut output_string = String::new();
|
|
||||||
|
|
||||||
if !headers.is_empty() && (headers.len() > 1 || headers[0] != "") {
|
|
||||||
output_string.push_str("|");
|
|
||||||
for header in &headers {
|
|
||||||
output_string.push_str(&htmlescape::encode_minimal(&header));
|
|
||||||
output_string.push_str("|");
|
|
||||||
}
|
|
||||||
output_string.push_str("\n|");
|
|
||||||
for _ in &headers {
|
|
||||||
output_string.push_str("-");
|
|
||||||
output_string.push_str("|");
|
|
||||||
}
|
|
||||||
output_string.push_str("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
for row in input {
|
|
||||||
match row.value {
|
|
||||||
UntaggedValue::Row(row) => {
|
|
||||||
output_string.push_str("|");
|
|
||||||
for header in &headers {
|
|
||||||
let data = row.get_data(header);
|
|
||||||
output_string.push_str(&format_leaf(data.borrow()).plain_string(100_000));
|
|
||||||
output_string.push_str("|");
|
|
||||||
}
|
|
||||||
output_string.push_str("\n");
|
|
||||||
}
|
|
||||||
p => {
|
|
||||||
output_string.push_str(
|
|
||||||
&(htmlescape::encode_minimal(&format_leaf(&p).plain_string(100_000))),
|
|
||||||
);
|
|
||||||
output_string.push_str("\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(OutputStream::one(ReturnSuccess::value(
|
|
||||||
UntaggedValue::string(output_string).into_value(name_tag),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::ShellError;
|
|
||||||
use super::ToMarkdown;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(ToMarkdown {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use nu_data::TaggedListBuilder;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{Dictionary, Signature, UntaggedValue};
|
|
||||||
|
|
||||||
const GIT_COMMIT_HASH: &str = include_str!(concat!(env!("OUT_DIR"), "/git_commit_hash"));
|
|
||||||
|
|
||||||
pub struct Version;
|
|
||||||
|
|
||||||
#[async_trait]
|
|
||||||
impl WholeStreamCommand for Version {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"version"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("version")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Display Nu version"
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
version(args, registry)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![Example {
|
|
||||||
description: "Display Nu version",
|
|
||||||
example: "version",
|
|
||||||
result: None,
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn version(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.args.span;
|
|
||||||
|
|
||||||
let mut indexmap = IndexMap::with_capacity(4);
|
|
||||||
|
|
||||||
indexmap.insert(
|
|
||||||
"version".to_string(),
|
|
||||||
UntaggedValue::string(clap::crate_version!()).into_value(&tag),
|
|
||||||
);
|
|
||||||
|
|
||||||
let commit_hash = Some(GIT_COMMIT_HASH.trim()).filter(|x| !x.is_empty());
|
|
||||||
if let Some(commit_hash) = commit_hash {
|
|
||||||
indexmap.insert(
|
|
||||||
"commit_hash".to_string(),
|
|
||||||
UntaggedValue::string(commit_hash).into_value(&tag),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
indexmap.insert("features".to_string(), features_enabled(&tag).into_value());
|
|
||||||
|
|
||||||
let value = UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag);
|
|
||||||
Ok(OutputStream::one(value))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn features_enabled(tag: impl Into<Tag>) -> TaggedListBuilder {
|
|
||||||
let mut names = TaggedListBuilder::new(tag);
|
|
||||||
|
|
||||||
names.push_untagged(UntaggedValue::string("default"));
|
|
||||||
|
|
||||||
#[cfg(feature = "clipboard-cli")]
|
|
||||||
{
|
|
||||||
names.push_untagged(UntaggedValue::string("clipboard"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "trash-support")]
|
|
||||||
{
|
|
||||||
names.push_untagged(UntaggedValue::string("trash"));
|
|
||||||
}
|
|
||||||
|
|
||||||
names
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::ShellError;
|
|
||||||
use super::Version;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
|
||||||
use crate::examples::test as test_examples;
|
|
||||||
|
|
||||||
Ok(test_examples(Version {})?)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,134 +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."
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn run(
|
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
which(args, registry).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! entry_builtin {
|
|
||||||
($arg:expr, $tag:expr) => {
|
|
||||||
entry(
|
|
||||||
$arg.clone(),
|
|
||||||
UntaggedValue::Primitive(Primitive::String("nushell built-in command".to_string()))
|
|
||||||
.into_value($tag.clone()),
|
|
||||||
true,
|
|
||||||
$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, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
|
|
||||||
let mut output = vec![];
|
|
||||||
|
|
||||||
let (WhichArgs { application, all }, _) = args.process(®istry).await?;
|
|
||||||
let external = application.starts_with('^');
|
|
||||||
let item = if external {
|
|
||||||
application.item[1..].to_string()
|
|
||||||
} else {
|
|
||||||
application.item.clone()
|
|
||||||
};
|
|
||||||
if !external {
|
|
||||||
let builtin = registry.has(&item);
|
|
||||||
if builtin {
|
|
||||||
output.push(ReturnSuccess::value(entry_builtin!(
|
|
||||||
item,
|
|
||||||
application.tag.clone()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 super::matchers::Matcher;
|
||||||
use crate::completion::{Completer, CompletionContext, Suggestion};
|
use crate::completion::{Completer, CompletionContext, Suggestion};
|
||||||
use crate::evaluation_context::EvaluationContext;
|
use nu_engine::EvaluationContext;
|
||||||
|
|
||||||
pub struct CommandCompleter;
|
pub struct CommandCompleter;
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ impl Completer for CommandCompleter {
|
|||||||
matcher: &dyn Matcher,
|
matcher: &dyn Matcher,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let context: &EvaluationContext = ctx.as_ref();
|
let context: &EvaluationContext = ctx.as_ref();
|
||||||
let mut commands: IndexSet<String> = IndexSet::from_iter(context.registry.names());
|
let mut commands: IndexSet<String> = IndexSet::from_iter(context.scope.get_command_names());
|
||||||
|
|
||||||
// Command suggestions can come from three possible sets:
|
// Command suggestions can come from three possible sets:
|
||||||
// 1. internal command names,
|
// 1. internal command names,
|
||||||
@ -38,7 +38,7 @@ impl Completer for CommandCompleter {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if partial != "" {
|
if !partial.is_empty() {
|
||||||
let path_completer = crate::completion::path::PathCompleter;
|
let path_completer = crate::completion::path::PathCompleter;
|
||||||
let path_results = path_completer.path_suggestions(partial, matcher);
|
let path_results = path_completer.path_suggestions(partial, matcher);
|
||||||
let iter = path_results.into_iter().filter_map(|path_suggestion| {
|
let iter = path_results.into_iter().filter_map(|path_suggestion| {
|
||||||
@ -65,6 +65,8 @@ fn pathext() -> Option<Vec<String>> {
|
|||||||
std::env::var_os("PATHEXT").map(|v| {
|
std::env::var_os("PATHEXT").map(|v| {
|
||||||
v.to_string_lossy()
|
v.to_string_lossy()
|
||||||
.split(';')
|
.split(';')
|
||||||
|
// Filter out empty tokens and ';' at the end
|
||||||
|
.filter(|f| f.len() > 1)
|
||||||
// Cut off the leading '.' character
|
// Cut off the leading '.' character
|
||||||
.map(|ext| ext[1..].to_string())
|
.map(|ext| ext[1..].to_string())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
@ -38,7 +38,7 @@ impl<'s> Flatten<'s> {
|
|||||||
.collect(),
|
.collect(),
|
||||||
Expression::Command => vec![LocationType::Command.spanned(e.span)],
|
Expression::Command => vec![LocationType::Command.spanned(e.span)],
|
||||||
Expression::Path(path) => self.expression(&path.head),
|
Expression::Path(path) => self.expression(&path.head),
|
||||||
Expression::Variable(_) => vec![LocationType::Variable.spanned(e.span)],
|
Expression::Variable(_, _) => vec![LocationType::Variable.spanned(e.span)],
|
||||||
|
|
||||||
Expression::Boolean(_)
|
Expression::Boolean(_)
|
||||||
| Expression::FilePath(_)
|
| Expression::FilePath(_)
|
||||||
@ -138,7 +138,7 @@ impl<'s> Flatten<'s> {
|
|||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pipeline(&self, pipeline: &Commands) -> Vec<CompletionLocation> {
|
fn pipeline(&self, pipeline: &Pipeline) -> Vec<CompletionLocation> {
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
|
||||||
for command in &pipeline.list {
|
for command in &pipeline.list {
|
||||||
@ -158,7 +158,11 @@ impl<'s> Flatten<'s> {
|
|||||||
|
|
||||||
/// Flattens the block into a Vec of completion locations
|
/// Flattens the block into a Vec of completion locations
|
||||||
pub fn completion_locations(&self, block: &Block) -> Vec<CompletionLocation> {
|
pub fn completion_locations(&self, block: &Block) -> Vec<CompletionLocation> {
|
||||||
block.block.iter().flat_map(|v| self.pipeline(v)).collect()
|
block
|
||||||
|
.block
|
||||||
|
.iter()
|
||||||
|
.flat_map(|g| g.pipelines.iter().flat_map(|v| self.pipeline(v)))
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(line: &'s str) -> Flatten<'s> {
|
pub fn new(line: &'s str) -> Flatten<'s> {
|
||||||
@ -252,7 +256,7 @@ pub fn completion_location(line: &str, block: &Block, pos: usize) -> Vec<Complet
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use nu_parser::SignatureRegistry;
|
use nu_parser::{classify_block, lex, parse_block, ParserScope};
|
||||||
use nu_protocol::{Signature, SyntaxShape};
|
use nu_protocol::{Signature, SyntaxShape};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -264,38 +268,52 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SignatureRegistry for VecRegistry {
|
impl ParserScope for VecRegistry {
|
||||||
fn has(&self, name: &str) -> bool {
|
fn has_signature(&self, name: &str) -> bool {
|
||||||
self.0.iter().any(|v| v.name == name)
|
self.0.iter().any(|v| v.name == name)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get(&self, name: &str) -> Option<nu_protocol::Signature> {
|
fn get_signature(&self, name: &str) -> Option<nu_protocol::Signature> {
|
||||||
self.0.iter().find(|v| v.name == name).map(Clone::clone)
|
self.0.iter().find(|v| v.name == name).map(Clone::clone)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_box(&self) -> Box<dyn SignatureRegistry> {
|
fn get_alias(&self, _name: &str) -> Option<Vec<Spanned<String>>> {
|
||||||
Box::new(self.clone())
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn add_alias(&self, _name: &str, _replacement: Vec<Spanned<String>>) {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_definition(&self, _block: Block) {}
|
||||||
|
|
||||||
|
fn get_definitions(&self) -> Vec<Block> {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_scope(&self) {}
|
||||||
|
|
||||||
|
fn exit_scope(&self) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod completion_location {
|
mod completion_location {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use nu_parser::{classify_block, lite_parse, SignatureRegistry};
|
use nu_parser::ParserScope;
|
||||||
|
|
||||||
fn completion_location(
|
fn completion_location(
|
||||||
line: &str,
|
line: &str,
|
||||||
registry: &dyn SignatureRegistry,
|
scope: &dyn ParserScope,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
) -> Vec<LocationType> {
|
) -> Vec<LocationType> {
|
||||||
let lite_block = match lite_parse(line, 0) {
|
let (tokens, _) = lex(line, 0);
|
||||||
Ok(v) => v,
|
let (lite_block, _) = parse_block(tokens);
|
||||||
Err(e) => e.partial.expect("lite_parse result"),
|
|
||||||
};
|
|
||||||
|
|
||||||
let block = classify_block(&lite_block, registry);
|
scope.enter_scope();
|
||||||
|
let (block, _) = classify_block(&lite_block, scope);
|
||||||
|
scope.exit_scope();
|
||||||
|
|
||||||
super::completion_location(line, &block.block, pos)
|
super::completion_location(line, &block, pos)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|v| v.item)
|
.map(|v| v.item)
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::matchers::Matcher;
|
use super::matchers::Matcher;
|
||||||
use crate::completion::{Completer, CompletionContext, Suggestion};
|
use crate::completion::{Completer, CompletionContext, Suggestion};
|
||||||
use crate::evaluation_context::EvaluationContext;
|
use nu_engine::EvaluationContext;
|
||||||
|
|
||||||
pub struct FlagCompleter {
|
pub struct FlagCompleter {
|
||||||
pub(crate) cmd: String,
|
pub(crate) cmd: String,
|
||||||
@ -15,7 +15,7 @@ impl Completer for FlagCompleter {
|
|||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let context: &EvaluationContext = ctx.as_ref();
|
let context: &EvaluationContext = ctx.as_ref();
|
||||||
|
|
||||||
if let Some(cmd) = context.registry.get_command(&self.cmd) {
|
if let Some(cmd) = context.scope.get_command(&self.cmd) {
|
||||||
let sig = cmd.signature();
|
let sig = cmd.signature();
|
||||||
let mut suggestions = Vec::new();
|
let mut suggestions = Vec::new();
|
||||||
for (name, (named_type, _desc)) in sig.named.iter() {
|
for (name, (named_type, _desc)) in sig.named.iter() {
|
||||||
|
@ -4,8 +4,8 @@ pub(crate) mod flag;
|
|||||||
pub(crate) mod matchers;
|
pub(crate) mod matchers;
|
||||||
pub(crate) mod path;
|
pub(crate) mod path;
|
||||||
|
|
||||||
use crate::evaluation_context::EvaluationContext;
|
|
||||||
use matchers::Matcher;
|
use matchers::Matcher;
|
||||||
|
use nu_engine::EvaluationContext;
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
pub struct Suggestion {
|
pub struct Suggestion {
|
||||||
|
@ -22,14 +22,14 @@ impl PathCompleter {
|
|||||||
None => ("", expanded),
|
None => ("", expanded),
|
||||||
};
|
};
|
||||||
|
|
||||||
let base_dir = if base_dir_name == "" {
|
let base_dir = if base_dir_name.is_empty() {
|
||||||
PathBuf::from(".")
|
PathBuf::from(".")
|
||||||
} else {
|
} else {
|
||||||
#[cfg(feature = "directories")]
|
#[cfg(feature = "directories")]
|
||||||
{
|
{
|
||||||
let home_prefix = format!("~{}", SEP);
|
let home_prefix = format!("~{}", SEP);
|
||||||
if base_dir_name.starts_with(&home_prefix) {
|
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.push(&base_dir_name[2..]);
|
||||||
home_dir
|
home_dir
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
pub(crate) mod directory_specific_environment;
|
pub(crate) mod directory_specific_environment;
|
||||||
pub(crate) mod environment;
|
pub(crate) mod environment;
|
||||||
pub(crate) mod environment_syncer;
|
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 indexmap::{IndexMap, IndexSet};
|
||||||
|
use nu_command::commands::autoenv;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::env::*;
|
use std::env::*;
|
||||||
@ -52,7 +51,7 @@ impl DirectorySpecificEnvironment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toml_if_trusted(&mut self, nu_env_file: &PathBuf) -> Result<NuEnvDoc, ShellError> {
|
fn toml_if_trusted(&mut self, nu_env_file: &Path) -> Result<NuEnvDoc, ShellError> {
|
||||||
let content = std::fs::read(&nu_env_file)?;
|
let content = std::fs::read(&nu_env_file)?;
|
||||||
|
|
||||||
if autoenv::file_is_trusted(&nu_env_file, &content)? {
|
if autoenv::file_is_trusted(&nu_env_file, &content)? {
|
||||||
@ -162,7 +161,7 @@ impl DirectorySpecificEnvironment {
|
|||||||
pub fn maybe_add_key(
|
pub fn maybe_add_key(
|
||||||
&mut self,
|
&mut self,
|
||||||
seen_vars: &mut IndexSet<EnvKey>,
|
seen_vars: &mut IndexSet<EnvKey>,
|
||||||
dir: &PathBuf,
|
dir: &Path,
|
||||||
key: &str,
|
key: &str,
|
||||||
val: &str,
|
val: &str,
|
||||||
) {
|
) {
|
||||||
@ -170,7 +169,7 @@ impl DirectorySpecificEnvironment {
|
|||||||
if !seen_vars.contains(key) {
|
if !seen_vars.contains(key) {
|
||||||
seen_vars.insert(key.to_string());
|
seen_vars.insert(key.to_string());
|
||||||
self.added_vars
|
self.added_vars
|
||||||
.entry(dir.clone())
|
.entry(PathBuf::from(dir))
|
||||||
.or_insert(IndexMap::new())
|
.or_insert(IndexMap::new())
|
||||||
.insert(key.to_string(), var_os(key));
|
.insert(key.to_string(), var_os(key));
|
||||||
|
|
||||||
|
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 crate::env::directory_specific_environment::*;
|
||||||
use indexmap::{indexmap, IndexSet};
|
use indexmap::{indexmap, IndexSet};
|
||||||
use nu_data::config::Conf;
|
use nu_data::config::Conf;
|
||||||
|
use nu_engine::Env;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{UntaggedValue, Value};
|
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)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
@ -128,7 +99,7 @@ impl Env for Environment {
|
|||||||
{
|
{
|
||||||
let mut new_paths = current_paths.clone();
|
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())
|
UntaggedValue::string(path.to_string_lossy()).into_value(tag.clone())
|
||||||
});
|
});
|
||||||
|
|
||||||
|
55
crates/nu-cli/src/env/environment_syncer.rs
vendored
55
crates/nu-cli/src/env/environment_syncer.rs
vendored
@ -1,9 +1,10 @@
|
|||||||
use crate::env::environment::{Env, Environment};
|
use crate::env::environment::Environment;
|
||||||
use crate::evaluation_context::EvaluationContext;
|
|
||||||
use nu_data::config::{Conf, NuConfig};
|
use nu_data::config::{Conf, NuConfig};
|
||||||
|
use nu_engine::Env;
|
||||||
|
use nu_engine::EvaluationContext;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::sync::Arc;
|
use std::sync::{atomic::Ordering, Arc};
|
||||||
|
|
||||||
pub struct EnvironmentSyncer {
|
pub struct EnvironmentSyncer {
|
||||||
pub env: Arc<Mutex<Box<Environment>>>,
|
pub env: Arc<Mutex<Box<Environment>>>,
|
||||||
@ -50,7 +51,7 @@ impl EnvironmentSyncer {
|
|||||||
|
|
||||||
pub fn did_config_change(&mut self) -> bool {
|
pub fn did_config_change(&mut self) -> bool {
|
||||||
let config = self.config.lock();
|
let config = self.config.lock();
|
||||||
config.is_modified().unwrap_or_else(|_| false)
|
config.is_modified().unwrap_or(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reload(&mut self) {
|
pub fn reload(&mut self) {
|
||||||
@ -63,8 +64,12 @@ impl EnvironmentSyncer {
|
|||||||
|
|
||||||
pub fn autoenv(&self, ctx: &mut EvaluationContext) -> Result<(), ShellError> {
|
pub fn autoenv(&self, ctx: &mut EvaluationContext) -> Result<(), ShellError> {
|
||||||
let mut environment = self.env.lock();
|
let mut environment = self.env.lock();
|
||||||
let auto = environment.autoenv(ctx.user_recently_used_autoenv_untrust);
|
let recently_used = ctx
|
||||||
ctx.user_recently_used_autoenv_untrust = false;
|
.user_recently_used_autoenv_untrust
|
||||||
|
.load(Ordering::SeqCst);
|
||||||
|
let auto = environment.autoenv(recently_used);
|
||||||
|
ctx.user_recently_used_autoenv_untrust
|
||||||
|
.store(false, Ordering::SeqCst);
|
||||||
auto
|
auto
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,9 +95,11 @@ impl EnvironmentSyncer {
|
|||||||
ctx.with_host(|host| {
|
ctx.with_host(|host| {
|
||||||
host.env_set(
|
host.env_set(
|
||||||
std::ffi::OsString::from(var.0),
|
std::ffi::OsString::from(var.0),
|
||||||
std::ffi::OsString::from(string),
|
std::ffi::OsString::from(&string),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ctx.scope.add_env_var_to_base(var.0, string);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,8 +130,14 @@ impl EnvironmentSyncer {
|
|||||||
|
|
||||||
if let Ok(paths_ready) = prepared {
|
if let Ok(paths_ready) = prepared {
|
||||||
ctx.with_host(|host| {
|
ctx.with_host(|host| {
|
||||||
host.env_set(std::ffi::OsString::from("PATH"), paths_ready);
|
host.env_set(
|
||||||
|
std::ffi::OsString::from("PATH"),
|
||||||
|
std::ffi::OsString::from(&paths_ready),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ctx.scope
|
||||||
|
.add_env_var_to_base("PATH", paths_ready.to_string_lossy().to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,10 +161,10 @@ impl EnvironmentSyncer {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::EnvironmentSyncer;
|
use super::EnvironmentSyncer;
|
||||||
use crate::env::environment::Env;
|
|
||||||
use crate::evaluation_context::EvaluationContext;
|
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use nu_data::config::tests::FakeConfig;
|
use nu_data::config::tests::FakeConfig;
|
||||||
|
use nu_engine::basic_evaluation_context;
|
||||||
|
use nu_engine::Env;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_test_support::fs::Stub::FileWithContent;
|
use nu_test_support::fs::Stub::FileWithContent;
|
||||||
use nu_test_support::playground::Playground;
|
use nu_test_support::playground::Playground;
|
||||||
@ -166,8 +179,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn syncs_env_if_new_env_entry_is_added_to_an_existing_configuration() -> Result<(), ShellError>
|
fn syncs_env_if_new_env_entry_is_added_to_an_existing_configuration() -> Result<(), ShellError>
|
||||||
{
|
{
|
||||||
let mut ctx = EvaluationContext::basic()?;
|
let mut ctx = basic_evaluation_context()?;
|
||||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||||
|
|
||||||
let mut expected = IndexMap::new();
|
let mut expected = IndexMap::new();
|
||||||
expected.insert(
|
expected.insert(
|
||||||
@ -198,7 +211,7 @@ mod tests {
|
|||||||
let new_file = dirs.test().join("updated_configuration.toml");
|
let new_file = dirs.test().join("updated_configuration.toml");
|
||||||
|
|
||||||
let fake_config = FakeConfig::new(&file);
|
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
|
// Here, the environment variables from the current session
|
||||||
// are cleared since we will load and set them from the
|
// are cleared since we will load and set them from the
|
||||||
@ -269,8 +282,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn syncs_env_if_new_env_entry_in_session_is_not_in_configuration_file() -> Result<(), ShellError>
|
fn syncs_env_if_new_env_entry_in_session_is_not_in_configuration_file() -> Result<(), ShellError>
|
||||||
{
|
{
|
||||||
let mut ctx = EvaluationContext::basic()?;
|
let mut ctx = basic_evaluation_context()?;
|
||||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||||
|
|
||||||
let mut expected = IndexMap::new();
|
let mut expected = IndexMap::new();
|
||||||
expected.insert(
|
expected.insert(
|
||||||
@ -368,8 +381,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nu_envs_have_higher_priority_and_does_not_get_overwritten() -> Result<(), ShellError> {
|
fn nu_envs_have_higher_priority_and_does_not_get_overwritten() -> Result<(), ShellError> {
|
||||||
let mut ctx = EvaluationContext::basic()?;
|
let mut ctx = basic_evaluation_context()?;
|
||||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||||
|
|
||||||
let mut expected = IndexMap::new();
|
let mut expected = IndexMap::new();
|
||||||
expected.insert(
|
expected.insert(
|
||||||
@ -444,8 +457,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn syncs_path_if_new_path_entry_in_session_is_not_in_configuration_file(
|
fn syncs_path_if_new_path_entry_in_session_is_not_in_configuration_file(
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
let mut ctx = EvaluationContext::basic()?;
|
let mut ctx = basic_evaluation_context()?;
|
||||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||||
|
|
||||||
let expected = std::env::join_paths(vec![
|
let expected = std::env::join_paths(vec![
|
||||||
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
||||||
@ -531,8 +544,8 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn nu_paths_have_higher_priority_and_new_paths_get_appended_to_the_end(
|
fn nu_paths_have_higher_priority_and_new_paths_get_appended_to_the_end(
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
let mut ctx = EvaluationContext::basic()?;
|
let mut ctx = basic_evaluation_context()?;
|
||||||
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
|
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
|
||||||
|
|
||||||
let expected = std::env::join_paths(vec![
|
let expected = std::env::join_paths(vec![
|
||||||
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
PathBuf::from("/Users/andresrobalino/.volta/bin"),
|
||||||
|
@ -1,195 +0,0 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::{command::CommandArgs, Command, UnevaluatedCallInfo};
|
|
||||||
use crate::env::host::Host;
|
|
||||||
use crate::shell::shell_manager::ShellManager;
|
|
||||||
use crate::stream::{InputStream, OutputStream};
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::{hir, Scope};
|
|
||||||
use nu_source::{Tag, Text};
|
|
||||||
use parking_lot::Mutex;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::sync::atomic::AtomicBool;
|
|
||||||
use std::sync::Arc;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct EvaluationContext {
|
|
||||||
pub registry: CommandRegistry,
|
|
||||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
|
||||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
|
||||||
pub ctrl_c: Arc<AtomicBool>,
|
|
||||||
pub raw_input: String,
|
|
||||||
pub user_recently_used_autoenv_untrust: bool,
|
|
||||||
pub(crate) shell_manager: ShellManager,
|
|
||||||
|
|
||||||
/// Windows-specific: keep track of previous cwd on each drive
|
|
||||||
pub windows_drives_previous_cwd: Arc<Mutex<std::collections::HashMap<String, String>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EvaluationContext {
|
|
||||||
pub(crate) fn registry(&self) -> &CommandRegistry {
|
|
||||||
&self.registry
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_raw(
|
|
||||||
raw_args: &CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> EvaluationContext {
|
|
||||||
EvaluationContext {
|
|
||||||
registry: registry.clone(),
|
|
||||||
host: raw_args.host.clone(),
|
|
||||||
current_errors: raw_args.current_errors.clone(),
|
|
||||||
ctrl_c: raw_args.ctrl_c.clone(),
|
|
||||||
shell_manager: raw_args.shell_manager.clone(),
|
|
||||||
user_recently_used_autoenv_untrust: false,
|
|
||||||
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
|
||||||
raw_input: String::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn from_args(args: &CommandArgs, registry: &CommandRegistry) -> EvaluationContext {
|
|
||||||
EvaluationContext {
|
|
||||||
registry: registry.clone(),
|
|
||||||
host: args.host.clone(),
|
|
||||||
current_errors: args.current_errors.clone(),
|
|
||||||
ctrl_c: args.ctrl_c.clone(),
|
|
||||||
shell_manager: args.shell_manager.clone(),
|
|
||||||
user_recently_used_autoenv_untrust: false,
|
|
||||||
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
|
||||||
raw_input: String::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn basic() -> Result<EvaluationContext, Box<dyn Error>> {
|
|
||||||
let registry = CommandRegistry::new();
|
|
||||||
|
|
||||||
Ok(EvaluationContext {
|
|
||||||
registry,
|
|
||||||
host: Arc::new(parking_lot::Mutex::new(Box::new(
|
|
||||||
crate::env::host::BasicHost,
|
|
||||||
))),
|
|
||||||
current_errors: Arc::new(Mutex::new(vec![])),
|
|
||||||
ctrl_c: Arc::new(AtomicBool::new(false)),
|
|
||||||
user_recently_used_autoenv_untrust: false,
|
|
||||||
shell_manager: ShellManager::basic()?,
|
|
||||||
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
|
|
||||||
raw_input: String::default(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn error(&mut self, error: ShellError) {
|
|
||||||
self.with_errors(|errors| errors.push(error))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn clear_errors(&mut self) {
|
|
||||||
self.current_errors.lock().clear()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn get_errors(&self) -> Vec<ShellError> {
|
|
||||||
self.current_errors.lock().clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn add_error(&self, err: ShellError) {
|
|
||||||
self.current_errors.lock().push(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn maybe_print_errors(&mut self, source: Text) -> bool {
|
|
||||||
let errors = self.current_errors.clone();
|
|
||||||
let mut errors = errors.lock();
|
|
||||||
|
|
||||||
if errors.len() > 0 {
|
|
||||||
let error = errors[0].clone();
|
|
||||||
*errors = vec![];
|
|
||||||
|
|
||||||
crate::cli::print_err(error, &source);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn configure<T>(
|
|
||||||
&mut self,
|
|
||||||
config: &dyn nu_data::config::Conf,
|
|
||||||
block: impl FnOnce(&dyn nu_data::config::Conf, &mut Self) -> T,
|
|
||||||
) {
|
|
||||||
block(config, &mut *self);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn with_host<T>(&mut self, block: impl FnOnce(&mut dyn Host) -> T) -> T {
|
|
||||||
let mut host = self.host.lock();
|
|
||||||
|
|
||||||
block(&mut *host)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn with_errors<T>(&mut self, block: impl FnOnce(&mut Vec<ShellError>) -> T) -> T {
|
|
||||||
let mut errors = self.current_errors.lock();
|
|
||||||
|
|
||||||
block(&mut *errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_commands(&mut self, commands: Vec<Command>) {
|
|
||||||
for command in commands {
|
|
||||||
self.registry.insert(command.name().to_string(), command);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub(crate) fn get_command(&self, name: &str) -> Option<Command> {
|
|
||||||
self.registry.get_command(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn is_command_registered(&self, name: &str) -> bool {
|
|
||||||
self.registry.has(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn expect_command(&self, name: &str) -> Result<Command, ShellError> {
|
|
||||||
self.registry.expect_command(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn run_command(
|
|
||||||
&mut self,
|
|
||||||
command: Command,
|
|
||||||
name_tag: Tag,
|
|
||||||
args: hir::Call,
|
|
||||||
scope: Arc<Scope>,
|
|
||||||
input: InputStream,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let command_args = self.command_args(args, input, name_tag, scope);
|
|
||||||
command.run(command_args, self.registry()).await
|
|
||||||
}
|
|
||||||
|
|
||||||
fn call_info(&self, args: hir::Call, name_tag: Tag, scope: Arc<Scope>) -> UnevaluatedCallInfo {
|
|
||||||
UnevaluatedCallInfo {
|
|
||||||
args,
|
|
||||||
name_tag,
|
|
||||||
scope,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn command_args(
|
|
||||||
&self,
|
|
||||||
args: hir::Call,
|
|
||||||
input: InputStream,
|
|
||||||
name_tag: Tag,
|
|
||||||
scope: Arc<Scope>,
|
|
||||||
) -> CommandArgs {
|
|
||||||
CommandArgs {
|
|
||||||
host: self.host.clone(),
|
|
||||||
ctrl_c: self.ctrl_c.clone(),
|
|
||||||
current_errors: self.current_errors.clone(),
|
|
||||||
shell_manager: self.shell_manager.clone(),
|
|
||||||
call_info: self.call_info(args, name_tag, scope),
|
|
||||||
input,
|
|
||||||
raw_input: self.raw_input.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_env(&self) -> IndexMap<String, String> {
|
|
||||||
let mut output = IndexMap::new();
|
|
||||||
for (var, value) in self.host.lock().vars() {
|
|
||||||
output.insert(var, value);
|
|
||||||
}
|
|
||||||
output
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,474 +0,0 @@
|
|||||||
use nu_errors::ShellError;
|
|
||||||
use nu_protocol::hir::ClassifiedBlock;
|
|
||||||
use nu_protocol::{
|
|
||||||
Primitive, ReturnSuccess, Scope, ShellTypeName, Signature, SyntaxShape, UntaggedValue, Value,
|
|
||||||
};
|
|
||||||
use nu_source::{AnchorLocation, TaggedItem};
|
|
||||||
|
|
||||||
use crate::prelude::*;
|
|
||||||
|
|
||||||
use indexmap::indexmap;
|
|
||||||
use num_bigint::BigInt;
|
|
||||||
|
|
||||||
use indexmap::IndexMap;
|
|
||||||
|
|
||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::classified::block::run_block;
|
|
||||||
use crate::commands::command::CommandArgs;
|
|
||||||
use crate::commands::{
|
|
||||||
whole_stream_command, BuildString, Command, Each, Echo, Get, Keep, StrCollect,
|
|
||||||
WholeStreamCommand, Wrap,
|
|
||||||
};
|
|
||||||
use crate::evaluation_context::EvaluationContext;
|
|
||||||
use crate::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 mut 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(Get {}),
|
|
||||||
whole_stream_command(Keep {}),
|
|
||||||
whole_stream_command(Each {}),
|
|
||||||
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, &mut 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(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
|
|
||||||
let examples = cmd.examples();
|
|
||||||
|
|
||||||
let mut 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(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, &mut 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 mut 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(Get {}),
|
|
||||||
whole_stream_command(Keep {}),
|
|
||||||
whole_stream_command(Each {}),
|
|
||||||
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, &mut 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: &mut EvaluationContext) -> Result<ClassifiedBlock, ShellError> {
|
|
||||||
let line = if line.ends_with('\n') {
|
|
||||||
&line[..line.len() - 1]
|
|
||||||
} else {
|
|
||||||
line
|
|
||||||
};
|
|
||||||
|
|
||||||
let lite_result = nu_parser::lite_parse(&line, 0)?;
|
|
||||||
|
|
||||||
// TODO ensure the command whose examples we're testing is actually in the pipeline
|
|
||||||
let mut classified_block = nu_parser::classify_block(&lite_result, ctx.registry());
|
|
||||||
classified_block.block.expand_it_usage();
|
|
||||||
Ok(classified_block)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn evaluate_block(
|
|
||||||
block: ClassifiedBlock,
|
|
||||||
ctx: &mut EvaluationContext,
|
|
||||||
) -> Result<Vec<Value>, ShellError> {
|
|
||||||
let input_stream = InputStream::empty();
|
|
||||||
let env = ctx.get_env();
|
|
||||||
|
|
||||||
let scope = Scope::from_env(env);
|
|
||||||
|
|
||||||
Ok(run_block(&block.block, ctx, input_stream, scope)
|
|
||||||
.await?
|
|
||||||
.drain_vec()
|
|
||||||
.await)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name_tag = args.call_info.name_tag.clone();
|
|
||||||
|
|
||||||
let (
|
|
||||||
Arguments {
|
|
||||||
path: mocked_path,
|
|
||||||
open: open_mock,
|
|
||||||
},
|
|
||||||
_input,
|
|
||||||
) = args.process(®istry).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,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name_tag = args.call_info.name_tag.clone();
|
|
||||||
let (MockEchoArgs { rest }, input) = args.process(®istry).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),
|
|
||||||
..
|
|
||||||
} => 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,
|
|
||||||
_: &CommandRegistry,
|
|
||||||
) -> 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 row(entries: IndexMap<String, Value>) -> Value {
|
|
||||||
UntaggedValue::row(entries).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(indexmap! {
|
|
||||||
"modified".to_string() => date("2019-07-23"),
|
|
||||||
"name".to_string() => string("Andrés.txt"),
|
|
||||||
"type".to_string() => string("File"),
|
|
||||||
"chickens".to_string() => int(10),
|
|
||||||
}),
|
|
||||||
row(indexmap! {
|
|
||||||
"modified".to_string() => date("2019-07-23"),
|
|
||||||
"name".to_string() => string("Jonathan"),
|
|
||||||
"type".to_string() => string("Dir"),
|
|
||||||
"chickens".to_string() => int(5),
|
|
||||||
}),
|
|
||||||
row(indexmap! {
|
|
||||||
"modified".to_string() => date("2019-09-24"),
|
|
||||||
"name".to_string() => string("Andrés.txt"),
|
|
||||||
"type".to_string() => string("File"),
|
|
||||||
"chickens".to_string() => int(20),
|
|
||||||
}),
|
|
||||||
row(indexmap! {
|
|
||||||
"modified".to_string() => date("2019-09-24"),
|
|
||||||
"name".to_string() => string("Yehuda"),
|
|
||||||
"type".to_string() => string("Dir"),
|
|
||||||
"chickens".to_string() => int(4),
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
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>;
|
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(
|
pub(crate) fn load_keybindings(
|
||||||
rl: &mut rustyline::Editor<crate::shell::Helper>,
|
rl: &mut rustyline::Editor<crate::shell::Helper>,
|
||||||
) -> Result<(), nu_errors::ShellError> {
|
) -> Result<(), nu_errors::ShellError> {
|
||||||
let filename = keybinding_path()?;
|
let filename = nu_data::keybinding::keybinding_path()?;
|
||||||
let contents = std::fs::read_to_string(filename);
|
let contents = std::fs::read_to_string(filename);
|
||||||
|
|
||||||
// Silently fail if there is no file there
|
// Silently fail if there is no file there
|
||||||
|
@ -14,51 +14,28 @@ extern crate quickcheck;
|
|||||||
extern crate quickcheck_macros;
|
extern crate quickcheck_macros;
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
mod command_registry;
|
|
||||||
mod commands;
|
|
||||||
#[cfg(feature = "rustyline-support")]
|
#[cfg(feature = "rustyline-support")]
|
||||||
mod completion;
|
mod completion;
|
||||||
mod deserializer;
|
|
||||||
mod documentation;
|
|
||||||
mod env;
|
mod env;
|
||||||
mod evaluate;
|
|
||||||
mod evaluation_context;
|
|
||||||
mod format;
|
mod format;
|
||||||
mod futures;
|
|
||||||
#[cfg(feature = "rustyline-support")]
|
|
||||||
mod git;
|
|
||||||
#[cfg(feature = "rustyline-support")]
|
#[cfg(feature = "rustyline-support")]
|
||||||
mod keybinding;
|
mod keybinding;
|
||||||
mod path;
|
mod line_editor;
|
||||||
mod plugin;
|
|
||||||
mod shell;
|
mod shell;
|
||||||
mod stream;
|
pub mod types;
|
||||||
pub mod utils;
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod examples;
|
|
||||||
|
|
||||||
#[cfg(feature = "rustyline-support")]
|
#[cfg(feature = "rustyline-support")]
|
||||||
pub use crate::cli::cli;
|
pub use crate::cli::cli;
|
||||||
|
|
||||||
pub use crate::cli::{
|
pub use crate::cli::{parse_and_eval, register_plugins, run_script_file};
|
||||||
create_default_context, parse_and_eval, process_line, register_plugins,
|
|
||||||
run_pipeline_standalone, run_vec_of_pipelines, LineResult,
|
|
||||||
};
|
|
||||||
pub use crate::command_registry::CommandRegistry;
|
|
||||||
pub use crate::commands::command::{
|
|
||||||
whole_stream_command, CommandArgs, EvaluatedWholeStreamCommandArgs, Example, WholeStreamCommand,
|
|
||||||
};
|
|
||||||
pub use crate::commands::help::get_help;
|
|
||||||
pub use crate::env::environment_syncer::EnvironmentSyncer;
|
pub use crate::env::environment_syncer::EnvironmentSyncer;
|
||||||
pub use crate::env::host::BasicHost;
|
pub use nu_command::commands::default_context::create_default_context;
|
||||||
pub use crate::evaluation_context::EvaluationContext;
|
|
||||||
pub use crate::prelude::ToOutputStream;
|
|
||||||
pub use crate::stream::{InputStream, InterruptibleStream, OutputStream};
|
|
||||||
pub use nu_data::config;
|
pub use nu_data::config;
|
||||||
pub use nu_data::dict::TaggedListBuilder;
|
pub use nu_data::dict::TaggedListBuilder;
|
||||||
pub use nu_data::primitive;
|
pub use nu_data::primitive;
|
||||||
pub use nu_data::value;
|
pub use nu_data::value;
|
||||||
|
pub use nu_stream::{InputStream, InterruptibleStream, OutputStream};
|
||||||
pub use nu_value_ext::ValueExt;
|
pub use nu_value_ext::ValueExt;
|
||||||
pub use num_traits::cast::ToPrimitive;
|
pub use num_traits::cast::ToPrimitive;
|
||||||
|
|
||||||
|
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)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
$crate::stream::InputStream::from_stream(objects.boxed())
|
|
||||||
} else {
|
|
||||||
$expr
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! trace_out_stream {
|
macro_rules! trace_out_stream {
|
||||||
(target: $target:tt, $desc:tt = $expr:expr) => {{
|
(target: $target:tt, $desc:tt = $expr:expr) => {{
|
||||||
@ -61,49 +39,26 @@ macro_rules! trace_out_stream {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$crate::stream::OutputStream::new(objects)
|
nu_stream::OutputStream::new(objects)
|
||||||
} else {
|
} else {
|
||||||
$expr
|
$expr
|
||||||
}
|
}
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) use nu_protocol::{errln, out, outln};
|
|
||||||
use nu_source::HasFallibleSpan;
|
|
||||||
|
|
||||||
pub(crate) use crate::command_registry::CommandRegistry;
|
|
||||||
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::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 crate::stream::{InputStream, InterruptibleStream, OutputStream};
|
|
||||||
pub(crate) use bigdecimal::BigDecimal;
|
|
||||||
pub(crate) use futures::stream::BoxStream;
|
|
||||||
pub(crate) use futures::{Stream, StreamExt};
|
pub(crate) use futures::{Stream, StreamExt};
|
||||||
pub(crate) use nu_source::{
|
pub(crate) use nu_engine::Host;
|
||||||
b, AnchorLocation, DebugDocBuilder, PrettyDebug, PrettyDebugWithSource, Span, SpannedItem, Tag,
|
#[allow(unused_imports)]
|
||||||
TaggedItem, Text,
|
pub(crate) use nu_errors::ShellError;
|
||||||
};
|
#[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 nu_value_ext::ValueExt;
|
||||||
pub(crate) use num_bigint::BigInt;
|
#[allow(unused_imports)]
|
||||||
pub(crate) use num_traits::cast::ToPrimitive;
|
pub(crate) use std::sync::atomic::Ordering;
|
||||||
pub(crate) use serde::Deserialize;
|
|
||||||
pub(crate) use std::collections::VecDeque;
|
|
||||||
pub(crate) use std::future::Future;
|
|
||||||
pub(crate) use std::sync::atomic::AtomicBool;
|
|
||||||
pub(crate) use std::sync::Arc;
|
|
||||||
|
|
||||||
pub(crate) use async_trait::async_trait;
|
|
||||||
pub(crate) use indexmap::IndexMap;
|
|
||||||
pub(crate) use itertools::Itertools;
|
|
||||||
|
|
||||||
|
#[allow(clippy::clippy::wrong_self_convention)]
|
||||||
pub trait FromInputStream {
|
pub trait FromInputStream {
|
||||||
fn from_input_stream(self) -> OutputStream;
|
fn from_input_stream(self) -> OutputStream;
|
||||||
}
|
}
|
||||||
@ -119,26 +74,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToInputStream {
|
#[allow(clippy::clippy::wrong_self_convention)]
|
||||||
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 {
|
pub trait ToOutputStream {
|
||||||
fn to_output_stream(self) -> OutputStream;
|
fn to_output_stream(self) -> OutputStream;
|
||||||
}
|
}
|
||||||
@ -154,16 +90,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")]
|
#[cfg(feature = "rustyline-support")]
|
||||||
pub(crate) mod completer;
|
pub(crate) mod completer;
|
||||||
pub(crate) mod filesystem_shell;
|
|
||||||
pub(crate) mod help_shell;
|
|
||||||
#[cfg(feature = "rustyline-support")]
|
#[cfg(feature = "rustyline-support")]
|
||||||
pub(crate) mod helper;
|
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")]
|
#[cfg(feature = "rustyline-support")]
|
||||||
pub(crate) use helper::Helper;
|
pub(crate) use helper::Helper;
|
||||||
|
@ -4,7 +4,8 @@ use crate::completion::matchers;
|
|||||||
use crate::completion::matchers::Matcher;
|
use crate::completion::matchers::Matcher;
|
||||||
use crate::completion::path::{PathCompleter, PathSuggestion};
|
use crate::completion::path::{PathCompleter, PathSuggestion};
|
||||||
use crate::completion::{self, Completer, Suggestion};
|
use crate::completion::{self, Completer, Suggestion};
|
||||||
use crate::evaluation_context::EvaluationContext;
|
use nu_engine::EvaluationContext;
|
||||||
|
use nu_parser::ParserScope;
|
||||||
use nu_source::Tag;
|
use nu_source::Tag;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@ -23,15 +24,12 @@ impl NuCompleter {
|
|||||||
use completion::engine::LocationType;
|
use completion::engine::LocationType;
|
||||||
|
|
||||||
let nu_context: &EvaluationContext = context.as_ref();
|
let nu_context: &EvaluationContext = context.as_ref();
|
||||||
let lite_block = match nu_parser::lite_parse(line, 0) {
|
|
||||||
Ok(block) => Some(block),
|
|
||||||
Err(result) => result.partial,
|
|
||||||
};
|
|
||||||
|
|
||||||
let locations = lite_block
|
nu_context.scope.enter_scope();
|
||||||
.map(|block| nu_parser::classify_block(&block, &nu_context.registry))
|
let (block, _) = nu_parser::parse(line, 0, &nu_context.scope);
|
||||||
.map(|block| completion::engine::completion_location(line, &block.block, pos))
|
nu_context.scope.exit_scope();
|
||||||
.unwrap_or_default();
|
|
||||||
|
let locations = completion::engine::completion_location(line, &block, pos);
|
||||||
|
|
||||||
let matcher = nu_data::config::config(Tag::unknown())
|
let matcher = nu_data::config::config(Tag::unknown())
|
||||||
.ok()
|
.ok()
|
||||||
|
@ -1,13 +1,8 @@
|
|||||||
use std::borrow::Cow::{self, Owned};
|
|
||||||
|
|
||||||
use nu_parser::SignatureRegistry;
|
|
||||||
use nu_source::{Tag, Tagged};
|
|
||||||
|
|
||||||
use crate::completion;
|
use crate::completion;
|
||||||
use crate::evaluation_context::EvaluationContext;
|
|
||||||
use crate::shell::completer::NuCompleter;
|
use crate::shell::completer::NuCompleter;
|
||||||
use crate::shell::painter::Painter;
|
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
|
||||||
use crate::shell::palette::DefaultPalette;
|
use nu_source::{Tag, Tagged};
|
||||||
|
use std::borrow::Cow::{self, Owned};
|
||||||
|
|
||||||
pub struct Helper {
|
pub struct Helper {
|
||||||
completer: NuCompleter,
|
completer: NuCompleter,
|
||||||
@ -87,11 +82,7 @@ impl rustyline::highlight::Highlighter for Helper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
|
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
|
||||||
Painter::paint_string(
|
Painter::paint_string(line, &self.context.scope, &DefaultPalette {})
|
||||||
line,
|
|
||||||
&self.context.registry().clone_box(),
|
|
||||||
&DefaultPalette {},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_char(&self, _line: &str, _pos: usize) -> bool {
|
fn highlight_char(&self, _line: &str, _pos: usize) -> bool {
|
||||||
@ -121,10 +112,17 @@ impl rustyline::validate::Validator for NuValidator {
|
|||||||
) -> rustyline::Result<rustyline::validate::ValidationResult> {
|
) -> rustyline::Result<rustyline::validate::ValidationResult> {
|
||||||
let src = ctx.input();
|
let src = ctx.input();
|
||||||
|
|
||||||
let lite_result = nu_parser::lite_parse(src, 0);
|
let (tokens, err) = nu_parser::lex(src, 0);
|
||||||
|
if let Some(err) = err {
|
||||||
|
if let nu_errors::ParseErrorReason::Eof { .. } = err.reason() {
|
||||||
|
return Ok(rustyline::validate::ValidationResult::Incomplete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(err) = lite_result {
|
let (_, err) = nu_parser::parse_block(tokens);
|
||||||
if let nu_errors::ParseErrorReason::Eof { .. } = err.cause.reason() {
|
|
||||||
|
if let Some(err) = err {
|
||||||
|
if let nu_errors::ParseErrorReason::Eof { .. } = err.reason() {
|
||||||
return Ok(rustyline::validate::ValidationResult::Incomplete);
|
return Ok(rustyline::validate::ValidationResult::Incomplete);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,3 +144,51 @@ fn vec_tag<T>(input: Vec<Tagged<T>>) -> Option<Tag> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl rustyline::Helper for Helper {}
|
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
crates/nu-cli/src/types.rs
Normal file
1
crates/nu-cli/src/types.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub(crate) mod deduction;
|
1096
crates/nu-cli/src/types/deduction.rs
Normal file
1096
crates/nu-cli/src/types/deduction.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,114 +0,0 @@
|
|||||||
use nu_test_support::nu;
|
|
||||||
use nu_test_support::playground::Playground;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn alias_args_work() {
|
|
||||||
Playground::setup("append_test_1", |dirs, _| {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: dirs.root(),
|
|
||||||
r#"
|
|
||||||
alias double_echo [a b] {echo $a $b}
|
|
||||||
double_echo 1 2 | to json
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "[1,2]");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn alias_missing_args_work() {
|
|
||||||
Playground::setup("append_test_1", |dirs, _| {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: dirs.root(),
|
|
||||||
r#"
|
|
||||||
alias double_echo [a b] {^echo $a $b}
|
|
||||||
double_echo bob
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "bob");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
fn alias_parses_path_tilde() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: "tests/fixtures/formats",
|
|
||||||
r#"
|
|
||||||
alias -i new-cd [dir] { cd $dir }
|
|
||||||
new-cd ~
|
|
||||||
pwd
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
assert!(actual.out.contains("home"));
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
assert!(actual.out.contains("Users"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn error_alias_wrong_shape_shallow() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
alias -i round-to [num digits] { echo $num | str from -d $digits }
|
|
||||||
round-to 3.45 a
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(actual.err.contains("Type"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn error_alias_wrong_shape_deep_invocation() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
alias -i round-to [nums digits] { echo $nums | each {= $(str from -d $digits)}}
|
|
||||||
round-to 3.45 a
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(actual.err.contains("Type"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn error_alias_wrong_shape_deep_binary() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
alias -i round-plus-one [nums digits] { echo $nums | each {= $(str from -d $digits | str to-decimal) + 1}}
|
|
||||||
round-plus-one 3.45 a
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(actual.err.contains("Type"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn error_alias_wrong_shape_deeper_binary() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
alias -i round-one-more [num digits] { echo $num | str from -d $(= $digits + 1) }
|
|
||||||
round-one-more 3.45 a
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(actual.err.contains("Type"));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn error_alias_syntax_shape_clash() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".",
|
|
||||||
r#"
|
|
||||||
alias -i clash [a] { echo 1.1 2 3 | each { str from -d $a } | range $a } }
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(actual.err.contains("alias"));
|
|
||||||
}
|
|
@ -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 | echo $it | to json
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "[1,2,3]");
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
use nu_test_support::{nu, pipeline};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn creates_the_resulting_string_from_the_given_fields() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: "tests/fixtures/formats", pipeline(
|
|
||||||
r#"
|
|
||||||
open cargo_sample.toml
|
|
||||||
| get package
|
|
||||||
| format "{name} has license {license}"
|
|
||||||
| echo $it
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "nu has license ISC");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn given_fields_can_be_column_paths() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: "tests/fixtures/formats", pipeline(
|
|
||||||
r#"
|
|
||||||
open cargo_sample.toml
|
|
||||||
| format "{package.name} is {package.description}"
|
|
||||||
| echo $it
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "nu is a new type of shell");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn can_use_variables() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: "tests/fixtures/formats", pipeline(
|
|
||||||
r#"
|
|
||||||
open cargo_sample.toml
|
|
||||||
| format "{$it.package.name} is {$it.package.description}"
|
|
||||||
| echo $it
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "nu is a new type of shell");
|
|
||||||
}
|
|
@ -1,127 +0,0 @@
|
|||||||
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
|
|
||||||
use nu_test_support::playground::Playground;
|
|
||||||
use nu_test_support::{nu, pipeline};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn regular_columns() {
|
|
||||||
Playground::setup("select_test_1", |dirs, sandbox| {
|
|
||||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
|
||||||
"los_tres_caballeros.csv",
|
|
||||||
r#"
|
|
||||||
first_name,last_name,rusty_at,type
|
|
||||||
Andrés,Robalino,10/11/2013,A
|
|
||||||
Jonathan,Turner,10/12/2013,B
|
|
||||||
Yehuda,Katz,10/11/2013,A
|
|
||||||
"#,
|
|
||||||
)]);
|
|
||||||
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: dirs.test(), pipeline(
|
|
||||||
r#"
|
|
||||||
open los_tres_caballeros.csv
|
|
||||||
| select rusty_at last_name
|
|
||||||
| nth 0
|
|
||||||
| get last_name
|
|
||||||
| echo $it
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "Robalino");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn complex_nested_columns() {
|
|
||||||
Playground::setup("select_test_2", |dirs, sandbox| {
|
|
||||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
|
||||||
"los_tres_caballeros.json",
|
|
||||||
r#"
|
|
||||||
{
|
|
||||||
"nu": {
|
|
||||||
"committers": [
|
|
||||||
{"name": "Andrés N. Robalino"},
|
|
||||||
{"name": "Jonathan Turner"},
|
|
||||||
{"name": "Yehuda Katz"}
|
|
||||||
],
|
|
||||||
"releases": [
|
|
||||||
{"version": "0.2"}
|
|
||||||
{"version": "0.8"},
|
|
||||||
{"version": "0.9999999"}
|
|
||||||
],
|
|
||||||
"0xATYKARNU": [
|
|
||||||
["Th", "e", " "],
|
|
||||||
["BIG", " ", "UnO"],
|
|
||||||
["punto", "cero"]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"#,
|
|
||||||
)]);
|
|
||||||
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: dirs.test(), pipeline(
|
|
||||||
r#"
|
|
||||||
open los_tres_caballeros.json
|
|
||||||
| select nu."0xATYKARNU" nu.committers.name nu.releases.version
|
|
||||||
| where "nu.releases.version" > "0.8"
|
|
||||||
| get "nu.releases.version"
|
|
||||||
| echo $it
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "0.9999999");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn allows_if_given_unknown_column_name_is_missing() {
|
|
||||||
Playground::setup("select_test_3", |dirs, sandbox| {
|
|
||||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
|
||||||
"los_tres_caballeros.csv",
|
|
||||||
r#"
|
|
||||||
first_name,last_name,rusty_at,type
|
|
||||||
Andrés,Robalino,10/11/2013,A
|
|
||||||
Jonathan,Turner,10/12/2013,B
|
|
||||||
Yehuda,Katz,10/11/2013,A
|
|
||||||
"#,
|
|
||||||
)]);
|
|
||||||
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: dirs.test(), pipeline(
|
|
||||||
r#"
|
|
||||||
open los_tres_caballeros.csv
|
|
||||||
| select rrusty_at first_name
|
|
||||||
| count
|
|
||||||
| echo $it
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "3");
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn column_names_with_spaces() {
|
|
||||||
Playground::setup("select_test_4", |dirs, sandbox| {
|
|
||||||
sandbox.with_files(vec![FileWithContentToBeTrimmed(
|
|
||||||
"test_data.csv",
|
|
||||||
r#"
|
|
||||||
first name,last name
|
|
||||||
Jonathan,Turner
|
|
||||||
Jonathan,Arns
|
|
||||||
"#,
|
|
||||||
)]);
|
|
||||||
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: dirs.test(), pipeline(
|
|
||||||
r#"
|
|
||||||
open test_data.csv
|
|
||||||
| select "last name"
|
|
||||||
| to json
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
let expected_output = r#"[{"last name":"Turner"},{"last name":"Arns"}]"#;
|
|
||||||
assert_eq!(actual.out, expected_output);
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
use nu_test_support::{nu, pipeline};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn out_md_simple() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".", pipeline(
|
|
||||||
r#"
|
|
||||||
echo 3 | to md
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "3");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn out_md_table() {
|
|
||||||
let actual = nu!(
|
|
||||||
cwd: ".", pipeline(
|
|
||||||
r#"
|
|
||||||
echo '{"name": "jason"}' | from json | to md
|
|
||||||
"#
|
|
||||||
));
|
|
||||||
|
|
||||||
assert_eq!(actual.out, "|name||-||jason|");
|
|
||||||
}
|
|
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.27.1"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
doctest = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
nu-data = { version = "0.27.1", path = "../nu-data" }
|
||||||
|
nu-engine = { version = "0.27.1", path = "../nu-engine" }
|
||||||
|
nu-errors = { version = "0.27.1", path = "../nu-errors" }
|
||||||
|
nu-json = { version = "0.27.1", path = "../nu-json" }
|
||||||
|
nu-parser = { version = "0.27.1", path = "../nu-parser" }
|
||||||
|
nu-plugin = { version = "0.27.1", path = "../nu-plugin" }
|
||||||
|
nu-protocol = { version = "0.27.1", path = "../nu-protocol" }
|
||||||
|
nu-source = { version = "0.27.1", path = "../nu-source" }
|
||||||
|
nu-stream = { version = "0.27.1", path = "../nu-stream" }
|
||||||
|
nu-table = { version = "0.27.1", path = "../nu-table" }
|
||||||
|
nu-test-support = { version = "0.27.1", path = "../nu-test-support" }
|
||||||
|
nu-value-ext = { version = "0.27.1", path = "../nu-value-ext" }
|
||||||
|
|
||||||
|
Inflector = "0.11"
|
||||||
|
ansi_term = "0.12.1"
|
||||||
|
arboard = { version = "1.1.0", optional = true }
|
||||||
|
async-recursion = "0.3.2"
|
||||||
|
async-trait = "0.1.42"
|
||||||
|
base64 = "0.13.0"
|
||||||
|
bigdecimal = { version = "0.2.0", features = ["serde"] }
|
||||||
|
byte-unit = "4.0.9"
|
||||||
|
bytes = "1.0.1"
|
||||||
|
calamine = "0.17.0"
|
||||||
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
|
chrono-tz = "0.5.3"
|
||||||
|
clap = "2.33.3"
|
||||||
|
codespan-reporting = "0.11.0"
|
||||||
|
csv = "1.1.3"
|
||||||
|
ctrlc = { version = "3.1.7", 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.28"
|
||||||
|
filesize = "0.2.0"
|
||||||
|
fs_extra = "1.2.0"
|
||||||
|
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
|
||||||
|
futures-util = "0.3.12"
|
||||||
|
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.1", features = ["serde-1"] }
|
||||||
|
itertools = "0.10.0"
|
||||||
|
lazy_static = "1.*"
|
||||||
|
log = "0.4.14"
|
||||||
|
meval = "0.2.0"
|
||||||
|
num-bigint = { version = "0.3.1", features = ["serde"] }
|
||||||
|
num-format = { version = "0.4.0", features = ["with-num-bigint"] }
|
||||||
|
num-traits = "0.2.14"
|
||||||
|
parking_lot = "0.11.1"
|
||||||
|
pin-utils = "0.1.0"
|
||||||
|
pretty-hex = "0.2.1"
|
||||||
|
ptree = { version = "0.3.1", optional = true }
|
||||||
|
query_interface = "0.3.5"
|
||||||
|
quick-xml = "0.21.0"
|
||||||
|
rand = "0.7.3"
|
||||||
|
rayon = "1.5.0"
|
||||||
|
regex = "1.4.3"
|
||||||
|
roxmltree = "0.14.0"
|
||||||
|
rust-embed = "5.9.0"
|
||||||
|
rustyline = { version = "7.1.0", optional = true }
|
||||||
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
|
serde_bytes = "0.11.5"
|
||||||
|
serde_ini = "0.2.0"
|
||||||
|
serde_json = "1.0.61"
|
||||||
|
serde_urlencoded = "0.7.0"
|
||||||
|
serde_yaml = "0.8.16"
|
||||||
|
sha2 = "0.9.3"
|
||||||
|
shellexpand = "2.1.0"
|
||||||
|
strip-ansi-escapes = "0.1.0"
|
||||||
|
sxd-document = "0.3.2"
|
||||||
|
sxd-xpath = "0.4.2"
|
||||||
|
tempfile = "3.2.0"
|
||||||
|
term = { version = "0.7.0", optional = true }
|
||||||
|
term_size = "0.3.2"
|
||||||
|
termcolor = "1.1.2"
|
||||||
|
titlecase = "1.1.0"
|
||||||
|
toml = "0.5.8"
|
||||||
|
trash = { version = "1.3.0", optional = true }
|
||||||
|
unicode-segmentation = "1.7.1"
|
||||||
|
url = "2.2.0"
|
||||||
|
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
|
||||||
|
which = { version = "4.0.2", optional = true }
|
||||||
|
zip = { version = "0.5.9", optional = true }
|
||||||
|
|
||||||
|
[target.'cfg(unix)'.dependencies]
|
||||||
|
umask = "1.0.0"
|
||||||
|
users = "0.11.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 = "1.0.3"
|
||||||
|
quickcheck_macros = "1.0.0"
|
||||||
|
|
||||||
|
[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()
|
||||||
|
}
|
@ -4,11 +4,10 @@ pub(crate) mod macros;
|
|||||||
mod from_delimited_data;
|
mod from_delimited_data;
|
||||||
mod to_delimited_data;
|
mod to_delimited_data;
|
||||||
|
|
||||||
pub(crate) mod alias;
|
|
||||||
pub(crate) mod ansi;
|
pub(crate) mod ansi;
|
||||||
pub(crate) mod append;
|
pub(crate) mod append;
|
||||||
pub(crate) mod args;
|
pub(crate) mod args;
|
||||||
pub(crate) mod autoenv;
|
pub mod autoenv;
|
||||||
pub(crate) mod autoenv_trust;
|
pub(crate) mod autoenv_trust;
|
||||||
pub(crate) mod autoenv_untrust;
|
pub(crate) mod autoenv_untrust;
|
||||||
pub(crate) mod autoview;
|
pub(crate) mod autoview;
|
||||||
@ -21,7 +20,7 @@ pub(crate) mod chart;
|
|||||||
pub(crate) mod classified;
|
pub(crate) mod classified;
|
||||||
#[cfg(feature = "clipboard-cli")]
|
#[cfg(feature = "clipboard-cli")]
|
||||||
pub(crate) mod clip;
|
pub(crate) mod clip;
|
||||||
pub(crate) mod command;
|
pub mod command;
|
||||||
pub(crate) mod compact;
|
pub(crate) mod compact;
|
||||||
pub(crate) mod config;
|
pub(crate) mod config;
|
||||||
pub(crate) mod constants;
|
pub(crate) mod constants;
|
||||||
@ -29,7 +28,9 @@ pub(crate) mod count;
|
|||||||
pub(crate) mod cp;
|
pub(crate) mod cp;
|
||||||
pub(crate) mod date;
|
pub(crate) mod date;
|
||||||
pub(crate) mod debug;
|
pub(crate) mod debug;
|
||||||
|
pub(crate) mod def;
|
||||||
pub(crate) mod default;
|
pub(crate) mod default;
|
||||||
|
pub mod default_context;
|
||||||
pub(crate) mod describe;
|
pub(crate) mod describe;
|
||||||
pub(crate) mod do_;
|
pub(crate) mod do_;
|
||||||
pub(crate) mod drop;
|
pub(crate) mod drop;
|
||||||
@ -42,6 +43,7 @@ pub(crate) mod every;
|
|||||||
pub(crate) mod exec;
|
pub(crate) mod exec;
|
||||||
pub(crate) mod exit;
|
pub(crate) mod exit;
|
||||||
pub(crate) mod first;
|
pub(crate) mod first;
|
||||||
|
pub(crate) mod flatten;
|
||||||
pub(crate) mod format;
|
pub(crate) mod format;
|
||||||
pub(crate) mod from;
|
pub(crate) mod from;
|
||||||
pub(crate) mod from_csv;
|
pub(crate) mod from_csv;
|
||||||
@ -61,6 +63,7 @@ pub(crate) mod from_yaml;
|
|||||||
pub(crate) mod get;
|
pub(crate) mod get;
|
||||||
pub(crate) mod group_by;
|
pub(crate) mod group_by;
|
||||||
pub(crate) mod group_by_date;
|
pub(crate) mod group_by_date;
|
||||||
|
pub(crate) mod hash_;
|
||||||
pub(crate) mod headers;
|
pub(crate) mod headers;
|
||||||
pub(crate) mod help;
|
pub(crate) mod help;
|
||||||
pub(crate) mod histogram;
|
pub(crate) mod histogram;
|
||||||
@ -70,6 +73,8 @@ pub(crate) mod insert;
|
|||||||
pub(crate) mod into_int;
|
pub(crate) mod into_int;
|
||||||
pub(crate) mod keep;
|
pub(crate) mod keep;
|
||||||
pub(crate) mod last;
|
pub(crate) mod last;
|
||||||
|
pub(crate) mod let_;
|
||||||
|
pub(crate) mod let_env;
|
||||||
pub(crate) mod lines;
|
pub(crate) mod lines;
|
||||||
pub(crate) mod ls;
|
pub(crate) mod ls;
|
||||||
pub(crate) mod math;
|
pub(crate) mod math;
|
||||||
@ -93,21 +98,24 @@ pub(crate) mod reject;
|
|||||||
pub(crate) mod rename;
|
pub(crate) mod rename;
|
||||||
pub(crate) mod reverse;
|
pub(crate) mod reverse;
|
||||||
pub(crate) mod rm;
|
pub(crate) mod rm;
|
||||||
pub(crate) mod run_alias;
|
|
||||||
pub(crate) mod run_external;
|
pub(crate) mod run_external;
|
||||||
pub(crate) mod save;
|
pub(crate) mod save;
|
||||||
pub(crate) mod select;
|
pub(crate) mod select;
|
||||||
|
pub(crate) mod seq;
|
||||||
|
pub(crate) mod seq_dates;
|
||||||
pub(crate) mod shells;
|
pub(crate) mod shells;
|
||||||
pub(crate) mod shuffle;
|
pub(crate) mod shuffle;
|
||||||
pub(crate) mod size;
|
pub(crate) mod size;
|
||||||
pub(crate) mod skip;
|
pub(crate) mod skip;
|
||||||
pub(crate) mod sleep;
|
pub(crate) mod sleep;
|
||||||
pub(crate) mod sort_by;
|
pub(crate) mod sort_by;
|
||||||
|
pub(crate) mod source;
|
||||||
pub(crate) mod split;
|
pub(crate) mod split;
|
||||||
pub(crate) mod split_by;
|
pub(crate) mod split_by;
|
||||||
pub(crate) mod str_;
|
pub(crate) mod str_;
|
||||||
pub(crate) mod table;
|
pub(crate) mod table;
|
||||||
pub(crate) mod tags;
|
pub(crate) mod tags;
|
||||||
|
pub(crate) mod termsize;
|
||||||
pub(crate) mod to;
|
pub(crate) mod to;
|
||||||
pub(crate) mod to_csv;
|
pub(crate) mod to_csv;
|
||||||
pub(crate) mod to_html;
|
pub(crate) mod to_html;
|
||||||
@ -129,11 +137,7 @@ pub(crate) mod wrap;
|
|||||||
|
|
||||||
pub(crate) use autoview::Autoview;
|
pub(crate) use autoview::Autoview;
|
||||||
pub(crate) use cd::Cd;
|
pub(crate) use cd::Cd;
|
||||||
pub(crate) use command::{
|
|
||||||
whole_stream_command, Command, Example, UnevaluatedCallInfo, WholeStreamCommand,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub(crate) use alias::Alias;
|
|
||||||
pub(crate) use ansi::Ansi;
|
pub(crate) use ansi::Ansi;
|
||||||
pub(crate) use append::Command as Append;
|
pub(crate) use append::Command as Append;
|
||||||
pub(crate) use autoenv::Autoenv;
|
pub(crate) use autoenv::Autoenv;
|
||||||
@ -150,8 +154,9 @@ pub(crate) use config::{
|
|||||||
};
|
};
|
||||||
pub(crate) use count::Count;
|
pub(crate) use count::Count;
|
||||||
pub(crate) use cp::Cpy;
|
pub(crate) use cp::Cpy;
|
||||||
pub(crate) use date::{Date, DateFormat, DateNow, DateUTC};
|
pub(crate) use date::{Date, DateFormat, DateListTimeZone, DateNow, DateToTable, DateToTimeZone};
|
||||||
pub(crate) use debug::Debug;
|
pub(crate) use debug::Debug;
|
||||||
|
pub(crate) use def::Def;
|
||||||
pub(crate) use default::Default;
|
pub(crate) use default::Default;
|
||||||
pub(crate) use describe::Describe;
|
pub(crate) use describe::Describe;
|
||||||
pub(crate) use do_::Do;
|
pub(crate) use do_::Do;
|
||||||
@ -175,7 +180,8 @@ pub(crate) use every::Every;
|
|||||||
pub(crate) use exec::Exec;
|
pub(crate) use exec::Exec;
|
||||||
pub(crate) use exit::Exit;
|
pub(crate) use exit::Exit;
|
||||||
pub(crate) use first::First;
|
pub(crate) use first::First;
|
||||||
pub(crate) use format::Format;
|
pub(crate) use flatten::Command as Flatten;
|
||||||
|
pub(crate) use format::{FileSize, Format};
|
||||||
pub(crate) use from::From;
|
pub(crate) use from::From;
|
||||||
pub(crate) use from_csv::FromCSV;
|
pub(crate) use from_csv::FromCSV;
|
||||||
pub(crate) use from_eml::FromEML;
|
pub(crate) use from_eml::FromEML;
|
||||||
@ -192,9 +198,10 @@ pub(crate) use from_xlsx::FromXLSX;
|
|||||||
pub(crate) use from_xml::FromXML;
|
pub(crate) use from_xml::FromXML;
|
||||||
pub(crate) use from_yaml::FromYAML;
|
pub(crate) use from_yaml::FromYAML;
|
||||||
pub(crate) use from_yaml::FromYML;
|
pub(crate) use from_yaml::FromYML;
|
||||||
pub(crate) use get::Get;
|
pub(crate) use get::Command as Get;
|
||||||
pub(crate) use group_by::Command as GroupBy;
|
pub(crate) use group_by::Command as GroupBy;
|
||||||
pub(crate) use group_by_date::GroupByDate;
|
pub(crate) use group_by_date::GroupByDate;
|
||||||
|
pub(crate) use hash_::{Hash, HashBase64};
|
||||||
pub(crate) use headers::Headers;
|
pub(crate) use headers::Headers;
|
||||||
pub(crate) use help::Help;
|
pub(crate) use help::Help;
|
||||||
pub(crate) use histogram::Histogram;
|
pub(crate) use histogram::Histogram;
|
||||||
@ -203,15 +210,17 @@ pub(crate) use insert::Command as Insert;
|
|||||||
pub(crate) use into_int::IntoInt;
|
pub(crate) use into_int::IntoInt;
|
||||||
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
|
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
|
||||||
pub(crate) use last::Last;
|
pub(crate) use last::Last;
|
||||||
|
pub(crate) use let_::Let;
|
||||||
|
pub(crate) use let_env::LetEnv;
|
||||||
pub(crate) use lines::Lines;
|
pub(crate) use lines::Lines;
|
||||||
pub(crate) use ls::Ls;
|
pub(crate) use ls::Ls;
|
||||||
pub(crate) use math::{
|
pub(crate) use math::{
|
||||||
Math, MathAverage, MathEval, MathMaximum, MathMedian, MathMinimum, MathMode, MathProduct,
|
Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian,
|
||||||
MathStddev, MathSummation, MathVariance,
|
MathMinimum, MathMode, MathProduct, MathRound, MathStddev, MathSummation, MathVariance,
|
||||||
};
|
};
|
||||||
pub(crate) use merge::Merge;
|
pub(crate) use merge::Merge;
|
||||||
pub(crate) use mkdir::Mkdir;
|
pub(crate) use mkdir::Mkdir;
|
||||||
pub(crate) use move_::{Move, MoveColumn, Mv};
|
pub(crate) use move_::{Move, Mv};
|
||||||
pub(crate) use next::Next;
|
pub(crate) use next::Next;
|
||||||
pub(crate) use nth::Nth;
|
pub(crate) use nth::Nth;
|
||||||
pub(crate) use open::Open;
|
pub(crate) use open::Open;
|
||||||
@ -226,7 +235,9 @@ pub(crate) use prev::Previous;
|
|||||||
pub(crate) use pwd::Pwd;
|
pub(crate) use pwd::Pwd;
|
||||||
#[cfg(feature = "uuid_crate")]
|
#[cfg(feature = "uuid_crate")]
|
||||||
pub(crate) use random::RandomUUID;
|
pub(crate) use random::RandomUUID;
|
||||||
pub(crate) use random::{Random, RandomBool, RandomDice, RandomInteger};
|
pub(crate) use random::{
|
||||||
|
Random, RandomBool, RandomChars, RandomDecimal, RandomDice, RandomInteger,
|
||||||
|
};
|
||||||
pub(crate) use range::Range;
|
pub(crate) use range::Range;
|
||||||
pub(crate) use reduce::Reduce;
|
pub(crate) use reduce::Reduce;
|
||||||
pub(crate) use reject::Reject;
|
pub(crate) use reject::Reject;
|
||||||
@ -235,28 +246,32 @@ pub(crate) use reverse::Reverse;
|
|||||||
pub(crate) use rm::Remove;
|
pub(crate) use rm::Remove;
|
||||||
pub(crate) use run_external::RunExternalCommand;
|
pub(crate) use run_external::RunExternalCommand;
|
||||||
pub(crate) use save::Save;
|
pub(crate) use save::Save;
|
||||||
pub(crate) use select::Select;
|
pub(crate) use select::Command as Select;
|
||||||
|
pub(crate) use seq::Seq;
|
||||||
|
pub(crate) use seq_dates::SeqDates;
|
||||||
pub(crate) use shells::Shells;
|
pub(crate) use shells::Shells;
|
||||||
pub(crate) use shuffle::Shuffle;
|
pub(crate) use shuffle::Shuffle;
|
||||||
pub(crate) use size::Size;
|
pub(crate) use size::Size;
|
||||||
pub(crate) use skip::{Skip, SkipUntil, SkipWhile};
|
pub(crate) use skip::{Skip, SkipUntil, SkipWhile};
|
||||||
pub(crate) use sleep::Sleep;
|
pub(crate) use sleep::Sleep;
|
||||||
pub(crate) use sort_by::SortBy;
|
pub(crate) use sort_by::SortBy;
|
||||||
|
pub(crate) use source::Source;
|
||||||
pub(crate) use split::{Split, SplitChars, SplitColumn, SplitRow};
|
pub(crate) use split::{Split, SplitChars, SplitColumn, SplitRow};
|
||||||
pub(crate) use split_by::SplitBy;
|
pub(crate) use split_by::SplitBy;
|
||||||
pub(crate) use str_::{
|
pub(crate) use str_::{
|
||||||
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
|
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
|
||||||
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad,
|
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad,
|
||||||
StrReverse, StrScreamingSnakeCase, StrSet, StrSnakeCase, StrStartsWith, StrSubstring,
|
StrReverse, StrScreamingSnakeCase, StrSnakeCase, StrStartsWith, StrSubstring, StrToDatetime,
|
||||||
StrToDatetime, StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
|
||||||
};
|
};
|
||||||
pub(crate) use table::Table;
|
pub(crate) use table::Table;
|
||||||
pub(crate) use tags::Tags;
|
pub(crate) use tags::Tags;
|
||||||
|
pub(crate) use termsize::TermSize;
|
||||||
pub(crate) use to::To;
|
pub(crate) use to::To;
|
||||||
pub(crate) use to_csv::ToCSV;
|
pub(crate) use to_csv::ToCSV;
|
||||||
pub(crate) use to_html::ToHTML;
|
pub(crate) use to_html::ToHTML;
|
||||||
pub(crate) use to_json::ToJSON;
|
pub(crate) use to_json::ToJSON;
|
||||||
pub(crate) use to_md::ToMarkdown;
|
pub(crate) use to_md::Command as ToMarkdown;
|
||||||
pub(crate) use to_toml::ToTOML;
|
pub(crate) use to_toml::ToTOML;
|
||||||
pub(crate) use to_tsv::ToTSV;
|
pub(crate) use to_tsv::ToTSV;
|
||||||
pub(crate) use to_url::ToURL;
|
pub(crate) use to_url::ToURL;
|
||||||
@ -274,23 +289,61 @@ pub(crate) use wrap::Wrap;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::commands::whole_stream_command;
|
|
||||||
use crate::examples::{test_anchors, test_examples};
|
use crate::examples::{test_anchors, test_examples};
|
||||||
|
use nu_engine::{whole_stream_command, Command};
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
|
|
||||||
fn commands() -> Vec<Command> {
|
fn full_tests() -> Vec<Command> {
|
||||||
vec![
|
vec![
|
||||||
whole_stream_command(Append),
|
whole_stream_command(Append),
|
||||||
whole_stream_command(GroupBy),
|
whole_stream_command(GroupBy),
|
||||||
whole_stream_command(Insert),
|
whole_stream_command(Insert),
|
||||||
|
whole_stream_command(Move),
|
||||||
whole_stream_command(Update),
|
whole_stream_command(Update),
|
||||||
whole_stream_command(Empty),
|
whole_stream_command(Empty),
|
||||||
|
// whole_stream_command(Select),
|
||||||
|
// whole_stream_command(Get),
|
||||||
|
// 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),
|
||||||
|
whole_stream_command(ToMarkdown),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn only_examples() -> Vec<Command> {
|
||||||
|
let mut commands = full_tests();
|
||||||
|
commands.extend(vec![whole_stream_command(Flatten)]);
|
||||||
|
commands
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
for cmd in commands() {
|
for cmd in only_examples() {
|
||||||
test_examples(cmd)?;
|
test_examples(cmd)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -299,7 +352,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn tracks_metadata() -> Result<(), ShellError> {
|
fn tracks_metadata() -> Result<(), ShellError> {
|
||||||
for cmd in commands() {
|
for cmd in full_tests() {
|
||||||
test_anchors(cmd)?;
|
test_anchors(cmd)?;
|
||||||
}
|
}
|
||||||
|
|
291
crates/nu-command/src/commands/ansi.rs
Normal file
291
crates/nu-command/src/commands/ansi.rs
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
pub struct Ansi;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct AnsiArgs {
|
||||||
|
code: Value,
|
||||||
|
escape: Option<Tagged<String>>,
|
||||||
|
osc: Option<Tagged<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl WholeStreamCommand for Ansi {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"ansi"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("ansi")
|
||||||
|
.optional(
|
||||||
|
"code",
|
||||||
|
SyntaxShape::Any,
|
||||||
|
"the name of the code to use like 'green' or 'reset' to reset the color",
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"escape", // \x1b[
|
||||||
|
SyntaxShape::Any,
|
||||||
|
"escape sequence without the escape character(s)",
|
||||||
|
Some('e'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"osc", // \x1b]
|
||||||
|
SyntaxShape::Any,
|
||||||
|
"operating system command (ocs) escape sequence without the escape character(s)",
|
||||||
|
Some('o'),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
r#"Output ANSI codes to change color
|
||||||
|
|
||||||
|
For escape sequences:
|
||||||
|
Escape: '\x1b[' is not required for --escape parameter
|
||||||
|
Format: #(;#)m
|
||||||
|
Example: 1;31m for bold red or 2;37;41m for dimmed white fg with red bg
|
||||||
|
There can be multiple text formatting sequence numbers
|
||||||
|
separated by a ; and ending with an m where the # is of the
|
||||||
|
following values:
|
||||||
|
attributes
|
||||||
|
0 reset / normal display
|
||||||
|
1 bold or increased intensity
|
||||||
|
2 faint or decreased intensity
|
||||||
|
3 italic on (non-mono font)
|
||||||
|
4 underline on
|
||||||
|
5 slow blink on
|
||||||
|
6 fast blink on
|
||||||
|
7 reverse video on
|
||||||
|
8 nondisplayed (invisible) on
|
||||||
|
9 strike-through on
|
||||||
|
|
||||||
|
foreground/bright colors background/bright colors
|
||||||
|
30/90 black 40/100 black
|
||||||
|
31/91 red 41/101 red
|
||||||
|
32/92 green 42/102 green
|
||||||
|
33/93 yellow 43/103 yellow
|
||||||
|
34/94 blue 44/104 blue
|
||||||
|
35/95 magenta 45/105 magenta
|
||||||
|
36/96 cyan 46/106 cyan
|
||||||
|
37/97 white 47/107 white
|
||||||
|
https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||||
|
|
||||||
|
OSC: '\x1b]' is not required for --osc parameter
|
||||||
|
Example: echo [$(ansi -o '0') 'some title' $(char bel)] | str collect
|
||||||
|
Format: #
|
||||||
|
0 Set window title and icon name
|
||||||
|
1 Set icon name
|
||||||
|
2 Set window title
|
||||||
|
4 Set/read color palette
|
||||||
|
9 iTerm2 Grown notifications
|
||||||
|
10 Set foreground color (x11 color spec)
|
||||||
|
11 Set background color (x11 color spec)
|
||||||
|
... others"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Change color to green",
|
||||||
|
example: r#"ansi green"#,
|
||||||
|
result: Some(vec![Value::from("\u{1b}[32m")]),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Reset the color",
|
||||||
|
example: r#"ansi reset"#,
|
||||||
|
result: Some(vec![Value::from("\u{1b}[0m")]),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description:
|
||||||
|
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
|
||||||
|
example: r#"echo [$(ansi rb) Hello " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#,
|
||||||
|
result: Some(vec![Value::from(
|
||||||
|
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld",
|
||||||
|
)]),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description:
|
||||||
|
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
|
||||||
|
example: r#"echo [$(ansi -e '3;93;41m') Hello $(ansi reset) " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#,
|
||||||
|
result: Some(vec![Value::from(
|
||||||
|
"\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld",
|
||||||
|
)]),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
let (AnsiArgs { code, escape, osc }, _) = args.process().await?;
|
||||||
|
|
||||||
|
if let Some(e) = escape {
|
||||||
|
let esc_vec: Vec<char> = e.item.chars().collect();
|
||||||
|
if esc_vec[0] == '\\' {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"no need for escape characters",
|
||||||
|
"no need for escape characters",
|
||||||
|
e.tag(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let output = format!("\x1b[{}", e.item);
|
||||||
|
return Ok(OutputStream::one(ReturnSuccess::value(
|
||||||
|
UntaggedValue::string(output).into_value(e.tag()),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(o) = osc {
|
||||||
|
let osc_vec: Vec<char> = o.item.chars().collect();
|
||||||
|
if osc_vec[0] == '\\' {
|
||||||
|
return Err(ShellError::labeled_error(
|
||||||
|
"no need for escape characters",
|
||||||
|
"no need for escape characters",
|
||||||
|
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);
|
||||||
|
return Ok(OutputStream::one(ReturnSuccess::value(
|
||||||
|
UntaggedValue::string(output).into_value(o.tag()),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
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(code.tag()),
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Err(ShellError::labeled_error(
|
||||||
|
"Unknown ansi code",
|
||||||
|
"unknown ansi code",
|
||||||
|
code.tag(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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()),
|
||||||
|
"gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()),
|
||||||
|
"gi" | "green_italic" => Some(Color::Green.italic().prefix().to_string()),
|
||||||
|
"gd" | "green_dimmed" => Some(Color::Green.dimmed().prefix().to_string()),
|
||||||
|
"gr" | "green_reverse" => Some(Color::Green.reverse().prefix().to_string()),
|
||||||
|
"r" | "red" => Some(Color::Red.prefix().to_string()),
|
||||||
|
"rb" | "red_bold" => Some(Color::Red.bold().prefix().to_string()),
|
||||||
|
"ru" | "red_underline" => Some(Color::Red.underline().prefix().to_string()),
|
||||||
|
"ri" | "red_italic" => Some(Color::Red.italic().prefix().to_string()),
|
||||||
|
"rd" | "red_dimmed" => Some(Color::Red.dimmed().prefix().to_string()),
|
||||||
|
"rr" | "red_reverse" => Some(Color::Red.reverse().prefix().to_string()),
|
||||||
|
"u" | "blue" => Some(Color::Blue.prefix().to_string()),
|
||||||
|
"ub" | "blue_bold" => Some(Color::Blue.bold().prefix().to_string()),
|
||||||
|
"uu" | "blue_underline" => Some(Color::Blue.underline().prefix().to_string()),
|
||||||
|
"ui" | "blue_italic" => Some(Color::Blue.italic().prefix().to_string()),
|
||||||
|
"ud" | "blue_dimmed" => Some(Color::Blue.dimmed().prefix().to_string()),
|
||||||
|
"ur" | "blue_reverse" => Some(Color::Blue.reverse().prefix().to_string()),
|
||||||
|
"b" | "black" => Some(Color::Black.prefix().to_string()),
|
||||||
|
"bb" | "black_bold" => Some(Color::Black.bold().prefix().to_string()),
|
||||||
|
"bu" | "black_underline" => Some(Color::Black.underline().prefix().to_string()),
|
||||||
|
"bi" | "black_italic" => Some(Color::Black.italic().prefix().to_string()),
|
||||||
|
"bd" | "black_dimmed" => Some(Color::Black.dimmed().prefix().to_string()),
|
||||||
|
"br" | "black_reverse" => Some(Color::Black.reverse().prefix().to_string()),
|
||||||
|
"y" | "yellow" => Some(Color::Yellow.prefix().to_string()),
|
||||||
|
"yb" | "yellow_bold" => Some(Color::Yellow.bold().prefix().to_string()),
|
||||||
|
"yu" | "yellow_underline" => Some(Color::Yellow.underline().prefix().to_string()),
|
||||||
|
"yi" | "yellow_italic" => Some(Color::Yellow.italic().prefix().to_string()),
|
||||||
|
"yd" | "yellow_dimmed" => Some(Color::Yellow.dimmed().prefix().to_string()),
|
||||||
|
"yr" | "yellow_reverse" => Some(Color::Yellow.reverse().prefix().to_string()),
|
||||||
|
"p" | "purple" => Some(Color::Purple.prefix().to_string()),
|
||||||
|
"pb" | "purple_bold" => Some(Color::Purple.bold().prefix().to_string()),
|
||||||
|
"pu" | "purple_underline" => Some(Color::Purple.underline().prefix().to_string()),
|
||||||
|
"pi" | "purple_italic" => Some(Color::Purple.italic().prefix().to_string()),
|
||||||
|
"pd" | "purple_dimmed" => Some(Color::Purple.dimmed().prefix().to_string()),
|
||||||
|
"pr" | "purple_reverse" => Some(Color::Purple.reverse().prefix().to_string()),
|
||||||
|
"c" | "cyan" => Some(Color::Cyan.prefix().to_string()),
|
||||||
|
"cb" | "cyan_bold" => Some(Color::Cyan.bold().prefix().to_string()),
|
||||||
|
"cu" | "cyan_underline" => Some(Color::Cyan.underline().prefix().to_string()),
|
||||||
|
"ci" | "cyan_italic" => Some(Color::Cyan.italic().prefix().to_string()),
|
||||||
|
"cd" | "cyan_dimmed" => Some(Color::Cyan.dimmed().prefix().to_string()),
|
||||||
|
"cr" | "cyan_reverse" => Some(Color::Cyan.reverse().prefix().to_string()),
|
||||||
|
"w" | "white" => Some(Color::White.prefix().to_string()),
|
||||||
|
"wb" | "white_bold" => Some(Color::White.bold().prefix().to_string()),
|
||||||
|
"wu" | "white_underline" => Some(Color::White.underline().prefix().to_string()),
|
||||||
|
"wi" | "white_italic" => Some(Color::White.italic().prefix().to_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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Ansi;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
|
test_examples(Ansi {})
|
||||||
|
}
|
||||||
|
}
|
85
crates/nu-command/src/commands/append.rs
Normal file
85
crates/nu-command/src/commands/append.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct Arguments {
|
||||||
|
value: Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Command;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl WholeStreamCommand for Command {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"append"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("append").required(
|
||||||
|
"row value",
|
||||||
|
SyntaxShape::Any,
|
||||||
|
"the value of the row to append to the table",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Append a row to the table"
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
let (Arguments { mut value }, input) = args.process().await?;
|
||||||
|
|
||||||
|
let input: Vec<Value> = input.collect().await;
|
||||||
|
|
||||||
|
if let Some(first) = input.get(0) {
|
||||||
|
value.tag = first.tag();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if we are trying to append a row literal
|
||||||
|
if let Value {
|
||||||
|
value: UntaggedValue::Table(values),
|
||||||
|
tag,
|
||||||
|
} = &value
|
||||||
|
{
|
||||||
|
if values.len() == 1 && values[0].is_row() {
|
||||||
|
value = values[0].value.clone().into_value(tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(futures::stream::iter(
|
||||||
|
input
|
||||||
|
.into_iter()
|
||||||
|
.chain(vec![value])
|
||||||
|
.map(ReturnSuccess::value),
|
||||||
|
)
|
||||||
|
.to_output_stream())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
use nu_protocol::row;
|
||||||
|
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Add values to the end of the table",
|
||||||
|
example: "echo [1 2 3] | append 4",
|
||||||
|
result: Some(vec![
|
||||||
|
UntaggedValue::int(1).into(),
|
||||||
|
UntaggedValue::int(2).into(),
|
||||||
|
UntaggedValue::int(3).into(),
|
||||||
|
UntaggedValue::int(4).into(),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Add row value to the end of the table",
|
||||||
|
example: "echo [[country]; [Ecuador] ['New Zealand']] | append [[country]; [USA]]",
|
||||||
|
result: Some(vec![
|
||||||
|
row! { "country".into() => Value::from("Ecuador")},
|
||||||
|
row! { "country".into() => Value::from("New Zealand")},
|
||||||
|
row! { "country".into() => Value::from("USA")},
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -1,12 +1,12 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use std::path::PathBuf;
|
use std::path::{Path, PathBuf};
|
||||||
pub struct Autoenv;
|
pub struct Autoenv;
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Default)]
|
#[derive(Deserialize, Serialize, Debug, Default)]
|
||||||
@ -20,11 +20,12 @@ impl Trusted {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn file_is_trusted(nu_env_file: &PathBuf, content: &[u8]) -> Result<bool, ShellError> {
|
pub fn file_is_trusted(nu_env_file: &Path, content: &[u8]) -> Result<bool, ShellError> {
|
||||||
let contentdigest = Sha256::digest(&content).as_slice().to_vec();
|
let contentdigest = Sha256::digest(&content).as_slice().to_vec();
|
||||||
let nufile = std::fs::canonicalize(nu_env_file)?;
|
let nufile = std::fs::canonicalize(nu_env_file)?;
|
||||||
|
|
||||||
let trusted = read_trusted()?;
|
let trusted = read_trusted()?;
|
||||||
|
|
||||||
Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest))
|
Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,15 +62,9 @@ The file can contain several optional sections:
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("autoenv")
|
Signature::build("autoenv")
|
||||||
}
|
}
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
|
||||||
_args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
Ok(OutputStream::one(ReturnSuccess::value(
|
Ok(OutputStream::one(ReturnSuccess::value(
|
||||||
UntaggedValue::string(crate::commands::help::get_help(&Autoenv, ®istry))
|
UntaggedValue::string(get_help(&Autoenv, &args.scope)).into_value(Tag::unknown()),
|
||||||
.into_value(Tag::unknown()),
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,15 +72,15 @@ The file can contain several optional sections:
|
|||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Example .nu-env file",
|
description: "Example .nu-env file",
|
||||||
example: r#"cat .nu-env
|
example: r#"cat .nu-env
|
||||||
[env]
|
[env]
|
||||||
mykey = "myvalue"
|
mykey = "myvalue"
|
||||||
|
|
||||||
[scriptvars]
|
[scriptvars]
|
||||||
myscript = "echo myval"
|
myscript = "echo myval"
|
||||||
|
|
||||||
[scripts]
|
[scripts]
|
||||||
entryscripts = ["touch hello.txt", "touch hello2.txt"]
|
entryscripts = ["touch hello.txt", "touch hello2.txt"]
|
||||||
exitscripts = ["touch bye.txt"]"#,
|
exitscripts = ["touch bye.txt"]"#,
|
||||||
result: None,
|
result: None,
|
||||||
}]
|
}]
|
||||||
}
|
}
|
@ -1,6 +1,6 @@
|
|||||||
use super::autoenv::read_trusted;
|
use super::autoenv::read_trusted;
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::SyntaxShape;
|
use nu_protocol::SyntaxShape;
|
||||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||||
@ -22,14 +22,11 @@ impl WholeStreamCommand for AutoenvTrust {
|
|||||||
"Trust a .nu-env file in the current or given directory"
|
"Trust a .nu-env file in the current or given directory"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
let tag = args.call_info.name_tag.clone();
|
||||||
|
let ctx = EvaluationContext::from_args(&args);
|
||||||
|
|
||||||
let file_to_trust = match args.call_info.evaluate(registry).await?.args.nth(0) {
|
let file_to_trust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
|
||||||
Some(Value {
|
Some(Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||||
tag: _,
|
tag: _,
|
@ -1,6 +1,6 @@
|
|||||||
use super::autoenv::Trusted;
|
use super::autoenv::Trusted;
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::SyntaxShape;
|
use nu_protocol::SyntaxShape;
|
||||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
|
||||||
@ -26,13 +26,10 @@ impl WholeStreamCommand for AutoenvUnTrust {
|
|||||||
"Untrust a .nu-env file in the current or given directory"
|
"Untrust a .nu-env file in the current or given directory"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
let tag = args.call_info.name_tag.clone();
|
||||||
let file_to_untrust = match args.call_info.evaluate(registry).await?.args.nth(0) {
|
let ctx = EvaluationContext::from_args(&args);
|
||||||
|
let file_to_untrust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
|
||||||
Some(Value {
|
Some(Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
value: UntaggedValue::Primitive(Primitive::String(ref path)),
|
||||||
tag: _,
|
tag: _,
|
@ -1,11 +1,11 @@
|
|||||||
use crate::commands::autoview::options::{ConfigExtensions, NuConfig as AutoViewConfiguration};
|
use crate::commands::autoview::options::{ConfigExtensions, NuConfig as AutoViewConfiguration};
|
||||||
use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand};
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::primitive::get_color_config;
|
use crate::primitive::get_color_config;
|
||||||
use nu_data::value::format_leaf;
|
use nu_data::value::format_leaf;
|
||||||
|
use nu_engine::{UnevaluatedCallInfo, WholeStreamCommand};
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
|
use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
|
||||||
use nu_protocol::{Primitive, Scope, Signature, UntaggedValue, Value};
|
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
|
||||||
use nu_table::TextStyle;
|
use nu_table::TextStyle;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use std::sync::atomic::AtomicBool;
|
use std::sync::atomic::AtomicBool;
|
||||||
@ -26,20 +26,15 @@ impl WholeStreamCommand for Command {
|
|||||||
"View the contents of the pipeline as a table or list."
|
"View the contents of the pipeline as a table or list."
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
autoview(RunnableContext {
|
autoview(RunnableContext {
|
||||||
input: args.input,
|
input: args.input,
|
||||||
registry: registry.clone(),
|
scope: args.scope.clone(),
|
||||||
shell_manager: args.shell_manager,
|
shell_manager: args.shell_manager,
|
||||||
host: args.host,
|
host: args.host,
|
||||||
ctrl_c: args.ctrl_c,
|
ctrl_c: args.ctrl_c,
|
||||||
current_errors: args.current_errors,
|
current_errors: args.current_errors,
|
||||||
name: args.call_info.name_tag,
|
name: args.call_info.name_tag,
|
||||||
raw_input: args.raw_input,
|
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
@ -65,7 +60,7 @@ pub struct RunnableContextWithoutInput {
|
|||||||
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
|
||||||
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
|
||||||
pub ctrl_c: Arc<AtomicBool>,
|
pub ctrl_c: Arc<AtomicBool>,
|
||||||
pub registry: CommandRegistry,
|
pub scope: Scope,
|
||||||
pub name: Tag,
|
pub name: Tag,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +71,7 @@ impl RunnableContextWithoutInput {
|
|||||||
host: context.host,
|
host: context.host,
|
||||||
ctrl_c: context.ctrl_c,
|
ctrl_c: context.ctrl_c,
|
||||||
current_errors: context.current_errors,
|
current_errors: context.current_errors,
|
||||||
registry: context.registry,
|
scope: context.scope,
|
||||||
name: context.name,
|
name: context.name,
|
||||||
};
|
};
|
||||||
(context.input, new_context)
|
(context.input, new_context)
|
||||||
@ -109,7 +104,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
|||||||
|
|
||||||
if let Some(table) = table {
|
if let Some(table) = table {
|
||||||
let command_args = create_default_command_args(&context).with_input(stream);
|
let command_args = create_default_command_args(&context).with_input(stream);
|
||||||
let result = table.run(command_args, &context.registry).await?;
|
let result = table.run(command_args).await?;
|
||||||
result.collect::<Vec<_>>().await;
|
result.collect::<Vec<_>>().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,7 +121,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
|||||||
);
|
);
|
||||||
let command_args =
|
let command_args =
|
||||||
create_default_command_args(&context).with_input(stream);
|
create_default_command_args(&context).with_input(stream);
|
||||||
let result = text.run(command_args, &context.registry).await?;
|
let result = text.run(command_args).await?;
|
||||||
result.collect::<Vec<_>>().await;
|
result.collect::<Vec<_>>().await;
|
||||||
} else {
|
} else {
|
||||||
out!("{}", s);
|
out!("{}", s);
|
||||||
@ -139,30 +134,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
|||||||
out!("{}", s);
|
out!("{}", s);
|
||||||
}
|
}
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::Line(ref s)),
|
value: UntaggedValue::Primitive(Primitive::FilePath(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, &context.registry).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)),
|
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
out!("{}", s.display());
|
out!("{}", s.display());
|
||||||
@ -201,6 +173,13 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
|||||||
let output = format_leaf(&x).plain_string(100_000);
|
let output = format_leaf(&x).plain_string(100_000);
|
||||||
out!("{}", output);
|
out!("{}", output);
|
||||||
}
|
}
|
||||||
|
Value {
|
||||||
|
value: UntaggedValue::Primitive(Primitive::Filesize(_)),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
let output = format_leaf(&x).plain_string(100_000);
|
||||||
|
out!("{}", output);
|
||||||
|
}
|
||||||
Value {
|
Value {
|
||||||
value: UntaggedValue::Primitive(Primitive::Date(d)),
|
value: UntaggedValue::Primitive(Primitive::Date(d)),
|
||||||
..
|
..
|
||||||
@ -224,7 +203,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
|||||||
stream.push_back(x);
|
stream.push_back(x);
|
||||||
let command_args =
|
let command_args =
|
||||||
create_default_command_args(&context).with_input(stream);
|
create_default_command_args(&context).with_input(stream);
|
||||||
let result = binary.run(command_args, &context.registry).await?;
|
let result = binary.run(command_args).await?;
|
||||||
result.collect::<Vec<_>>().await;
|
result.collect::<Vec<_>>().await;
|
||||||
} else {
|
} else {
|
||||||
use pretty_hex::*;
|
use pretty_hex::*;
|
||||||
@ -276,7 +255,12 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
|||||||
|
|
||||||
nu_table::draw_table(&table, term_width, &color_hm);
|
nu_table::draw_table(&table, term_width, &color_hm);
|
||||||
}
|
}
|
||||||
|
Value {
|
||||||
|
value: UntaggedValue::Primitive(Primitive::Nothing),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// Do nothing
|
||||||
|
}
|
||||||
Value {
|
Value {
|
||||||
value: ref item, ..
|
value: ref item, ..
|
||||||
} => {
|
} => {
|
||||||
@ -285,7 +269,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
|
|||||||
stream.push_back(x);
|
stream.push_back(x);
|
||||||
let command_args =
|
let command_args =
|
||||||
create_default_command_args(&context).with_input(stream);
|
create_default_command_args(&context).with_input(stream);
|
||||||
let result = table.run(command_args, &context.registry).await?;
|
let result = table.run(command_args).await?;
|
||||||
result.collect::<Vec<_>>().await;
|
result.collect::<Vec<_>>().await;
|
||||||
} else {
|
} else {
|
||||||
out!("{:?}", item);
|
out!("{:?}", item);
|
||||||
@ -318,8 +302,8 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
|
|||||||
external_redirection: ExternalRedirection::Stdout,
|
external_redirection: ExternalRedirection::Stdout,
|
||||||
},
|
},
|
||||||
name_tag: context.name.clone(),
|
name_tag: context.name.clone(),
|
||||||
scope: Scope::create(),
|
|
||||||
},
|
},
|
||||||
|
scope: Scope::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,6 +316,6 @@ mod tests {
|
|||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
Ok(test_examples(Command {})?)
|
test_examples(Command {})
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,12 @@
|
|||||||
use crate::commands::classified::block::run_block;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
#[cfg(feature = "rich-benchmark")]
|
// #[cfg(feature = "rich-benchmark")]
|
||||||
use heim::cpu::time;
|
// use heim::cpu::time;
|
||||||
|
use nu_engine::run_block;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
hir::{Block, ClassifiedCommand, Commands, InternalCommand},
|
hir::{Block, CapturedBlock, ClassifiedCommand, Group, InternalCommand, Pipeline},
|
||||||
Dictionary, Scope, Signature, SyntaxShape, UntaggedValue, Value,
|
Dictionary, Signature, SyntaxShape, UntaggedValue, Value,
|
||||||
};
|
};
|
||||||
use rand::{
|
use rand::{
|
||||||
distributions::Alphanumeric,
|
distributions::Alphanumeric,
|
||||||
@ -19,8 +19,8 @@ pub struct Benchmark;
|
|||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
struct BenchmarkArgs {
|
struct BenchmarkArgs {
|
||||||
block: Block,
|
block: CapturedBlock,
|
||||||
passthrough: Option<Block>,
|
passthrough: Option<CapturedBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
@ -48,12 +48,8 @@ impl WholeStreamCommand for Benchmark {
|
|||||||
"Runs a block and returns the time it took to execute it"
|
"Runs a block and returns the time it took to execute it"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
benchmark(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
benchmark(args, registry).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -72,78 +68,73 @@ impl WholeStreamCommand for Benchmark {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn benchmark(
|
async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
raw_args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
|
|
||||||
let tag = raw_args.call_info.args.span;
|
let tag = raw_args.call_info.args.span;
|
||||||
let mut context = EvaluationContext::from_raw(&raw_args, ®istry);
|
let mut context = EvaluationContext::from_args(&raw_args);
|
||||||
let scope = raw_args.call_info.scope.clone();
|
let scope = raw_args.scope.clone();
|
||||||
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process(®istry).await?;
|
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process().await?;
|
||||||
|
|
||||||
let env = scope.env();
|
let env = scope.get_env_vars();
|
||||||
let name = generate_free_name(&env);
|
let name = generate_free_name(&env);
|
||||||
let mut env = IndexMap::new();
|
|
||||||
env.insert(name, generate_random_env_value());
|
scope.add_env_var(name, generate_random_env_value());
|
||||||
let scope = Scope::append_env(scope, env);
|
|
||||||
|
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
|
||||||
#[cfg(feature = "rich-benchmark")]
|
// #[cfg(feature = "rich-benchmark")]
|
||||||
let start = time().await;
|
// let start = time().await;
|
||||||
|
|
||||||
let result = run_block(&block, &mut context, input, scope.clone()).await;
|
context.scope.enter_scope();
|
||||||
|
let result = run_block(&block.block, &context, input).await;
|
||||||
|
context.scope.exit_scope();
|
||||||
let output = result?.into_vec().await;
|
let output = result?.into_vec().await;
|
||||||
|
|
||||||
#[cfg(feature = "rich-benchmark")]
|
// #[cfg(feature = "rich-benchmark")]
|
||||||
let end = time().await;
|
// let end = time().await;
|
||||||
|
|
||||||
let end_time = Instant::now();
|
let end_time = Instant::now();
|
||||||
context.clear_errors();
|
context.clear_errors();
|
||||||
|
|
||||||
// return basic runtime
|
// return basic runtime
|
||||||
#[cfg(not(feature = "rich-benchmark"))]
|
//#[cfg(not(feature = "rich-benchmark"))]
|
||||||
{
|
{
|
||||||
let mut indexmap = IndexMap::with_capacity(1);
|
let mut indexmap = IndexMap::with_capacity(1);
|
||||||
|
|
||||||
let real_time = into_big_int(end_time - start_time);
|
let real_time = into_big_int(end_time - start_time);
|
||||||
indexmap.insert("real time".to_string(), real_time);
|
indexmap.insert("real time".to_string(), real_time);
|
||||||
benchmark_output(indexmap, output, passthrough, &tag, &mut context, scope).await
|
benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
|
||||||
}
|
}
|
||||||
// return advanced stats
|
// return advanced stats
|
||||||
#[cfg(feature = "rich-benchmark")]
|
// #[cfg(feature = "rich-benchmark")]
|
||||||
if let (Ok(start), Ok(end)) = (start, end) {
|
// if let (Ok(start), Ok(end)) = (start, end) {
|
||||||
let mut indexmap = IndexMap::with_capacity(4);
|
// let mut indexmap = IndexMap::with_capacity(4);
|
||||||
|
|
||||||
let real_time = into_big_int(end_time - start_time);
|
// let real_time = into_big_int(end_time - start_time);
|
||||||
indexmap.insert("real time".to_string(), real_time);
|
// indexmap.insert("real time".to_string(), real_time);
|
||||||
|
|
||||||
let user_time = into_big_int(end.user() - start.user());
|
// let user_time = into_big_int(end.user() - start.user());
|
||||||
indexmap.insert("user time".to_string(), user_time);
|
// indexmap.insert("user time".to_string(), user_time);
|
||||||
|
|
||||||
let system_time = into_big_int(end.system() - start.system());
|
// let system_time = into_big_int(end.system() - start.system());
|
||||||
indexmap.insert("system time".to_string(), system_time);
|
// indexmap.insert("system time".to_string(), system_time);
|
||||||
|
|
||||||
let idle_time = into_big_int(end.idle() - start.idle());
|
// let idle_time = into_big_int(end.idle() - start.idle());
|
||||||
indexmap.insert("idle time".to_string(), idle_time);
|
// indexmap.insert("idle time".to_string(), idle_time);
|
||||||
|
|
||||||
benchmark_output(indexmap, output, passthrough, &tag, &mut context, scope).await
|
// benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
|
||||||
} else {
|
// } else {
|
||||||
Err(ShellError::untagged_runtime_error(
|
// Err(ShellError::untagged_runtime_error(
|
||||||
"Could not retreive CPU time",
|
// "Could not retrieve CPU time",
|
||||||
))
|
// ))
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn benchmark_output<T, Output>(
|
async fn benchmark_output<T, Output>(
|
||||||
indexmap: IndexMap<String, BigInt>,
|
indexmap: IndexMap<String, BigInt>,
|
||||||
block_output: Output,
|
block_output: Output,
|
||||||
passthrough: Option<Block>,
|
passthrough: Option<CapturedBlock>,
|
||||||
tag: T,
|
tag: T,
|
||||||
context: &mut EvaluationContext,
|
context: &mut EvaluationContext,
|
||||||
scope: Arc<Scope>,
|
|
||||||
) -> Result<OutputStream, ShellError>
|
) -> Result<OutputStream, ShellError>
|
||||||
where
|
where
|
||||||
T: Into<Tag> + Copy,
|
T: Into<Tag> + Copy,
|
||||||
@ -161,9 +152,12 @@ where
|
|||||||
let benchmark_output = InputStream::one(value);
|
let benchmark_output = InputStream::one(value);
|
||||||
|
|
||||||
// add autoview for an empty block
|
// add autoview for an empty block
|
||||||
let time_block = add_implicit_autoview(time_block);
|
let time_block = add_implicit_autoview(time_block.block);
|
||||||
|
|
||||||
let _ = run_block(&time_block, context, benchmark_output, scope).await?;
|
context.scope.enter_scope();
|
||||||
|
let result = run_block(&time_block, context, benchmark_output).await;
|
||||||
|
context.scope.exit_scope();
|
||||||
|
result?;
|
||||||
context.clear_errors();
|
context.clear_errors();
|
||||||
|
|
||||||
Ok(block_output.into())
|
Ok(block_output.into())
|
||||||
@ -175,15 +169,19 @@ where
|
|||||||
|
|
||||||
fn add_implicit_autoview(mut block: Block) -> Block {
|
fn add_implicit_autoview(mut block: Block) -> Block {
|
||||||
if block.block.is_empty() {
|
if block.block.is_empty() {
|
||||||
block.push({
|
let group = Group::new(
|
||||||
let mut commands = Commands::new(block.span);
|
vec![{
|
||||||
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
|
let mut commands = Pipeline::new(block.span);
|
||||||
"autoview".to_string(),
|
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
|
||||||
block.span,
|
"autoview".to_string(),
|
||||||
block.span,
|
block.span,
|
||||||
)));
|
block.span,
|
||||||
commands
|
)));
|
||||||
});
|
commands
|
||||||
|
}],
|
||||||
|
block.span,
|
||||||
|
);
|
||||||
|
block.push(group);
|
||||||
}
|
}
|
||||||
block
|
block
|
||||||
}
|
}
|
@ -1,8 +1,8 @@
|
|||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use nu_data::value::format_leaf;
|
use nu_data::value::format_leaf;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@ -27,13 +27,9 @@ impl WholeStreamCommand for BuildString {
|
|||||||
"Builds a string from the arguments"
|
"Builds a string from the arguments"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let tag = args.call_info.name_tag.clone();
|
let tag = args.call_info.name_tag.clone();
|
||||||
let (BuildStringArgs { rest }, _) = args.process(®istry).await?;
|
let (BuildStringArgs { rest }, _) = args.process().await?;
|
||||||
|
|
||||||
let mut output_string = String::new();
|
let mut output_string = String::new();
|
||||||
|
|
@ -1,7 +1,7 @@
|
|||||||
use crate::commands::{command::EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use chrono::{Datelike, Local, NaiveDate};
|
use chrono::{Datelike, Local, NaiveDate};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
use nu_engine::{EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
|
|
||||||
@ -41,12 +41,8 @@ impl WholeStreamCommand for Cal {
|
|||||||
"Display a calendar."
|
"Display a calendar."
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
cal(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
cal(args, registry).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -70,12 +66,8 @@ impl WholeStreamCommand for Cal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn cal(
|
pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
args: CommandArgs,
|
let args = args.evaluate_once().await?;
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
let args = args.evaluate_once(®istry).await?;
|
|
||||||
let mut calendar_vec_deque = VecDeque::new();
|
let mut calendar_vec_deque = VecDeque::new();
|
||||||
let tag = args.call_info.name_tag.clone();
|
let tag = args.call_info.name_tag.clone();
|
||||||
|
|
||||||
@ -345,6 +337,6 @@ mod tests {
|
|||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
Ok(test_examples(Cal {})?)
|
test_examples(Cal {})
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,16 +1,9 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use nu_engine::shell::CdArgs;
|
||||||
|
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, SyntaxShape};
|
use nu_protocol::{Signature, SyntaxShape};
|
||||||
use nu_source::Tagged;
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
|
||||||
pub struct CdArgs {
|
|
||||||
pub(crate) path: Option<Tagged<PathBuf>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Cd;
|
pub struct Cd;
|
||||||
|
|
||||||
@ -23,7 +16,7 @@ impl WholeStreamCommand for Cd {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("cd").optional(
|
Signature::build("cd").optional(
|
||||||
"directory",
|
"directory",
|
||||||
SyntaxShape::Path,
|
SyntaxShape::FilePath,
|
||||||
"the directory to change to",
|
"the directory to change to",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -32,14 +25,10 @@ impl WholeStreamCommand for Cd {
|
|||||||
"Change to a new path."
|
"Change to a new path."
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name = args.call_info.name_tag.clone();
|
let name = args.call_info.name_tag.clone();
|
||||||
let shell_manager = args.shell_manager.clone();
|
let shell_manager = args.shell_manager.clone();
|
||||||
let (args, _): (CdArgs, _) = args.process(®istry).await?;
|
let (args, _): (CdArgs, _) = args.process().await?;
|
||||||
shell_manager.cd(args, name)
|
shell_manager.cd(args, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,6 +67,6 @@ mod tests {
|
|||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
Ok(test_examples(Cd {})?)
|
test_examples(Cd {})
|
||||||
}
|
}
|
||||||
}
|
}
|
148
crates/nu-command/src/commands/char_.rs
Normal file
148
crates/nu-command/src/commands/char_.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
|
use nu_source::Tagged;
|
||||||
|
|
||||||
|
pub struct Char;
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
struct CharArgs {
|
||||||
|
name: Tagged<String>,
|
||||||
|
unicode: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl WholeStreamCommand for Char {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"char"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("char")
|
||||||
|
.required(
|
||||||
|
"character",
|
||||||
|
SyntaxShape::Any,
|
||||||
|
"the name of the character to output",
|
||||||
|
)
|
||||||
|
.switch("unicode", "unicode string i.e. 1f378", Some('u'))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Output special characters (eg. 'newline')"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Output newline",
|
||||||
|
example: r#"char newline"#,
|
||||||
|
result: Some(vec![Value::from("\n")]),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Output prompt character, newline and a hamburger character",
|
||||||
|
example: r#"echo $(char prompt) $(char newline) $(char hamburger)"#,
|
||||||
|
result: Some(vec![
|
||||||
|
UntaggedValue::string("\u{25b6}").into(),
|
||||||
|
UntaggedValue::string("\n").into(),
|
||||||
|
UntaggedValue::string("\u{2261}").into(),
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Output unicode character",
|
||||||
|
example: r#"char -u 1f378"#,
|
||||||
|
result: Some(vec![Value::from("\u{1f378}")]),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
|
let (CharArgs { name, unicode }, _) = args.process().await?;
|
||||||
|
|
||||||
|
if unicode {
|
||||||
|
let decoded_char = string_to_unicode_char(&name.item);
|
||||||
|
if let Some(output) = decoded_char {
|
||||||
|
Ok(OutputStream::one(ReturnSuccess::value(
|
||||||
|
UntaggedValue::string(output).into_value(name.tag()),
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Err(ShellError::labeled_error(
|
||||||
|
"error decoding unicode character",
|
||||||
|
"error decoding unicode character",
|
||||||
|
name.tag(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let special_character = str_to_character(&name.item);
|
||||||
|
if let Some(output) = special_character {
|
||||||
|
Ok(OutputStream::one(ReturnSuccess::value(
|
||||||
|
UntaggedValue::string(output).into_value(name.tag()),
|
||||||
|
)))
|
||||||
|
} else {
|
||||||
|
Err(ShellError::labeled_error(
|
||||||
|
"error finding named character",
|
||||||
|
"error finding named character",
|
||||||
|
name.tag(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn string_to_unicode_char(s: &str) -> Option<char> {
|
||||||
|
u32::from_str_radix(s, 16)
|
||||||
|
.ok()
|
||||||
|
.and_then(std::char::from_u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn str_to_character(s: &str) -> Option<String> {
|
||||||
|
match s {
|
||||||
|
"newline" | "enter" | "nl" => Some("\n".into()),
|
||||||
|
"tab" => Some("\t".into()),
|
||||||
|
"sp" | "space" => Some(" ".into()),
|
||||||
|
// Unicode names came from https://www.compart.com/en/unicode
|
||||||
|
// Private Use Area (U+E000-U+F8FF)
|
||||||
|
// Unicode can't be mixed with Ansi or it will break width calculation
|
||||||
|
"branch" => Some('\u{e0a0}'.to_string()), //
|
||||||
|
"segment" => Some('\u{e0b0}'.to_string()), //
|
||||||
|
|
||||||
|
"identical_to" | "hamburger" => Some('\u{2261}'.to_string()), // ≡
|
||||||
|
"not_identical_to" | "branch_untracked" => Some('\u{2262}'.to_string()), // ≢
|
||||||
|
"strictly_equivalent_to" | "branch_identical" => Some('\u{2263}'.to_string()), // ≣
|
||||||
|
|
||||||
|
"upwards_arrow" | "branch_ahead" => Some('\u{2191}'.to_string()), // ↑
|
||||||
|
"downwards_arrow" | "branch_behind" => Some('\u{2193}'.to_string()), // ↓
|
||||||
|
"up_down_arrow" | "branch_ahead_behind" => Some('\u{2195}'.to_string()), // ↕
|
||||||
|
|
||||||
|
"black_right_pointing_triangle" | "prompt" => Some('\u{25b6}'.to_string()), // ▶
|
||||||
|
"vector_or_cross_product" | "failed" => Some('\u{2a2f}'.to_string()), // ⨯
|
||||||
|
"high_voltage_sign" | "elevated" => Some('\u{26a1}'.to_string()), // ⚡
|
||||||
|
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
|
||||||
|
"hash" | "hashtag" | "pound_sign" | "sharp" | "root" => Some("#".into()), // #
|
||||||
|
|
||||||
|
// Weather symbols
|
||||||
|
"sun" | "sunny" | "sunrise" => Some("☀️".to_string()),
|
||||||
|
"moon" => Some("🌛".to_string()),
|
||||||
|
"cloudy" | "cloud" | "clouds" => Some("☁️".to_string()),
|
||||||
|
"rainy" | "rain" => Some("🌦️".to_string()),
|
||||||
|
"foggy" | "fog" => Some("🌫️".to_string()),
|
||||||
|
"mist" | "haze" => Some("\u{2591}".to_string()),
|
||||||
|
"snowy" | "snow" => Some("❄️".to_string()),
|
||||||
|
"thunderstorm" | "thunder" => Some("🌩️".to_string()),
|
||||||
|
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Char;
|
||||||
|
use super::ShellError;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
|
test_examples(Char {})
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||||
|
|
||||||
@ -20,21 +20,15 @@ impl WholeStreamCommand for Chart {
|
|||||||
"Displays charts."
|
"Displays charts."
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
if args.scope.get_command("chart bar").is_none() {
|
||||||
_args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
if registry.get_command("chart bar").is_none() {
|
|
||||||
return Err(ShellError::untagged_runtime_error(
|
return Err(ShellError::untagged_runtime_error(
|
||||||
"nu_plugin_chart not installed.",
|
"nu_plugin_chart not installed.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let registry = registry.clone();
|
|
||||||
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
||||||
UntaggedValue::string(crate::commands::help::get_help(&Chart, ®istry))
|
UntaggedValue::string(get_help(&Chart, &args.scope)).into_value(Tag::unknown()),
|
||||||
.into_value(Tag::unknown()),
|
|
||||||
))))
|
))))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -48,6 +42,6 @@ mod tests {
|
|||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
Ok(test_examples(Chart {})?)
|
test_examples(Chart {})
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,8 +1,9 @@
|
|||||||
use crate::commands::classified::maybe_text_codec::{MaybeTextCodec, StringOrBinary};
|
|
||||||
use crate::evaluate::evaluate_baseline_expr;
|
|
||||||
use crate::futures::ThreadedReceiver;
|
use crate::futures::ThreadedReceiver;
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::evaluate_baseline_expr;
|
||||||
|
use nu_engine::{MaybeTextCodec, StringOrBinary};
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::ops::Deref;
|
use std::ops::Deref;
|
||||||
use std::process::{Command, Stdio};
|
use std::process::{Command, Stdio};
|
||||||
@ -13,15 +14,16 @@ use futures_codec::FramedRead;
|
|||||||
use log::trace;
|
use log::trace;
|
||||||
|
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
|
use nu_protocol::hir::Expression;
|
||||||
use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
|
use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
|
||||||
use nu_protocol::{Primitive, Scope, ShellTypeName, UntaggedValue, Value};
|
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
|
||||||
use nu_source::Tag;
|
use nu_source::Tag;
|
||||||
|
use nu_stream::trace_stream;
|
||||||
|
|
||||||
pub(crate) async fn run_external_command(
|
pub(crate) async fn run_external_command(
|
||||||
command: ExternalCommand,
|
command: ExternalCommand,
|
||||||
context: &mut EvaluationContext,
|
context: &mut EvaluationContext,
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
scope: Arc<Scope>,
|
|
||||||
external_redirection: ExternalRedirection,
|
external_redirection: ExternalRedirection,
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
trace!(target: "nu::run::external", "-> {}", command.name);
|
trace!(target: "nu::run::external", "-> {}", command.name);
|
||||||
@ -34,14 +36,13 @@ pub(crate) async fn run_external_command(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
run_with_stdin(command, context, input, scope, external_redirection).await
|
run_with_stdin(command, context, input, external_redirection).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run_with_stdin(
|
async fn run_with_stdin(
|
||||||
command: ExternalCommand,
|
command: ExternalCommand,
|
||||||
context: &mut EvaluationContext,
|
context: &mut EvaluationContext,
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
scope: Arc<Scope>,
|
|
||||||
external_redirection: ExternalRedirection,
|
external_redirection: ExternalRedirection,
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
let path = context.shell_manager.path();
|
let path = context.shell_manager.path();
|
||||||
@ -50,7 +51,8 @@ async fn run_with_stdin(
|
|||||||
|
|
||||||
let mut command_args = vec![];
|
let mut command_args = vec![];
|
||||||
for arg in command.args.iter() {
|
for arg in command.args.iter() {
|
||||||
let value = evaluate_baseline_expr(arg, &context.registry, scope.clone()).await?;
|
let is_literal = matches!(arg.expr, Expression::Literal(_));
|
||||||
|
let value = evaluate_baseline_expr(arg, context).await?;
|
||||||
|
|
||||||
// Skip any arguments that don't really exist, treating them as optional
|
// Skip any arguments that don't really exist, treating them as optional
|
||||||
// FIXME: we may want to preserve the gap in the future, though it's hard to say
|
// FIXME: we may want to preserve the gap in the future, though it's hard to say
|
||||||
@ -65,8 +67,10 @@ async fn run_with_stdin(
|
|||||||
for t in table {
|
for t in table {
|
||||||
match &t.value {
|
match &t.value {
|
||||||
UntaggedValue::Primitive(_) => {
|
UntaggedValue::Primitive(_) => {
|
||||||
command_args
|
command_args.push((
|
||||||
.push(t.convert_to_string().trim_end_matches('\n').to_string());
|
t.convert_to_string().trim_end_matches('\n').to_string(),
|
||||||
|
is_literal,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
@ -80,19 +84,19 @@ async fn run_with_stdin(
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let trimmed_value_string = value.as_string()?.trim_end_matches('\n').to_string();
|
let trimmed_value_string = value.as_string()?.trim_end_matches('\n').to_string();
|
||||||
command_args.push(trimmed_value_string);
|
command_args.push((trimmed_value_string, is_literal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let process_args = command_args
|
let process_args = command_args
|
||||||
.iter()
|
.iter()
|
||||||
.map(|arg| {
|
.map(|(arg, _is_literal)| {
|
||||||
let home_dir;
|
let home_dir;
|
||||||
|
|
||||||
#[cfg(feature = "dirs")]
|
#[cfg(feature = "dirs")]
|
||||||
{
|
{
|
||||||
home_dir = dirs::home_dir;
|
home_dir = dirs_next::home_dir;
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "dirs"))]
|
#[cfg(not(feature = "dirs"))]
|
||||||
{
|
{
|
||||||
@ -103,8 +107,9 @@ async fn run_with_stdin(
|
|||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
{
|
{
|
||||||
if argument_contains_whitespace(&arg) && !argument_is_quoted(&arg) {
|
if !_is_literal {
|
||||||
add_quotes(&arg)
|
let escaped = escape_double_quotes(&arg);
|
||||||
|
add_double_quotes(&escaped)
|
||||||
} else {
|
} else {
|
||||||
arg.as_ref().to_string()
|
arg.as_ref().to_string()
|
||||||
}
|
}
|
||||||
@ -126,7 +131,7 @@ async fn run_with_stdin(
|
|||||||
&process_args[..],
|
&process_args[..],
|
||||||
input,
|
input,
|
||||||
external_redirection,
|
external_redirection,
|
||||||
scope,
|
&context.scope,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +141,7 @@ fn spawn(
|
|||||||
args: &[String],
|
args: &[String],
|
||||||
input: InputStream,
|
input: InputStream,
|
||||||
external_redirection: ExternalRedirection,
|
external_redirection: ExternalRedirection,
|
||||||
scope: Arc<Scope>,
|
scope: &Scope,
|
||||||
) -> Result<InputStream, ShellError> {
|
) -> Result<InputStream, ShellError> {
|
||||||
let command = command.clone();
|
let command = command.clone();
|
||||||
|
|
||||||
@ -167,7 +172,7 @@ fn spawn(
|
|||||||
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
trace!(target: "nu::run::external", "cwd = {:?}", &path);
|
||||||
|
|
||||||
process.env_clear();
|
process.env_clear();
|
||||||
process.envs(scope.env());
|
process.envs(scope.get_env_vars());
|
||||||
|
|
||||||
// We want stdout regardless of what
|
// We want stdout regardless of what
|
||||||
// we are doing ($it case or pipe stdin)
|
// we are doing ($it case or pipe stdin)
|
||||||
@ -217,38 +222,20 @@ fn spawn(
|
|||||||
for value in block_on_stream(input) {
|
for value in block_on_stream(input) {
|
||||||
match &value.value {
|
match &value.value {
|
||||||
UntaggedValue::Primitive(Primitive::Nothing) => continue,
|
UntaggedValue::Primitive(Primitive::Nothing) => continue,
|
||||||
UntaggedValue::Primitive(Primitive::String(s))
|
UntaggedValue::Primitive(Primitive::String(s)) => {
|
||||||
| UntaggedValue::Primitive(Primitive::Line(s)) => {
|
if stdin_write.write(s.as_bytes()).is_err() {
|
||||||
if let Err(e) = stdin_write.write(s.as_bytes()) {
|
// Other side has closed, so exit
|
||||||
let message = format!("Unable to write to stdin (error = {})", e);
|
return Ok(());
|
||||||
|
|
||||||
let _ = stdin_write_tx.send(Ok(Value {
|
|
||||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
|
||||||
message,
|
|
||||||
"application may have closed before completing pipeline",
|
|
||||||
&stdin_name_tag,
|
|
||||||
)),
|
|
||||||
tag: stdin_name_tag,
|
|
||||||
}));
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UntaggedValue::Primitive(Primitive::Binary(b)) => {
|
UntaggedValue::Primitive(Primitive::Binary(b)) => {
|
||||||
if let Err(e) = stdin_write.write(b) {
|
if stdin_write.write(b).is_err() {
|
||||||
let message = format!("Unable to write to stdin (error = {})", e);
|
// Other side has closed, so exit
|
||||||
|
return Ok(());
|
||||||
let _ = stdin_write_tx.send(Ok(Value {
|
|
||||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
|
||||||
message,
|
|
||||||
"application may have closed before completing pipeline",
|
|
||||||
&stdin_name_tag,
|
|
||||||
)),
|
|
||||||
tag: stdin_name_tag,
|
|
||||||
}));
|
|
||||||
return Err(());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unsupported => {
|
unsupported => {
|
||||||
|
println!("Unsupported: {:?}", unsupported);
|
||||||
let _ = stdin_write_tx.send(Ok(Value {
|
let _ = stdin_write_tx.send(Ok(Value {
|
||||||
value: UntaggedValue::Error(ShellError::labeled_error(
|
value: UntaggedValue::Error(ShellError::labeled_error(
|
||||||
format!(
|
format!(
|
||||||
@ -477,7 +464,7 @@ pub fn did_find_command(#[allow(unused)] name: &str) -> bool {
|
|||||||
let cmd_builtins = [
|
let cmd_builtins = [
|
||||||
"assoc", "break", "color", "copy", "date", "del", "dir", "dpath", "echo", "erase",
|
"assoc", "break", "color", "copy", "date", "del", "dir", "dpath", "echo", "erase",
|
||||||
"for", "ftype", "md", "mkdir", "mklink", "move", "path", "ren", "rename", "rd",
|
"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)
|
cmd_builtins.contains(&name)
|
||||||
@ -494,11 +481,6 @@ where
|
|||||||
shellexpand::tilde_with_context(input, home_dir)
|
shellexpand::tilde_with_context(input, home_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn argument_contains_whitespace(argument: &str) -> bool {
|
|
||||||
argument.chars().any(|c| c.is_whitespace())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn argument_is_quoted(argument: &str) -> bool {
|
fn argument_is_quoted(argument: &str) -> bool {
|
||||||
if argument.len() < 2 {
|
if argument.len() < 2 {
|
||||||
return false;
|
return false;
|
||||||
@ -509,10 +491,20 @@ fn argument_is_quoted(argument: &str) -> bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn add_quotes(argument: &str) -> String {
|
fn add_double_quotes(argument: &str) -> String {
|
||||||
format!("\"{}\"", argument)
|
format!("\"{}\"", argument)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
fn escape_double_quotes(argument: &str) -> Cow<'_, str> {
|
||||||
|
// allocate new string only if required
|
||||||
|
if argument.contains('"') {
|
||||||
|
Cow::Owned(argument.replace('"', r#"\""#))
|
||||||
|
} else {
|
||||||
|
Cow::Borrowed(argument)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn remove_quotes(argument: &str) -> Option<&str> {
|
fn remove_quotes(argument: &str) -> Option<&str> {
|
||||||
if !argument_is_quoted(argument) {
|
if !argument_is_quoted(argument) {
|
||||||
@ -538,20 +530,19 @@ fn shell_os_paths() -> Vec<std::path::PathBuf> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
add_quotes, argument_contains_whitespace, argument_is_quoted, expand_tilde, remove_quotes,
|
add_double_quotes, argument_is_quoted, escape_double_quotes, expand_tilde, remove_quotes,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "which")]
|
#[cfg(feature = "which")]
|
||||||
use super::{run_external_command, EvaluationContext, InputStream};
|
use super::{run_external_command, InputStream};
|
||||||
|
|
||||||
#[cfg(feature = "which")]
|
#[cfg(feature = "which")]
|
||||||
use futures::executor::block_on;
|
use futures::executor::block_on;
|
||||||
#[cfg(feature = "which")]
|
#[cfg(feature = "which")]
|
||||||
|
use nu_engine::basic_evaluation_context;
|
||||||
|
#[cfg(feature = "which")]
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
#[cfg(feature = "which")]
|
#[cfg(feature = "which")]
|
||||||
use nu_protocol::Scope;
|
|
||||||
#[cfg(feature = "which")]
|
|
||||||
use nu_test_support::commands::ExternalBuilder;
|
use nu_test_support::commands::ExternalBuilder;
|
||||||
|
|
||||||
// async fn read(mut stream: OutputStream) -> Option<Value> {
|
// async fn read(mut stream: OutputStream) -> Option<Value> {
|
||||||
// match stream.try_next().await {
|
// match stream.try_next().await {
|
||||||
// Ok(val) => {
|
// Ok(val) => {
|
||||||
@ -572,17 +563,13 @@ mod tests {
|
|||||||
|
|
||||||
let input = InputStream::empty();
|
let input = InputStream::empty();
|
||||||
let mut ctx =
|
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(
|
assert!(
|
||||||
cmd,
|
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
|
||||||
&mut ctx,
|
.await
|
||||||
input,
|
.is_err()
|
||||||
Scope::create(),
|
);
|
||||||
ExternalRedirection::Stdout
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.is_err());
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -590,7 +577,7 @@ mod tests {
|
|||||||
// async fn failure_run() -> Result<(), ShellError> {
|
// async fn failure_run() -> Result<(), ShellError> {
|
||||||
// let cmd = ExternalBuilder::for_name("fail").build();
|
// 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)
|
// let stream = run_external_command(cmd, &mut ctx, None, false)
|
||||||
// .await?
|
// .await?
|
||||||
// .expect("There was a problem running the external command.");
|
// .expect("There was a problem running the external command.");
|
||||||
@ -618,10 +605,10 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn checks_contains_whitespace_from_argument_to_be_passed_in() {
|
fn checks_escape_double_quotes() {
|
||||||
assert_eq!(argument_contains_whitespace("andrés"), false);
|
assert_eq!(escape_double_quotes("andrés"), "andrés");
|
||||||
assert_eq!(argument_contains_whitespace("and rés"), true);
|
assert_eq!(escape_double_quotes(r#"an"drés"#), r#"an\"drés"#);
|
||||||
assert_eq!(argument_contains_whitespace(r#"and\ rés"#), true);
|
assert_eq!(escape_double_quotes(r#""an"drés""#), r#"\"an\"drés\""#);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -649,9 +636,8 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn adds_quotes_to_argument_to_be_passed_in() {
|
fn adds_double_quotes_to_argument_to_be_passed_in() {
|
||||||
assert_eq!(add_quotes("andrés"), "\"andrés\"");
|
assert_eq!(add_double_quotes("andrés"), "\"andrés\"");
|
||||||
//assert_eq!(add_quotes("\"andrés\""), "\"andrés\"");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
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 crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::Signature;
|
use nu_protocol::Signature;
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
@ -20,7 +20,7 @@ impl WholeStreamCommand for Clear {
|
|||||||
"Clears the terminal"
|
"Clears the terminal"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(&self, _: CommandArgs, _: &CommandRegistry) -> Result<OutputStream, ShellError> {
|
async fn run(&self, _: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
if cfg!(windows) {
|
if cfg!(windows) {
|
||||||
Command::new("cmd")
|
Command::new("cmd")
|
||||||
.args(&["/C", "cls"])
|
.args(&["/C", "cls"])
|
@ -1,11 +1,10 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Signature, Value};
|
use nu_protocol::{Signature, Value};
|
||||||
|
|
||||||
use clipboard::{ClipboardContext, ClipboardProvider};
|
use arboard::Clipboard;
|
||||||
|
|
||||||
pub struct Clip;
|
pub struct Clip;
|
||||||
|
|
||||||
@ -23,12 +22,8 @@ impl WholeStreamCommand for Clip {
|
|||||||
"Copy the contents of the pipeline to the copy/paste buffer"
|
"Copy the contents of the pipeline to the copy/paste buffer"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
clip(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
clip(args, registry).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -47,23 +42,19 @@ impl WholeStreamCommand for Clip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn clip(
|
pub async fn clip(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
args: CommandArgs,
|
|
||||||
_registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let input = args.input;
|
let input = args.input;
|
||||||
let name = args.call_info.name_tag.clone();
|
let name = args.call_info.name_tag.clone();
|
||||||
let values: Vec<Value> = input.collect().await;
|
let values: Vec<Value> = input.collect().await;
|
||||||
|
|
||||||
if let Ok(clip_context) = ClipboardProvider::new() {
|
if let Ok(mut clip_context) = Clipboard::new() {
|
||||||
let mut clip_context: ClipboardContext = clip_context;
|
|
||||||
let mut new_copy_data = String::new();
|
let mut new_copy_data = String::new();
|
||||||
|
|
||||||
if !values.is_empty() {
|
if !values.is_empty() {
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for i in values.iter() {
|
for i in values.iter() {
|
||||||
if !first {
|
if !first {
|
||||||
new_copy_data.push_str("\n");
|
new_copy_data.push('\n');
|
||||||
} else {
|
} else {
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
@ -81,7 +72,7 @@ pub async fn clip(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match clip_context.set_contents(new_copy_data) {
|
match clip_context.set_text(new_copy_data) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(ShellError::labeled_error(
|
return Err(ShellError::labeled_error(
|
||||||
@ -110,6 +101,6 @@ mod tests {
|
|||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
Ok(test_examples(Clip {})?)
|
test_examples(Clip {})
|
||||||
}
|
}
|
||||||
}
|
}
|
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,8 +1,7 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use futures::future;
|
use futures::future;
|
||||||
use futures::stream::StreamExt;
|
use futures::stream::StreamExt;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
@ -28,12 +27,8 @@ impl WholeStreamCommand for Compact {
|
|||||||
"Creates a table with non-empty rows"
|
"Creates a table with non-empty rows"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
compact(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
compact(args, registry).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -45,12 +40,8 @@ impl WholeStreamCommand for Compact {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn compact(
|
pub async fn compact(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
args: CommandArgs,
|
let (CompactArgs { rest: columns }, input) = args.process().await?;
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let registry = registry.clone();
|
|
||||||
let (CompactArgs { rest: columns }, input) = args.process(®istry).await?;
|
|
||||||
Ok(input
|
Ok(input
|
||||||
.filter_map(move |item| {
|
.filter_map(move |item| {
|
||||||
future::ready(if columns.is_empty() {
|
future::ready(if columns.is_empty() {
|
||||||
@ -90,6 +81,6 @@ mod tests {
|
|||||||
fn examples_work_as_expected() -> Result<(), ShellError> {
|
fn examples_work_as_expected() -> Result<(), ShellError> {
|
||||||
use crate::examples::test as test_examples;
|
use crate::examples::test as test_examples;
|
||||||
|
|
||||||
Ok(test_examples(Compact {})?)
|
test_examples(Compact {})
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,5 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||||
|
|
||||||
@ -20,12 +19,8 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
"clear the config"
|
"clear the config"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
clear(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
clear(args, registry).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -37,10 +32,7 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn clear(
|
pub async fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
args: CommandArgs,
|
|
||||||
_registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name_span = args.call_info.name_tag.clone();
|
let name_span = args.call_info.name_tag.clone();
|
||||||
|
|
||||||
// NOTE: None because we are not loading a new config file, we just want to read from the
|
// NOTE: None because we are not loading a new config file, we just want to read from the
|
@ -1,8 +1,9 @@
|
|||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
use crate::{CommandArgs, CommandRegistry, OutputStream};
|
use nu_engine::CommandArgs;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
|
||||||
|
use nu_stream::OutputStream;
|
||||||
|
|
||||||
pub struct Command;
|
pub struct Command;
|
||||||
|
|
||||||
@ -20,11 +21,7 @@ impl WholeStreamCommand for Command {
|
|||||||
"Configuration management."
|
"Configuration management."
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
|
||||||
args: CommandArgs,
|
|
||||||
_registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name_span = args.call_info.name_tag.clone();
|
let name_span = args.call_info.name_tag.clone();
|
||||||
let name = args.call_info.name_tag;
|
let name = args.call_info.name_tag;
|
||||||
let result = nu_data::config::read(name_span, &None)?;
|
let result = nu_data::config::read(name_span, &None)?;
|
@ -1,6 +1,5 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
|
|
||||||
@ -29,12 +28,8 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
"Gets a value from the config"
|
"Gets a value from the config"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
get(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
get(args, registry).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -46,12 +41,9 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get(
|
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name_tag = args.call_info.name_tag.clone();
|
let name_tag = args.call_info.name_tag.clone();
|
||||||
let (GetArgs { path }, _) = args.process(®istry).await?;
|
let (GetArgs { path }, _) = args.process().await?;
|
||||||
|
|
||||||
// NOTE: None because we are not loading a new config file, we just want to read from the
|
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||||
// existing config
|
// existing config
|
@ -1,6 +1,5 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
@ -22,7 +21,7 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("config load").required(
|
Signature::build("config load").required(
|
||||||
"load",
|
"load",
|
||||||
SyntaxShape::Path,
|
SyntaxShape::FilePath,
|
||||||
"Path to load the config from",
|
"Path to load the config from",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -31,22 +30,15 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
"Loads the config from the path given"
|
"Loads the config from the path given"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
set(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
set(args, registry).await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set(
|
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name = args.call_info.name_tag.clone();
|
let name = args.call_info.name_tag.clone();
|
||||||
let name_span = args.call_info.name_tag.clone();
|
let name_span = args.call_info.name_tag.clone();
|
||||||
let (LoadArgs { load }, _) = args.process(®istry).await?;
|
let (LoadArgs { load }, _) = args.process().await?;
|
||||||
|
|
||||||
let configuration = load.item().clone();
|
let configuration = load.item().clone();
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
|
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
|
||||||
|
|
||||||
@ -20,12 +19,8 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
"return the path to the config file"
|
"return the path to the config file"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
path(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
path(args, registry).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -37,13 +32,10 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn path(
|
pub async fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
args: CommandArgs,
|
|
||||||
_registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let path = config::default_path()?;
|
let path = config::default_path()?;
|
||||||
|
|
||||||
Ok(OutputStream::one(ReturnSuccess::value(
|
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,6 +1,5 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
|
||||||
use nu_source::Tagged;
|
use nu_source::Tagged;
|
||||||
@ -30,12 +29,8 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
"Removes a value from the config"
|
"Removes a value from the config"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
remove(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
remove(args, registry).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -47,12 +42,9 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn remove(
|
pub async fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name_span = args.call_info.name_tag.clone();
|
let name_span = args.call_info.name_tag.clone();
|
||||||
let (RemoveArgs { remove }, _) = args.process(®istry).await?;
|
let (RemoveArgs { remove }, _) = args.process().await?;
|
||||||
|
|
||||||
let mut result = nu_data::config::read(name_span, &None)?;
|
let mut result = nu_data::config::read(name_span, &None)?;
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
use crate::command_registry::CommandRegistry;
|
|
||||||
use crate::commands::WholeStreamCommand;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
use nu_engine::WholeStreamCommand;
|
||||||
use nu_errors::ShellError;
|
use nu_errors::ShellError;
|
||||||
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
|
||||||
|
|
||||||
@ -28,12 +27,8 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
"Sets a value in the config"
|
"Sets a value in the config"
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn run(
|
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
&self,
|
set(args).await
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
set(args, registry).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -62,12 +57,9 @@ impl WholeStreamCommand for SubCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set(
|
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
|
||||||
args: CommandArgs,
|
|
||||||
registry: &CommandRegistry,
|
|
||||||
) -> Result<OutputStream, ShellError> {
|
|
||||||
let name_tag = args.call_info.name_tag.clone();
|
let name_tag = args.call_info.name_tag.clone();
|
||||||
let (SetArgs { path, mut value }, _) = args.process(®istry).await?;
|
let (SetArgs { path, mut value }, _) = args.process().await?;
|
||||||
|
|
||||||
// NOTE: None because we are not loading a new config file, we just want to read from the
|
// NOTE: None because we are not loading a new config file, we just want to read from the
|
||||||
// existing config
|
// existing config
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user