forked from extern/nushell
Compare commits
111 Commits
Author | SHA1 | Date | |
---|---|---|---|
17a265b197 | |||
3fabc8e1e6 | |||
517ef7cde7 | |||
ad14b763f9 | |||
f74694d5a3 | |||
1ea39abcff | |||
7402589775 | |||
e0cd5a714a | |||
c6eea5de6b | |||
809416e3f0 | |||
040d812343 | |||
72465e6724 | |||
ab480856a5 | |||
6ae497eedc | |||
421bc828ef | |||
ed65886ae5 | |||
8c7e2dbdf9 | |||
afb4209f10 | |||
1d8775d237 | |||
8787ec9fe8 | |||
1f810cd26a | |||
f4d7d19370 | |||
e616b2e247 | |||
2a39332d51 | |||
3c6b10c6b2 | |||
2a9226a55c | |||
3d65fd7cc4 | |||
36ddbfdc85 | |||
76292ef10c | |||
9ae2e528c5 | |||
2849e28c2b | |||
9d0e52b94d | |||
731f5f8523 | |||
9d6d43ee55 | |||
f9e99048c4 | |||
e1df8d14b4 | |||
b9419e0f36 | |||
e03c354e89 | |||
2e44e4d33c | |||
5cbaabeeab | |||
d64e381085 | |||
41306aa7e0 | |||
0bb2e47c98 | |||
ef660be285 | |||
4bac90a3b2 | |||
4182fc203e | |||
10e36c4233 | |||
bef397228f | |||
5cf47767d7 | |||
8f2d2535dc | |||
2aae8e6382 | |||
ba12b0de0d | |||
3552d03f6c | |||
8d5165c449 | |||
d8027656b5 | |||
4f57c5d56e | |||
2c5c81815a | |||
b97bfe9297 | |||
db07657e40 | |||
2d98d0fcc2 | |||
e6f6f17c6d | |||
166a927c20 | |||
625fe8866c | |||
69e7aa9fc9 | |||
a775cfe177 | |||
9e4a2ab824 | |||
24aa1f312a | |||
cde56741fb | |||
bbe694a622 | |||
7e575a718b | |||
ea9ca8b4ed | |||
0fe2884397 | |||
2982a2c963 | |||
6a43e1a64d | |||
3b5172a8fa | |||
be32aeee70 | |||
adcc74ab8d | |||
8acced56b2 | |||
f823c7cb5d | |||
26e6516626 | |||
5979e0cd0c | |||
3ba1bfc369 | |||
efa0e6eb62 | |||
159b4bd7dc | |||
2611c9525e | |||
0353eb4a12 | |||
92c4097f8d | |||
a909c60f05 | |||
56a9eab7eb | |||
7221eb7f39 | |||
b0b0482d71 | |||
49ab559992 | |||
3dd21c635a | |||
835bbb2e44 | |||
2ee2370a71 | |||
54dd65cfe1 | |||
b004aacd69 | |||
48b7b415e2 | |||
544cea95e1 | |||
8aa2632661 | |||
5419e8ae9d | |||
d4d28ab796 | |||
b8db928c58 | |||
bf45a5860e | |||
ca543fc8af | |||
57cf805e12 | |||
1ae9157985 | |||
206a6ae6c9 | |||
82ac590412 | |||
9a274128ce | |||
5664ee7bda |
20
.github/dependabot.yml
vendored
Normal file
20
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
# docs
|
||||||
|
# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "cargo"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
||||||
|
ignore:
|
||||||
|
- dependency-name: "*"
|
||||||
|
update-types: ["version-update:semver-patch"]
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly"
|
8
.github/workflows/ci.yml
vendored
8
.github/workflows/ci.yml
vendored
@ -40,7 +40,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
|
||||||
|
|
||||||
- name: cargo fmt
|
- name: cargo fmt
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
@ -77,7 +77,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
||||||
@ -101,7 +101,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
|
||||||
|
|
||||||
- name: Install Nushell
|
- name: Install Nushell
|
||||||
run: cargo install --locked --path=. --profile ci --no-default-features
|
run: cargo install --locked --path=. --profile ci --no-default-features
|
||||||
@ -141,7 +141,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
run: cargo clippy --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||||
|
17
.github/workflows/release-pkg.nu
vendored
17
.github/workflows/release-pkg.nu
vendored
@ -7,18 +7,29 @@
|
|||||||
# 1. https://github.com/volks73/cargo-wix
|
# 1. https://github.com/volks73/cargo-wix
|
||||||
|
|
||||||
# Added 2022-11-29 when Windows packaging wouldn't work
|
# Added 2022-11-29 when Windows packaging wouldn't work
|
||||||
# because softprops/action-gh-release was broken
|
|
||||||
# To run this manual for windows
|
# To run this manual for windows
|
||||||
|
# unset CARGO_TARGET_DIR if set
|
||||||
|
# hide-env CARGO_TARGET_DIR
|
||||||
# let-env TARGET = 'x86_64-pc-windows-msvc'
|
# let-env TARGET = 'x86_64-pc-windows-msvc'
|
||||||
# let-env TARGET_RUSTFLAGS = ''
|
# let-env TARGET_RUSTFLAGS = ''
|
||||||
# let-env GITHUB_WORKSPACE = 'C:\Users\dschroeder\source\repos\forks\nushell'
|
# let-env GITHUB_WORKSPACE = 'C:\Users\dschroeder\source\repos\forks\nushell'
|
||||||
|
# let-env GITHUB_OUTPUT = 'C:\Users\dschroeder\source\repos\forks\nushell\output\out.txt'
|
||||||
|
# let-env OS = 'windows-latest'
|
||||||
|
# You need to run this twice. The first pass makes the output folder and builds everything
|
||||||
|
# The second pass generates the msi file
|
||||||
# Pass 1 let-env _EXTRA_ = 'bin'
|
# Pass 1 let-env _EXTRA_ = 'bin'
|
||||||
# Pass 2 let-env _EXTRA_ = 'msi'
|
# Pass 2 let-env _EXTRA_ = 'msi'
|
||||||
# make sure 7z.exe is in your path https://www.7-zip.org/download.html
|
# make sure 7z.exe is in your path https://www.7-zip.org/download.html
|
||||||
|
# let-env Path = ($env.Path | append 'c:\apps\7-zip')
|
||||||
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
|
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
|
||||||
|
# let-env Path = ($env.Path | append 'c:\path\to\aria2c')
|
||||||
# make sure you have the wixtools installed https://wixtoolset.org/
|
# make sure you have the wixtools installed https://wixtoolset.org/
|
||||||
# set os below like this because it's what github's runner is named
|
# let-env Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
|
||||||
# let os = 'windows-latest'
|
# After msi is generated, if you have to update winget-pkgs repo, you'll need to patch the release
|
||||||
|
# by deleting the existing msi and uploading this new msi. Then you'll need to update the hash
|
||||||
|
# on the winget-pkgs PR. To generate the hash, run this command
|
||||||
|
# open target\wix\nu-0.74.0-x86_64-pc-windows-msvc.msi | hash sha256
|
||||||
|
# Then, just take the output and put it in the winget-pkgs PR for the hash on the msi
|
||||||
|
|
||||||
|
|
||||||
# The main binary file to be released
|
# The main binary file to be released
|
||||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -70,7 +70,7 @@ jobs:
|
|||||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
uses: actions-rust-lang/setup-rust-toolchain@v1.3.5
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
|||||||
stale:
|
stale:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/stale@v3
|
- uses: actions/stale@v6
|
||||||
with:
|
with:
|
||||||
#debug-only: true
|
#debug-only: true
|
||||||
ascending: true
|
ascending: true
|
||||||
|
13
.github/workflows/typos.yml
vendored
Normal file
13
.github/workflows/typos.yml
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
name: Typos
|
||||||
|
on: [pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run:
|
||||||
|
name: Spell Check with Typos
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout Actions Repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Check spelling of book
|
||||||
|
uses: crate-ci/typos@master
|
12
.typos.toml
Normal file
12
.typos.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[files]
|
||||||
|
extend-exclude = ["crates/nu-command/tests/commands/table.rs", "*.tsv", "*.json", "*.txt"]
|
||||||
|
|
||||||
|
[default.extend-words]
|
||||||
|
# Ignore false-positives
|
||||||
|
nd = "nd"
|
||||||
|
fo = "fo"
|
||||||
|
ons = "ons"
|
||||||
|
ba = "ba"
|
||||||
|
Plasticos = "Plasticos"
|
||||||
|
IIF = "IIF"
|
||||||
|
numer = "numer"
|
513
Cargo.lock
generated
513
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
63
Cargo.toml
63
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
|||||||
name = "nu"
|
name = "nu"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.60"
|
rust-version = "1.60"
|
||||||
version = "0.74.0"
|
version = "0.75.0"
|
||||||
|
|
||||||
# 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
|
||||||
|
|
||||||
@ -43,25 +43,25 @@ chrono = { version = "0.4.23", features = ["serde"] }
|
|||||||
crossterm = "0.24.0"
|
crossterm = "0.24.0"
|
||||||
ctrlc = "3.2.1"
|
ctrlc = "3.2.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-cli = { path="./crates/nu-cli", version = "0.74.0" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.75.0" }
|
||||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.74.0" }
|
nu-color-config = { path = "./crates/nu-color-config", version = "0.75.0" }
|
||||||
nu-command = { path="./crates/nu-command", version = "0.74.0" }
|
nu-command = { path = "./crates/nu-command", version = "0.75.0" }
|
||||||
nu-engine = { path="./crates/nu-engine", version = "0.74.0" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.75.0" }
|
||||||
nu-json = { path="./crates/nu-json", version = "0.74.0" }
|
nu-json = { path = "./crates/nu-json", version = "0.75.0" }
|
||||||
nu-parser = { path="./crates/nu-parser", version = "0.74.0" }
|
nu-parser = { path = "./crates/nu-parser", version = "0.75.0" }
|
||||||
nu-path = { path="./crates/nu-path", version = "0.74.0" }
|
nu-path = { path = "./crates/nu-path", version = "0.75.0" }
|
||||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.74.0" }
|
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.75.0" }
|
||||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.74.0" }
|
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.75.0" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.74.0" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.75.0" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.74.0" }
|
nu-system = { path = "./crates/nu-system", version = "0.75.0" }
|
||||||
nu-table = { path = "./crates/nu-table", version = "0.74.0" }
|
nu-table = { path = "./crates/nu-table", version = "0.75.0" }
|
||||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.74.0" }
|
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.75.0" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.74.0" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.75.0" }
|
||||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
reedline = { version = "0.15.0", features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
rayon = "1.5.1"
|
rayon = "1.6.1"
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
simplelog = "0.12.0"
|
simplelog = "0.12.0"
|
||||||
time = "0.3.12"
|
time = "0.3.12"
|
||||||
@ -76,22 +76,29 @@ signal-hook = { version = "0.3.14", default-features = false }
|
|||||||
winres = "0.1"
|
winres = "0.1"
|
||||||
|
|
||||||
[target.'cfg(target_family = "unix")'.dependencies]
|
[target.'cfg(target_family = "unix")'.dependencies]
|
||||||
nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"]}
|
nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"] }
|
||||||
atty = "0.2"
|
atty = "0.2"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="./crates/nu-test-support", version = "0.74.0" }
|
nu-test-support = { path = "./crates/nu-test-support", version = "0.75.0" }
|
||||||
tempfile = "3.2.0"
|
tempfile = "3.2.0"
|
||||||
assert_cmd = "2.0.2"
|
assert_cmd = "2.0.2"
|
||||||
criterion = "0.4"
|
criterion = "0.4"
|
||||||
pretty_assertions = "1.0.0"
|
pretty_assertions = "1.0.0"
|
||||||
serial_test = "0.8.0"
|
serial_test = "1.0.0"
|
||||||
hamcrest2 = "0.3.0"
|
hamcrest2 = "0.3.0"
|
||||||
rstest = {version = "0.15.0", default-features = false}
|
rstest = { version = "0.15.0", default-features = false }
|
||||||
itertools = "0.10.3"
|
itertools = "0.10.3"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"]
|
plugin = [
|
||||||
|
"nu-plugin",
|
||||||
|
"nu-cli/plugin",
|
||||||
|
"nu-parser/plugin",
|
||||||
|
"nu-command/plugin",
|
||||||
|
"nu-protocol/plugin",
|
||||||
|
"nu-engine/plugin",
|
||||||
|
]
|
||||||
# extra used to be more useful but now it's the same as default. Leaving it in for backcompat with existing build scripts
|
# extra used to be more useful but now it's the same as default. Leaving it in for backcompat with existing build scripts
|
||||||
extra = ["default"]
|
extra = ["default"]
|
||||||
default = ["plugin", "which-support", "trash-support", "sqlite"]
|
default = ["plugin", "which-support", "trash-support", "sqlite"]
|
||||||
@ -146,13 +153,5 @@ path = "src/main.rs"
|
|||||||
# Run all benchmarks with `cargo bench`
|
# Run all benchmarks with `cargo bench`
|
||||||
# Run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
|
# Run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "encoder_benchmark"
|
name = "benchmarks"
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "eval_benchmark"
|
|
||||||
harness = false
|
|
||||||
|
|
||||||
[[bench]]
|
|
||||||
name = "parser_benchmark"
|
|
||||||
harness = false
|
harness = false
|
187
benches/benchmarks.rs
Normal file
187
benches/benchmarks.rs
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
||||||
|
use nu_cli::eval_source;
|
||||||
|
use nu_parser::parse;
|
||||||
|
use nu_plugin::{EncodingType, PluginResponse};
|
||||||
|
use nu_protocol::{PipelineData, Span, Value};
|
||||||
|
use nu_utils::{get_default_config, get_default_env};
|
||||||
|
|
||||||
|
// FIXME: All benchmarks live in this 1 file to speed up build times when benchmarking.
|
||||||
|
// When the *_benchmarks functions were in different files, `cargo bench` would build
|
||||||
|
// an executable for every single one - incredibly slowly. Would be nice to figure out
|
||||||
|
// a way to split things up again.
|
||||||
|
|
||||||
|
fn parser_benchmarks(c: &mut Criterion) {
|
||||||
|
let mut engine_state = nu_command::create_default_context();
|
||||||
|
// parsing config.nu breaks without PWD set
|
||||||
|
engine_state.add_env_var(
|
||||||
|
"PWD".into(),
|
||||||
|
Value::string("/some/dir".to_string(), Span::test_data()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let default_env = get_default_env().as_bytes();
|
||||||
|
c.bench_function("parse_default_env_file", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
||||||
|
|mut working_set| parse(&mut working_set, None, default_env, false, &[]),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let default_config = get_default_config().as_bytes();
|
||||||
|
c.bench_function("parse_default_config_file", |b| {
|
||||||
|
b.iter_batched(
|
||||||
|
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
||||||
|
|mut working_set| parse(&mut working_set, None, default_config, false, &[]),
|
||||||
|
BatchSize::SmallInput,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function("eval default_env.nu", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut engine_state = nu_command::create_default_context();
|
||||||
|
let mut stack = nu_protocol::engine::Stack::new();
|
||||||
|
eval_source(
|
||||||
|
&mut engine_state,
|
||||||
|
&mut stack,
|
||||||
|
get_default_env().as_bytes(),
|
||||||
|
"default_env.nu",
|
||||||
|
PipelineData::empty(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function("eval default_config.nu", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut engine_state = nu_command::create_default_context();
|
||||||
|
// parsing config.nu breaks without PWD set
|
||||||
|
engine_state.add_env_var(
|
||||||
|
"PWD".into(),
|
||||||
|
Value::string("/some/dir".to_string(), Span::test_data()),
|
||||||
|
);
|
||||||
|
let mut stack = nu_protocol::engine::Stack::new();
|
||||||
|
eval_source(
|
||||||
|
&mut engine_state,
|
||||||
|
&mut stack,
|
||||||
|
get_default_config().as_bytes(),
|
||||||
|
"default_config.nu",
|
||||||
|
PipelineData::empty(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn eval_benchmarks(c: &mut Criterion) {
|
||||||
|
c.bench_function("eval default_env.nu", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut engine_state = nu_command::create_default_context();
|
||||||
|
let mut stack = nu_protocol::engine::Stack::new();
|
||||||
|
eval_source(
|
||||||
|
&mut engine_state,
|
||||||
|
&mut stack,
|
||||||
|
get_default_env().as_bytes(),
|
||||||
|
"default_env.nu",
|
||||||
|
PipelineData::empty(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
c.bench_function("eval default_config.nu", |b| {
|
||||||
|
b.iter(|| {
|
||||||
|
let mut engine_state = nu_command::create_default_context();
|
||||||
|
// parsing config.nu breaks without PWD set
|
||||||
|
engine_state.add_env_var(
|
||||||
|
"PWD".into(),
|
||||||
|
Value::string("/some/dir".to_string(), Span::test_data()),
|
||||||
|
);
|
||||||
|
let mut stack = nu_protocol::engine::Stack::new();
|
||||||
|
eval_source(
|
||||||
|
&mut engine_state,
|
||||||
|
&mut stack,
|
||||||
|
get_default_config().as_bytes(),
|
||||||
|
"default_config.nu",
|
||||||
|
PipelineData::empty(),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate a new table data with `row_cnt` rows, `col_cnt` columns.
|
||||||
|
fn encoding_test_data(row_cnt: usize, col_cnt: usize) -> Value {
|
||||||
|
let columns: Vec<String> = (0..col_cnt).map(|x| format!("col_{x}")).collect();
|
||||||
|
let vals: Vec<Value> = (0..col_cnt as i64).map(Value::test_int).collect();
|
||||||
|
|
||||||
|
Value::List {
|
||||||
|
vals: (0..row_cnt)
|
||||||
|
.map(|_| Value::test_record(columns.clone(), vals.clone()))
|
||||||
|
.collect(),
|
||||||
|
span: Span::test_data(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encoding_benchmarks(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("Encoding");
|
||||||
|
let test_cnt_pairs = [
|
||||||
|
(100, 5),
|
||||||
|
(100, 10),
|
||||||
|
(100, 15),
|
||||||
|
(1000, 5),
|
||||||
|
(1000, 10),
|
||||||
|
(1000, 15),
|
||||||
|
(10000, 5),
|
||||||
|
(10000, 10),
|
||||||
|
(10000, 15),
|
||||||
|
];
|
||||||
|
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
|
||||||
|
for fmt in ["json", "msgpack"] {
|
||||||
|
group.bench_function(&format!("{fmt} encode {row_cnt} * {col_cnt}"), |b| {
|
||||||
|
let mut res = vec![];
|
||||||
|
let test_data =
|
||||||
|
PluginResponse::Value(Box::new(encoding_test_data(row_cnt, col_cnt)));
|
||||||
|
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
|
||||||
|
b.iter(|| encoder.encode_response(&test_data, &mut res))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decoding_benchmarks(c: &mut Criterion) {
|
||||||
|
let mut group = c.benchmark_group("Decoding");
|
||||||
|
let test_cnt_pairs = [
|
||||||
|
(100, 5),
|
||||||
|
(100, 10),
|
||||||
|
(100, 15),
|
||||||
|
(1000, 5),
|
||||||
|
(1000, 10),
|
||||||
|
(1000, 15),
|
||||||
|
(10000, 5),
|
||||||
|
(10000, 10),
|
||||||
|
(10000, 15),
|
||||||
|
];
|
||||||
|
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
|
||||||
|
for fmt in ["json", "msgpack"] {
|
||||||
|
group.bench_function(&format!("{fmt} decode for {row_cnt} * {col_cnt}"), |b| {
|
||||||
|
let mut res = vec![];
|
||||||
|
let test_data =
|
||||||
|
PluginResponse::Value(Box::new(encoding_test_data(row_cnt, col_cnt)));
|
||||||
|
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
|
||||||
|
encoder.encode_response(&test_data, &mut res).unwrap();
|
||||||
|
let mut binary_data = std::io::Cursor::new(res);
|
||||||
|
b.iter(|| {
|
||||||
|
binary_data.set_position(0);
|
||||||
|
encoder.decode_response(&mut binary_data)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
criterion_group!(
|
||||||
|
benches,
|
||||||
|
parser_benchmarks,
|
||||||
|
eval_benchmarks,
|
||||||
|
encoding_benchmarks,
|
||||||
|
decoding_benchmarks
|
||||||
|
);
|
||||||
|
criterion_main!(benches);
|
@ -1,76 +0,0 @@
|
|||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use nu_plugin::{EncodingType, PluginResponse};
|
|
||||||
use nu_protocol::{Span, Value};
|
|
||||||
|
|
||||||
// generate a new table data with `row_cnt` rows, `col_cnt` columns.
|
|
||||||
fn new_test_data(row_cnt: usize, col_cnt: usize) -> Value {
|
|
||||||
let columns: Vec<String> = (0..col_cnt).map(|x| format!("col_{x}")).collect();
|
|
||||||
let vals: Vec<Value> = (0..col_cnt as i64).map(Value::test_int).collect();
|
|
||||||
|
|
||||||
Value::List {
|
|
||||||
vals: (0..row_cnt)
|
|
||||||
.map(|_| Value::test_record(columns.clone(), vals.clone()))
|
|
||||||
.collect(),
|
|
||||||
span: Span::test_data(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_encoding(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("Encoding");
|
|
||||||
let test_cnt_pairs = [
|
|
||||||
(100, 5),
|
|
||||||
(100, 10),
|
|
||||||
(100, 15),
|
|
||||||
(1000, 5),
|
|
||||||
(1000, 10),
|
|
||||||
(1000, 15),
|
|
||||||
(10000, 5),
|
|
||||||
(10000, 10),
|
|
||||||
(10000, 15),
|
|
||||||
];
|
|
||||||
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
|
|
||||||
for fmt in ["json", "msgpack"] {
|
|
||||||
group.bench_function(&format!("{fmt} encode {row_cnt} * {col_cnt}"), |b| {
|
|
||||||
let mut res = vec![];
|
|
||||||
let test_data = PluginResponse::Value(Box::new(new_test_data(row_cnt, col_cnt)));
|
|
||||||
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
|
|
||||||
b.iter(|| encoder.encode_response(&test_data, &mut res))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn bench_decoding(c: &mut Criterion) {
|
|
||||||
let mut group = c.benchmark_group("Decoding");
|
|
||||||
let test_cnt_pairs = [
|
|
||||||
(100, 5),
|
|
||||||
(100, 10),
|
|
||||||
(100, 15),
|
|
||||||
(1000, 5),
|
|
||||||
(1000, 10),
|
|
||||||
(1000, 15),
|
|
||||||
(10000, 5),
|
|
||||||
(10000, 10),
|
|
||||||
(10000, 15),
|
|
||||||
];
|
|
||||||
for (row_cnt, col_cnt) in test_cnt_pairs.into_iter() {
|
|
||||||
for fmt in ["json", "msgpack"] {
|
|
||||||
group.bench_function(&format!("{fmt} decode for {row_cnt} * {col_cnt}"), |b| {
|
|
||||||
let mut res = vec![];
|
|
||||||
let test_data = PluginResponse::Value(Box::new(new_test_data(row_cnt, col_cnt)));
|
|
||||||
let encoder = EncodingType::try_from_bytes(fmt.as_bytes()).unwrap();
|
|
||||||
encoder.encode_response(&test_data, &mut res).unwrap();
|
|
||||||
let mut binary_data = std::io::Cursor::new(res);
|
|
||||||
b.iter(|| {
|
|
||||||
binary_data.set_position(0);
|
|
||||||
encoder.decode_response(&mut binary_data)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, bench_encoding, bench_decoding);
|
|
||||||
criterion_main!(benches);
|
|
@ -1,42 +0,0 @@
|
|||||||
use criterion::{criterion_group, criterion_main, Criterion};
|
|
||||||
use nu_cli::eval_source;
|
|
||||||
use nu_protocol::{PipelineData, Span, Value};
|
|
||||||
use nu_utils::{get_default_config, get_default_env};
|
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
|
||||||
c.bench_function("eval default_env.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = nu_command::create_default_context();
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_env().as_bytes(),
|
|
||||||
"default_env.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
c.bench_function("eval default_config.nu", |b| {
|
|
||||||
b.iter(|| {
|
|
||||||
let mut engine_state = nu_command::create_default_context();
|
|
||||||
// parsing config.nu breaks without PWD set
|
|
||||||
engine_state.add_env_var(
|
|
||||||
"PWD".into(),
|
|
||||||
Value::string("/some/dir".to_string(), Span::test_data()),
|
|
||||||
);
|
|
||||||
let mut stack = nu_protocol::engine::Stack::new();
|
|
||||||
eval_source(
|
|
||||||
&mut engine_state,
|
|
||||||
&mut stack,
|
|
||||||
get_default_config().as_bytes(),
|
|
||||||
"default_config.nu",
|
|
||||||
PipelineData::empty(),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
|
||||||
criterion_main!(benches);
|
|
@ -1,34 +0,0 @@
|
|||||||
use criterion::{criterion_group, criterion_main, BatchSize, Criterion};
|
|
||||||
use nu_parser::parse;
|
|
||||||
use nu_protocol::{Span, Value};
|
|
||||||
use nu_utils::{get_default_config, get_default_env};
|
|
||||||
|
|
||||||
fn criterion_benchmark(c: &mut Criterion) {
|
|
||||||
let mut engine_state = nu_command::create_default_context();
|
|
||||||
// parsing config.nu breaks without PWD set
|
|
||||||
engine_state.add_env_var(
|
|
||||||
"PWD".into(),
|
|
||||||
Value::string("/some/dir".to_string(), Span::test_data()),
|
|
||||||
);
|
|
||||||
|
|
||||||
let default_env = get_default_env().as_bytes();
|
|
||||||
c.bench_function("parse_default_env_file", |b| {
|
|
||||||
b.iter_batched(
|
|
||||||
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
|
||||||
|mut working_set| parse(&mut working_set, None, default_env, false, &[]),
|
|
||||||
BatchSize::SmallInput,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let default_config = get_default_config().as_bytes();
|
|
||||||
c.bench_function("parse_default_config_file", |b| {
|
|
||||||
b.iter_batched(
|
|
||||||
|| nu_protocol::engine::StateWorkingSet::new(&engine_state),
|
|
||||||
|mut working_set| parse(&mut working_set, None, default_config, false, &[]),
|
|
||||||
BatchSize::SmallInput,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
criterion_group!(benches, criterion_benchmark);
|
|
||||||
criterion_main!(benches);
|
|
@ -5,34 +5,34 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.74.0"
|
version = "0.75.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="../nu-test-support", version = "0.74.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.75.0" }
|
||||||
nu-command = { path = "../nu-command", version = "0.74.0" }
|
nu-command = { path = "../nu-command", version = "0.75.0" }
|
||||||
rstest = {version = "0.15.0", default-features = false}
|
rstest = { version = "0.15.0", default-features = false }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.74.0" }
|
nu-engine = { path = "../nu-engine", version = "0.75.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.74.0" }
|
nu-path = { path = "../nu-path", version = "0.75.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.74.0" }
|
nu-parser = { path = "../nu-parser", version = "0.75.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.74.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.75.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.74.0" }
|
nu-utils = { path = "../nu-utils", version = "0.75.0" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.74.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.75.0" }
|
||||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
reedline = { version = "0.15.0", features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.24.0"
|
||||||
fancy-regex = "0.10.0"
|
fancy-regex = "0.11.0"
|
||||||
fuzzy-matcher = "0.3.7"
|
fuzzy-matcher = "0.3.7"
|
||||||
is_executable = "1.0.1"
|
is_executable = "1.0.1"
|
||||||
once_cell = "1.16.0"
|
once_cell = "1.17.0"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
|
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
sysinfo = "0.26.2"
|
sysinfo = "0.27.7"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -164,7 +164,7 @@ impl Completer for CommandCompletion {
|
|||||||
.flattened
|
.flattened
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
.skip_while(|x| x.0.end > pos)
|
.skip_while(|x| x.0.end + offset > pos)
|
||||||
.take_while(|x| {
|
.take_while(|x| {
|
||||||
matches!(
|
matches!(
|
||||||
x.1,
|
x.1,
|
||||||
|
@ -104,18 +104,25 @@ impl NuCompleter {
|
|||||||
return Some(result);
|
return Some(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => println!("failed to eval completer block: {}", err),
|
Err(err) => println!("failed to eval completer block: {err}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||||
|
// pos: is the position of the cursor in the shell input.
|
||||||
|
// e.g. lets say you have an alias -> `alias ll = ls -l` and you type in the shell:
|
||||||
|
// > ll -a | c
|
||||||
|
// and your cursor is right after `c` then `pos` = 9
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let offset = working_set.next_span_start();
|
let mut offset = working_set.next_span_start();
|
||||||
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
|
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
|
||||||
let initial_line = line.to_string();
|
// new_line: vector containing all alias "translations" so if it was `ll` now is `ls -l`.
|
||||||
let alias_total_offset: usize = alias_offset.iter().sum();
|
// alias_offset:vector the offset between the name and the alias)
|
||||||
|
let initial_line = line.to_string(); // Entire line in the shell input.
|
||||||
|
let alias_total_offset: usize = alias_offset.iter().sum(); // the sum of all alias offsets.
|
||||||
new_line.insert(alias_total_offset + pos, b'a');
|
new_line.insert(alias_total_offset + pos, b'a');
|
||||||
let pos = offset + pos;
|
let pos = offset + pos;
|
||||||
let config = self.engine_state.get_config();
|
let config = self.engine_state.get_config();
|
||||||
@ -128,7 +135,8 @@ impl NuCompleter {
|
|||||||
PipelineElement::Expression(_, expr)
|
PipelineElement::Expression(_, expr)
|
||||||
| PipelineElement::Redirection(_, _, expr)
|
| PipelineElement::Redirection(_, _, expr)
|
||||||
| PipelineElement::And(_, expr)
|
| PipelineElement::And(_, expr)
|
||||||
| PipelineElement::Or(_, expr) => {
|
| PipelineElement::Or(_, expr)
|
||||||
|
| PipelineElement::SeparateRedirection { out: (_, expr), .. } => {
|
||||||
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||||
let span_offset: usize = alias_offset.iter().sum();
|
let span_offset: usize = alias_offset.iter().sum();
|
||||||
let mut spans: Vec<String> = vec![];
|
let mut spans: Vec<String> = vec![];
|
||||||
@ -155,14 +163,22 @@ impl NuCompleter {
|
|||||||
most_left_variable(flat_idx, &working_set, flattened.clone());
|
most_left_variable(flat_idx, &working_set, flattened.clone());
|
||||||
|
|
||||||
// Create a new span
|
// Create a new span
|
||||||
let new_span = if flat_idx == 0 {
|
// if flat_idx == 0
|
||||||
Span::new(flat.0.start, flat.0.end - 1 - span_offset)
|
let mut span_start = flat.0.start;
|
||||||
} else {
|
let mut span_end = flat.0.end - 1 - span_offset;
|
||||||
Span::new(
|
|
||||||
flat.0.start - span_offset,
|
if flat_idx != 0 {
|
||||||
flat.0.end - 1 - span_offset,
|
span_start = flat.0.start - span_offset;
|
||||||
)
|
span_end = flat.0.end - 1 - span_offset;
|
||||||
};
|
}
|
||||||
|
|
||||||
|
if span_end < span_start {
|
||||||
|
span_start = flat.0.start;
|
||||||
|
span_end = flat.0.end - 1;
|
||||||
|
offset += span_offset
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_span = Span::new(span_start, span_end);
|
||||||
|
|
||||||
// Parses the prefix. Completion should look up to the cursor position, not after.
|
// Parses the prefix. Completion should look up to the cursor position, not after.
|
||||||
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
|
||||||
@ -177,6 +193,10 @@ impl NuCompleter {
|
|||||||
most_left_var.unwrap_or((vec![], vec![])),
|
most_left_var.unwrap_or((vec![], vec![])),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if offset > new_span.start {
|
||||||
|
offset -= span_offset;
|
||||||
|
}
|
||||||
|
|
||||||
return self.process_completion(
|
return self.process_completion(
|
||||||
&mut completer,
|
&mut completer,
|
||||||
&working_set,
|
&working_set,
|
||||||
|
@ -74,8 +74,8 @@ impl Completer for CustomCompletion {
|
|||||||
let mut custom_completion_options = None;
|
let mut custom_completion_options = None;
|
||||||
|
|
||||||
// Parse result
|
// Parse result
|
||||||
let suggestions = match result {
|
let suggestions = result
|
||||||
Ok(pd) => {
|
.map(|pd| {
|
||||||
let value = pd.into_value(span);
|
let value = pd.into_value(span);
|
||||||
match &value {
|
match &value {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
@ -132,9 +132,8 @@ impl Completer for CustomCompletion {
|
|||||||
Value::List { vals, .. } => map_value_completions(vals.iter(), span, offset),
|
Value::List { vals, .. } => map_value_completions(vals.iter(), span, offset),
|
||||||
_ => vec![],
|
_ => vec![],
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
_ => vec![],
|
.unwrap_or_default();
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(custom_completion_options) = custom_completion_options {
|
if let Some(custom_completion_options) = custom_completion_options {
|
||||||
filter(&prefix, suggestions, &custom_completion_options)
|
filter(&prefix, suggestions, &custom_completion_options)
|
||||||
|
@ -33,14 +33,7 @@ impl Completer for DirectoryCompletion {
|
|||||||
_: usize,
|
_: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
let cwd = self.engine_state.current_work_dir();
|
||||||
match d.as_string() {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(_) => "".to_string(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
let partial = String::from_utf8_lossy(&prefix).to_string();
|
let partial = String::from_utf8_lossy(&prefix).to_string();
|
||||||
|
|
||||||
// Filter only the folders
|
// Filter only the folders
|
||||||
@ -126,7 +119,7 @@ pub fn directory_completion(
|
|||||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||||
if matches(&partial, &file_name, options) {
|
if matches(&partial, &file_name, options) {
|
||||||
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
||||||
format!("{}{}", base_dir_name, file_name)
|
format!("{base_dir_name}{file_name}")
|
||||||
} else {
|
} else {
|
||||||
file_name.to_string()
|
file_name.to_string()
|
||||||
};
|
};
|
||||||
@ -142,7 +135,7 @@ pub fn directory_completion(
|
|||||||
|| path.contains(' ')
|
|| path.contains(' ')
|
||||||
|| path.contains('#')
|
|| path.contains('#')
|
||||||
{
|
{
|
||||||
path = format!("`{}`", path);
|
path = format!("`{path}`");
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((span, path))
|
Some((span, path))
|
||||||
|
@ -58,7 +58,7 @@ impl Completer for DotNuCompletion {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Check if the base_dir is a folder
|
// Check if the base_dir is a folder
|
||||||
if base_dir != format!(".{}", SEP) {
|
if base_dir != format!(".{SEP}") {
|
||||||
// Add the base dir into the directories to be searched
|
// Add the base dir into the directories to be searched
|
||||||
search_dirs.push(base_dir.clone());
|
search_dirs.push(base_dir.clone());
|
||||||
|
|
||||||
@ -70,14 +70,7 @@ impl Completer for DotNuCompletion {
|
|||||||
partial = base_dir_partial;
|
partial = base_dir_partial;
|
||||||
} else {
|
} else {
|
||||||
// Fetch the current folder
|
// Fetch the current folder
|
||||||
let current_folder = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
let current_folder = self.engine_state.current_work_dir();
|
||||||
match d.as_string() {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(_) => "".to_string(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
is_current_folder = true;
|
is_current_folder = true;
|
||||||
|
|
||||||
// Add the current folder and the lib dirs into the
|
// Add the current folder and the lib dirs into the
|
||||||
|
@ -30,14 +30,7 @@ impl Completer for FileCompletion {
|
|||||||
_: usize,
|
_: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
let cwd = self.engine_state.current_work_dir();
|
||||||
match d.as_string() {
|
|
||||||
Ok(s) => s,
|
|
||||||
Err(_) => "".to_string(),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
"".to_string()
|
|
||||||
};
|
|
||||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||||
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options)
|
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -131,7 +124,7 @@ pub fn file_path_completion(
|
|||||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||||
if matches(&partial, &file_name, options) {
|
if matches(&partial, &file_name, options) {
|
||||||
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
let mut path = if prepend_base_dir(original_input, &base_dir_name) {
|
||||||
format!("{}{}", base_dir_name, file_name)
|
format!("{base_dir_name}{file_name}")
|
||||||
} else {
|
} else {
|
||||||
file_name.to_string()
|
file_name.to_string()
|
||||||
};
|
};
|
||||||
@ -149,7 +142,7 @@ pub fn file_path_completion(
|
|||||||
|| path.contains('(')
|
|| path.contains('(')
|
||||||
|| path.contains(')')
|
|| path.contains(')')
|
||||||
{
|
{
|
||||||
path = format!("`{}`", path);
|
path = format!("`{path}`");
|
||||||
}
|
}
|
||||||
|
|
||||||
Some((span, path))
|
Some((span, path))
|
||||||
@ -177,7 +170,7 @@ pub fn matches(partial: &str, from: &str, options: &CompletionOptions) -> bool {
|
|||||||
|
|
||||||
/// Returns whether the base_dir should be prepended to the file path
|
/// Returns whether the base_dir should be prepended to the file path
|
||||||
pub fn prepend_base_dir(input: &str, base_dir: &str) -> bool {
|
pub fn prepend_base_dir(input: &str, base_dir: &str) -> bool {
|
||||||
if base_dir == format!(".{}", SEP) {
|
if base_dir == format!(".{SEP}") {
|
||||||
// if the current base_dir path is the local folder we only add a "./" prefix if the user
|
// if the current base_dir path is the local folder we only add a "./" prefix if the user
|
||||||
// input already includes a local folder prefix.
|
// input already includes a local folder prefix.
|
||||||
let manually_entered = {
|
let manually_entered = {
|
||||||
|
@ -262,6 +262,20 @@ fn nested_suggestions(
|
|||||||
|
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
Value::LazyRecord { val, .. } => {
|
||||||
|
// Add all the columns as completion
|
||||||
|
for column_name in val.column_names() {
|
||||||
|
output.push(Suggestion {
|
||||||
|
value: column_name.to_string(),
|
||||||
|
description: None,
|
||||||
|
extra: None,
|
||||||
|
span: current_span,
|
||||||
|
append_whitespace: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
output
|
||||||
|
}
|
||||||
|
|
||||||
_ => output,
|
_ => output,
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
use crate::util::{eval_source, report_error};
|
use crate::util::{eval_source, report_error};
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use log::info;
|
|
||||||
#[cfg(feature = "plugin")]
|
|
||||||
use nu_parser::ParseError;
|
use nu_parser::ParseError;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
@ -9,6 +7,8 @@ use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
|||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_protocol::Spanned;
|
use nu_protocol::Spanned;
|
||||||
use nu_protocol::{HistoryFileFormat, PipelineData};
|
use nu_protocol::{HistoryFileFormat, PipelineData};
|
||||||
|
#[cfg(feature = "plugin")]
|
||||||
|
use nu_utils::utils::perf;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
@ -24,6 +24,8 @@ pub fn read_plugin_file(
|
|||||||
plugin_file: Option<Spanned<String>>,
|
plugin_file: Option<Spanned<String>>,
|
||||||
storage_path: &str,
|
storage_path: &str,
|
||||||
) {
|
) {
|
||||||
|
let start_time = std::time::Instant::now();
|
||||||
|
let mut plug_path = String::new();
|
||||||
// Reading signatures from signature file
|
// Reading signatures from signature file
|
||||||
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
||||||
add_plugin_file(engine_state, plugin_file, storage_path);
|
add_plugin_file(engine_state, plugin_file, storage_path);
|
||||||
@ -31,7 +33,7 @@ pub fn read_plugin_file(
|
|||||||
let plugin_path = engine_state.plugin_signatures.clone();
|
let plugin_path = engine_state.plugin_signatures.clone();
|
||||||
if let Some(plugin_path) = plugin_path {
|
if let Some(plugin_path) = plugin_path {
|
||||||
let plugin_filename = plugin_path.to_string_lossy();
|
let plugin_filename = plugin_path.to_string_lossy();
|
||||||
|
plug_path = plugin_filename.to_string();
|
||||||
if let Ok(contents) = std::fs::read(&plugin_path) {
|
if let Ok(contents) = std::fs::read(&plugin_path) {
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
@ -43,7 +45,13 @@ pub fn read_plugin_file(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
|
perf(
|
||||||
|
&format!("read_plugin_file {}", &plug_path),
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
@ -56,13 +64,12 @@ pub fn add_plugin_file(
|
|||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
match canonicalize_with(&plugin_file.item, cwd) {
|
if let Ok(path) = canonicalize_with(&plugin_file.item, cwd) {
|
||||||
Ok(path) => engine_state.plugin_signatures = Some(path),
|
engine_state.plugin_signatures = Some(path)
|
||||||
Err(_) => {
|
} else {
|
||||||
let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span);
|
let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span);
|
||||||
report_error(&working_set, &e);
|
report_error(&working_set, &e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
||||||
// Path to store plugins signatures
|
// Path to store plugins signatures
|
||||||
plugin_path.push(storage_path);
|
plugin_path.push(storage_path);
|
||||||
|
@ -29,10 +29,7 @@ pub fn evaluate_file(
|
|||||||
|
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
let file_path = {
|
let file_path = canonicalize_with(&path, cwd).unwrap_or_else(|e| {
|
||||||
match canonicalize_with(&path, &cwd) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(
|
report_error(
|
||||||
&working_set,
|
&working_set,
|
||||||
@ -42,13 +39,9 @@ pub fn evaluate_file(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
});
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let file_path_str = match file_path.to_str() {
|
let file_path_str = file_path.to_str().unwrap_or_else(|| {
|
||||||
Some(s) => s,
|
|
||||||
None => {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(
|
report_error(
|
||||||
&working_set,
|
&working_set,
|
||||||
@ -61,12 +54,11 @@ pub fn evaluate_file(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
});
|
||||||
};
|
|
||||||
|
|
||||||
let file = match std::fs::read(&file_path).into_diagnostic() {
|
let file = std::fs::read(&file_path)
|
||||||
Ok(p) => p,
|
.into_diagnostic()
|
||||||
Err(e) => {
|
.unwrap_or_else(|e| {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(
|
report_error(
|
||||||
&working_set,
|
&working_set,
|
||||||
@ -80,13 +72,21 @@ pub fn evaluate_file(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
});
|
||||||
};
|
|
||||||
|
|
||||||
engine_state.start_in_file(Some(file_path_str));
|
engine_state.start_in_file(Some(file_path_str));
|
||||||
|
|
||||||
let mut parent = file_path.clone();
|
let parent = file_path.parent().unwrap_or_else(|| {
|
||||||
parent.pop();
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
report_error(
|
||||||
|
&working_set,
|
||||||
|
&ShellError::FileNotFoundCustom(
|
||||||
|
format!("The file path '{file_path_str}' does not have a parent"),
|
||||||
|
Span::unknown(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
std::process::exit(1);
|
||||||
|
});
|
||||||
|
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
"FILE_PWD".to_string(),
|
"FILE_PWD".to_string(),
|
||||||
@ -121,7 +121,7 @@ pub fn evaluate_file(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_table_or_error(
|
pub(crate) fn print_table_or_error(
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
mut pipeline_data: PipelineData,
|
mut pipeline_data: PipelineData,
|
||||||
@ -137,14 +137,11 @@ pub fn print_table_or_error(
|
|||||||
|
|
||||||
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
|
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
report_error(&working_set, error);
|
report_error(&working_set, error);
|
||||||
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
match engine_state.find_decl("table".as_bytes(), &[]) {
|
if let Some(decl_id) = engine_state.find_decl("table".as_bytes(), &[]) {
|
||||||
Some(decl_id) => {
|
|
||||||
let command = engine_state.get_decl(decl_id);
|
let command = engine_state.get_decl(decl_id);
|
||||||
if command.get_block_id().is_some() {
|
if command.get_block_id().is_some() {
|
||||||
print_or_exit(pipeline_data, engine_state, config);
|
print_or_exit(pipeline_data, engine_state, config);
|
||||||
@ -162,18 +159,14 @@ pub fn print_table_or_error(
|
|||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
report_error(&working_set, &error);
|
report_error(&working_set, &error);
|
||||||
|
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
None => {
|
|
||||||
print_or_exit(pipeline_data, engine_state, config);
|
print_or_exit(pipeline_data, engine_state, config);
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Make sure everything has finished
|
// Make sure everything has finished
|
||||||
if let Some(exit_code) = exit_code {
|
if let Some(exit_code) = exit_code {
|
||||||
@ -199,9 +192,7 @@ fn print_or_exit(pipeline_data: PipelineData, engine_state: &mut EngineState, co
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut out = item.into_string("\n", config);
|
let out = item.into_string("\n", config) + "\n";
|
||||||
out.push('\n');
|
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{err}"));
|
||||||
|
|
||||||
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{}", err));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -411,10 +411,10 @@ impl DescriptionMenu {
|
|||||||
RESET
|
RESET
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!(" {}\r\n", example)
|
format!(" {example}\r\n")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
format!(" {}\r\n", example)
|
format!(" {example}\r\n")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -429,7 +429,7 @@ impl DescriptionMenu {
|
|||||||
examples,
|
examples,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
format!("\r\n\r\nExamples:\r\n{}", examples,)
|
format!("\r\n\r\nExamples:\r\n{examples}",)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,13 +81,10 @@ fn convert_to_suggestions(
|
|||||||
) -> Vec<Suggestion> {
|
) -> Vec<Suggestion> {
|
||||||
match value {
|
match value {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => {
|
||||||
let text = match value
|
let text = value
|
||||||
.get_data_by_key("value")
|
.get_data_by_key("value")
|
||||||
.and_then(|val| val.as_string().ok())
|
.and_then(|val| val.as_string().ok())
|
||||||
{
|
.unwrap_or_else(|| "No value key".to_string());
|
||||||
Some(val) => val,
|
|
||||||
None => "No value key".to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let description = value
|
let description = value
|
||||||
.get_data_by_key("description")
|
.get_data_by_key("description")
|
||||||
@ -157,7 +154,7 @@ fn convert_to_suggestions(
|
|||||||
.flat_map(|val| convert_to_suggestions(val, line, pos, only_buffer_difference))
|
.flat_map(|val| convert_to_suggestions(val, line, pos, only_buffer_difference))
|
||||||
.collect(),
|
.collect(),
|
||||||
_ => vec![Suggestion {
|
_ => vec![Suggestion {
|
||||||
value: format!("Not a record: {:?}", value),
|
value: format!("Not a record: {value:?}"),
|
||||||
description: None,
|
description: None,
|
||||||
extra: None,
|
extra: None,
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
|
@ -35,7 +35,7 @@ impl Command for NuHighlight {
|
|||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
let engine_state = engine_state.clone();
|
let engine_state = std::sync::Arc::new(engine_state.clone());
|
||||||
let config = engine_state.get_config().clone();
|
let config = engine_state.get_config().clone();
|
||||||
|
|
||||||
let highlighter = crate::NuHighlighter {
|
let highlighter = crate::NuHighlighter {
|
||||||
|
@ -91,7 +91,7 @@ impl NushellPrompt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_wrapped_custom_string(&self, str: String) -> String {
|
fn default_wrapped_custom_string(&self, str: String) -> String {
|
||||||
format!("({})", str)
|
format!("({str})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ impl Prompt for NushellPrompt {
|
|||||||
if let Some(prompt_string) = &self.left_prompt_string {
|
if let Some(prompt_string) = &self.left_prompt_string {
|
||||||
prompt_string.replace('\n', "\r\n").into()
|
prompt_string.replace('\n', "\r\n").into()
|
||||||
} else {
|
} else {
|
||||||
let default = DefaultPrompt::new();
|
let default = DefaultPrompt::default();
|
||||||
default
|
default
|
||||||
.render_prompt_left()
|
.render_prompt_left()
|
||||||
.to_string()
|
.to_string()
|
||||||
@ -118,7 +118,7 @@ impl Prompt for NushellPrompt {
|
|||||||
if let Some(prompt_string) = &self.right_prompt_string {
|
if let Some(prompt_string) = &self.right_prompt_string {
|
||||||
prompt_string.replace('\n', "\r\n").into()
|
prompt_string.replace('\n', "\r\n").into()
|
||||||
} else {
|
} else {
|
||||||
let default = DefaultPrompt::new();
|
let default = DefaultPrompt::default();
|
||||||
default
|
default
|
||||||
.render_prompt_right()
|
.render_prompt_right()
|
||||||
.to_string()
|
.to_string()
|
||||||
@ -130,32 +130,36 @@ impl Prompt for NushellPrompt {
|
|||||||
fn render_prompt_indicator(&self, edit_mode: PromptEditMode) -> Cow<str> {
|
fn render_prompt_indicator(&self, edit_mode: PromptEditMode) -> Cow<str> {
|
||||||
match edit_mode {
|
match edit_mode {
|
||||||
PromptEditMode::Default => match &self.default_prompt_indicator {
|
PromptEditMode::Default => match &self.default_prompt_indicator {
|
||||||
Some(indicator) => indicator.as_str().into(),
|
Some(indicator) => indicator,
|
||||||
None => "〉".into(),
|
None => "〉",
|
||||||
},
|
}
|
||||||
|
.into(),
|
||||||
PromptEditMode::Emacs => match &self.default_prompt_indicator {
|
PromptEditMode::Emacs => match &self.default_prompt_indicator {
|
||||||
Some(indicator) => indicator.as_str().into(),
|
Some(indicator) => indicator,
|
||||||
None => "〉".into(),
|
None => "〉",
|
||||||
},
|
}
|
||||||
|
.into(),
|
||||||
PromptEditMode::Vi(vi_mode) => match vi_mode {
|
PromptEditMode::Vi(vi_mode) => match vi_mode {
|
||||||
PromptViMode::Normal => match &self.default_vi_normal_prompt_indicator {
|
PromptViMode::Normal => match &self.default_vi_normal_prompt_indicator {
|
||||||
Some(indicator) => indicator.as_str().into(),
|
Some(indicator) => indicator,
|
||||||
None => ": ".into(),
|
None => ": ",
|
||||||
},
|
},
|
||||||
PromptViMode::Insert => match &self.default_vi_insert_prompt_indicator {
|
PromptViMode::Insert => match &self.default_vi_insert_prompt_indicator {
|
||||||
Some(indicator) => indicator.as_str().into(),
|
Some(indicator) => indicator,
|
||||||
None => "〉".into(),
|
None => "〉",
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
PromptEditMode::Custom(str) => self.default_wrapped_custom_string(str).into(),
|
PromptEditMode::Custom(str) => self.default_wrapped_custom_string(str).into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
||||||
match &self.default_multiline_indicator {
|
match &self.default_multiline_indicator {
|
||||||
Some(indicator) => indicator.as_str().into(),
|
Some(indicator) => indicator,
|
||||||
None => "::: ".into(),
|
None => "::: ",
|
||||||
}
|
}
|
||||||
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_prompt_history_search_indicator(
|
fn render_prompt_history_search_indicator(
|
||||||
|
@ -46,14 +46,12 @@ fn get_prompt_string(
|
|||||||
column!()
|
column!()
|
||||||
);
|
);
|
||||||
|
|
||||||
match ret_val {
|
ret_val
|
||||||
Ok(ret_val) => Some(ret_val),
|
.map_err(|err| {
|
||||||
Err(err) => {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
None
|
})
|
||||||
}
|
.ok()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Value::Block { val: block_id, .. } => {
|
Value::Block { val: block_id, .. } => {
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
@ -66,14 +64,12 @@ fn get_prompt_string(
|
|||||||
column!()
|
column!()
|
||||||
);
|
);
|
||||||
|
|
||||||
match ret_val {
|
ret_val
|
||||||
Ok(ret_val) => Some(ret_val),
|
.map_err(|err| {
|
||||||
Err(err) => {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &err);
|
report_error(&working_set, &err);
|
||||||
None
|
})
|
||||||
}
|
.ok()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
|
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -81,8 +77,7 @@ fn get_prompt_string(
|
|||||||
.and_then(|pipeline_data| {
|
.and_then(|pipeline_data| {
|
||||||
let output = pipeline_data.collect_string("", config).ok();
|
let output = pipeline_data.collect_string("", config).ok();
|
||||||
|
|
||||||
match output {
|
output.map(|mut x| {
|
||||||
Some(mut x) => {
|
|
||||||
// Just remove the very last newline.
|
// Just remove the very last newline.
|
||||||
if x.ends_with('\n') {
|
if x.ends_with('\n') {
|
||||||
x.pop();
|
x.pop();
|
||||||
@ -91,10 +86,8 @@ fn get_prompt_string(
|
|||||||
if x.ends_with('\r') {
|
if x.ends_with('\r') {
|
||||||
x.pop();
|
x.pop();
|
||||||
}
|
}
|
||||||
Some(x)
|
x
|
||||||
}
|
})
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,12 +104,12 @@ pub(crate) fn update_prompt<'prompt>(
|
|||||||
// Now that we have the prompt string lets ansify it.
|
// Now that we have the prompt string lets ansify it.
|
||||||
// <133 A><prompt><133 B><command><133 C><command output>
|
// <133 A><prompt><133 B><command><133 C><command output>
|
||||||
let left_prompt_string = if config.shell_integration {
|
let left_prompt_string = if config.shell_integration {
|
||||||
match left_prompt_string {
|
if let Some(prompt_string) = left_prompt_string {
|
||||||
Some(prompt_string) => Some(format!(
|
Some(format!(
|
||||||
"{}{}{}",
|
"{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}"
|
||||||
PRE_PROMPT_MARKER, prompt_string, POST_PROMPT_MARKER
|
))
|
||||||
)),
|
} else {
|
||||||
None => left_prompt_string,
|
left_prompt_string
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
left_prompt_string
|
left_prompt_string
|
||||||
|
@ -651,14 +651,15 @@ fn add_parsed_keybinding(
|
|||||||
let pos1 = char_iter.next();
|
let pos1 = char_iter.next();
|
||||||
let pos2 = char_iter.next();
|
let pos2 = char_iter.next();
|
||||||
|
|
||||||
let char = match (pos1, pos2) {
|
let char = if let (Some(char), None) = (pos1, pos2) {
|
||||||
(Some(char), None) => Ok(char),
|
char
|
||||||
_ => Err(ShellError::UnsupportedConfigValue(
|
} else {
|
||||||
|
return Err(ShellError::UnsupportedConfigValue(
|
||||||
"char_<CHAR: unicode codepoint>".to_string(),
|
"char_<CHAR: unicode codepoint>".to_string(),
|
||||||
c.to_string(),
|
c.to_string(),
|
||||||
keybinding.keycode.span()?,
|
keybinding.keycode.span()?,
|
||||||
)),
|
));
|
||||||
}?;
|
};
|
||||||
|
|
||||||
KeyCode::Char(char)
|
KeyCode::Char(char)
|
||||||
}
|
}
|
||||||
@ -682,7 +683,7 @@ fn add_parsed_keybinding(
|
|||||||
.filter(|num| matches!(num, 1..=20))
|
.filter(|num| matches!(num, 1..=20))
|
||||||
.ok_or(ShellError::UnsupportedConfigValue(
|
.ok_or(ShellError::UnsupportedConfigValue(
|
||||||
"(f1|f2|...|f20)".to_string(),
|
"(f1|f2|...|f20)".to_string(),
|
||||||
format!("unknown function key: {}", c),
|
format!("unknown function key: {c}"),
|
||||||
keybinding.keycode.span()?,
|
keybinding.keycode.span()?,
|
||||||
))?;
|
))?;
|
||||||
KeyCode::F(fn_num)
|
KeyCode::F(fn_num)
|
||||||
|
@ -5,18 +5,21 @@ use crate::{
|
|||||||
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
|
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
|
||||||
NuHighlighter, NuValidator, NushellPrompt,
|
NuHighlighter, NuValidator, NushellPrompt,
|
||||||
};
|
};
|
||||||
use log::{info, trace, warn};
|
use crossterm::cursor::CursorShape;
|
||||||
|
use log::{trace, warn};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
use nu_color_config::StyleComputer;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
|
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
|
||||||
use nu_parser::{lex, parse, trim_quotes_str};
|
use nu_parser::{lex, parse, trim_quotes_str};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::PathMember,
|
ast::PathMember,
|
||||||
|
config::NuCursorShape,
|
||||||
engine::{EngineState, ReplOperation, Stack, StateWorkingSet},
|
engine::{EngineState, ReplOperation, Stack, StateWorkingSet},
|
||||||
format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span,
|
format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span,
|
||||||
Spanned, Type, Value, VarId,
|
Spanned, Type, Value, VarId,
|
||||||
};
|
};
|
||||||
use reedline::{DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
|
use nu_utils::utils::perf;
|
||||||
|
use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
|
||||||
use std::{
|
use std::{
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
sync::atomic::Ordering,
|
sync::atomic::Ordering,
|
||||||
@ -39,6 +42,7 @@ pub fn evaluate_repl(
|
|||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
nushell_path: &str,
|
nushell_path: &str,
|
||||||
prerun_command: Option<Spanned<String>>,
|
prerun_command: Option<Spanned<String>>,
|
||||||
|
entire_start_time: Instant,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use reedline::{FileBackedHistory, Reedline, Signal};
|
use reedline::{FileBackedHistory, Reedline, Signal};
|
||||||
|
|
||||||
@ -56,18 +60,19 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
let mut nu_prompt = NushellPrompt::new();
|
let mut nu_prompt = NushellPrompt::new();
|
||||||
|
|
||||||
info!(
|
let start_time = std::time::Instant::now();
|
||||||
"translate environment vars {}:{}:{}",
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
if let Some(e) = convert_env_values(engine_state, stack) {
|
if let Some(e) = convert_env_values(engine_state, stack) {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &e);
|
report_error(&working_set, &e);
|
||||||
}
|
}
|
||||||
|
perf(
|
||||||
|
"translate env vars",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
);
|
||||||
|
|
||||||
// seed env vars
|
// seed env vars
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
@ -77,33 +82,25 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
stack.add_env_var("LAST_EXIT_CODE".into(), Value::int(0, Span::unknown()));
|
stack.add_env_var("LAST_EXIT_CODE".into(), Value::int(0, Span::unknown()));
|
||||||
|
|
||||||
info!(
|
let mut start_time = std::time::Instant::now();
|
||||||
"load config initially {}:{}:{}",
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!()
|
|
||||||
);
|
|
||||||
|
|
||||||
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
|
|
||||||
|
|
||||||
let mut line_editor = Reedline::create();
|
let mut line_editor = Reedline::create();
|
||||||
|
|
||||||
// Now that reedline is created, get the history session id and store it in engine_state
|
// Now that reedline is created, get the history session id and store it in engine_state
|
||||||
let hist_sesh = match line_editor.get_history_session_id() {
|
let hist_sesh = line_editor
|
||||||
Some(id) => i64::from(id),
|
.get_history_session_id()
|
||||||
None => 0,
|
.map(i64::from)
|
||||||
};
|
.unwrap_or(0);
|
||||||
engine_state.history_session_id = hist_sesh;
|
engine_state.history_session_id = hist_sesh;
|
||||||
|
perf("setup reedline", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
let history_path = crate::config_files::get_history_path(
|
let history_path = crate::config_files::get_history_path(
|
||||||
nushell_path,
|
nushell_path,
|
||||||
engine_state.config.history_file_format,
|
engine_state.config.history_file_format,
|
||||||
);
|
);
|
||||||
if let Some(history_path) = history_path.as_deref() {
|
if let Some(history_path) = history_path.as_deref() {
|
||||||
info!("setup history {}:{}:{}", file!(), line!(), column!());
|
|
||||||
|
|
||||||
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
|
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
|
||||||
HistoryFileFormat::PlainText => Box::new(
|
HistoryFileFormat::PlainText => Box::new(
|
||||||
FileBackedHistory::with_file(
|
FileBackedHistory::with_file(
|
||||||
@ -118,7 +115,9 @@ pub fn evaluate_repl(
|
|||||||
};
|
};
|
||||||
line_editor = line_editor.with_history(history);
|
line_editor = line_editor.with_history(history);
|
||||||
};
|
};
|
||||||
|
perf("setup history", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
let sys = sysinfo::System::new();
|
let sys = sysinfo::System::new();
|
||||||
|
|
||||||
let show_banner = config.show_banner;
|
let show_banner = config.show_banner;
|
||||||
@ -126,61 +125,89 @@ pub fn evaluate_repl(
|
|||||||
if show_banner {
|
if show_banner {
|
||||||
let banner = get_banner(engine_state, stack);
|
let banner = get_banner(engine_state, stack);
|
||||||
if use_ansi {
|
if use_ansi {
|
||||||
println!("{}", banner);
|
println!("{banner}");
|
||||||
} else {
|
} else {
|
||||||
println!("{}", nu_utils::strip_ansi_string_likely(banner));
|
println!("{}", nu_utils::strip_ansi_string_likely(banner));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
perf(
|
||||||
|
"get sysinfo/show banner",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
);
|
||||||
|
|
||||||
if let Some(s) = prerun_command {
|
if let Some(s) = prerun_command {
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
s.item.as_bytes(),
|
s.item.as_bytes(),
|
||||||
&format!("entry #{}", entry_num),
|
&format!("entry #{entry_num}"),
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
);
|
);
|
||||||
engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?;
|
engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
info!(
|
let loop_start_time = std::time::Instant::now();
|
||||||
"load config each loop {}:{}:{}",
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!()
|
|
||||||
);
|
|
||||||
|
|
||||||
let cwd = get_guaranteed_cwd(engine_state, stack);
|
let cwd = get_guaranteed_cwd(engine_state, stack);
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
// Before doing anything, merge the environment from the previous REPL iteration into the
|
// Before doing anything, merge the environment from the previous REPL iteration into the
|
||||||
// permanent state.
|
// permanent state.
|
||||||
if let Err(err) = engine_state.merge_env(stack, cwd) {
|
if let Err(err) = engine_state.merge_env(stack, cwd) {
|
||||||
report_error_new(engine_state, &err);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
|
perf("merge env", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
//Reset the ctrl-c handler
|
//Reset the ctrl-c handler
|
||||||
if let Some(ctrlc) = &mut engine_state.ctrlc {
|
if let Some(ctrlc) = &mut engine_state.ctrlc {
|
||||||
ctrlc.store(false, Ordering::SeqCst);
|
ctrlc.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
perf("reset ctrlc", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
// Reset the SIGQUIT handler
|
// Reset the SIGQUIT handler
|
||||||
if let Some(sig_quit) = engine_state.get_sig_quit() {
|
if let Some(sig_quit) = engine_state.get_sig_quit() {
|
||||||
sig_quit.store(false, Ordering::SeqCst);
|
sig_quit.store(false, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
perf("reset sig_quit", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
|
|
||||||
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
|
||||||
|
|
||||||
info!("update reedline {}:{}:{}", file!(), line!(), column!());
|
|
||||||
let engine_reference = std::sync::Arc::new(engine_state.clone());
|
let engine_reference = std::sync::Arc::new(engine_state.clone());
|
||||||
|
|
||||||
|
// Find the configured cursor shapes for each mode
|
||||||
|
let cursor_config = CursorConfig {
|
||||||
|
vi_insert: Some(map_nucursorshape_to_cursorshape(
|
||||||
|
config.cursor_shape_vi_insert,
|
||||||
|
)),
|
||||||
|
vi_normal: Some(map_nucursorshape_to_cursorshape(
|
||||||
|
config.cursor_shape_vi_normal,
|
||||||
|
)),
|
||||||
|
emacs: Some(map_nucursorshape_to_cursorshape(config.cursor_shape_emacs)),
|
||||||
|
};
|
||||||
|
perf(
|
||||||
|
"get config/cursor config",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
);
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
|
|
||||||
line_editor = line_editor
|
line_editor = line_editor
|
||||||
.with_highlighter(Box::new(NuHighlighter {
|
.with_highlighter(Box::new(NuHighlighter {
|
||||||
engine_state: engine_state.clone(),
|
engine_state: engine_reference.clone(),
|
||||||
config: config.clone(),
|
config: config.clone(),
|
||||||
}))
|
}))
|
||||||
.with_validator(Box::new(NuValidator {
|
.with_validator(Box::new(NuValidator {
|
||||||
engine_state: engine_state.clone(),
|
engine_state: engine_reference.clone(),
|
||||||
}))
|
}))
|
||||||
.with_completer(Box::new(NuCompleter::new(
|
.with_completer(Box::new(NuCompleter::new(
|
||||||
engine_reference.clone(),
|
engine_reference.clone(),
|
||||||
@ -188,10 +215,13 @@ pub fn evaluate_repl(
|
|||||||
)))
|
)))
|
||||||
.with_quick_completions(config.quick_completions)
|
.with_quick_completions(config.quick_completions)
|
||||||
.with_partial_completions(config.partial_completions)
|
.with_partial_completions(config.partial_completions)
|
||||||
.with_ansi_colors(config.use_ansi_coloring);
|
.with_ansi_colors(config.use_ansi_coloring)
|
||||||
|
.with_cursor_config(cursor_config);
|
||||||
|
perf("reedline builder", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
let style_computer = StyleComputer::from_config(engine_state, stack);
|
let style_computer = StyleComputer::from_config(engine_state, stack);
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
line_editor = if config.use_ansi_coloring {
|
line_editor = if config.use_ansi_coloring {
|
||||||
line_editor.with_hinter(Box::new({
|
line_editor.with_hinter(Box::new({
|
||||||
// As of Nov 2022, "hints" color_config closures only get `null` passed in.
|
// As of Nov 2022, "hints" color_config closures only get `null` passed in.
|
||||||
@ -201,16 +231,23 @@ pub fn evaluate_repl(
|
|||||||
} else {
|
} else {
|
||||||
line_editor.disable_hints()
|
line_editor.disable_hints()
|
||||||
};
|
};
|
||||||
|
perf(
|
||||||
|
"reedline coloring/style_computer",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
);
|
||||||
|
|
||||||
line_editor = match add_menus(line_editor, engine_reference, stack, config) {
|
start_time = std::time::Instant::now();
|
||||||
Ok(line_editor) => line_editor,
|
line_editor = add_menus(line_editor, engine_reference, stack, config).unwrap_or_else(|e| {
|
||||||
Err(e) => {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &e);
|
report_error(&working_set, &e);
|
||||||
Reedline::create()
|
Reedline::create()
|
||||||
}
|
});
|
||||||
};
|
perf("reedline menus", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
let buffer_editor = if !config.buffer_editor.is_empty() {
|
let buffer_editor = if !config.buffer_editor.is_empty() {
|
||||||
Some(config.buffer_editor.clone())
|
Some(config.buffer_editor.clone())
|
||||||
} else {
|
} else {
|
||||||
@ -231,17 +268,23 @@ pub fn evaluate_repl(
|
|||||||
} else {
|
} else {
|
||||||
line_editor
|
line_editor
|
||||||
};
|
};
|
||||||
|
perf(
|
||||||
|
"reedline buffer_editor",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
);
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
if config.sync_history_on_enter {
|
if config.sync_history_on_enter {
|
||||||
info!("sync history {}:{}:{}", file!(), line!(), column!());
|
|
||||||
|
|
||||||
if let Err(e) = line_editor.sync_history() {
|
if let Err(e) = line_editor.sync_history() {
|
||||||
warn!("Failed to sync history: {}", e);
|
warn!("Failed to sync history: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
perf("sync_history", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
|
start_time = std::time::Instant::now();
|
||||||
|
|
||||||
// Changing the line editor based on the found keybindings
|
// Changing the line editor based on the found keybindings
|
||||||
line_editor = match create_keybindings(config) {
|
line_editor = match create_keybindings(config) {
|
||||||
Ok(keybindings) => match keybindings {
|
Ok(keybindings) => match keybindings {
|
||||||
@ -263,9 +306,9 @@ pub fn evaluate_repl(
|
|||||||
line_editor
|
line_editor
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
perf("keybindings", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
|
start_time = std::time::Instant::now();
|
||||||
|
|
||||||
// Right before we start our prompt and take input from the user,
|
// Right before we start our prompt and take input from the user,
|
||||||
// fire the "pre_prompt" hook
|
// fire the "pre_prompt" hook
|
||||||
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
||||||
@ -273,7 +316,9 @@ pub fn evaluate_repl(
|
|||||||
report_error_new(engine_state, &err);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
perf("pre-prompt hook", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
// Next, check all the environment variables they ask for
|
// Next, check all the environment variables they ask for
|
||||||
// fire the "env_change" hook
|
// fire the "env_change" hook
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
@ -282,19 +327,23 @@ pub fn evaluate_repl(
|
|||||||
{
|
{
|
||||||
report_error_new(engine_state, &error)
|
report_error_new(engine_state, &error)
|
||||||
}
|
}
|
||||||
|
perf("env-change hook", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
|
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
|
||||||
|
perf("update_prompt", start_time, file!(), line!(), column!());
|
||||||
|
|
||||||
entry_num += 1;
|
entry_num += 1;
|
||||||
|
|
||||||
info!(
|
if entry_num == 1 && show_banner {
|
||||||
"finished setup, starting repl {}:{}:{}",
|
println!(
|
||||||
file!(),
|
"Startup Time: {}",
|
||||||
line!(),
|
format_duration(entire_start_time.elapsed().as_nanos() as i64)
|
||||||
column!()
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
start_time = std::time::Instant::now();
|
||||||
let input = line_editor.read_line(prompt);
|
let input = line_editor.read_line(prompt);
|
||||||
let shell_integration = config.shell_integration;
|
let shell_integration = config.shell_integration;
|
||||||
|
|
||||||
@ -421,7 +470,7 @@ pub fn evaluate_repl(
|
|||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
s.as_bytes(),
|
s.as_bytes(),
|
||||||
&format!("entry #{}", entry_num),
|
&format!("entry #{entry_num}"),
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -479,7 +528,7 @@ pub fn evaluate_repl(
|
|||||||
// ESC]0;stringBEL -- Set icon name and window title to string
|
// ESC]0;stringBEL -- Set icon name and window title to string
|
||||||
// ESC]1;stringBEL -- Set icon name to string
|
// ESC]1;stringBEL -- Set icon name to string
|
||||||
// ESC]2;stringBEL -- Set window title to string
|
// ESC]2;stringBEL -- Set window title to string
|
||||||
run_ansi_sequence(&format!("\x1b]2;{}\x07", maybe_abbrev_path))?;
|
run_ansi_sequence(&format!("\x1b]2;{maybe_abbrev_path}\x07"))?;
|
||||||
}
|
}
|
||||||
run_ansi_sequence(RESET_APPLICATION_MODE)?;
|
run_ansi_sequence(RESET_APPLICATION_MODE)?;
|
||||||
}
|
}
|
||||||
@ -519,7 +568,7 @@ pub fn evaluate_repl(
|
|||||||
Err(err) => {
|
Err(err) => {
|
||||||
let message = err.to_string();
|
let message = err.to_string();
|
||||||
if !message.contains("duration") {
|
if !message.contains("duration") {
|
||||||
eprintln!("Error: {:?}", err);
|
eprintln!("Error: {err:?}");
|
||||||
// TODO: Identify possible error cases where a hard failure is preferable
|
// TODO: Identify possible error cases where a hard failure is preferable
|
||||||
// Ignoring and reporting could hide bigger problems
|
// Ignoring and reporting could hide bigger problems
|
||||||
// e.g. https://github.com/nushell/nushell/issues/6452
|
// e.g. https://github.com/nushell/nushell/issues/6452
|
||||||
@ -530,11 +579,34 @@ pub fn evaluate_repl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
perf(
|
||||||
|
"processing line editor input",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
);
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"finished repl loop",
|
||||||
|
loop_start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> CursorShape {
|
||||||
|
match shape {
|
||||||
|
NuCursorShape::Block => CursorShape::Block,
|
||||||
|
NuCursorShape::UnderScore => CursorShape::UnderScore,
|
||||||
|
NuCursorShape::Line => CursorShape::Line,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String {
|
fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String {
|
||||||
let age = match eval_string_with_input(
|
let age = match eval_string_with_input(
|
||||||
engine_state,
|
engine_state,
|
||||||
@ -558,15 +630,7 @@ Our {}Documentation{} is located at {}http://nushell.sh{}
|
|||||||
{}Tweet{} us at {}@nu_shell{}
|
{}Tweet{} us at {}@nu_shell{}
|
||||||
|
|
||||||
It's been this long since {}Nushell{}'s first commit:
|
It's been this long since {}Nushell{}'s first commit:
|
||||||
{}
|
{}{}
|
||||||
|
|
||||||
{}You can disable this banner using the {}config nu{}{} command
|
|
||||||
to modify the config.nu file and setting show_banner to false.
|
|
||||||
|
|
||||||
let-env config = {{
|
|
||||||
show_banner: false
|
|
||||||
...
|
|
||||||
}}{}
|
|
||||||
"#,
|
"#,
|
||||||
"\x1b[32m", //start line 1 green
|
"\x1b[32m", //start line 1 green
|
||||||
"\x1b[32m", //start line 2
|
"\x1b[32m", //start line 2
|
||||||
@ -598,10 +662,6 @@ let-env config = {{
|
|||||||
"\x1b[32m", //before Nushell
|
"\x1b[32m", //before Nushell
|
||||||
"\x1b[0m", //after Nushell
|
"\x1b[0m", //after Nushell
|
||||||
age,
|
age,
|
||||||
"\x1b[2;37m", //before banner disable dim white
|
|
||||||
"\x1b[2;36m", //before config nu dim cyan
|
|
||||||
"\x1b[0m", //after config nu
|
|
||||||
"\x1b[2;37m", //after config nu dim white
|
|
||||||
"\x1b[0m", //after banner disable
|
"\x1b[0m", //after banner disable
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -960,9 +1020,9 @@ fn run_hook_block(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)
|
let pipeline_data =
|
||||||
{
|
eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)?;
|
||||||
Ok(pipeline_data) => {
|
|
||||||
if let PipelineData::Value(Value::Error { error }, _) = pipeline_data {
|
if let PipelineData::Value(Value::Error { error }, _) = pipeline_data {
|
||||||
return Err(error);
|
return Err(error);
|
||||||
}
|
}
|
||||||
@ -983,24 +1043,18 @@ fn run_hook_block(
|
|||||||
stack.add_env_var(var, value);
|
stack.add_env_var(var, value);
|
||||||
}
|
}
|
||||||
Ok(pipeline_data)
|
Ok(pipeline_data)
|
||||||
}
|
|
||||||
Err(err) => Err(err),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
|
fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
|
||||||
match io::stdout().write_all(seq.as_bytes()) {
|
io::stdout().write_all(seq.as_bytes()).map_err(|e| {
|
||||||
Ok(it) => it,
|
ShellError::GenericError(
|
||||||
Err(err) => {
|
|
||||||
return Err(ShellError::GenericError(
|
|
||||||
"Error writing ansi sequence".into(),
|
"Error writing ansi sequence".into(),
|
||||||
err.to_string(),
|
e.to_string(),
|
||||||
Some(Span::unknown()),
|
Some(Span::unknown()),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
));
|
)
|
||||||
}
|
})?;
|
||||||
};
|
|
||||||
io::stdout().flush().map_err(|e| {
|
io::stdout().flush().map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error flushing stdio".into(),
|
"Error flushing stdio".into(),
|
||||||
|
@ -6,9 +6,10 @@ use nu_protocol::ast::{Argument, Block, Expr, Expression, PipelineElement};
|
|||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
use nu_protocol::{Config, Span};
|
use nu_protocol::{Config, Span};
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct NuHighlighter {
|
pub struct NuHighlighter {
|
||||||
pub engine_state: EngineState,
|
pub engine_state: Arc<EngineState>,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,7 +234,8 @@ fn find_matching_block_end_in_block(
|
|||||||
PipelineElement::Expression(_, e)
|
PipelineElement::Expression(_, e)
|
||||||
| PipelineElement::Redirection(_, _, e)
|
| PipelineElement::Redirection(_, _, e)
|
||||||
| PipelineElement::And(_, e)
|
| PipelineElement::And(_, e)
|
||||||
| PipelineElement::Or(_, e) => {
|
| PipelineElement::Or(_, e)
|
||||||
|
| PipelineElement::SeparateRedirection { out: (_, e), .. } => {
|
||||||
if e.span.contains(global_cursor_offset) {
|
if e.span.contains(global_cursor_offset) {
|
||||||
if let Some(pos) = find_matching_block_end_in_expr(
|
if let Some(pos) = find_matching_block_end_in_expr(
|
||||||
line,
|
line,
|
||||||
|
@ -9,6 +9,7 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
|
use nu_utils::utils::perf;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
// This will collect environment variables from std::env and adds them to a stack.
|
// This will collect environment variables from std::env and adds them to a stack.
|
||||||
@ -43,7 +44,7 @@ fn gather_env_vars(
|
|||||||
report_error(
|
report_error(
|
||||||
&working_set,
|
&working_set,
|
||||||
&ShellError::GenericError(
|
&ShellError::GenericError(
|
||||||
format!("Environment variable was not captured: {}", env_str),
|
format!("Environment variable was not captured: {env_str}"),
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
None,
|
None,
|
||||||
Some(msg.into()),
|
Some(msg.into()),
|
||||||
@ -79,8 +80,7 @@ fn gather_env_vars(
|
|||||||
"".to_string(),
|
"".to_string(),
|
||||||
None,
|
None,
|
||||||
Some(format!(
|
Some(format!(
|
||||||
"Retrieving current directory failed: {:?} not a valid utf-8 path",
|
"Retrieving current directory failed: {init_cwd:?} not a valid utf-8 path"
|
||||||
init_cwd
|
|
||||||
)),
|
)),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
),
|
),
|
||||||
@ -204,6 +204,8 @@ pub fn eval_source(
|
|||||||
fname: &str,
|
fname: &str,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
let start_time = std::time::Instant::now();
|
||||||
|
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
let (output, err) = parse(
|
let (output, err) = parse(
|
||||||
@ -282,6 +284,13 @@ pub fn eval_source(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
perf(
|
||||||
|
&format!("eval_source {}", &fname),
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
);
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -315,27 +324,19 @@ pub fn report_error_new(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_init_cwd() -> PathBuf {
|
pub fn get_init_cwd() -> PathBuf {
|
||||||
match std::env::current_dir() {
|
std::env::current_dir().unwrap_or_else(|_| {
|
||||||
Ok(cwd) => cwd,
|
std::env::var("PWD")
|
||||||
Err(_) => match std::env::var("PWD") {
|
.map(Into::into)
|
||||||
Ok(cwd) => PathBuf::from(cwd),
|
.unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
|
||||||
Err(_) => match nu_path::home_dir() {
|
})
|
||||||
Some(cwd) => cwd,
|
|
||||||
None => PathBuf::new(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
||||||
match nu_engine::env::current_dir(engine_state, stack) {
|
nu_engine::env::current_dir(engine_state, stack).unwrap_or_else(|e| {
|
||||||
Ok(p) => p,
|
|
||||||
Err(e) => {
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &e);
|
report_error(&working_set, &e);
|
||||||
get_init_cwd()
|
get_init_cwd()
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use nu_parser::{parse, ParseError};
|
use nu_parser::{parse, ParseError};
|
||||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||||
use reedline::{ValidationResult, Validator};
|
use reedline::{ValidationResult, Validator};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct NuValidator {
|
pub struct NuValidator {
|
||||||
pub engine_state: EngineState,
|
pub engine_state: Arc<EngineState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Validator for NuValidator {
|
impl Validator for NuValidator {
|
||||||
|
@ -173,7 +173,7 @@ fn file_completions() {
|
|||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test completions for the current folder
|
// Test completions for the current folder
|
||||||
let target_dir = format!("cp {}", dir_str);
|
let target_dir = format!("cp {dir_str}");
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
@ -494,7 +494,7 @@ fn folder_with_directorycompletions() {
|
|||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test completions for the current folder
|
// Test completions for the current folder
|
||||||
let target_dir = format!("cd {}", dir_str);
|
let target_dir = format!("cd {dir_str}");
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
@ -815,3 +815,41 @@ fn extern_complete_flags(mut extern_completer: NuCompleter) {
|
|||||||
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
|
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(expected, suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn alias_offset_bug_7748() {
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Create an alias
|
||||||
|
let alias = r#"alias ea = ^$env.EDITOR /tmp/test.s"#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
// Issue #7748
|
||||||
|
// Nushell crashes when an alias name is shorter than the alias command
|
||||||
|
// and the alias command is a external command
|
||||||
|
// This happens because of offset is not correct.
|
||||||
|
// This crashes before PR #7779
|
||||||
|
let _suggestions = completer.complete("e", 1);
|
||||||
|
//println!(" --------- suggestions: {:?}", suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn alias_offset_bug_7754() {
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
// Create an alias
|
||||||
|
let alias = r#"alias ll = ls -l"#;
|
||||||
|
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
|
// Issue #7754
|
||||||
|
// Nushell crashes when an alias name is shorter than the alias command
|
||||||
|
// and the alias command contains pipes.
|
||||||
|
// This crashes before PR #7756
|
||||||
|
let _suggestions = completer.complete("ll -a | c", 9);
|
||||||
|
|
||||||
|
//println!(" --------- suggestions: {:?}", suggestions);
|
||||||
|
}
|
||||||
|
@ -5,18 +5,18 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.74.0"
|
version = "0.75.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
serde = { version="1.0.123", features=["derive"] }
|
serde = { version="1.0.123", features=["derive"] }
|
||||||
# used only for text_style Alignments
|
# used only for text_style Alignments
|
||||||
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
tabled = { version = "0.10.0", features = ["color"], default-features = false }
|
||||||
|
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.74.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.75.0" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
nu-utils = { path = "../nu-utils", version = "0.74.0" }
|
nu-utils = { path = "../nu-utils", version = "0.75.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.74.0" }
|
nu-engine = { path = "../nu-engine", version = "0.75.0" }
|
||||||
nu-json = { path="../nu-json", version = "0.74.0" }
|
nu-json = { path="../nu-json", version = "0.75.0" }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path="../nu-test-support", version = "0.74.0" }
|
nu-test-support = { path="../nu-test-support", version = "0.75.0" }
|
||||||
|
@ -65,7 +65,7 @@ fn color_to_string(color: Color) -> Option<String> {
|
|||||||
Color::White => Some(String::from("white")),
|
Color::White => Some(String::from("white")),
|
||||||
Color::LightGray => Some(String::from("light_gray")),
|
Color::LightGray => Some(String::from("light_gray")),
|
||||||
Color::Default => Some(String::from("default")),
|
Color::Default => Some(String::from("default")),
|
||||||
Color::Rgb(r, g, b) => Some(format!("#{:X}{:X}{:X}", r, g, b)),
|
Color::Rgb(r, g, b) => Some(format!("#{r:X}{g:X}{b:X}")),
|
||||||
Color::Fixed(_) => None,
|
Color::Fixed(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -226,10 +226,10 @@ fn test_computable_style_static() {
|
|||||||
let style2 = Style::default().underline();
|
let style2 = Style::default().underline();
|
||||||
// Create a "dummy" style_computer for this test.
|
// Create a "dummy" style_computer for this test.
|
||||||
let dummy_engine_state = EngineState::new();
|
let dummy_engine_state = EngineState::new();
|
||||||
let mut dummy_stack = Stack::new();
|
let dummy_stack = Stack::new();
|
||||||
let style_computer = StyleComputer::new(
|
let style_computer = StyleComputer::new(
|
||||||
&dummy_engine_state,
|
&dummy_engine_state,
|
||||||
&mut dummy_stack,
|
&dummy_stack,
|
||||||
HashMap::from([
|
HashMap::from([
|
||||||
("string".into(), ComputableStyle::Static(style1)),
|
("string".into(), ComputableStyle::Static(style1)),
|
||||||
("row_index".into(), ComputableStyle::Static(style2)),
|
("row_index".into(), ComputableStyle::Static(style2)),
|
||||||
|
@ -5,52 +5,53 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
version = "0.74.0"
|
version = "0.75.0"
|
||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
|
|
||||||
# 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-color-config = { path = "../nu-color-config", version = "0.74.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.75.0" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.74.0" }
|
nu-engine = { path = "../nu-engine", version = "0.75.0" }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.74.0" }
|
nu-glob = { path = "../nu-glob", version = "0.75.0" }
|
||||||
nu-json = { path = "../nu-json", version = "0.74.0" }
|
nu-json = { path = "../nu-json", version = "0.75.0" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.74.0" }
|
nu-parser = { path = "../nu-parser", version = "0.75.0" }
|
||||||
nu-path = { path = "../nu-path", version = "0.74.0" }
|
nu-path = { path = "../nu-path", version = "0.75.0" }
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.74.0" }
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.75.0" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.74.0" }
|
nu-protocol = { path = "../nu-protocol", version = "0.75.0" }
|
||||||
nu-system = { path = "../nu-system", version = "0.74.0" }
|
nu-system = { path = "../nu-system", version = "0.75.0" }
|
||||||
nu-table = { path = "../nu-table", version = "0.74.0" }
|
nu-table = { path = "../nu-table", version = "0.75.0" }
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.74.0" }
|
nu-term-grid = { path = "../nu-term-grid", version = "0.75.0" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.74.0" }
|
nu-utils = { path = "../nu-utils", version = "0.75.0" }
|
||||||
nu-explore = { path = "../nu-explore", version = "0.74.0" }
|
nu-explore = { path = "../nu-explore", version = "0.75.0" }
|
||||||
nu-ansi-term = "0.46.0"
|
nu-ansi-term = "0.46.0"
|
||||||
num-format = { version = "0.4.3" }
|
num-format = { version = "0.4.3" }
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
alphanumeric-sort = "1.4.4"
|
alphanumeric-sort = "1.4.4"
|
||||||
atty = "0.2.14"
|
atty = "0.2.14"
|
||||||
base64 = "0.13.0"
|
base64 = "0.21.0"
|
||||||
byteorder = "1.4.3"
|
byteorder = "1.4.3"
|
||||||
bytesize = "1.1.0"
|
bytesize = "1.1.0"
|
||||||
calamine = "0.19.1"
|
calamine = "0.19.1"
|
||||||
chrono = { version = "0.4.23", features = ["unstable-locales", "std"], default-features = false }
|
chrono = { version = "0.4.23", features = ["unstable-locales", "std"], default-features = false }
|
||||||
chrono-humanize = "0.2.1"
|
chrono-humanize = "0.2.1"
|
||||||
chrono-tz = "0.6.3"
|
chrono-tz = "0.8.1"
|
||||||
crossterm = "0.24.0"
|
crossterm = "0.24.0"
|
||||||
csv = "1.1.6"
|
csv = "1.1.6"
|
||||||
dialoguer = { default-features = false, version = "0.9.0" }
|
dialoguer = { default-features = false, version = "0.10.3" }
|
||||||
digest = { default-features = false, version = "0.10.0" }
|
digest = { default-features = false, version = "0.10.0" }
|
||||||
dtparse = "1.2.0"
|
dtparse = "1.2.0"
|
||||||
eml-parser = "0.1.0"
|
eml-parser = "0.1.0"
|
||||||
encoding_rs = "0.8.30"
|
encoding_rs = "0.8.30"
|
||||||
fancy-regex = "0.10.0"
|
fancy-regex = "0.11.0"
|
||||||
filesize = "0.2.0"
|
filesize = "0.2.0"
|
||||||
filetime = "0.2.15"
|
filetime = "0.2.15"
|
||||||
fs_extra = "1.2.0"
|
fs_extra = "1.2.0"
|
||||||
htmlescape = "0.3.1"
|
htmlescape = "0.3.1"
|
||||||
ical = "0.7.0"
|
ical = "0.7.0"
|
||||||
indexmap = { version="1.7", features=["serde-1"] }
|
indexmap = { version = "1.7", features = ["serde-1"] }
|
||||||
|
indicatif = "0.17.2"
|
||||||
Inflector = "0.11"
|
Inflector = "0.11"
|
||||||
is-root = "0.1.2"
|
is-root = "0.1.2"
|
||||||
itertools = "0.10.0"
|
itertools = "0.10.0"
|
||||||
@ -62,26 +63,26 @@ mime_guess = "2.0.4"
|
|||||||
notify = "4.0.17"
|
notify = "4.0.17"
|
||||||
num = { version = "0.4.0", optional = true }
|
num = { version = "0.4.0", optional = true }
|
||||||
num-traits = "0.2.14"
|
num-traits = "0.2.14"
|
||||||
once_cell = "1.0"
|
once_cell = "1.17"
|
||||||
open = "3.2.0"
|
open = "3.2.0"
|
||||||
pathdiff = "0.2.1"
|
pathdiff = "0.2.1"
|
||||||
powierza-coefficient = "1.0.2"
|
powierza-coefficient = "1.0.2"
|
||||||
quick-xml = "0.25"
|
quick-xml = "0.27"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rayon = "1.5.1"
|
rayon = "1.6.1"
|
||||||
regex = "1.6.0"
|
regex = "1.7.1"
|
||||||
reqwest = {version = "0.11", features = ["blocking", "json"] }
|
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||||
roxmltree = "0.16.0"
|
roxmltree = "0.17.0"
|
||||||
rust-embed = "6.3.0"
|
rust-embed = "6.3.0"
|
||||||
same-file = "1.0.6"
|
same-file = "1.0.6"
|
||||||
serde = { version="1.0.123", features=["derive"] }
|
serde = { version = "1.0.123", features = ["derive"] }
|
||||||
serde_ini = "0.2.0"
|
serde_ini = "0.2.0"
|
||||||
serde_urlencoded = "0.7.0"
|
serde_urlencoded = "0.7.0"
|
||||||
serde_yaml = "0.9.4"
|
serde_yaml = "0.9.4"
|
||||||
sha2 = "0.10.0"
|
sha2 = "0.10.0"
|
||||||
# Disable default features b/c the default features build Git (very slow to compile)
|
# Disable default features b/c the default features build Git (very slow to compile)
|
||||||
shadow-rs = { version = "0.16.1", default-features = false }
|
shadow-rs = { version = "0.20.0", default-features = false }
|
||||||
sysinfo = "0.26.2"
|
sysinfo = "0.27.7"
|
||||||
terminal_size = "0.2.1"
|
terminal_size = "0.2.1"
|
||||||
thiserror = "1.0.31"
|
thiserror = "1.0.31"
|
||||||
titlecase = "2.0.0"
|
titlecase = "2.0.0"
|
||||||
@ -89,12 +90,12 @@ toml = "0.5.8"
|
|||||||
unicode-segmentation = "1.8.0"
|
unicode-segmentation = "1.8.0"
|
||||||
url = "2.2.1"
|
url = "2.2.1"
|
||||||
percent-encoding = "2.2.0"
|
percent-encoding = "2.2.0"
|
||||||
uuid = { version = "1.1.2", features = ["v4"] }
|
uuid = { version = "1.2.2", features = ["v4"] }
|
||||||
which = { version = "4.3.0", optional = true }
|
which = { version = "4.3.0", optional = true }
|
||||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
reedline = { version = "0.15.0", features = ["bashisms", "sqlite"] }
|
||||||
wax = { version = "0.5.0" }
|
wax = { version = "0.5.0" }
|
||||||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||||
sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
|
sqlparser = { version = "0.30.0", features = ["serde"], optional = true }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winreg = "0.10.1"
|
winreg = "0.10.1"
|
||||||
@ -105,11 +106,11 @@ users = "0.11.0"
|
|||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
|
||||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
||||||
version = "3.0.0"
|
version = "3.0.1"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.polars]
|
[dependencies.polars]
|
||||||
version = "0.25.0"
|
version = "0.26.1"
|
||||||
optional = true
|
optional = true
|
||||||
features = [
|
features = [
|
||||||
"arg_where",
|
"arg_where",
|
||||||
@ -140,12 +141,8 @@ features = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies.windows]
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
version = "0.43.0"
|
version = "0.44.0"
|
||||||
features = [
|
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
|
||||||
"Win32_Foundation",
|
|
||||||
"Win32_Storage_FileSystem",
|
|
||||||
"Win32_System_SystemServices",
|
|
||||||
]
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
trash-support = ["trash"]
|
trash-support = ["trash"]
|
||||||
@ -155,14 +152,14 @@ dataframe = ["polars", "num", "sqlparser"]
|
|||||||
sqlite = ["rusqlite"] # TODO: given that rusqlite is included in reedline, should we just always include it?
|
sqlite = ["rusqlite"] # TODO: given that rusqlite is included in reedline, should we just always include it?
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.16.1", default-features = false }
|
shadow-rs = { version = "0.20.0", default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.74.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.75.0" }
|
||||||
|
|
||||||
hamcrest2 = "0.3.0"
|
hamcrest2 = "0.3.0"
|
||||||
dirs-next = "2.0.0"
|
dirs-next = "2.0.0"
|
||||||
proptest = "1.0.0"
|
proptest = "1.0.0"
|
||||||
quickcheck = "1.0.3"
|
quickcheck = "1.0.3"
|
||||||
quickcheck_macros = "1.0.0"
|
quickcheck_macros = "1.0.0"
|
||||||
rstest = {version = "0.15.0", default-features = false}
|
rstest = { version = "0.15.0", default-features = false }
|
||||||
|
@ -4,7 +4,7 @@ fn main() -> shadow_rs::SdResult<()> {
|
|||||||
// Look up the current Git commit ourselves instead of relying on shadow_rs,
|
// Look up the current Git commit ourselves instead of relying on shadow_rs,
|
||||||
// because shadow_rs does it in a really slow-to-compile way (it builds libgit2)
|
// because shadow_rs does it in a really slow-to-compile way (it builds libgit2)
|
||||||
let hash = get_git_hash().unwrap_or_default();
|
let hash = get_git_hash().unwrap_or_default();
|
||||||
println!("cargo:rustc-env=NU_COMMIT_HASH={}", hash);
|
println!("cargo:rustc-env=NU_COMMIT_HASH={hash}");
|
||||||
|
|
||||||
shadow_rs::new()
|
shadow_rs::new()
|
||||||
}
|
}
|
||||||
|
@ -106,8 +106,7 @@ where
|
|||||||
error: ShellError::GenericError(
|
error: ShellError::GenericError(
|
||||||
"Rotate left result beyond the range of 64 bit signed number".to_string(),
|
"Rotate left result beyond the range of 64 bit signed number".to_string(),
|
||||||
format!(
|
format!(
|
||||||
"{} of the specified number of bytes rotate left {} bits exceed limit",
|
"{val} of the specified number of bytes rotate left {bits} bits exceed limit"
|
||||||
val, bits
|
|
||||||
),
|
),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
@ -132,7 +131,7 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
|
|||||||
SignedOne => get_rotate_left(val as i8, bits, span),
|
SignedOne => get_rotate_left(val as i8, bits, span),
|
||||||
SignedTwo => get_rotate_left(val as i16, bits, span),
|
SignedTwo => get_rotate_left(val as i16, bits, span),
|
||||||
SignedFour => get_rotate_left(val as i32, bits, span),
|
SignedFour => get_rotate_left(val as i32, bits, span),
|
||||||
SignedEight => get_rotate_left(val as i64, bits, span),
|
SignedEight => get_rotate_left(val, bits, span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
// Propagate errors by explicitly matching them before the final case.
|
||||||
|
@ -110,8 +110,7 @@ where
|
|||||||
error: ShellError::GenericError(
|
error: ShellError::GenericError(
|
||||||
"Rotate right result beyond the range of 64 bit signed number".to_string(),
|
"Rotate right result beyond the range of 64 bit signed number".to_string(),
|
||||||
format!(
|
format!(
|
||||||
"{} of the specified number of bytes rotate right {} bits exceed limit",
|
"{val} of the specified number of bytes rotate right {bits} bits exceed limit"
|
||||||
val, bits
|
|
||||||
),
|
),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
@ -136,7 +135,7 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
|
|||||||
SignedOne => get_rotate_right(val as i8, bits, span),
|
SignedOne => get_rotate_right(val as i8, bits, span),
|
||||||
SignedTwo => get_rotate_right(val as i16, bits, span),
|
SignedTwo => get_rotate_right(val as i16, bits, span),
|
||||||
SignedFour => get_rotate_right(val as i32, bits, span),
|
SignedFour => get_rotate_right(val as i32, bits, span),
|
||||||
SignedEight => get_rotate_right(val as i64, bits, span),
|
SignedEight => get_rotate_right(val, bits, span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
// Propagate errors by explicitly matching them before the final case.
|
||||||
|
@ -118,8 +118,7 @@ where
|
|||||||
error: ShellError::GenericError(
|
error: ShellError::GenericError(
|
||||||
"Shift left result beyond the range of 64 bit signed number".to_string(),
|
"Shift left result beyond the range of 64 bit signed number".to_string(),
|
||||||
format!(
|
format!(
|
||||||
"{} of the specified number of bytes shift left {} bits exceed limit",
|
"{val} of the specified number of bytes shift left {bits} bits exceed limit"
|
||||||
val, bits
|
|
||||||
),
|
),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
@ -131,10 +130,7 @@ where
|
|||||||
None => Value::Error {
|
None => Value::Error {
|
||||||
error: ShellError::GenericError(
|
error: ShellError::GenericError(
|
||||||
"Shift left failed".to_string(),
|
"Shift left failed".to_string(),
|
||||||
format!(
|
format!("{val} shift left {bits} bits failed, you may shift too many bits"),
|
||||||
"{} shift left {} bits failed, you may shift too many bits",
|
|
||||||
val, bits
|
|
||||||
),
|
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -158,7 +154,7 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
|
|||||||
SignedOne => get_shift_left(val as i8, bits, span),
|
SignedOne => get_shift_left(val as i8, bits, span),
|
||||||
SignedTwo => get_shift_left(val as i16, bits, span),
|
SignedTwo => get_shift_left(val as i16, bits, span),
|
||||||
SignedFour => get_shift_left(val as i32, bits, span),
|
SignedFour => get_shift_left(val as i32, bits, span),
|
||||||
SignedEight => get_shift_left(val as i64, bits, span),
|
SignedEight => get_shift_left(val, bits, span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
// Propagate errors by explicitly matching them before the final case.
|
||||||
|
@ -108,8 +108,7 @@ where
|
|||||||
error: ShellError::GenericError(
|
error: ShellError::GenericError(
|
||||||
"Shift right result beyond the range of 64 bit signed number".to_string(),
|
"Shift right result beyond the range of 64 bit signed number".to_string(),
|
||||||
format!(
|
format!(
|
||||||
"{} of the specified number of bytes shift right {} bits exceed limit",
|
"{val} of the specified number of bytes shift right {bits} bits exceed limit"
|
||||||
val, bits
|
|
||||||
),
|
),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
@ -121,10 +120,7 @@ where
|
|||||||
None => Value::Error {
|
None => Value::Error {
|
||||||
error: ShellError::GenericError(
|
error: ShellError::GenericError(
|
||||||
"Shift right failed".to_string(),
|
"Shift right failed".to_string(),
|
||||||
format!(
|
format!("{val} shift right {bits} bits failed, you may shift too many bits"),
|
||||||
"{} shift right {} bits failed, you may shift too many bits",
|
|
||||||
val, bits
|
|
||||||
),
|
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -148,7 +144,7 @@ fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: Num
|
|||||||
SignedOne => get_shift_right(val as i8, bits, span),
|
SignedOne => get_shift_right(val as i8, bits, span),
|
||||||
SignedTwo => get_shift_right(val as i16, bits, span),
|
SignedTwo => get_shift_right(val as i16, bits, span),
|
||||||
SignedFour => get_shift_right(val as i32, bits, span),
|
SignedFour => get_shift_right(val as i32, bits, span),
|
||||||
SignedEight => get_shift_right(val as i64, bits, span),
|
SignedEight => get_shift_right(val, bits, span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
// Propagate errors by explicitly matching them before the final case.
|
||||||
|
@ -70,8 +70,8 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::String { val, span } => {
|
Value::String { val, span } => {
|
||||||
let splitted_result = val.split_once(',');
|
let split_result = val.split_once(',');
|
||||||
match splitted_result {
|
match split_result {
|
||||||
Some((start, end)) => (start.to_string(), end.to_string(), span),
|
Some((start, end)) => (start.to_string(), end.to_string(), span),
|
||||||
None => {
|
None => {
|
||||||
return Err(ShellError::UnsupportedInput(
|
return Err(ShellError::UnsupportedInput(
|
||||||
@ -98,32 +98,26 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
|
|||||||
let start: isize = if start.is_empty() || start == "_" {
|
let start: isize = if start.is_empty() || start == "_" {
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
match start.trim().parse() {
|
start.trim().parse().map_err(|_| {
|
||||||
Ok(s) => s,
|
ShellError::UnsupportedInput(
|
||||||
Err(_) => {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"could not perform subbytes".to_string(),
|
"could not perform subbytes".to_string(),
|
||||||
"with this range".to_string(),
|
"with this range".to_string(),
|
||||||
head,
|
head,
|
||||||
span,
|
span,
|
||||||
))
|
)
|
||||||
}
|
})?
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let end: isize = if end.is_empty() || end == "_" {
|
let end: isize = if end.is_empty() || end == "_" {
|
||||||
isize::max_value()
|
isize::max_value()
|
||||||
} else {
|
} else {
|
||||||
match end.trim().parse() {
|
end.trim().parse().map_err(|_| {
|
||||||
Ok(s) => s,
|
ShellError::UnsupportedInput(
|
||||||
Err(_) => {
|
|
||||||
return Err(ShellError::UnsupportedInput(
|
|
||||||
"could not perform subbytes".to_string(),
|
"could not perform subbytes".to_string(),
|
||||||
"with this range".to_string(),
|
"with this range".to_string(),
|
||||||
head,
|
head,
|
||||||
span,
|
span,
|
||||||
))
|
)
|
||||||
}
|
})?
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok((start, end, span))
|
Ok((start, end, span))
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|||||||
|
|
||||||
// Note:
|
// Note:
|
||||||
// remove_all from start and end will generate the same result.
|
// remove_all from start and end will generate the same result.
|
||||||
// so we'll put `remove_all` relative logic into else clouse.
|
// so we'll put `remove_all` relative logic into else clause.
|
||||||
if arg.end && !remove_all {
|
if arg.end && !remove_all {
|
||||||
let (mut left, mut right) = (
|
let (mut left, mut right) = (
|
||||||
input.len() as isize - arg.pattern.len() as isize,
|
input.len() as isize - arg.pattern.len() as isize,
|
||||||
@ -172,7 +172,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
|||||||
left -= 1;
|
left -= 1;
|
||||||
right -= 1;
|
right -= 1;
|
||||||
}
|
}
|
||||||
// append the remaining thing to result, this can be happeneed when
|
// append the remaining thing to result, this can be happening when
|
||||||
// we have something to remove and remove_all is False.
|
// we have something to remove and remove_all is False.
|
||||||
let mut remain = input[..left as usize].iter().copied().rev().collect();
|
let mut remain = input[..left as usize].iter().copied().rev().collect();
|
||||||
result.append(&mut remain);
|
result.append(&mut remain);
|
||||||
|
@ -129,18 +129,15 @@ impl Command for Histogram {
|
|||||||
let span = call.head;
|
let span = call.head;
|
||||||
let data_as_value = input.into_value(span);
|
let data_as_value = input.into_value(span);
|
||||||
// `input` is not a list, here we can return an error.
|
// `input` is not a list, here we can return an error.
|
||||||
match data_as_value.as_list() {
|
run_histogram(
|
||||||
Ok(list_value) => run_histogram(
|
data_as_value.as_list()?.to_vec(),
|
||||||
list_value.to_vec(),
|
|
||||||
column_name,
|
column_name,
|
||||||
frequency_column_name,
|
frequency_column_name,
|
||||||
calc_method,
|
calc_method,
|
||||||
span,
|
span,
|
||||||
// Note that as_list() filters out Value::Error here.
|
// Note that as_list() filters out Value::Error here.
|
||||||
data_as_value.expect_span(),
|
data_as_value.expect_span(),
|
||||||
),
|
)
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +166,7 @@ fn run_histogram(
|
|||||||
ShellError::UnsupportedInput(
|
ShellError::UnsupportedInput(
|
||||||
"Since --column-name was not provided, only lists of hashable values are supported.".to_string(),
|
"Since --column-name was not provided, only lists of hashable values are supported.".to_string(),
|
||||||
format!(
|
format!(
|
||||||
"input type: {:?}", t
|
"input type: {t:?}"
|
||||||
),
|
),
|
||||||
head_span,
|
head_span,
|
||||||
span,
|
span,
|
||||||
|
@ -103,31 +103,31 @@ fn fmt_it(num: i64, span: Span) -> Value {
|
|||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
|
||||||
cols.push("binary".into());
|
cols.push("binary".into());
|
||||||
vals.push(Value::string(format!("{:#b}", num), span));
|
vals.push(Value::string(format!("{num:#b}"), span));
|
||||||
|
|
||||||
cols.push("debug".into());
|
cols.push("debug".into());
|
||||||
vals.push(Value::string(format!("{:#?}", num), span));
|
vals.push(Value::string(format!("{num:#?}"), span));
|
||||||
|
|
||||||
cols.push("display".into());
|
cols.push("display".into());
|
||||||
vals.push(Value::string(format!("{}", num), span));
|
vals.push(Value::string(format!("{num}"), span));
|
||||||
|
|
||||||
cols.push("lowerexp".into());
|
cols.push("lowerexp".into());
|
||||||
vals.push(Value::string(format!("{:#e}", num), span));
|
vals.push(Value::string(format!("{num:#e}"), span));
|
||||||
|
|
||||||
cols.push("lowerhex".into());
|
cols.push("lowerhex".into());
|
||||||
vals.push(Value::string(format!("{:#x}", num), span));
|
vals.push(Value::string(format!("{num:#x}"), span));
|
||||||
|
|
||||||
cols.push("octal".into());
|
cols.push("octal".into());
|
||||||
vals.push(Value::string(format!("{:#o}", num), span));
|
vals.push(Value::string(format!("{num:#o}"), span));
|
||||||
|
|
||||||
// cols.push("pointer".into());
|
// cols.push("pointer".into());
|
||||||
// vals.push(Value::string(format!("{:#p}", &num), span));
|
// vals.push(Value::string(format!("{:#p}", &num), span));
|
||||||
|
|
||||||
cols.push("upperexp".into());
|
cols.push("upperexp".into());
|
||||||
vals.push(Value::string(format!("{:#E}", num), span));
|
vals.push(Value::string(format!("{num:#E}"), span));
|
||||||
|
|
||||||
cols.push("upperhex".into());
|
cols.push("upperhex".into());
|
||||||
vals.push(Value::string(format!("{:#X}", num), span));
|
vals.push(Value::string(format!("{num:#X}"), span));
|
||||||
|
|
||||||
Value::Record { cols, vals, span }
|
Value::Record { cols, vals, span }
|
||||||
}
|
}
|
||||||
|
@ -232,7 +232,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
|||||||
return Value::Error {
|
return Value::Error {
|
||||||
error: ShellError::UnsupportedInput(
|
error: ShellError::UnsupportedInput(
|
||||||
"timestamp is out of range; it should between -8e+12 and 8e+12".to_string(),
|
"timestamp is out of range; it should between -8e+12 and 8e+12".to_string(),
|
||||||
format!("timestamp is {:?}", ts),
|
format!("timestamp is {ts:?}"),
|
||||||
head,
|
head,
|
||||||
// Again, can safely unwrap this from here on
|
// Again, can safely unwrap this from here on
|
||||||
input.expect_span(),
|
input.expect_span(),
|
||||||
|
@ -367,8 +367,7 @@ fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> {
|
|||||||
"string".to_string(),
|
"string".to_string(),
|
||||||
span,
|
span,
|
||||||
Some(format!(
|
Some(format!(
|
||||||
r#"string "{}" does not represent a valid integer"#,
|
r#"string "{trimmed}" does not represent a valid integer"#
|
||||||
trimmed
|
|
||||||
)),
|
)),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
|
@ -206,7 +206,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
|||||||
if decimals {
|
if decimals {
|
||||||
let decimal_value = digits.unwrap_or(2) as usize;
|
let decimal_value = digits.unwrap_or(2) as usize;
|
||||||
Value::String {
|
Value::String {
|
||||||
val: format!("{:.*}", decimal_value, val),
|
val: format!("{val:.decimal_value$}"),
|
||||||
span,
|
span,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -234,12 +234,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
|||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Error { error } => Value::String {
|
Value::Error { error } => Value::String {
|
||||||
val: {
|
val: into_code(error).unwrap_or_default(),
|
||||||
match into_code(error) {
|
|
||||||
Some(code) => code,
|
|
||||||
None => "".to_string(),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
span,
|
span,
|
||||||
},
|
},
|
||||||
Value::Nothing { .. } => Value::String {
|
Value::Nothing { .. } => Value::String {
|
||||||
|
@ -41,7 +41,7 @@ impl Command for Ast {
|
|||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
||||||
let (output, err) = parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]);
|
let (output, err) = parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]);
|
||||||
eprintln!("output: {:#?}\nerror: {:#?}", output, err);
|
eprintln!("output: {output:#?}\nerror: {err:#?}");
|
||||||
|
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
|
use std::thread;
|
||||||
|
|
||||||
use nu_engine::{eval_block_with_early_return, CallExt};
|
use nu_engine::{eval_block_with_early_return, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, SyntaxShape,
|
||||||
|
Type, Value,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -106,7 +109,7 @@ impl Command for Do {
|
|||||||
block,
|
block,
|
||||||
input,
|
input,
|
||||||
call.redirect_stdout,
|
call.redirect_stdout,
|
||||||
capture_errors || ignore_shell_errors || ignore_program_errors,
|
call.redirect_stdout,
|
||||||
);
|
);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
@ -118,6 +121,58 @@ impl Command for Do {
|
|||||||
metadata,
|
metadata,
|
||||||
trim_end_newline,
|
trim_end_newline,
|
||||||
}) if capture_errors => {
|
}) if capture_errors => {
|
||||||
|
// Use a thread to receive stdout message.
|
||||||
|
// Or we may get a deadlock if child process sends out too much bytes to stderr.
|
||||||
|
//
|
||||||
|
// For example: in normal linux system, stderr pipe's limit is 65535 bytes.
|
||||||
|
// if child process sends out 65536 bytes, the process will be hanged because no consumer
|
||||||
|
// consumes the first 65535 bytes
|
||||||
|
// So we need a thread to receive stdout message, then the current thread can continue to consume
|
||||||
|
// stderr messages.
|
||||||
|
let stdout_handler = stdout.map(|stdout_stream| {
|
||||||
|
thread::Builder::new()
|
||||||
|
.name("stderr redirector".to_string())
|
||||||
|
.spawn(move || {
|
||||||
|
let ctrlc = stdout_stream.ctrlc.clone();
|
||||||
|
let span = stdout_stream.span;
|
||||||
|
RawStream::new(
|
||||||
|
Box::new(
|
||||||
|
vec![stdout_stream.into_bytes().map(|s| s.item)].into_iter(),
|
||||||
|
),
|
||||||
|
ctrlc,
|
||||||
|
span,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.expect("Failed to create thread")
|
||||||
|
});
|
||||||
|
|
||||||
|
// Intercept stderr so we can return it in the error if the exit code is non-zero.
|
||||||
|
// The threading issues mentioned above dictate why we also need to intercept stdout.
|
||||||
|
let mut stderr_ctrlc = None;
|
||||||
|
let stderr_msg = match stderr {
|
||||||
|
None => "".to_string(),
|
||||||
|
Some(stderr_stream) => {
|
||||||
|
stderr_ctrlc = stderr_stream.ctrlc.clone();
|
||||||
|
stderr_stream.into_string().map(|s| s.item)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let stdout = if let Some(handle) = stdout_handler {
|
||||||
|
match handle.join() {
|
||||||
|
Err(err) => {
|
||||||
|
return Err(ShellError::ExternalCommand(
|
||||||
|
"Fail to receive external commands stdout message".to_string(),
|
||||||
|
format!("{err:?}"),
|
||||||
|
span,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(res) => Some(res),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let mut exit_code_ctrlc = None;
|
let mut exit_code_ctrlc = None;
|
||||||
let exit_code: Vec<Value> = match exit_code {
|
let exit_code: Vec<Value> = match exit_code {
|
||||||
None => vec![],
|
None => vec![],
|
||||||
@ -128,11 +183,6 @@ impl Command for Do {
|
|||||||
};
|
};
|
||||||
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
|
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
|
||||||
if *code != 0 {
|
if *code != 0 {
|
||||||
let stderr_msg = match stderr {
|
|
||||||
None => "".to_string(),
|
|
||||||
Some(stderr_stream) => stderr_stream.into_string().map(|s| s.item)?,
|
|
||||||
};
|
|
||||||
|
|
||||||
return Err(ShellError::ExternalCommand(
|
return Err(ShellError::ExternalCommand(
|
||||||
"External command failed".to_string(),
|
"External command failed".to_string(),
|
||||||
stderr_msg,
|
stderr_msg,
|
||||||
@ -143,7 +193,12 @@ impl Command for Do {
|
|||||||
|
|
||||||
Ok(PipelineData::ExternalStream {
|
Ok(PipelineData::ExternalStream {
|
||||||
stdout,
|
stdout,
|
||||||
stderr,
|
stderr: Some(RawStream::new(
|
||||||
|
Box::new(vec![Ok(stderr_msg.into_bytes())].into_iter()),
|
||||||
|
stderr_ctrlc,
|
||||||
|
span,
|
||||||
|
None,
|
||||||
|
)),
|
||||||
exit_code: Some(ListStream::from_stream(
|
exit_code: Some(ListStream::from_stream(
|
||||||
exit_code.into_iter(),
|
exit_code.into_iter(),
|
||||||
exit_code_ctrlc,
|
exit_code_ctrlc,
|
||||||
@ -168,10 +223,9 @@ impl Command for Do {
|
|||||||
metadata,
|
metadata,
|
||||||
trim_end_newline,
|
trim_end_newline,
|
||||||
}),
|
}),
|
||||||
Ok(PipelineData::Value(Value::Error { .. }, ..)) if ignore_shell_errors => {
|
Ok(PipelineData::Value(Value::Error { .. }, ..)) | Err(_) if ignore_shell_errors => {
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
}
|
}
|
||||||
Err(_) if ignore_shell_errors => Ok(PipelineData::empty()),
|
|
||||||
r => r,
|
r => r,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@ pub fn highlight_search_string(
|
|||||||
needle: &str,
|
needle: &str,
|
||||||
string_style: &Style,
|
string_style: &Style,
|
||||||
) -> Result<String, ShellError> {
|
) -> Result<String, ShellError> {
|
||||||
let regex_string = format!("(?i){}", needle);
|
let regex_string = format!("(?i){needle}");
|
||||||
let regex = match Regex::new(®ex_string) {
|
let regex = match Regex::new(®ex_string) {
|
||||||
Ok(regex) => regex,
|
Ok(regex) => regex,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
@ -132,7 +132,7 @@ fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec<Value> {
|
|||||||
let decl = engine_state.get_decl(decl_id);
|
let decl = engine_state.get_decl(decl_id);
|
||||||
let sig = decl.signature().update_from_command(name, decl.borrow());
|
let sig = decl.signature().update_from_command(name, decl.borrow());
|
||||||
|
|
||||||
let signatures = sig.to_string();
|
let signatures = sig.to_string().trim_start().replace("\n ", "\n");
|
||||||
let key = sig.name;
|
let key = sig.name;
|
||||||
let usage = sig.usage;
|
let usage = sig.usage;
|
||||||
let search_terms = sig.search_terms;
|
let search_terms = sig.search_terms;
|
||||||
@ -163,13 +163,9 @@ fn build_help_commands(engine_state: &EngineState, span: Span) -> Vec<Value> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
cols.push("search_terms".into());
|
cols.push("search_terms".into());
|
||||||
vals.push(if search_terms.is_empty() {
|
vals.push(Value::String {
|
||||||
Value::nothing(span)
|
|
||||||
} else {
|
|
||||||
Value::String {
|
|
||||||
val: search_terms.join(", "),
|
val: search_terms.join(", "),
|
||||||
span,
|
span,
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
found_cmds_vec.push(Value::Record { cols, vals, span });
|
found_cmds_vec.push(Value::Record { cols, vals, span });
|
||||||
|
@ -151,15 +151,11 @@ pub fn help_modules(
|
|||||||
long_desc.push_str(&format!("{G}Module{RESET}: {C}{name}{RESET}"));
|
long_desc.push_str(&format!("{G}Module{RESET}: {C}{name}{RESET}"));
|
||||||
long_desc.push_str("\n\n");
|
long_desc.push_str("\n\n");
|
||||||
|
|
||||||
if !module.decls.is_empty() {
|
if !module.decls.is_empty() || module.main.is_some() {
|
||||||
let commands: Vec<(Vec<u8>, DeclId)> = engine_state.get_decls_sorted(false).collect();
|
let commands: Vec<(Vec<u8>, DeclId)> = engine_state.get_decls_sorted(false).collect();
|
||||||
|
|
||||||
let mut module_commands: Vec<(&[u8], DeclId)> = module
|
let mut module_commands = module.decls();
|
||||||
.decls
|
module_commands.sort_by(|a, b| a.0.cmp(&b.0));
|
||||||
.iter()
|
|
||||||
.map(|(name, id)| (name.as_ref(), *id))
|
|
||||||
.collect();
|
|
||||||
module_commands.sort_by(|a, b| a.0.cmp(b.0));
|
|
||||||
|
|
||||||
let commands_str = module_commands
|
let commands_str = module_commands
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -123,7 +123,6 @@ pub fn version(
|
|||||||
// Get a list of command names and check for plugins
|
// Get a list of command names and check for plugins
|
||||||
let installed_plugins = engine_state
|
let installed_plugins = engine_state
|
||||||
.plugin_decls()
|
.plugin_decls()
|
||||||
.into_iter()
|
|
||||||
.filter(|x| x.is_plugin().is_some())
|
.filter(|x| x.is_plugin().is_some())
|
||||||
.map(|x| x.name())
|
.map(|x| x.name())
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -117,7 +117,7 @@ fn action(
|
|||||||
|
|
||||||
let table_columns_creation = columns
|
let table_columns_creation = columns
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, sql_type)| format!("{} {}", name, sql_type))
|
.map(|(name, sql_type)| format!("{name} {sql_type}"))
|
||||||
.join(",");
|
.join(",");
|
||||||
|
|
||||||
// get the values
|
// get the values
|
||||||
@ -156,10 +156,8 @@ fn action(
|
|||||||
let conn = open_sqlite_db(Path::new(&file.item), file.span)?;
|
let conn = open_sqlite_db(Path::new(&file.item), file.span)?;
|
||||||
|
|
||||||
// create a string for sql table creation
|
// create a string for sql table creation
|
||||||
let create_statement = format!(
|
let create_statement =
|
||||||
"CREATE TABLE IF NOT EXISTS {} ({})",
|
format!("CREATE TABLE IF NOT EXISTS {table_name} ({table_columns_creation})");
|
||||||
table_name, table_columns_creation
|
|
||||||
);
|
|
||||||
|
|
||||||
// prepare the string as a sqlite statement
|
// prepare the string as a sqlite statement
|
||||||
let mut stmt = conn.prepare(&create_statement).map_err(|e| {
|
let mut stmt = conn.prepare(&create_statement).map_err(|e| {
|
||||||
@ -191,7 +189,7 @@ fn action(
|
|||||||
// ('dd', 'ee', 'ff')
|
// ('dd', 'ee', 'ff')
|
||||||
|
|
||||||
// create the string for inserting data into the table
|
// create the string for inserting data into the table
|
||||||
let insert_statement = format!("INSERT INTO {} VALUES {}", table_name, table_values);
|
let insert_statement = format!("INSERT INTO {table_name} VALUES {table_values}");
|
||||||
|
|
||||||
// prepare the string as a sqlite statement
|
// prepare the string as a sqlite statement
|
||||||
let mut stmt = conn.prepare(&insert_statement).map_err(|e| {
|
let mut stmt = conn.prepare(&insert_statement).map_err(|e| {
|
||||||
@ -262,11 +260,15 @@ fn nu_value_to_string(value: Value, separator: &str) -> String {
|
|||||||
.map(|(x, y)| format!("{}: {}", x, nu_value_to_string(y.clone(), ", ")))
|
.map(|(x, y)| format!("{}: {}", x, nu_value_to_string(y.clone(), ", ")))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator),
|
.join(separator),
|
||||||
Value::Block { val, .. } => format!("<Block {}>", val),
|
Value::LazyRecord { val, .. } => match val.collect() {
|
||||||
Value::Closure { val, .. } => format!("<Closure {}>", val),
|
Ok(val) => nu_value_to_string(val, separator),
|
||||||
|
Err(error) => format!("{error:?}"),
|
||||||
|
},
|
||||||
|
Value::Block { val, .. } => format!("<Block {val}>"),
|
||||||
|
Value::Closure { val, .. } => format!("<Closure {val}>"),
|
||||||
Value::Nothing { .. } => String::new(),
|
Value::Nothing { .. } => String::new(),
|
||||||
Value::Error { error } => format!("{:?}", error),
|
Value::Error { error } => format!("{error:?}"),
|
||||||
Value::Binary { val, .. } => format!("{:?}", val),
|
Value::Binary { val, .. } => format!("{val:?}"),
|
||||||
Value::CellPath { val, .. } => val.into_string(),
|
Value::CellPath { val, .. } => val.into_string(),
|
||||||
Value::CustomValue { val, .. } => val.value_string(),
|
Value::CustomValue { val, .. } => val.value_string(),
|
||||||
}
|
}
|
||||||
|
@ -107,12 +107,7 @@ impl SQLiteDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_connection(&self) -> Result<Connection, rusqlite::Error> {
|
pub fn open_connection(&self) -> Result<Connection, rusqlite::Error> {
|
||||||
let conn = match Connection::open(&self.path) {
|
Connection::open(&self.path)
|
||||||
Ok(conn) => conn,
|
|
||||||
Err(err) => return Err(err),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(conn)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_tables(&self, conn: &Connection) -> Result<Vec<DbTable>, rusqlite::Error> {
|
pub fn get_tables(&self, conn: &Connection) -> Result<Vec<DbTable>, rusqlite::Error> {
|
||||||
@ -369,7 +364,7 @@ fn read_single_table(
|
|||||||
call_span: Span,
|
call_span: Span,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
ctrlc: Option<Arc<AtomicBool>>,
|
||||||
) -> Result<Value, rusqlite::Error> {
|
) -> Result<Value, rusqlite::Error> {
|
||||||
let stmt = conn.prepare(&format!("SELECT * FROM {}", table_name))?;
|
let stmt = conn.prepare(&format!("SELECT * FROM {table_name}"))?;
|
||||||
prepared_statement_to_nu_list(stmt, call_span, ctrlc)
|
prepared_statement_to_nu_list(stmt, call_span, ctrlc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -431,7 +426,7 @@ fn read_entire_sqlite_db(
|
|||||||
let table_name: String = row?;
|
let table_name: String = row?;
|
||||||
table_names.push(table_name.clone());
|
table_names.push(table_name.clone());
|
||||||
|
|
||||||
let table_stmt = conn.prepare(&format!("select * from [{}]", table_name))?;
|
let table_stmt = conn.prepare(&format!("select * from [{table_name}]"))?;
|
||||||
let rows = prepared_statement_to_nu_list(table_stmt, call_span, ctrlc.clone())?;
|
let rows = prepared_statement_to_nu_list(table_stmt, call_span, ctrlc.clone())?;
|
||||||
tables.push(rows);
|
tables.push(rows);
|
||||||
}
|
}
|
||||||
@ -582,18 +577,13 @@ mod test {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn open_connection_in_memory() -> Result<Connection, ShellError> {
|
pub fn open_connection_in_memory() -> Result<Connection, ShellError> {
|
||||||
let db = match Connection::open_in_memory() {
|
Connection::open_in_memory().map_err(|err| {
|
||||||
Ok(conn) => conn,
|
ShellError::GenericError(
|
||||||
Err(err) => {
|
|
||||||
return Err(ShellError::GenericError(
|
|
||||||
"Failed to open SQLite connection in memory".into(),
|
"Failed to open SQLite connection in memory".into(),
|
||||||
err.to_string(),
|
err.to_string(),
|
||||||
Some(Span::test_data()),
|
Some(Span::test_data()),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
))
|
)
|
||||||
}
|
})
|
||||||
};
|
|
||||||
|
|
||||||
Ok(db)
|
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ fn command(
|
|||||||
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
||||||
|
|
||||||
df.as_ref()
|
df.as_ref()
|
||||||
.select(&col_string)
|
.select(col_string)
|
||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error selecting columns".into(),
|
"Error selecting columns".into(),
|
||||||
|
@ -51,9 +51,8 @@ impl Command for ListDF {
|
|||||||
|
|
||||||
let vals = vals
|
let vals = vals
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(name, value)| match NuDataFrame::try_from_value(value) {
|
.filter_map(|(name, value)| {
|
||||||
Ok(df) => Some((name, df)),
|
NuDataFrame::try_from_value(value).ok().map(|df| (name, df))
|
||||||
Err(_) => None,
|
|
||||||
})
|
})
|
||||||
.map(|(name, df)| {
|
.map(|(name, df)| {
|
||||||
let name = Value::String {
|
let name = Value::String {
|
||||||
|
@ -103,14 +103,13 @@ fn command(
|
|||||||
|
|
||||||
let type_id = match &type_option {
|
let type_id = match &type_option {
|
||||||
Some(ref t) => Some((t.item.to_owned(), "Invalid type", t.span)),
|
Some(ref t) => Some((t.item.to_owned(), "Invalid type", t.span)),
|
||||||
None => match file.item.extension() {
|
None => file.item.extension().map(|e| {
|
||||||
Some(e) => Some((
|
(
|
||||||
e.to_string_lossy().into_owned(),
|
e.to_string_lossy().into_owned(),
|
||||||
"Invalid extension",
|
"Invalid extension",
|
||||||
file.span,
|
file.span,
|
||||||
)),
|
)
|
||||||
None => None,
|
}),
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match type_id {
|
match type_id {
|
||||||
@ -120,10 +119,7 @@ fn command(
|
|||||||
"ipc" | "arrow" => from_ipc(engine_state, stack, call),
|
"ipc" | "arrow" => from_ipc(engine_state, stack, call),
|
||||||
"json" => from_json(engine_state, stack, call),
|
"json" => from_json(engine_state, stack, call),
|
||||||
_ => Err(ShellError::FileNotFoundCustom(
|
_ => Err(ShellError::FileNotFoundCustom(
|
||||||
format!(
|
format!("{msg}. Supported values: csv, tsv, parquet, ipc, arrow, json"),
|
||||||
"{}. Supported values: csv, tsv, parquet, ipc, arrow, json",
|
|
||||||
msg
|
|
||||||
),
|
|
||||||
blamed,
|
blamed,
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
@ -155,7 +151,7 @@ fn from_parquet(
|
|||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Parquet reader error".into(),
|
"Parquet reader error".into(),
|
||||||
format!("{:?}", e),
|
format!("{e:?}"),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -189,7 +185,7 @@ fn from_parquet(
|
|||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Parquet reader error".into(),
|
"Parquet reader error".into(),
|
||||||
format!("{:?}", e),
|
format!("{e:?}"),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -220,7 +216,7 @@ fn from_ipc(
|
|||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"IPC reader error".into(),
|
"IPC reader error".into(),
|
||||||
format!("{:?}", e),
|
format!("{e:?}"),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -254,7 +250,7 @@ fn from_ipc(
|
|||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"IPC reader error".into(),
|
"IPC reader error".into(),
|
||||||
format!("{:?}", e),
|
format!("{e:?}"),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -290,7 +286,7 @@ fn from_json(
|
|||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Json reader error".into(),
|
"Json reader error".into(),
|
||||||
format!("{:?}", e),
|
format!("{e:?}"),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -354,7 +350,7 @@ fn from_csv(
|
|||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Parquet reader error".into(),
|
"Parquet reader error".into(),
|
||||||
format!("{:?}", e),
|
format!("{e:?}"),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -420,7 +416,7 @@ fn from_csv(
|
|||||||
.map_err(|e| {
|
.map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Parquet reader error".into(),
|
"Parquet reader error".into(),
|
||||||
format!("{:?}", e),
|
format!("{e:?}"),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
|
@ -77,15 +77,15 @@ impl SQLContext {
|
|||||||
Ok(match select_item {
|
Ok(match select_item {
|
||||||
SelectItem::UnnamedExpr(expr) => {
|
SelectItem::UnnamedExpr(expr) => {
|
||||||
let expr = parse_sql_expr(expr)?;
|
let expr = parse_sql_expr(expr)?;
|
||||||
raw_projection_before_alias.insert(format!("{:?}", expr), i);
|
raw_projection_before_alias.insert(format!("{expr:?}"), i);
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
SelectItem::ExprWithAlias { expr, alias } => {
|
SelectItem::ExprWithAlias { expr, alias } => {
|
||||||
let expr = parse_sql_expr(expr)?;
|
let expr = parse_sql_expr(expr)?;
|
||||||
raw_projection_before_alias.insert(format!("{:?}", expr), i);
|
raw_projection_before_alias.insert(format!("{expr:?}"), i);
|
||||||
expr.alias(&alias.value)
|
expr.alias(&alias.value)
|
||||||
}
|
}
|
||||||
SelectItem::QualifiedWildcard(_) | SelectItem::Wildcard => {
|
SelectItem::QualifiedWildcard(_, _) | SelectItem::Wildcard(_) => {
|
||||||
contain_wildcard = true;
|
contain_wildcard = true;
|
||||||
col("*")
|
col("*")
|
||||||
}
|
}
|
||||||
@ -133,7 +133,7 @@ impl SQLContext {
|
|||||||
// and its projections columns, keeping the original index
|
// and its projections columns, keeping the original index
|
||||||
let (exclude_expr, groupby_pos): (Vec<_>, Vec<_>) = group_by
|
let (exclude_expr, groupby_pos): (Vec<_>, Vec<_>) = group_by
|
||||||
.iter()
|
.iter()
|
||||||
.map(|expr| raw_projection_before_alias.get(&format!("{:?}", expr)))
|
.map(|expr| raw_projection_before_alias.get(&format!("{expr:?}")))
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter(|(_, proj_p)| proj_p.is_some())
|
.filter(|(_, proj_p)| proj_p.is_some())
|
||||||
.map(|(gb_p, proj_p)| (*proj_p.unwrap_or(&0), (*proj_p.unwrap_or(&0), gb_p)))
|
.map(|(gb_p, proj_p)| (*proj_p.unwrap_or(&0), (*proj_p.unwrap_or(&0), gb_p)))
|
||||||
@ -173,7 +173,7 @@ impl SQLContext {
|
|||||||
|
|
||||||
pub fn execute(&self, query: &str) -> Result<LazyFrame, PolarsError> {
|
pub fn execute(&self, query: &str) -> Result<LazyFrame, PolarsError> {
|
||||||
let ast = Parser::parse_sql(&self.dialect, query)
|
let ast = Parser::parse_sql(&self.dialect, query)
|
||||||
.map_err(|e| PolarsError::ComputeError(format!("{:?}", e).into()))?;
|
.map_err(|e| PolarsError::ComputeError(format!("{e:?}").into()))?;
|
||||||
if ast.len() != 1 {
|
if ast.len() != 1 {
|
||||||
Err(PolarsError::ComputeError(
|
Err(PolarsError::ComputeError(
|
||||||
"One and only one statement at a time please".into(),
|
"One and only one statement at a time please".into(),
|
||||||
@ -196,7 +196,7 @@ impl SQLContext {
|
|||||||
Some(SqlExpr::Value(SQLValue::Number(nrow, _))) => {
|
Some(SqlExpr::Value(SQLValue::Number(nrow, _))) => {
|
||||||
let nrow = nrow.parse().map_err(|err| {
|
let nrow = nrow.parse().map_err(|err| {
|
||||||
PolarsError::ComputeError(
|
PolarsError::ComputeError(
|
||||||
format!("Conversion Error: {:?}", err).into(),
|
format!("Conversion Error: {err:?}").into(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
rs.limit(nrow)
|
rs.limit(nrow)
|
||||||
@ -211,7 +211,7 @@ impl SQLContext {
|
|||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(PolarsError::ComputeError(
|
return Err(PolarsError::ComputeError(
|
||||||
format!("Statement type {:?} is not supported", ast).into(),
|
format!("Statement type {ast:?} is not supported").into(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -28,19 +28,20 @@ fn map_sql_polars_datatype(data_type: &SQLDataType) -> Result<DataType> {
|
|||||||
|
|
||||||
SQLDataType::Boolean => DataType::Boolean,
|
SQLDataType::Boolean => DataType::Boolean,
|
||||||
SQLDataType::Date => DataType::Date,
|
SQLDataType::Date => DataType::Date,
|
||||||
SQLDataType::Time => DataType::Time,
|
SQLDataType::Time(_, _) => DataType::Time,
|
||||||
SQLDataType::Timestamp => DataType::Datetime(TimeUnit::Milliseconds, None),
|
SQLDataType::Timestamp(_, _) => DataType::Datetime(TimeUnit::Milliseconds, None),
|
||||||
SQLDataType::Interval => DataType::Duration(TimeUnit::Milliseconds),
|
SQLDataType::Interval => DataType::Duration(TimeUnit::Milliseconds),
|
||||||
SQLDataType::Array(inner_type) => {
|
SQLDataType::Array(inner_type) => match inner_type {
|
||||||
DataType::List(Box::new(map_sql_polars_datatype(inner_type)?))
|
Some(inner_type) => DataType::List(Box::new(map_sql_polars_datatype(inner_type)?)),
|
||||||
|
None => {
|
||||||
|
return Err(PolarsError::ComputeError(
|
||||||
|
"SQL Datatype Array(None) was not supported in polars-sql yet!".into(),
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
},
|
||||||
_ => {
|
_ => {
|
||||||
return Err(PolarsError::ComputeError(
|
return Err(PolarsError::ComputeError(
|
||||||
format!(
|
format!("SQL Datatype {data_type:?} was not supported in polars-sql yet!").into(),
|
||||||
"SQL Datatype {:?} was not supported in polars-sql yet!",
|
|
||||||
data_type
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -70,7 +71,7 @@ fn binary_op_(left: Expr, right: Expr, op: &SQLBinaryOperator) -> Result<Expr> {
|
|||||||
SQLBinaryOperator::Xor => left.xor(right),
|
SQLBinaryOperator::Xor => left.xor(right),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(PolarsError::ComputeError(
|
return Err(PolarsError::ComputeError(
|
||||||
format!("SQL Operator {:?} was not supported in polars-sql yet!", op).into(),
|
format!("SQL Operator {op:?} was not supported in polars-sql yet!").into(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -82,11 +83,11 @@ fn literal_expr(value: &SqlValue) -> Result<Expr> {
|
|||||||
// Check for existence of decimal separator dot
|
// Check for existence of decimal separator dot
|
||||||
if s.contains('.') {
|
if s.contains('.') {
|
||||||
s.parse::<f64>().map(lit).map_err(|_| {
|
s.parse::<f64>().map(lit).map_err(|_| {
|
||||||
PolarsError::ComputeError(format!("Can't parse literal {:?}", s).into())
|
PolarsError::ComputeError(format!("Can't parse literal {s:?}").into())
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
s.parse::<i64>().map(lit).map_err(|_| {
|
s.parse::<i64>().map(lit).map_err(|_| {
|
||||||
PolarsError::ComputeError(format!("Can't parse literal {:?}", s).into())
|
PolarsError::ComputeError(format!("Can't parse literal {s:?}").into())
|
||||||
})
|
})
|
||||||
}?
|
}?
|
||||||
}
|
}
|
||||||
@ -98,11 +99,7 @@ fn literal_expr(value: &SqlValue) -> Result<Expr> {
|
|||||||
SqlValue::Null => Expr::Literal(LiteralValue::Null),
|
SqlValue::Null => Expr::Literal(LiteralValue::Null),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(PolarsError::ComputeError(
|
return Err(PolarsError::ComputeError(
|
||||||
format!(
|
format!("Parsing SQL Value {value:?} was not supported in polars-sql yet!").into(),
|
||||||
"Parsing SQL Value {:?} was not supported in polars-sql yet!",
|
|
||||||
value
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -122,11 +119,7 @@ pub fn parse_sql_expr(expr: &SqlExpr) -> Result<Expr> {
|
|||||||
SqlExpr::Value(value) => literal_expr(value)?,
|
SqlExpr::Value(value) => literal_expr(value)?,
|
||||||
_ => {
|
_ => {
|
||||||
return Err(PolarsError::ComputeError(
|
return Err(PolarsError::ComputeError(
|
||||||
format!(
|
format!("Expression: {expr:?} was not supported in polars-sql yet!").into(),
|
||||||
"Expression: {:?} was not supported in polars-sql yet!",
|
|
||||||
expr
|
|
||||||
)
|
|
||||||
.into(),
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -180,8 +173,7 @@ fn parse_sql_function(sql_function: &SQLFunction) -> Result<Expr> {
|
|||||||
_ => {
|
_ => {
|
||||||
return Err(PolarsError::ComputeError(
|
return Err(PolarsError::ComputeError(
|
||||||
format!(
|
format!(
|
||||||
"Function {:?} with args {:?} was not supported in polars-sql yet!",
|
"Function {function_name:?} with args {args:?} was not supported in polars-sql yet!"
|
||||||
function_name, args
|
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
))
|
))
|
||||||
|
@ -185,22 +185,22 @@ fn command(
|
|||||||
.cast(&DataType::Float64)
|
.cast(&DataType::Float64)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|ca| match ca.get(0) {
|
.and_then(|ca| match ca.get(0) {
|
||||||
AnyValue::Float64(v) => Some(v),
|
Ok(AnyValue::Float64(v)) => Some(v),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mean = match col.mean_as_series().get(0) {
|
let mean = match col.mean_as_series().get(0) {
|
||||||
AnyValue::Float64(v) => Some(v),
|
Ok(AnyValue::Float64(v)) => Some(v),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let median = match col.median_as_series().get(0) {
|
let median = match col.median_as_series().get(0) {
|
||||||
AnyValue::Float64(v) => Some(v),
|
Ok(AnyValue::Float64(v)) => Some(v),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let std = match col.std_as_series(0).get(0) {
|
let std = match col.std_as_series(0).get(0) {
|
||||||
AnyValue::Float64(v) => Some(v),
|
Ok(AnyValue::Float64(v)) => Some(v),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -209,7 +209,7 @@ fn command(
|
|||||||
.cast(&DataType::Float64)
|
.cast(&DataType::Float64)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|ca| match ca.get(0) {
|
.and_then(|ca| match ca.get(0) {
|
||||||
AnyValue::Float64(v) => Some(v),
|
Ok(AnyValue::Float64(v)) => Some(v),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -221,7 +221,7 @@ fn command(
|
|||||||
.ok()
|
.ok()
|
||||||
.and_then(|ca| ca.cast(&DataType::Float64).ok())
|
.and_then(|ca| ca.cast(&DataType::Float64).ok())
|
||||||
.and_then(|ca| match ca.get(0) {
|
.and_then(|ca| match ca.get(0) {
|
||||||
AnyValue::Float64(v) => Some(v),
|
Ok(AnyValue::Float64(v)) => Some(v),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -232,7 +232,7 @@ fn command(
|
|||||||
.cast(&DataType::Float64)
|
.cast(&DataType::Float64)
|
||||||
.ok()
|
.ok()
|
||||||
.and_then(|ca| match ca.get(0) {
|
.and_then(|ca| match ca.get(0) {
|
||||||
AnyValue::Float64(v) => Some(v),
|
Ok(AnyValue::Float64(v)) => Some(v),
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -59,6 +59,10 @@ impl Command for ExprAlias {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["aka", "abbr", "otherwise"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -43,6 +43,10 @@ impl Command for ExprArgWhere {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["condition", "match", "if"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -37,6 +37,10 @@ impl Command for ExprAsNu {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["convert", "conversion"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
|
@ -43,6 +43,10 @@ impl Command for ExprCol {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["create"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -69,6 +69,10 @@ impl Command for ExprConcatStr {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["join", "connect", "update"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -65,6 +65,10 @@ impl Command for ExprIsIn {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["check", "contained", "is-contain", "match"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -42,6 +42,10 @@ impl Command for ExprLit {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["string", "literal", "expression"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -23,7 +23,7 @@ impl Command for ExprOtherwise {
|
|||||||
.required(
|
.required(
|
||||||
"otherwise expression",
|
"otherwise expression",
|
||||||
SyntaxShape::Any,
|
SyntaxShape::Any,
|
||||||
"expressioini to apply when no when predicate matches",
|
"expression to apply when no when predicate matches",
|
||||||
)
|
)
|
||||||
.input_type(Type::Any)
|
.input_type(Type::Any)
|
||||||
.output_type(Type::Custom("expression".into()))
|
.output_type(Type::Custom("expression".into()))
|
||||||
@ -79,6 +79,10 @@ impl Command for ExprOtherwise {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["condition", "else"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -5,7 +5,7 @@ use nu_protocol::{
|
|||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use polars::prelude::QuantileInterpolOptions;
|
use polars::prelude::{lit, QuantileInterpolOptions};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ExprQuantile;
|
pub struct ExprQuantile;
|
||||||
@ -55,6 +55,10 @@ impl Command for ExprQuantile {
|
|||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["statistics", "percentile", "distribution"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -68,7 +72,7 @@ impl Command for ExprQuantile {
|
|||||||
let expr = NuExpression::try_from_value(value)?;
|
let expr = NuExpression::try_from_value(value)?;
|
||||||
let expr: NuExpression = expr
|
let expr: NuExpression = expr
|
||||||
.into_polars()
|
.into_polars()
|
||||||
.quantile(quantile, QuantileInterpolOptions::default())
|
.quantile(lit(quantile), QuantileInterpolOptions::default())
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
Ok(PipelineData::Value(
|
Ok(PipelineData::Value(
|
||||||
|
@ -85,6 +85,10 @@ impl Command for ExprWhen {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["condition", "match", "if", "else"]
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -128,7 +128,7 @@ impl Command for LazyAggregate {
|
|||||||
if matches!(dtype, Some(DataType::Object(..))) {
|
if matches!(dtype, Some(DataType::Object(..))) {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"Object type column not supported for aggregation".into(),
|
"Object type column not supported for aggregation".into(),
|
||||||
format!("Column '{}' is type Object", name),
|
format!("Column '{name}' is type Object"),
|
||||||
Some(call.head),
|
Some(call.head),
|
||||||
Some("Aggregations cannot be performed on Object type columns. Use dtype command to check column types".into()),
|
Some("Aggregations cannot be performed on Object type columns. Use dtype command to check column types".into()),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
|
@ -5,7 +5,7 @@ use nu_protocol::{
|
|||||||
engine::{Command, EngineState, Stack},
|
engine::{Command, EngineState, Stack},
|
||||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||||
};
|
};
|
||||||
use polars::prelude::QuantileInterpolOptions;
|
use polars::prelude::{lit, QuantileInterpolOptions};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct LazyQuantile;
|
pub struct LazyQuantile;
|
||||||
@ -60,7 +60,7 @@ impl Command for LazyQuantile {
|
|||||||
let lazy = NuLazyFrame::new(
|
let lazy = NuLazyFrame::new(
|
||||||
lazy.from_eager,
|
lazy.from_eager,
|
||||||
lazy.into_polars()
|
lazy.into_polars()
|
||||||
.quantile(quantile, QuantileInterpolOptions::default()),
|
.quantile(lit(quantile), QuantileInterpolOptions::default()),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(PipelineData::Value(lazy.into_value(call.head)?, None))
|
Ok(PipelineData::Value(lazy.into_value(call.head)?, None))
|
||||||
|
@ -79,7 +79,7 @@ fn command(
|
|||||||
let res = if not_exact {
|
let res = if not_exact {
|
||||||
casted.as_date_not_exact(Some(format.as_str()))
|
casted.as_date_not_exact(Some(format.as_str()))
|
||||||
} else {
|
} else {
|
||||||
casted.as_date(Some(format.as_str()))
|
casted.as_date(Some(format.as_str()), false)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res = res
|
let mut res = res
|
||||||
|
@ -112,7 +112,7 @@ fn command(
|
|||||||
let res = if not_exact {
|
let res = if not_exact {
|
||||||
casted.as_datetime_not_exact(Some(format.as_str()), TimeUnit::Milliseconds)
|
casted.as_datetime_not_exact(Some(format.as_str()), TimeUnit::Milliseconds)
|
||||||
} else {
|
} else {
|
||||||
casted.as_datetime(Some(format.as_str()), TimeUnit::Milliseconds)
|
casted.as_datetime(Some(format.as_str()), TimeUnit::Milliseconds, false, false)
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut res = res
|
let mut res = res
|
||||||
|
@ -35,7 +35,7 @@ impl Command for GetWeekDay {
|
|||||||
result: Some(
|
result: Some(
|
||||||
NuDataFrame::try_from_columns(vec![Column::new(
|
NuDataFrame::try_from_columns(vec![Column::new(
|
||||||
"0".to_string(),
|
"0".to_string(),
|
||||||
vec![Value::test_int(1), Value::test_int(1)],
|
vec![Value::test_int(2), Value::test_int(2)],
|
||||||
)])
|
)])
|
||||||
.expect("simple df for test should not fail")
|
.expect("simple df for test should not fail")
|
||||||
.into_value(Span::test_data()),
|
.into_value(Span::test_data()),
|
||||||
|
@ -71,19 +71,19 @@ pub fn test_dataframe(cmds: Vec<Box<dyn Command + 'static>>) {
|
|||||||
|
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
match eval_block(
|
let result = eval_block(
|
||||||
&engine_state,
|
&engine_state,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
&block,
|
&block,
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
true,
|
true,
|
||||||
true,
|
true,
|
||||||
) {
|
)
|
||||||
Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err),
|
.unwrap_or_else(|err| panic!("test eval error in `{}`: {:?}", example.example, err))
|
||||||
Ok(result) => {
|
.into_value(Span::test_data());
|
||||||
let result = result.into_value(Span::test_data());
|
|
||||||
println!("input: {}", example.example);
|
println!("input: {}", example.example);
|
||||||
println!("result: {:?}", result);
|
println!("result: {result:?}");
|
||||||
println!("done: {:?}", start.elapsed());
|
println!("done: {:?}", start.elapsed());
|
||||||
|
|
||||||
// Note. Value implements PartialEq for Bool, Int, Float, String and Block
|
// Note. Value implements PartialEq for Bool, Int, Float, String and Block
|
||||||
@ -92,12 +92,9 @@ pub fn test_dataframe(cmds: Vec<Box<dyn Command + 'static>>) {
|
|||||||
if let Some(expected) = example.result {
|
if let Some(expected) = example.result {
|
||||||
if result != expected {
|
if result != expected {
|
||||||
panic!(
|
panic!(
|
||||||
"the example result is different to expected value: {:?} != {:?}",
|
"the example result is different to expected value: {result:?} != {expected:?}"
|
||||||
result, expected
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -307,10 +307,7 @@ pub fn create_column(
|
|||||||
.skip(from_row)
|
.skip(from_row)
|
||||||
.take(size)
|
.take(size)
|
||||||
.map(|v| match v {
|
.map(|v| match v {
|
||||||
Some(a) => Value::Int {
|
Some(a) => Value::Int { val: a, span },
|
||||||
val: a as i64,
|
|
||||||
span,
|
|
||||||
},
|
|
||||||
None => Value::Nothing { span },
|
None => Value::Nothing { span },
|
||||||
})
|
})
|
||||||
.collect::<Vec<Value>>();
|
.collect::<Vec<Value>>();
|
||||||
@ -423,7 +420,7 @@ pub fn create_column(
|
|||||||
"Error casting object from series".into(),
|
"Error casting object from series".into(),
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
None,
|
None,
|
||||||
Some(format!("Object not supported for conversion: {}", x)),
|
Some(format!("Object not supported for conversion: {x}")),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
)),
|
)),
|
||||||
Some(ca) => {
|
Some(ca) => {
|
||||||
@ -467,7 +464,7 @@ pub fn create_column(
|
|||||||
error: ShellError::UnsupportedInput(
|
error: ShellError::UnsupportedInput(
|
||||||
"The given local datetime representation is invalid."
|
"The given local datetime representation is invalid."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
format!("timestamp is {:?}", a),
|
format!("timestamp is {a:?}"),
|
||||||
span,
|
span,
|
||||||
Span::unknown(),
|
Span::unknown(),
|
||||||
),
|
),
|
||||||
@ -482,7 +479,7 @@ pub fn create_column(
|
|||||||
error: ShellError::UnsupportedInput(
|
error: ShellError::UnsupportedInput(
|
||||||
"The given local datetime representation is invalid."
|
"The given local datetime representation is invalid."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
format!("timestamp is {:?}", a),
|
format!("timestamp is {a:?}"),
|
||||||
span,
|
span,
|
||||||
Span::unknown(),
|
Span::unknown(),
|
||||||
),
|
),
|
||||||
@ -532,7 +529,7 @@ pub fn create_column(
|
|||||||
error: ShellError::UnsupportedInput(
|
error: ShellError::UnsupportedInput(
|
||||||
"The given local datetime representation is invalid."
|
"The given local datetime representation is invalid."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
format!("timestamp is {:?}", a),
|
format!("timestamp is {a:?}"),
|
||||||
span,
|
span,
|
||||||
Span::unknown(),
|
Span::unknown(),
|
||||||
),
|
),
|
||||||
@ -547,7 +544,7 @@ pub fn create_column(
|
|||||||
error: ShellError::UnsupportedInput(
|
error: ShellError::UnsupportedInput(
|
||||||
"The given local datetime representation is invalid."
|
"The given local datetime representation is invalid."
|
||||||
.to_string(),
|
.to_string(),
|
||||||
format!("timestamp is {:?}", a),
|
format!("timestamp is {a:?}"),
|
||||||
span,
|
span,
|
||||||
Span::unknown(),
|
Span::unknown(),
|
||||||
),
|
),
|
||||||
@ -597,7 +594,7 @@ pub fn create_column(
|
|||||||
"Error creating Dataframe".into(),
|
"Error creating Dataframe".into(),
|
||||||
"".to_string(),
|
"".to_string(),
|
||||||
None,
|
None,
|
||||||
Some(format!("Value not supported in nushell: {}", e)),
|
Some(format!("Value not supported in nushell: {e}")),
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ impl NuDataFrame {
|
|||||||
Value::CustomValue { .. } => return Self::try_from_value(value),
|
Value::CustomValue { .. } => return Self::try_from_value(value),
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
let cols = (0..vals.len())
|
let cols = (0..vals.len())
|
||||||
.map(|i| format!("{}", i))
|
.map(|i| format!("{i}"))
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
conversion::insert_record(&mut column_values, &cols, &vals)?
|
conversion::insert_record(&mut column_values, &cols, &vals)?
|
||||||
@ -181,7 +181,7 @@ impl NuDataFrame {
|
|||||||
let dataframe = DataFrame::new(columns).map_err(|e| {
|
let dataframe = DataFrame::new(columns).map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error creating dataframe".into(),
|
"Error creating dataframe".into(),
|
||||||
format!("Unable to create DataFrame: {}", e),
|
format!("Unable to create DataFrame: {e}"),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -426,7 +426,6 @@ impl NuDataFrame {
|
|||||||
.collect::<Vec<(String, std::vec::IntoIter<Value>)>>();
|
.collect::<Vec<(String, std::vec::IntoIter<Value>)>>();
|
||||||
|
|
||||||
let values = (0..size)
|
let values = (0..size)
|
||||||
.into_iter()
|
|
||||||
.map(|i| {
|
.map(|i| {
|
||||||
let mut cols = vec![];
|
let mut cols = vec![];
|
||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
|
@ -187,7 +187,7 @@ impl NuDataFrame {
|
|||||||
Err(e) => Err({
|
Err(e) => Err({
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error appending dataframe".into(),
|
"Error appending dataframe".into(),
|
||||||
format!("Unable to append: {}", e),
|
format!("Unable to append: {e}"),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -200,7 +200,7 @@ impl NuDataFrame {
|
|||||||
let df_new = DataFrame::new(new_cols).map_err(|e| {
|
let df_new = DataFrame::new(new_cols).map_err(|e| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
"Error appending dataframe".into(),
|
"Error appending dataframe".into(),
|
||||||
format!("Unable to append dataframes: {}", e),
|
format!("Unable to append dataframes: {e}"),
|
||||||
Some(span),
|
Some(span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
|
@ -227,7 +227,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
let value = Value::String {
|
let value = Value::String {
|
||||||
val: format!("{:?}", literal),
|
val: format!("{literal:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
let right_val = expr_to_value(right, span);
|
let right_val = expr_to_value(right, span);
|
||||||
|
|
||||||
let operator = Value::String {
|
let operator = Value::String {
|
||||||
val: format!("{:?}", op),
|
val: format!("{op:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -289,12 +289,9 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
interpol,
|
interpol,
|
||||||
} => {
|
} => {
|
||||||
let expr = expr_to_value(expr.as_ref(), span);
|
let expr = expr_to_value(expr.as_ref(), span);
|
||||||
let quantile = Value::Float {
|
let quantile = expr_to_value(quantile.as_ref(), span);
|
||||||
val: *quantile,
|
|
||||||
span,
|
|
||||||
};
|
|
||||||
let interpol = Value::String {
|
let interpol = Value::String {
|
||||||
val: format!("{:?}", interpol),
|
val: format!("{interpol:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -376,7 +373,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
let vals = dtypes
|
let vals = dtypes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|d| Value::String {
|
.map(|d| Value::String {
|
||||||
val: format!("{}", d),
|
val: format!("{d}"),
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -386,7 +383,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
Expr::Sort { expr, options } => {
|
Expr::Sort { expr, options } => {
|
||||||
let expr = expr_to_value(expr.as_ref(), span);
|
let expr = expr_to_value(expr.as_ref(), span);
|
||||||
let options = Value::String {
|
let options = Value::String {
|
||||||
val: format!("{:?}", options),
|
val: format!("{options:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
let cols = vec!["expr".into(), "options".into()];
|
let cols = vec!["expr".into(), "options".into()];
|
||||||
@ -404,7 +401,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
} => {
|
} => {
|
||||||
let expr = expr_to_value(expr.as_ref(), span);
|
let expr = expr_to_value(expr.as_ref(), span);
|
||||||
let dtype = Value::String {
|
let dtype = Value::String {
|
||||||
val: format!("{:?}", data_type),
|
val: format!("{data_type:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
let strict = Value::Bool { val: *strict, span };
|
let strict = Value::Bool { val: *strict, span };
|
||||||
@ -485,7 +482,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
let excluded = excluded
|
let excluded = excluded
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| Value::String {
|
.map(|e| Value::String {
|
||||||
val: format!("{:?}", e),
|
val: format!("{e:?}"),
|
||||||
span,
|
span,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
@ -505,7 +502,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
Expr::RenameAlias { expr, function } => {
|
Expr::RenameAlias { expr, function } => {
|
||||||
let expr = expr_to_value(expr.as_ref(), span);
|
let expr = expr_to_value(expr.as_ref(), span);
|
||||||
let function = Value::String {
|
let function = Value::String {
|
||||||
val: format!("{:?}", function),
|
val: format!("{function:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -527,15 +524,15 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
let input = Value::List { vals: input, span };
|
let input = Value::List { vals: input, span };
|
||||||
|
|
||||||
let function = Value::String {
|
let function = Value::String {
|
||||||
val: format!("{:?}", function),
|
val: format!("{function:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
let output_type = Value::String {
|
let output_type = Value::String {
|
||||||
val: format!("{:?}", output_type),
|
val: format!("{output_type:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
let options = Value::String {
|
let options = Value::String {
|
||||||
val: format!("{:?}", options),
|
val: format!("{options:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -561,11 +558,11 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
let input = Value::List { vals: input, span };
|
let input = Value::List { vals: input, span };
|
||||||
|
|
||||||
let function = Value::String {
|
let function = Value::String {
|
||||||
val: format!("{:?}", function),
|
val: format!("{function:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
let options = Value::String {
|
let options = Value::String {
|
||||||
val: format!("{:?}", options),
|
val: format!("{options:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -600,7 +597,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
|||||||
.unwrap_or_else(|| Value::nothing(span));
|
.unwrap_or_else(|| Value::nothing(span));
|
||||||
|
|
||||||
let options = Value::String {
|
let options = Value::String {
|
||||||
val: format!("{:?}", options),
|
val: format!("{options:?}"),
|
||||||
span,
|
span,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ where
|
|||||||
.unwrap_or(Locale::en_US);
|
.unwrap_or(Locale::en_US);
|
||||||
let format = date_time.format_localized(formatter, locale);
|
let format = date_time.format_localized(formatter, locale);
|
||||||
|
|
||||||
match formatter_buf.write_fmt(format_args!("{}", format)) {
|
match formatter_buf.write_fmt(format_args!("{format}")) {
|
||||||
Ok(_) => Value::String {
|
Ok(_) => Value::String {
|
||||||
val: formatter_buf,
|
val: formatter_buf,
|
||||||
span,
|
span,
|
||||||
|
@ -46,7 +46,6 @@ impl Command for SubCommand {
|
|||||||
}];
|
}];
|
||||||
Value::Record { cols, vals, span }
|
Value::Record { cols, vals, span }
|
||||||
})
|
})
|
||||||
.into_iter()
|
|
||||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,6 +95,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
Each,
|
Each,
|
||||||
EachWhile,
|
EachWhile,
|
||||||
Empty,
|
Empty,
|
||||||
|
Enumerate,
|
||||||
Every,
|
Every,
|
||||||
Filter,
|
Filter,
|
||||||
Find,
|
Find,
|
||||||
@ -288,6 +289,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
Ansi,
|
Ansi,
|
||||||
AnsiGradient,
|
AnsiGradient,
|
||||||
AnsiStrip,
|
AnsiStrip,
|
||||||
|
AnsiLink,
|
||||||
Clear,
|
Clear,
|
||||||
Du,
|
Du,
|
||||||
KeybindingsDefault,
|
KeybindingsDefault,
|
||||||
@ -354,7 +356,6 @@ pub fn create_default_context() -> EngineState {
|
|||||||
Use,
|
Use,
|
||||||
Upsert,
|
Upsert,
|
||||||
Where,
|
Where,
|
||||||
ToUrl,
|
|
||||||
ToXml,
|
ToXml,
|
||||||
ToYaml,
|
ToYaml,
|
||||||
};
|
};
|
||||||
@ -433,10 +434,13 @@ pub fn create_default_context() -> EngineState {
|
|||||||
|
|
||||||
// Network
|
// Network
|
||||||
bind_command! {
|
bind_command! {
|
||||||
Fetch,
|
Http,
|
||||||
Post,
|
HttpGet,
|
||||||
|
HttpPost,
|
||||||
Url,
|
Url,
|
||||||
|
UrlBuildQuery,
|
||||||
UrlEncode,
|
UrlEncode,
|
||||||
|
UrlJoin,
|
||||||
UrlParse,
|
UrlParse,
|
||||||
Port,
|
Port,
|
||||||
}
|
}
|
||||||
@ -491,7 +495,7 @@ pub fn create_default_context() -> EngineState {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = engine_state.merge_delta(delta) {
|
if let Err(err) = engine_state.merge_delta(delta) {
|
||||||
eprintln!("Error creating default context: {:?}", err);
|
eprintln!("Error creating default context: {err:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
engine_state
|
engine_state
|
||||||
|
@ -18,5 +18,7 @@ pub fn deprecated_commands() -> HashMap<String, String> {
|
|||||||
"build-string".to_string(),
|
"build-string".to_string(),
|
||||||
"str join'/'string concatenation with '+'".to_string(),
|
"str join'/'string concatenation with '+'".to_string(),
|
||||||
),
|
),
|
||||||
|
("fetch".to_string(), "http get".to_string()),
|
||||||
|
("post".to_string(), "http post".to_string()),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,7 @@ impl Command for ConfigReset {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ok(mut file) = std::fs::File::create(nu_config) {
|
if let Ok(mut file) = std::fs::File::create(nu_config) {
|
||||||
if writeln!(&mut file, "{}", config_file).is_err() {
|
if writeln!(&mut file, "{config_file}").is_err() {
|
||||||
return Err(ShellError::FileNotFoundCustom(
|
return Err(ShellError::FileNotFoundCustom(
|
||||||
"config.nu could not be written to".into(),
|
"config.nu could not be written to".into(),
|
||||||
span,
|
span,
|
||||||
@ -102,7 +102,7 @@ impl Command for ConfigReset {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Ok(mut file) = std::fs::File::create(env_config) {
|
if let Ok(mut file) = std::fs::File::create(env_config) {
|
||||||
if writeln!(&mut file, "{}", config_file).is_err() {
|
if writeln!(&mut file, "{config_file}").is_err() {
|
||||||
return Err(ShellError::FileNotFoundCustom(
|
return Err(ShellError::FileNotFoundCustom(
|
||||||
"env.nu could not be written to".into(),
|
"env.nu could not be written to".into(),
|
||||||
span,
|
span,
|
||||||
|
2
crates/nu-command/src/env/env_command.rs
vendored
2
crates/nu-command/src/env/env_command.rs
vendored
@ -54,7 +54,7 @@ impl Command for Env {
|
|||||||
vals.push(Value::string(name, span));
|
vals.push(Value::string(name, span));
|
||||||
|
|
||||||
cols.push("type".into());
|
cols.push("type".into());
|
||||||
vals.push(Value::string(format!("{}", val_type), span));
|
vals.push(Value::string(format!("{val_type}"), span));
|
||||||
|
|
||||||
cols.push("value".into());
|
cols.push("value".into());
|
||||||
vals.push(val);
|
vals.push(val);
|
||||||
|
@ -20,7 +20,7 @@ mod test_examples {
|
|||||||
engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet},
|
engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet},
|
||||||
Example, PipelineData, Signature, Span, Type, Value,
|
Example, PipelineData, Signature, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use std::{collections::HashSet, path::PathBuf};
|
use std::collections::HashSet;
|
||||||
|
|
||||||
pub fn test_examples(cmd: impl Command + 'static) {
|
pub fn test_examples(cmd: impl Command + 'static) {
|
||||||
let examples = cmd.examples();
|
let examples = cmd.examples();
|
||||||
@ -45,7 +45,7 @@ mod test_examples {
|
|||||||
signature.vectorizes_over_list,
|
signature.vectorizes_over_list,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
check_example_evaluates_to_expected_output(&example, &cwd, &mut engine_state);
|
check_example_evaluates_to_expected_output(&example, cwd.as_path(), &mut engine_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
check_all_signature_input_output_types_entries_have_examples(
|
check_all_signature_input_output_types_entries_have_examples(
|
||||||
@ -104,7 +104,7 @@ mod test_examples {
|
|||||||
|
|
||||||
fn check_example_input_and_output_types_match_command_signature(
|
fn check_example_input_and_output_types_match_command_signature(
|
||||||
example: &Example,
|
example: &Example,
|
||||||
cwd: &PathBuf,
|
cwd: &std::path::Path,
|
||||||
engine_state: &mut Box<EngineState>,
|
engine_state: &mut Box<EngineState>,
|
||||||
signature_input_output_types: &Vec<(Type, Type)>,
|
signature_input_output_types: &Vec<(Type, Type)>,
|
||||||
signature_operates_on_cell_paths: bool,
|
signature_operates_on_cell_paths: bool,
|
||||||
@ -204,7 +204,7 @@ mod test_examples {
|
|||||||
|
|
||||||
fn check_example_evaluates_to_expected_output(
|
fn check_example_evaluates_to_expected_output(
|
||||||
example: &Example,
|
example: &Example,
|
||||||
cwd: &PathBuf,
|
cwd: &std::path::Path,
|
||||||
engine_state: &mut Box<EngineState>,
|
engine_state: &mut Box<EngineState>,
|
||||||
) {
|
) {
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
@ -251,7 +251,7 @@ mod test_examples {
|
|||||||
{:?}",
|
{:?}",
|
||||||
declared_type_transformations
|
declared_type_transformations
|
||||||
.difference(&witnessed_type_transformations)
|
.difference(&witnessed_type_transformations)
|
||||||
.map(|(s1, s2)| format!("{} -> {}", s1, s2))
|
.map(|(s1, s2)| format!("{s1} -> {s2}"))
|
||||||
.join(", ")
|
.join(", ")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -260,7 +260,7 @@ mod test_examples {
|
|||||||
fn eval(
|
fn eval(
|
||||||
contents: &str,
|
contents: &str,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
cwd: &PathBuf,
|
cwd: &std::path::Path,
|
||||||
engine_state: &mut Box<EngineState>,
|
engine_state: &mut Box<EngineState>,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
let (block, delta) = parse(contents, engine_state);
|
let (block, delta) = parse(contents, engine_state);
|
||||||
@ -273,7 +273,7 @@ mod test_examples {
|
|||||||
nu_parser::parse(&mut working_set, None, contents.as_bytes(), false, &[]);
|
nu_parser::parse(&mut working_set, None, contents.as_bytes(), false, &[]);
|
||||||
|
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
panic!("test parse error in `{}`: {:?}", contents, err)
|
panic!("test parse error in `{contents}`: {err:?}")
|
||||||
}
|
}
|
||||||
|
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
@ -282,7 +282,7 @@ mod test_examples {
|
|||||||
fn eval_block(
|
fn eval_block(
|
||||||
block: Block,
|
block: Block,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
cwd: &PathBuf,
|
cwd: &std::path::Path,
|
||||||
engine_state: &mut Box<EngineState>,
|
engine_state: &mut Box<EngineState>,
|
||||||
delta: StateDelta,
|
delta: StateDelta,
|
||||||
) -> Value {
|
) -> Value {
|
||||||
@ -302,12 +302,11 @@ mod test_examples {
|
|||||||
|
|
||||||
fn eval_pipeline_without_terminal_expression(
|
fn eval_pipeline_without_terminal_expression(
|
||||||
src: &str,
|
src: &str,
|
||||||
cwd: &PathBuf,
|
cwd: &std::path::Path,
|
||||||
engine_state: &mut Box<EngineState>,
|
engine_state: &mut Box<EngineState>,
|
||||||
) -> Option<Value> {
|
) -> Option<Value> {
|
||||||
let (mut block, delta) = parse(src, engine_state);
|
let (mut block, delta) = parse(src, engine_state);
|
||||||
match block.pipelines.len() {
|
if block.pipelines.len() == 1 {
|
||||||
1 => {
|
|
||||||
let n_expressions = block.pipelines[0].elements.len();
|
let n_expressions = block.pipelines[0].elements.len();
|
||||||
block.pipelines[0].elements.truncate(&n_expressions - 1);
|
block.pipelines[0].elements.truncate(&n_expressions - 1);
|
||||||
|
|
||||||
@ -317,11 +316,9 @@ mod test_examples {
|
|||||||
} else {
|
} else {
|
||||||
Some(Value::nothing(Span::test_data()))
|
Some(Value::nothing(Span::test_data()))
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
_ => {
|
|
||||||
// E.g. multiple semicolon-separated statements
|
// E.g. multiple semicolon-separated statements
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
use crate::filesystem::cd_query::query;
|
use crate::filesystem::cd_query::query;
|
||||||
use crate::{get_current_shell, get_shells};
|
use crate::{get_current_shell, get_shells};
|
||||||
|
#[cfg(unix)]
|
||||||
|
use libc::gid_t;
|
||||||
use nu_engine::{current_dir, CallExt};
|
use nu_engine::{current_dir, CallExt};
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
@ -8,22 +10,16 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
// when the file under the fold executable
|
// For checking whether we have permission to cd to a directory
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod permission_mods {
|
mod file_permissions {
|
||||||
pub type Mode = u32;
|
pub type Mode = u32;
|
||||||
pub mod unix {
|
|
||||||
use super::Mode;
|
|
||||||
pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode;
|
pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode;
|
||||||
pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
|
pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
|
||||||
pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
|
pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// use to return the message of the result of change director
|
// The result of checking whether we have permission to cd to a directory
|
||||||
// TODO: windows, maybe should use file_attributes function in https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
|
|
||||||
// TODO: the meaning of the result of the function can be found in https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
|
||||||
// TODO: if have realize the logic on windows, remove the cfg
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum PermissionResult<'a> {
|
enum PermissionResult<'a> {
|
||||||
PermissionOk,
|
PermissionOk,
|
||||||
@ -97,14 +93,14 @@ impl Command for Cd {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ShellError::DirectoryNotFound(
|
return Err(ShellError::DirectoryNotFound(
|
||||||
v.span,
|
v.span,
|
||||||
Some(format!("IO Error: {:?}", e)),
|
Some(format!("IO Error: {e:?}")),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(ShellError::DirectoryNotFound(
|
return Err(ShellError::DirectoryNotFound(
|
||||||
v.span,
|
v.span,
|
||||||
Some(format!("IO Error: {:?}", e1)),
|
Some(format!("IO Error: {e1:?}")),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,7 +123,7 @@ impl Command for Cd {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ShellError::DirectoryNotFound(
|
return Err(ShellError::DirectoryNotFound(
|
||||||
v.span,
|
v.span,
|
||||||
Some(format!("IO Error: {:?}", e)),
|
Some(format!("IO Error: {e:?}")),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -146,14 +142,14 @@ impl Command for Cd {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ShellError::DirectoryNotFound(
|
return Err(ShellError::DirectoryNotFound(
|
||||||
v.span,
|
v.span,
|
||||||
Some(format!("IO Error: {:?}", e)),
|
Some(format!("IO Error: {e:?}")),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return Err(ShellError::DirectoryNotFound(
|
return Err(ShellError::DirectoryNotFound(
|
||||||
v.span,
|
v.span,
|
||||||
Some(format!("IO Error: {:?}", e1)),
|
Some(format!("IO Error: {e1:?}")),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -167,8 +163,10 @@ impl Command for Cd {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let path_tointo = path.clone();
|
let path_value = Value::String {
|
||||||
let path_value = Value::String { val: path, span };
|
val: path.clone(),
|
||||||
|
span,
|
||||||
|
};
|
||||||
let cwd = Value::string(cwd.to_string_lossy(), call.head);
|
let cwd = Value::string(cwd.to_string_lossy(), call.head);
|
||||||
|
|
||||||
let mut shells = get_shells(engine_state, stack, cwd);
|
let mut shells = get_shells(engine_state, stack, cwd);
|
||||||
@ -191,16 +189,15 @@ impl Command for Cd {
|
|||||||
stack.add_env_var("OLDPWD".into(), oldpwd)
|
stack.add_env_var("OLDPWD".into(), oldpwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
match have_permission(&path) {
|
||||||
//FIXME: this only changes the current scope, but instead this environment variable
|
//FIXME: this only changes the current scope, but instead this environment variable
|
||||||
//should probably be a block that loads the information from the state in the overlay
|
//should probably be a block that loads the information from the state in the overlay
|
||||||
match have_permission(&path_tointo) {
|
|
||||||
PermissionResult::PermissionOk => {
|
PermissionResult::PermissionOk => {
|
||||||
stack.add_env_var("PWD".into(), path_value);
|
stack.add_env_var("PWD".into(), path_value);
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
}
|
}
|
||||||
PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError(format!(
|
PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError(format!(
|
||||||
"Cannot change directory to {}: {}",
|
"Cannot change directory to {path}: {reason}"
|
||||||
path_tointo, reason
|
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -225,6 +222,9 @@ impl Command for Cd {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Maybe we should use file_attributes() from https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
|
||||||
|
// More on that here: https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
|
fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
|
||||||
match dir.as_ref().read_dir() {
|
match dir.as_ref().read_dir() {
|
||||||
@ -246,35 +246,40 @@ fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
|
|||||||
use std::os::unix::fs::MetadataExt;
|
use std::os::unix::fs::MetadataExt;
|
||||||
let bits = metadata.mode();
|
let bits = metadata.mode();
|
||||||
let has_bit = |bit| bits & bit == bit;
|
let has_bit = |bit| bits & bit == bit;
|
||||||
let current_user = users::get_current_uid();
|
let current_user_uid = users::get_current_uid();
|
||||||
if current_user == 0 {
|
if current_user_uid == 0 {
|
||||||
return PermissionResult::PermissionOk;
|
return PermissionResult::PermissionOk;
|
||||||
}
|
}
|
||||||
let current_group = users::get_current_gid();
|
let current_user_gid = users::get_current_gid();
|
||||||
let owner_user = metadata.uid();
|
let owner_user = metadata.uid();
|
||||||
let owner_group = metadata.gid();
|
let owner_group = metadata.gid();
|
||||||
match (current_user == owner_user, current_group == owner_group) {
|
match (
|
||||||
|
current_user_uid == owner_user,
|
||||||
|
current_user_gid == owner_group,
|
||||||
|
) {
|
||||||
(true, _) => {
|
(true, _) => {
|
||||||
if has_bit(permission_mods::unix::USER_EXECUTE) {
|
if has_bit(file_permissions::USER_EXECUTE) {
|
||||||
PermissionResult::PermissionOk
|
PermissionResult::PermissionOk
|
||||||
} else {
|
} else {
|
||||||
PermissionResult::PermissionDenied(
|
PermissionResult::PermissionDenied(
|
||||||
"You are the owner but do not have the execute permission",
|
"You are the owner but do not have execute permission",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(false, true) => {
|
(false, true) => {
|
||||||
if has_bit(permission_mods::unix::GROUP_EXECUTE) {
|
if has_bit(file_permissions::GROUP_EXECUTE) {
|
||||||
PermissionResult::PermissionOk
|
PermissionResult::PermissionOk
|
||||||
} else {
|
} else {
|
||||||
PermissionResult::PermissionDenied(
|
PermissionResult::PermissionDenied(
|
||||||
"You are in the group but do not have the execute permission",
|
"You are in the group but do not have execute permission",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// other_user or root
|
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
if has_bit(permission_mods::unix::OTHER_EXECUTE) {
|
if has_bit(file_permissions::OTHER_EXECUTE)
|
||||||
|
|| (has_bit(file_permissions::GROUP_EXECUTE)
|
||||||
|
&& any_group(current_user_gid, owner_group))
|
||||||
|
{
|
||||||
PermissionResult::PermissionOk
|
PermissionResult::PermissionOk
|
||||||
} else {
|
} else {
|
||||||
PermissionResult::PermissionDenied(
|
PermissionResult::PermissionDenied(
|
||||||
@ -284,6 +289,32 @@ fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => PermissionResult::PermissionDenied("Could not retrieve the metadata"),
|
Err(_) => PermissionResult::PermissionDenied("Could not retrieve file metadata"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn any_group(current_user_gid: gid_t, owner_group: u32) -> bool {
|
||||||
|
users::get_current_username()
|
||||||
|
.map(|name| {
|
||||||
|
users::get_user_groups(&name, current_user_gid)
|
||||||
|
.map(|mut groups| {
|
||||||
|
// Fixes https://github.com/ogham/rust-users/issues/44
|
||||||
|
// If a user isn't in more than one group then this fix won't work,
|
||||||
|
// However its common for a user to be in more than one group, so this should work for most.
|
||||||
|
if groups.len() == 2 && groups[1].gid() == 0 {
|
||||||
|
// We have no way of knowing if this is due to the issue or the user is actually in the root group
|
||||||
|
// So we will assume they are in the root group and leave it.
|
||||||
|
// It's not the end of the world if we are wrong, they will just get a permission denied error once inside.
|
||||||
|
} else {
|
||||||
|
groups.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
groups
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
.into_iter()
|
||||||
|
.any(|group| group.gid() == owner_group)
|
||||||
|
}
|
||||||
|
@ -168,8 +168,7 @@ impl Command for Cp {
|
|||||||
canonicalize_with(dst.as_path(), ¤t_dir_path).unwrap_or(dst);
|
canonicalize_with(dst.as_path(), ¤t_dir_path).unwrap_or(dst);
|
||||||
let res = if src == dst {
|
let res = if src == dst {
|
||||||
let message = format!(
|
let message = format!(
|
||||||
"src {:?} and dst {:?} are identical(not copied)",
|
"src {source:?} and dst {destination:?} are identical(not copied)"
|
||||||
source, destination
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
|
@ -26,6 +26,21 @@ impl Command for Glob {
|
|||||||
"directory depth to search",
|
"directory depth to search",
|
||||||
Some('d'),
|
Some('d'),
|
||||||
)
|
)
|
||||||
|
.switch(
|
||||||
|
"no-dir",
|
||||||
|
"Whether to filter out directories from the returned paths",
|
||||||
|
Some('D'),
|
||||||
|
)
|
||||||
|
.switch(
|
||||||
|
"no-file",
|
||||||
|
"Whether to filter out files from the returned paths",
|
||||||
|
Some('F'),
|
||||||
|
)
|
||||||
|
.switch(
|
||||||
|
"no-symlink",
|
||||||
|
"Whether to filter out symlinks from the returned paths",
|
||||||
|
Some('S'),
|
||||||
|
)
|
||||||
.category(Category::FileSystem)
|
.category(Category::FileSystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,6 +96,11 @@ impl Command for Glob {
|
|||||||
example: "glob <[a-d]:1,10>",
|
example: "glob <[a-d]:1,10>",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Search for folders that begin with an uppercase ASCII letter, ignoring files and symlinks",
|
||||||
|
example: r#"glob "[A-Z]*" --no-file --no-symlink"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +120,10 @@ impl Command for Glob {
|
|||||||
let glob_pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let glob_pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
let depth = call.get_flag(engine_state, stack, "depth")?;
|
let depth = call.get_flag(engine_state, stack, "depth")?;
|
||||||
|
|
||||||
|
let no_dirs = call.has_flag("no-dir");
|
||||||
|
let no_files = call.has_flag("no-file");
|
||||||
|
let no_symlinks = call.has_flag("no-symlink");
|
||||||
|
|
||||||
if glob_pattern.item.is_empty() {
|
if glob_pattern.item.is_empty() {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"glob pattern must not be empty".to_string(),
|
"glob pattern must not be empty".to_string(),
|
||||||
@ -121,7 +145,7 @@ impl Command for Glob {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
"error with glob pattern".to_string(),
|
"error with glob pattern".to_string(),
|
||||||
format!("{}", e),
|
format!("{e}"),
|
||||||
Some(glob_pattern.span),
|
Some(glob_pattern.span),
|
||||||
None,
|
None,
|
||||||
Vec::new(),
|
Vec::new(),
|
||||||
@ -139,6 +163,13 @@ impl Command for Glob {
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.flatten()
|
.flatten()
|
||||||
|
.filter(|entry| {
|
||||||
|
let file_type = entry.file_type();
|
||||||
|
|
||||||
|
!(no_dirs && file_type.is_dir()
|
||||||
|
|| no_files && file_type.is_file()
|
||||||
|
|| no_symlinks && file_type.is_symlink())
|
||||||
|
})
|
||||||
.map(|entry| Value::String {
|
.map(|entry| Value::String {
|
||||||
val: entry.into_path().to_string_lossy().to_string(),
|
val: entry.into_path().to_string_lossy().to_string(),
|
||||||
span,
|
span,
|
||||||
|
@ -178,7 +178,6 @@ impl Command for Ls {
|
|||||||
let mut hidden_dirs = vec![];
|
let mut hidden_dirs = vec![];
|
||||||
|
|
||||||
Ok(paths_peek
|
Ok(paths_peek
|
||||||
.into_iter()
|
|
||||||
.filter_map(move |x| match x {
|
.filter_map(move |x| match x {
|
||||||
Ok(path) => {
|
Ok(path) => {
|
||||||
let metadata = match std::fs::symlink_metadata(&path) {
|
let metadata = match std::fs::symlink_metadata(&path) {
|
||||||
|
@ -70,7 +70,7 @@ impl Command for Mkdir {
|
|||||||
|
|
||||||
if let Err(reason) = dir_res {
|
if let Err(reason) = dir_res {
|
||||||
return Err(ShellError::CreateNotPossible(
|
return Err(ShellError::CreateNotPossible(
|
||||||
format!("failed to create directory: {}", reason),
|
format!("failed to create directory: {reason}"),
|
||||||
call.positional_nth(i)
|
call.positional_nth(i)
|
||||||
.expect("already checked through directories")
|
.expect("already checked through directories")
|
||||||
.span,
|
.span,
|
||||||
|
@ -290,7 +290,7 @@ fn move_file(
|
|||||||
);
|
);
|
||||||
if let Err(e) = interaction {
|
if let Err(e) = interaction {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
format!("Error during interaction: {:}", e),
|
format!("Error during interaction: {e:}"),
|
||||||
"could not move".into(),
|
"could not move".into(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
@ -325,10 +325,10 @@ fn move_item(from: &Path, from_span: Span, to: &Path) -> Result<(), ShellError>
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
let error_kind = match e.kind {
|
let error_kind = match e.kind {
|
||||||
fs_extra::error::ErrorKind::Io(io) => {
|
fs_extra::error::ErrorKind::Io(io) => {
|
||||||
format!("I/O error: {}", io)
|
format!("I/O error: {io}")
|
||||||
}
|
}
|
||||||
fs_extra::error::ErrorKind::StripPrefix(sp) => {
|
fs_extra::error::ErrorKind::StripPrefix(sp) => {
|
||||||
format!("Strip prefix error: {}", sp)
|
format!("Strip prefix error: {sp}")
|
||||||
}
|
}
|
||||||
fs_extra::error::ErrorKind::OsString(os) => {
|
fs_extra::error::ErrorKind::OsString(os) => {
|
||||||
format!("OsString error: {:?}", os.to_str())
|
format!("OsString error: {:?}", os.to_str())
|
||||||
@ -336,10 +336,7 @@ fn move_item(from: &Path, from_span: Span, to: &Path) -> Result<(), ShellError>
|
|||||||
_ => e.to_string(),
|
_ => e.to_string(),
|
||||||
};
|
};
|
||||||
Err(ShellError::GenericError(
|
Err(ShellError::GenericError(
|
||||||
format!(
|
format!("Could not move {from:?} to {to:?}. Error Kind: {error_kind}"),
|
||||||
"Could not move {:?} to {:?}. Error Kind: {}",
|
|
||||||
from, to, error_kind
|
|
||||||
),
|
|
||||||
"could not move".into(),
|
"could not move".into(),
|
||||||
Some(from_span),
|
Some(from_span),
|
||||||
None,
|
None,
|
||||||
|
@ -139,6 +139,7 @@ impl Command for Open {
|
|||||||
Box::new(BufferedReader { input: buf_reader }),
|
Box::new(BufferedReader { input: buf_reader }),
|
||||||
ctrlc,
|
ctrlc,
|
||||||
call_span,
|
call_span,
|
||||||
|
None,
|
||||||
)),
|
)),
|
||||||
stderr: None,
|
stderr: None,
|
||||||
exit_code: None,
|
exit_code: None,
|
||||||
@ -155,7 +156,7 @@ impl Command for Open {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if let Some(ext) = ext {
|
if let Some(ext) = ext {
|
||||||
match engine_state.find_decl(format!("from {}", ext).as_bytes(), &[]) {
|
match engine_state.find_decl(format!("from {ext}").as_bytes(), &[]) {
|
||||||
Some(converter_id) => {
|
Some(converter_id) => {
|
||||||
let decl = engine_state.get_decl(converter_id);
|
let decl = engine_state.get_decl(converter_id);
|
||||||
if let Some(block_id) = decl.get_block_id() {
|
if let Some(block_id) = decl.get_block_id() {
|
||||||
@ -166,7 +167,7 @@ impl Command for Open {
|
|||||||
}
|
}
|
||||||
.map_err(|inner| {
|
.map_err(|inner| {
|
||||||
ShellError::GenericError(
|
ShellError::GenericError(
|
||||||
format!("Error while parsing as {}", ext),
|
format!("Error while parsing as {ext}"),
|
||||||
format!("Could not parse '{}' with `from {}`", path.display(), ext),
|
format!("Could not parse '{}' with `from {}`", path.display(), ext),
|
||||||
Some(arg_span),
|
Some(arg_span),
|
||||||
Some(format!("Check out `help from {}` or `help from` for more options or open raw data with `open --raw '{}'`", ext, path.display())),
|
Some(format!("Check out `help from {}` or `help from` for more options or open raw data with `open --raw '{}'`", ext, path.display())),
|
||||||
|
@ -318,7 +318,7 @@ fn rm(
|
|||||||
);
|
);
|
||||||
if let Err(e) = interaction {
|
if let Err(e) = interaction {
|
||||||
return Err(ShellError::GenericError(
|
return Err(ShellError::GenericError(
|
||||||
format!("Error during interaction: {:}", e),
|
format!("Error during interaction: {e:}"),
|
||||||
"could not move".into(),
|
"could not move".into(),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
@ -375,7 +375,7 @@ fn rm(
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else if trash || (rm_always_trash && !permanent) {
|
} else if trash || (rm_always_trash && !permanent) {
|
||||||
trash::delete(&f).map_err(|e: trash::Error| {
|
trash::delete(&f).map_err(|e: trash::Error| {
|
||||||
Error::new(ErrorKind::Other, format!("{:?}\nTry '--trash' flag", e))
|
Error::new(ErrorKind::Other, format!("{e:?}\nTry '--trash' flag"))
|
||||||
})
|
})
|
||||||
} else if metadata.is_file() || is_socket || is_fifo {
|
} else if metadata.is_file() || is_socket || is_fifo {
|
||||||
std::fs::remove_file(&f)
|
std::fs::remove_file(&f)
|
||||||
@ -403,7 +403,7 @@ fn rm(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = result {
|
if let Err(e) = result {
|
||||||
let msg = format!("Could not delete because: {:}", e);
|
let msg = format!("Could not delete because: {e:}");
|
||||||
Value::Error {
|
Value::Error {
|
||||||
error: ShellError::GenericError(
|
error: ShellError::GenericError(
|
||||||
msg,
|
msg,
|
||||||
|
@ -8,6 +8,9 @@ use nu_protocol::{
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufWriter, Write};
|
use std::io::{BufWriter, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use std::thread;
|
||||||
|
|
||||||
|
use crate::progress_bar;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Save;
|
pub struct Save;
|
||||||
@ -47,6 +50,7 @@ impl Command for Save {
|
|||||||
.switch("raw", "save file as raw binary", Some('r'))
|
.switch("raw", "save file as raw binary", Some('r'))
|
||||||
.switch("append", "append input to the end of the file", Some('a'))
|
.switch("append", "append input to the end of the file", Some('a'))
|
||||||
.switch("force", "overwrite the destination", Some('f'))
|
.switch("force", "overwrite the destination", Some('f'))
|
||||||
|
.switch("progress", "enable progress bar", Some('p'))
|
||||||
.category(Category::FileSystem)
|
.category(Category::FileSystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,6 +64,7 @@ impl Command for Save {
|
|||||||
let raw = call.has_flag("raw");
|
let raw = call.has_flag("raw");
|
||||||
let append = call.has_flag("append");
|
let append = call.has_flag("append");
|
||||||
let force = call.has_flag("force");
|
let force = call.has_flag("force");
|
||||||
|
let progress = call.has_flag("progress");
|
||||||
|
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
|
|
||||||
@ -81,16 +86,20 @@ impl Command for Save {
|
|||||||
|
|
||||||
// delegate a thread to redirect stderr to result.
|
// delegate a thread to redirect stderr to result.
|
||||||
let handler = stderr.map(|stderr_stream| match stderr_file {
|
let handler = stderr.map(|stderr_stream| match stderr_file {
|
||||||
Some(stderr_file) => {
|
Some(stderr_file) => thread::Builder::new()
|
||||||
std::thread::spawn(move || stream_to_file(stderr_stream, stderr_file, span))
|
.name("stderr redirector".to_string())
|
||||||
}
|
.spawn(move || stream_to_file(stderr_stream, stderr_file, span, progress))
|
||||||
None => std::thread::spawn(move || {
|
.expect("Failed to create thread"),
|
||||||
|
None => thread::Builder::new()
|
||||||
|
.name("stderr redirector".to_string())
|
||||||
|
.spawn(move || {
|
||||||
let _ = stderr_stream.into_bytes();
|
let _ = stderr_stream.into_bytes();
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
}),
|
})
|
||||||
|
.expect("Failed to create thread"),
|
||||||
});
|
});
|
||||||
|
|
||||||
let res = stream_to_file(stream, file, span);
|
let res = stream_to_file(stream, file, span, progress);
|
||||||
if let Some(h) = handler {
|
if let Some(h) = handler {
|
||||||
h.join().map_err(|err| {
|
h.join().map_err(|err| {
|
||||||
ShellError::ExternalCommand(
|
ShellError::ExternalCommand(
|
||||||
@ -104,6 +113,20 @@ impl Command for Save {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PipelineData::ListStream(ls, _)
|
||||||
|
if raw || prepare_path(&path, append, force)?.0.extension().is_none() =>
|
||||||
|
{
|
||||||
|
let (mut file, _) = get_files(&path, &stderr_path, append, force)?;
|
||||||
|
for val in ls {
|
||||||
|
file.write_all(&value_to_bytes(val)?)
|
||||||
|
.map_err(|err| ShellError::IOError(err.to_string()))?;
|
||||||
|
file.write_all("\n".as_bytes())
|
||||||
|
.map_err(|err| ShellError::IOError(err.to_string()))?;
|
||||||
|
}
|
||||||
|
file.flush()?;
|
||||||
|
|
||||||
|
Ok(PipelineData::empty())
|
||||||
|
}
|
||||||
input => {
|
input => {
|
||||||
let bytes =
|
let bytes =
|
||||||
input_to_bytes(input, Path::new(&path.item), raw, engine_state, stack, span)?;
|
input_to_bytes(input, Path::new(&path.item), raw, engine_state, stack, span)?;
|
||||||
@ -178,7 +201,7 @@ fn input_to_bytes(
|
|||||||
convert_to_extension(engine_state, &ext, stack, input, span)
|
convert_to_extension(engine_state, &ext, stack, input, span)
|
||||||
} else {
|
} else {
|
||||||
let value = input.into_value(span);
|
let value = input.into_value(span);
|
||||||
string_binary_list_value_to_bytes(value, span)
|
value_to_bytes(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +215,7 @@ fn convert_to_extension(
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<Vec<u8>, ShellError> {
|
) -> Result<Vec<u8>, ShellError> {
|
||||||
let converter = engine_state.find_decl(format!("to {}", extension).as_bytes(), &[]);
|
let converter = engine_state.find_decl(format!("to {extension}").as_bytes(), &[]);
|
||||||
|
|
||||||
let output = match converter {
|
let output = match converter {
|
||||||
Some(converter_id) => {
|
Some(converter_id) => {
|
||||||
@ -208,13 +231,13 @@ fn convert_to_extension(
|
|||||||
None => input.into_value(span),
|
None => input.into_value(span),
|
||||||
};
|
};
|
||||||
|
|
||||||
string_binary_list_value_to_bytes(output, span)
|
value_to_bytes(output)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert [`Value::String`] [`Value::Binary`] or [`Value::List`] into [`Vec`] of bytes
|
/// Convert [`Value::String`] [`Value::Binary`] or [`Value::List`] into [`Vec`] of bytes
|
||||||
///
|
///
|
||||||
/// Propagates [`Value::Error`] and creates error otherwise
|
/// Propagates [`Value::Error`] and creates error otherwise
|
||||||
fn string_binary_list_value_to_bytes(value: Value, span: Span) -> Result<Vec<u8>, ShellError> {
|
fn value_to_bytes(value: Value) -> Result<Vec<u8>, ShellError> {
|
||||||
match value {
|
match value {
|
||||||
Value::String { val, .. } => Ok(val.into_bytes()),
|
Value::String { val, .. } => Ok(val.into_bytes()),
|
||||||
Value::Binary { val, .. } => Ok(val),
|
Value::Binary { val, .. } => Ok(val),
|
||||||
@ -230,13 +253,7 @@ fn string_binary_list_value_to_bytes(value: Value, span: Span) -> Result<Vec<u8>
|
|||||||
}
|
}
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
// Propagate errors by explicitly matching them before the final case.
|
||||||
Value::Error { error } => Err(error),
|
Value::Error { error } => Err(error),
|
||||||
other => Err(ShellError::OnlySupportsThisInputType(
|
other => Ok(other.as_string()?.into_bytes()),
|
||||||
"string, binary or list".into(),
|
|
||||||
other.get_type().to_string(),
|
|
||||||
span,
|
|
||||||
// This line requires the Value::Error match above.
|
|
||||||
other.expect_span(),
|
|
||||||
)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -332,10 +349,29 @@ fn stream_to_file(
|
|||||||
mut stream: RawStream,
|
mut stream: RawStream,
|
||||||
file: File,
|
file: File,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
progress: bool,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let mut writer = BufWriter::new(file);
|
let mut writer = BufWriter::new(file);
|
||||||
|
|
||||||
stream
|
let mut bytes_processed: u64 = 0;
|
||||||
|
let bytes_processed_p = &mut bytes_processed;
|
||||||
|
let file_total_size = stream.known_size;
|
||||||
|
let mut process_failed = false;
|
||||||
|
let process_failed_p = &mut process_failed;
|
||||||
|
|
||||||
|
// Create the progress bar
|
||||||
|
// It looks a bit messy but I am doing it this way to avoid
|
||||||
|
// creating the bar when is not needed
|
||||||
|
let (mut bar_opt, bar_opt_clone) = if progress {
|
||||||
|
let tmp_bar = progress_bar::NuProgressBar::new(file_total_size);
|
||||||
|
let tmp_bar_clone = tmp_bar.clone();
|
||||||
|
|
||||||
|
(Some(tmp_bar), Some(tmp_bar_clone))
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = stream
|
||||||
.try_for_each(move |result| {
|
.try_for_each(move |result| {
|
||||||
let buf = match result {
|
let buf = match result {
|
||||||
Ok(v) => match v {
|
Ok(v) => match v {
|
||||||
@ -353,13 +389,39 @@ fn stream_to_file(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(err) => return Err(err),
|
Err(err) => {
|
||||||
|
*process_failed_p = true;
|
||||||
|
return Err(err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// If the `progress` flag is set then
|
||||||
|
if progress {
|
||||||
|
// Update the total amount of bytes that has been saved and then print the progress bar
|
||||||
|
*bytes_processed_p += buf.len() as u64;
|
||||||
|
if let Some(bar) = &mut bar_opt {
|
||||||
|
bar.update_bar(*bytes_processed_p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(err) = writer.write(&buf) {
|
if let Err(err) = writer.write(&buf) {
|
||||||
|
*process_failed_p = true;
|
||||||
return Err(ShellError::IOError(err.to_string()));
|
return Err(ShellError::IOError(err.to_string()));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.map(|_| PipelineData::empty())
|
.map(|_| PipelineData::empty());
|
||||||
|
|
||||||
|
// If the `progress` flag is set then
|
||||||
|
if progress {
|
||||||
|
// If the process failed, stop the progress bar with an error message.
|
||||||
|
if process_failed {
|
||||||
|
if let Some(bar) = bar_opt_clone {
|
||||||
|
bar.abandoned_msg("# Error while saving #".to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// And finally return the stream result.
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user