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
|
||||
|
||||
- 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
|
||||
run: cargo fmt --all -- --check
|
||||
@ -77,7 +77,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- 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
|
||||
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
||||
@ -101,7 +101,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- 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
|
||||
run: cargo install --locked --path=. --profile ci --no-default-features
|
||||
@ -141,7 +141,7 @@ jobs:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- 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
|
||||
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
|
||||
|
||||
# Added 2022-11-29 when Windows packaging wouldn't work
|
||||
# because softprops/action-gh-release was broken
|
||||
# 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_RUSTFLAGS = ''
|
||||
# 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 2 let-env _EXTRA_ = 'msi'
|
||||
# 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
|
||||
# let-env Path = ($env.Path | append 'c:\path\to\aria2c')
|
||||
# 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 os = 'windows-latest'
|
||||
# let-env Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
|
||||
# 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
|
||||
|
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@ -70,7 +70,7 @@ jobs:
|
||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||
|
||||
- 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
|
||||
uses: hustcer/setup-nu@v3
|
||||
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
@ -12,7 +12,7 @@ jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
- uses: actions/stale@v6
|
||||
with:
|
||||
#debug-only: 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"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
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
|
||||
|
||||
@ -43,25 +43,25 @@ chrono = { version = "0.4.23", features = ["serde"] }
|
||||
crossterm = "0.24.0"
|
||||
ctrlc = "3.2.1"
|
||||
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-cli = { path="./crates/nu-cli", version = "0.74.0" }
|
||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.74.0" }
|
||||
nu-command = { path="./crates/nu-command", version = "0.74.0" }
|
||||
nu-engine = { path="./crates/nu-engine", version = "0.74.0" }
|
||||
nu-json = { path="./crates/nu-json", version = "0.74.0" }
|
||||
nu-parser = { path="./crates/nu-parser", version = "0.74.0" }
|
||||
nu-path = { path="./crates/nu-path", version = "0.74.0" }
|
||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.74.0" }
|
||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.74.0" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.74.0" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.74.0" }
|
||||
nu-table = { path = "./crates/nu-table", version = "0.74.0" }
|
||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.74.0" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.74.0" }
|
||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||
nu-cli = { path = "./crates/nu-cli", version = "0.75.0" }
|
||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.75.0" }
|
||||
nu-command = { path = "./crates/nu-command", version = "0.75.0" }
|
||||
nu-engine = { path = "./crates/nu-engine", version = "0.75.0" }
|
||||
nu-json = { path = "./crates/nu-json", version = "0.75.0" }
|
||||
nu-parser = { path = "./crates/nu-parser", version = "0.75.0" }
|
||||
nu-path = { path = "./crates/nu-path", version = "0.75.0" }
|
||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.75.0" }
|
||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.75.0" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.75.0" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.75.0" }
|
||||
nu-table = { path = "./crates/nu-table", version = "0.75.0" }
|
||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.75.0" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.75.0" }
|
||||
reedline = { version = "0.15.0", features = ["bashisms", "sqlite"] }
|
||||
|
||||
rayon = "1.5.1"
|
||||
rayon = "1.6.1"
|
||||
is_executable = "1.0.1"
|
||||
simplelog = "0.12.0"
|
||||
time = "0.3.12"
|
||||
@ -76,22 +76,29 @@ signal-hook = { version = "0.3.14", default-features = false }
|
||||
winres = "0.1"
|
||||
|
||||
[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"
|
||||
|
||||
[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"
|
||||
assert_cmd = "2.0.2"
|
||||
criterion = "0.4"
|
||||
pretty_assertions = "1.0.0"
|
||||
serial_test = "0.8.0"
|
||||
serial_test = "1.0.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"
|
||||
|
||||
[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 = ["default"]
|
||||
default = ["plugin", "which-support", "trash-support", "sqlite"]
|
||||
@ -146,13 +153,5 @@ path = "src/main.rs"
|
||||
# Run all benchmarks with `cargo bench`
|
||||
# Run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
|
||||
[[bench]]
|
||||
name = "encoder_benchmark"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "eval_benchmark"
|
||||
harness = false
|
||||
|
||||
[[bench]]
|
||||
name = "parser_benchmark"
|
||||
name = "benchmarks"
|
||||
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"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.74.0"
|
||||
version = "0.75.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path="../nu-test-support", version = "0.74.0" }
|
||||
nu-command = { path = "../nu-command", version = "0.74.0" }
|
||||
rstest = {version = "0.15.0", default-features = false}
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.75.0" }
|
||||
nu-command = { path = "../nu-command", version = "0.75.0" }
|
||||
rstest = { version = "0.15.0", default-features = false }
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.74.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.74.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.74.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.74.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.74.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.75.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.75.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.75.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.75.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.75.0" }
|
||||
nu-ansi-term = "0.46.0"
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.74.0" }
|
||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.75.0" }
|
||||
reedline = { version = "0.15.0", features = ["bashisms", "sqlite"] }
|
||||
|
||||
atty = "0.2.14"
|
||||
chrono = { default-features = false, features = ["std"], version = "0.4.23" }
|
||||
crossterm = "0.24.0"
|
||||
fancy-regex = "0.10.0"
|
||||
fancy-regex = "0.11.0"
|
||||
fuzzy-matcher = "0.3.7"
|
||||
is_executable = "1.0.1"
|
||||
once_cell = "1.16.0"
|
||||
once_cell = "1.17.0"
|
||||
log = "0.4"
|
||||
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
|
||||
miette = { version = "5.5.0", features = ["fancy-no-backtrace"] }
|
||||
percent-encoding = "2"
|
||||
sysinfo = "0.26.2"
|
||||
sysinfo = "0.27.7"
|
||||
thiserror = "1.0.31"
|
||||
|
||||
[features]
|
||||
|
@ -164,7 +164,7 @@ impl Completer for CommandCompletion {
|
||||
.flattened
|
||||
.iter()
|
||||
.rev()
|
||||
.skip_while(|x| x.0.end > pos)
|
||||
.skip_while(|x| x.0.end + offset > pos)
|
||||
.take_while(|x| {
|
||||
matches!(
|
||||
x.1,
|
||||
|
@ -104,18 +104,25 @@ impl NuCompleter {
|
||||
return Some(result);
|
||||
}
|
||||
}
|
||||
Err(err) => println!("failed to eval completer block: {}", err),
|
||||
Err(err) => println!("failed to eval completer block: {err}"),
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
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 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 initial_line = line.to_string();
|
||||
let alias_total_offset: usize = alias_offset.iter().sum();
|
||||
// new_line: vector containing all alias "translations" so if it was `ll` now is `ls -l`.
|
||||
// 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');
|
||||
let pos = offset + pos;
|
||||
let config = self.engine_state.get_config();
|
||||
@ -128,7 +135,8 @@ impl NuCompleter {
|
||||
PipelineElement::Expression(_, expr)
|
||||
| PipelineElement::Redirection(_, _, expr)
|
||||
| PipelineElement::And(_, expr)
|
||||
| PipelineElement::Or(_, expr) => {
|
||||
| PipelineElement::Or(_, expr)
|
||||
| PipelineElement::SeparateRedirection { out: (_, expr), .. } => {
|
||||
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
|
||||
let span_offset: usize = alias_offset.iter().sum();
|
||||
let mut spans: Vec<String> = vec![];
|
||||
@ -155,14 +163,22 @@ impl NuCompleter {
|
||||
most_left_variable(flat_idx, &working_set, flattened.clone());
|
||||
|
||||
// Create a new span
|
||||
let new_span = if flat_idx == 0 {
|
||||
Span::new(flat.0.start, flat.0.end - 1 - span_offset)
|
||||
} else {
|
||||
Span::new(
|
||||
flat.0.start - span_offset,
|
||||
flat.0.end - 1 - span_offset,
|
||||
)
|
||||
};
|
||||
// if flat_idx == 0
|
||||
let mut span_start = flat.0.start;
|
||||
let mut span_end = flat.0.end - 1 - span_offset;
|
||||
|
||||
if flat_idx != 0 {
|
||||
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.
|
||||
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![])),
|
||||
);
|
||||
|
||||
if offset > new_span.start {
|
||||
offset -= span_offset;
|
||||
}
|
||||
|
||||
return self.process_completion(
|
||||
&mut completer,
|
||||
&working_set,
|
||||
|
@ -74,8 +74,8 @@ impl Completer for CustomCompletion {
|
||||
let mut custom_completion_options = None;
|
||||
|
||||
// Parse result
|
||||
let suggestions = match result {
|
||||
Ok(pd) => {
|
||||
let suggestions = result
|
||||
.map(|pd| {
|
||||
let value = pd.into_value(span);
|
||||
match &value {
|
||||
Value::Record { .. } => {
|
||||
@ -132,9 +132,8 @@ impl Completer for CustomCompletion {
|
||||
Value::List { vals, .. } => map_value_completions(vals.iter(), span, offset),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
_ => vec![],
|
||||
};
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(custom_completion_options) = custom_completion_options {
|
||||
filter(&prefix, suggestions, &custom_completion_options)
|
||||
|
@ -33,14 +33,7 @@ impl Completer for DirectoryCompletion {
|
||||
_: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
||||
match d.as_string() {
|
||||
Ok(s) => s,
|
||||
Err(_) => "".to_string(),
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let cwd = self.engine_state.current_work_dir();
|
||||
let partial = String::from_utf8_lossy(&prefix).to_string();
|
||||
|
||||
// Filter only the folders
|
||||
@ -126,7 +119,7 @@ pub fn directory_completion(
|
||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||
if matches(&partial, &file_name, options) {
|
||||
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 {
|
||||
file_name.to_string()
|
||||
};
|
||||
@ -142,7 +135,7 @@ pub fn directory_completion(
|
||||
|| path.contains(' ')
|
||||
|| path.contains('#')
|
||||
{
|
||||
path = format!("`{}`", path);
|
||||
path = format!("`{path}`");
|
||||
}
|
||||
|
||||
Some((span, path))
|
||||
|
@ -58,7 +58,7 @@ impl Completer for DotNuCompletion {
|
||||
};
|
||||
|
||||
// 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
|
||||
search_dirs.push(base_dir.clone());
|
||||
|
||||
@ -70,14 +70,7 @@ impl Completer for DotNuCompletion {
|
||||
partial = base_dir_partial;
|
||||
} else {
|
||||
// Fetch the current folder
|
||||
let current_folder = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
||||
match d.as_string() {
|
||||
Ok(s) => s,
|
||||
Err(_) => "".to_string(),
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let current_folder = self.engine_state.current_work_dir();
|
||||
is_current_folder = true;
|
||||
|
||||
// Add the current folder and the lib dirs into the
|
||||
|
@ -30,14 +30,7 @@ impl Completer for FileCompletion {
|
||||
_: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<Suggestion> {
|
||||
let cwd = if let Some(d) = self.engine_state.get_env_var("PWD") {
|
||||
match d.as_string() {
|
||||
Ok(s) => s,
|
||||
Err(_) => "".to_string(),
|
||||
}
|
||||
} else {
|
||||
"".to_string()
|
||||
};
|
||||
let cwd = self.engine_state.current_work_dir();
|
||||
let prefix = String::from_utf8_lossy(&prefix).to_string();
|
||||
let output: Vec<_> = file_path_completion(span, &prefix, &cwd, options)
|
||||
.into_iter()
|
||||
@ -131,7 +124,7 @@ pub fn file_path_completion(
|
||||
let mut file_name = entry.file_name().to_string_lossy().into_owned();
|
||||
if matches(&partial, &file_name, options) {
|
||||
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 {
|
||||
file_name.to_string()
|
||||
};
|
||||
@ -149,7 +142,7 @@ pub fn file_path_completion(
|
||||
|| path.contains('(')
|
||||
|| path.contains(')')
|
||||
{
|
||||
path = format!("`{}`", path);
|
||||
path = format!("`{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
|
||||
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
|
||||
// input already includes a local folder prefix.
|
||||
let manually_entered = {
|
||||
|
@ -262,6 +262,20 @@ fn nested_suggestions(
|
||||
|
||||
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,
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
use crate::util::{eval_source, report_error};
|
||||
#[cfg(feature = "plugin")]
|
||||
use log::info;
|
||||
#[cfg(feature = "plugin")]
|
||||
use nu_parser::ParseError;
|
||||
#[cfg(feature = "plugin")]
|
||||
use nu_path::canonicalize_with;
|
||||
@ -9,6 +7,8 @@ use nu_protocol::engine::{EngineState, Stack, StateWorkingSet};
|
||||
#[cfg(feature = "plugin")]
|
||||
use nu_protocol::Spanned;
|
||||
use nu_protocol::{HistoryFileFormat, PipelineData};
|
||||
#[cfg(feature = "plugin")]
|
||||
use nu_utils::utils::perf;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
@ -24,6 +24,8 @@ pub fn read_plugin_file(
|
||||
plugin_file: Option<Spanned<String>>,
|
||||
storage_path: &str,
|
||||
) {
|
||||
let start_time = std::time::Instant::now();
|
||||
let mut plug_path = String::new();
|
||||
// Reading signatures from signature file
|
||||
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
||||
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();
|
||||
if let Some(plugin_path) = plugin_path {
|
||||
let plugin_filename = plugin_path.to_string_lossy();
|
||||
|
||||
plug_path = plugin_filename.to_string();
|
||||
if let Ok(contents) = std::fs::read(&plugin_path) {
|
||||
eval_source(
|
||||
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")]
|
||||
@ -56,13 +64,12 @@ pub fn add_plugin_file(
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
let cwd = working_set.get_cwd();
|
||||
|
||||
match canonicalize_with(&plugin_file.item, cwd) {
|
||||
Ok(path) => engine_state.plugin_signatures = Some(path),
|
||||
Err(_) => {
|
||||
if let Ok(path) = canonicalize_with(&plugin_file.item, cwd) {
|
||||
engine_state.plugin_signatures = Some(path)
|
||||
} else {
|
||||
let e = ParseError::FileNotFound(plugin_file.item, plugin_file.span);
|
||||
report_error(&working_set, &e);
|
||||
}
|
||||
}
|
||||
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
||||
// Path to store plugins signatures
|
||||
plugin_path.push(storage_path);
|
||||
|
@ -29,10 +29,7 @@ pub fn evaluate_file(
|
||||
|
||||
let cwd = current_dir(engine_state, stack)?;
|
||||
|
||||
let file_path = {
|
||||
match canonicalize_with(&path, &cwd) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
let file_path = canonicalize_with(&path, cwd).unwrap_or_else(|e| {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
report_error(
|
||||
&working_set,
|
||||
@ -42,13 +39,9 @@ pub fn evaluate_file(
|
||||
),
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
let file_path_str = match file_path.to_str() {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
let file_path_str = file_path.to_str().unwrap_or_else(|| {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
report_error(
|
||||
&working_set,
|
||||
@ -61,12 +54,11 @@ pub fn evaluate_file(
|
||||
),
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
let file = match std::fs::read(&file_path).into_diagnostic() {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
let file = std::fs::read(&file_path)
|
||||
.into_diagnostic()
|
||||
.unwrap_or_else(|e| {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
report_error(
|
||||
&working_set,
|
||||
@ -80,13 +72,21 @@ pub fn evaluate_file(
|
||||
),
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
engine_state.start_in_file(Some(file_path_str));
|
||||
|
||||
let mut parent = file_path.clone();
|
||||
parent.pop();
|
||||
let parent = file_path.parent().unwrap_or_else(|| {
|
||||
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(
|
||||
"FILE_PWD".to_string(),
|
||||
@ -121,7 +121,7 @@ pub fn evaluate_file(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn print_table_or_error(
|
||||
pub(crate) fn print_table_or_error(
|
||||
engine_state: &mut EngineState,
|
||||
stack: &mut Stack,
|
||||
mut pipeline_data: PipelineData,
|
||||
@ -137,14 +137,11 @@ pub fn print_table_or_error(
|
||||
|
||||
if let PipelineData::Value(Value::Error { error }, ..) = &pipeline_data {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
|
||||
report_error(&working_set, error);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
match engine_state.find_decl("table".as_bytes(), &[]) {
|
||||
Some(decl_id) => {
|
||||
if let Some(decl_id) = engine_state.find_decl("table".as_bytes(), &[]) {
|
||||
let command = engine_state.get_decl(decl_id);
|
||||
if command.get_block_id().is_some() {
|
||||
print_or_exit(pipeline_data, engine_state, config);
|
||||
@ -162,18 +159,14 @@ pub fn print_table_or_error(
|
||||
}
|
||||
Err(error) => {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
|
||||
report_error(&working_set, &error);
|
||||
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
} else {
|
||||
print_or_exit(pipeline_data, engine_state, config);
|
||||
}
|
||||
};
|
||||
|
||||
// Make sure everything has finished
|
||||
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);
|
||||
}
|
||||
|
||||
let mut out = item.into_string("\n", config);
|
||||
out.push('\n');
|
||||
|
||||
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{}", err));
|
||||
let out = item.into_string("\n", config) + "\n";
|
||||
let _ = stdout_write_all_and_flush(out).map_err(|err| eprintln!("{err}"));
|
||||
}
|
||||
}
|
||||
|
@ -411,10 +411,10 @@ impl DescriptionMenu {
|
||||
RESET
|
||||
)
|
||||
} else {
|
||||
format!(" {}\r\n", example)
|
||||
format!(" {example}\r\n")
|
||||
}
|
||||
} else {
|
||||
format!(" {}\r\n", example)
|
||||
format!(" {example}\r\n")
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
@ -429,7 +429,7 @@ impl DescriptionMenu {
|
||||
examples,
|
||||
)
|
||||
} 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> {
|
||||
match value {
|
||||
Value::Record { .. } => {
|
||||
let text = match value
|
||||
let text = value
|
||||
.get_data_by_key("value")
|
||||
.and_then(|val| val.as_string().ok())
|
||||
{
|
||||
Some(val) => val,
|
||||
None => "No value key".to_string(),
|
||||
};
|
||||
.unwrap_or_else(|| "No value key".to_string());
|
||||
|
||||
let description = value
|
||||
.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))
|
||||
.collect(),
|
||||
_ => vec![Suggestion {
|
||||
value: format!("Not a record: {:?}", value),
|
||||
value: format!("Not a record: {value:?}"),
|
||||
description: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
|
@ -35,7 +35,7 @@ impl Command for NuHighlight {
|
||||
let head = call.head;
|
||||
|
||||
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 highlighter = crate::NuHighlighter {
|
||||
|
@ -91,7 +91,7 @@ impl NushellPrompt {
|
||||
}
|
||||
|
||||
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 {
|
||||
prompt_string.replace('\n', "\r\n").into()
|
||||
} else {
|
||||
let default = DefaultPrompt::new();
|
||||
let default = DefaultPrompt::default();
|
||||
default
|
||||
.render_prompt_left()
|
||||
.to_string()
|
||||
@ -118,7 +118,7 @@ impl Prompt for NushellPrompt {
|
||||
if let Some(prompt_string) = &self.right_prompt_string {
|
||||
prompt_string.replace('\n', "\r\n").into()
|
||||
} else {
|
||||
let default = DefaultPrompt::new();
|
||||
let default = DefaultPrompt::default();
|
||||
default
|
||||
.render_prompt_right()
|
||||
.to_string()
|
||||
@ -130,32 +130,36 @@ impl Prompt for NushellPrompt {
|
||||
fn render_prompt_indicator(&self, edit_mode: PromptEditMode) -> Cow<str> {
|
||||
match edit_mode {
|
||||
PromptEditMode::Default => match &self.default_prompt_indicator {
|
||||
Some(indicator) => indicator.as_str().into(),
|
||||
None => "〉".into(),
|
||||
},
|
||||
Some(indicator) => indicator,
|
||||
None => "〉",
|
||||
}
|
||||
.into(),
|
||||
PromptEditMode::Emacs => match &self.default_prompt_indicator {
|
||||
Some(indicator) => indicator.as_str().into(),
|
||||
None => "〉".into(),
|
||||
},
|
||||
Some(indicator) => indicator,
|
||||
None => "〉",
|
||||
}
|
||||
.into(),
|
||||
PromptEditMode::Vi(vi_mode) => match vi_mode {
|
||||
PromptViMode::Normal => match &self.default_vi_normal_prompt_indicator {
|
||||
Some(indicator) => indicator.as_str().into(),
|
||||
None => ": ".into(),
|
||||
Some(indicator) => indicator,
|
||||
None => ": ",
|
||||
},
|
||||
PromptViMode::Insert => match &self.default_vi_insert_prompt_indicator {
|
||||
Some(indicator) => indicator.as_str().into(),
|
||||
None => "〉".into(),
|
||||
},
|
||||
Some(indicator) => indicator,
|
||||
None => "〉",
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
PromptEditMode::Custom(str) => self.default_wrapped_custom_string(str).into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
||||
match &self.default_multiline_indicator {
|
||||
Some(indicator) => indicator.as_str().into(),
|
||||
None => "::: ".into(),
|
||||
Some(indicator) => indicator,
|
||||
None => "::: ",
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn render_prompt_history_search_indicator(
|
||||
|
@ -46,14 +46,12 @@ fn get_prompt_string(
|
||||
column!()
|
||||
);
|
||||
|
||||
match ret_val {
|
||||
Ok(ret_val) => Some(ret_val),
|
||||
Err(err) => {
|
||||
ret_val
|
||||
.map_err(|err| {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
report_error(&working_set, &err);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
Value::Block { val: block_id, .. } => {
|
||||
let block = engine_state.get_block(block_id);
|
||||
@ -66,14 +64,12 @@ fn get_prompt_string(
|
||||
column!()
|
||||
);
|
||||
|
||||
match ret_val {
|
||||
Ok(ret_val) => Some(ret_val),
|
||||
Err(err) => {
|
||||
ret_val
|
||||
.map_err(|err| {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
report_error(&working_set, &err);
|
||||
None
|
||||
}
|
||||
}
|
||||
})
|
||||
.ok()
|
||||
}
|
||||
Value::String { .. } => Some(PipelineData::Value(v.clone(), None)),
|
||||
_ => None,
|
||||
@ -81,8 +77,7 @@ fn get_prompt_string(
|
||||
.and_then(|pipeline_data| {
|
||||
let output = pipeline_data.collect_string("", config).ok();
|
||||
|
||||
match output {
|
||||
Some(mut x) => {
|
||||
output.map(|mut x| {
|
||||
// Just remove the very last newline.
|
||||
if x.ends_with('\n') {
|
||||
x.pop();
|
||||
@ -91,10 +86,8 @@ fn get_prompt_string(
|
||||
if x.ends_with('\r') {
|
||||
x.pop();
|
||||
}
|
||||
Some(x)
|
||||
}
|
||||
None => None,
|
||||
}
|
||||
x
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@ -111,12 +104,12 @@ pub(crate) fn update_prompt<'prompt>(
|
||||
// Now that we have the prompt string lets ansify it.
|
||||
// <133 A><prompt><133 B><command><133 C><command output>
|
||||
let left_prompt_string = if config.shell_integration {
|
||||
match left_prompt_string {
|
||||
Some(prompt_string) => Some(format!(
|
||||
"{}{}{}",
|
||||
PRE_PROMPT_MARKER, prompt_string, POST_PROMPT_MARKER
|
||||
)),
|
||||
None => left_prompt_string,
|
||||
if let Some(prompt_string) = left_prompt_string {
|
||||
Some(format!(
|
||||
"{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}"
|
||||
))
|
||||
} else {
|
||||
left_prompt_string
|
||||
}
|
||||
} else {
|
||||
left_prompt_string
|
||||
|
@ -651,14 +651,15 @@ fn add_parsed_keybinding(
|
||||
let pos1 = char_iter.next();
|
||||
let pos2 = char_iter.next();
|
||||
|
||||
let char = match (pos1, pos2) {
|
||||
(Some(char), None) => Ok(char),
|
||||
_ => Err(ShellError::UnsupportedConfigValue(
|
||||
let char = if let (Some(char), None) = (pos1, pos2) {
|
||||
char
|
||||
} else {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
"char_<CHAR: unicode codepoint>".to_string(),
|
||||
c.to_string(),
|
||||
keybinding.keycode.span()?,
|
||||
)),
|
||||
}?;
|
||||
));
|
||||
};
|
||||
|
||||
KeyCode::Char(char)
|
||||
}
|
||||
@ -682,7 +683,7 @@ fn add_parsed_keybinding(
|
||||
.filter(|num| matches!(num, 1..=20))
|
||||
.ok_or(ShellError::UnsupportedConfigValue(
|
||||
"(f1|f2|...|f20)".to_string(),
|
||||
format!("unknown function key: {}", c),
|
||||
format!("unknown function key: {c}"),
|
||||
keybinding.keycode.span()?,
|
||||
))?;
|
||||
KeyCode::F(fn_num)
|
||||
|
@ -5,18 +5,21 @@ use crate::{
|
||||
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
|
||||
NuHighlighter, NuValidator, NushellPrompt,
|
||||
};
|
||||
use log::{info, trace, warn};
|
||||
use crossterm::cursor::CursorShape;
|
||||
use log::{trace, warn};
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use nu_color_config::StyleComputer;
|
||||
use nu_engine::{convert_env_values, eval_block, eval_block_with_early_return};
|
||||
use nu_parser::{lex, parse, trim_quotes_str};
|
||||
use nu_protocol::{
|
||||
ast::PathMember,
|
||||
config::NuCursorShape,
|
||||
engine::{EngineState, ReplOperation, Stack, StateWorkingSet},
|
||||
format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span,
|
||||
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::{
|
||||
io::{self, Write},
|
||||
sync::atomic::Ordering,
|
||||
@ -39,6 +42,7 @@ pub fn evaluate_repl(
|
||||
stack: &mut Stack,
|
||||
nushell_path: &str,
|
||||
prerun_command: Option<Spanned<String>>,
|
||||
entire_start_time: Instant,
|
||||
) -> Result<()> {
|
||||
use reedline::{FileBackedHistory, Reedline, Signal};
|
||||
|
||||
@ -56,18 +60,19 @@ pub fn evaluate_repl(
|
||||
|
||||
let mut nu_prompt = NushellPrompt::new();
|
||||
|
||||
info!(
|
||||
"translate environment vars {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
// Translate environment variables from Strings to Values
|
||||
if let Some(e) = convert_env_values(engine_state, stack) {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
report_error(&working_set, &e);
|
||||
}
|
||||
perf(
|
||||
"translate env vars",
|
||||
start_time,
|
||||
file!(),
|
||||
line!(),
|
||||
column!(),
|
||||
);
|
||||
|
||||
// seed env vars
|
||||
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()));
|
||||
|
||||
info!(
|
||||
"load config initially {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
|
||||
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
let mut start_time = std::time::Instant::now();
|
||||
let mut line_editor = Reedline::create();
|
||||
|
||||
// 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() {
|
||||
Some(id) => i64::from(id),
|
||||
None => 0,
|
||||
};
|
||||
let hist_sesh = line_editor
|
||||
.get_history_session_id()
|
||||
.map(i64::from)
|
||||
.unwrap_or(0);
|
||||
engine_state.history_session_id = hist_sesh;
|
||||
perf("setup reedline", start_time, file!(), line!(), column!());
|
||||
|
||||
let config = engine_state.get_config();
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
let history_path = crate::config_files::get_history_path(
|
||||
nushell_path,
|
||||
engine_state.config.history_file_format,
|
||||
);
|
||||
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 {
|
||||
HistoryFileFormat::PlainText => Box::new(
|
||||
FileBackedHistory::with_file(
|
||||
@ -118,7 +115,9 @@ pub fn evaluate_repl(
|
||||
};
|
||||
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 show_banner = config.show_banner;
|
||||
@ -126,61 +125,89 @@ pub fn evaluate_repl(
|
||||
if show_banner {
|
||||
let banner = get_banner(engine_state, stack);
|
||||
if use_ansi {
|
||||
println!("{}", banner);
|
||||
println!("{banner}");
|
||||
} else {
|
||||
println!("{}", nu_utils::strip_ansi_string_likely(banner));
|
||||
}
|
||||
}
|
||||
perf(
|
||||
"get sysinfo/show banner",
|
||||
start_time,
|
||||
file!(),
|
||||
line!(),
|
||||
column!(),
|
||||
);
|
||||
|
||||
if let Some(s) = prerun_command {
|
||||
eval_source(
|
||||
engine_state,
|
||||
stack,
|
||||
s.item.as_bytes(),
|
||||
&format!("entry #{}", entry_num),
|
||||
&format!("entry #{entry_num}"),
|
||||
PipelineData::empty(),
|
||||
);
|
||||
engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?;
|
||||
}
|
||||
|
||||
loop {
|
||||
info!(
|
||||
"load config each loop {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
let loop_start_time = std::time::Instant::now();
|
||||
|
||||
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
|
||||
// permanent state.
|
||||
if let Err(err) = engine_state.merge_env(stack, cwd) {
|
||||
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
|
||||
if let Some(ctrlc) = &mut engine_state.ctrlc {
|
||||
ctrlc.store(false, Ordering::SeqCst);
|
||||
}
|
||||
perf("reset ctrlc", start_time, file!(), line!(), column!());
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
// Reset the SIGQUIT handler
|
||||
if let Some(sig_quit) = engine_state.get_sig_quit() {
|
||||
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();
|
||||
|
||||
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
info!("update reedline {}:{}:{}", file!(), line!(), column!());
|
||||
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
|
||||
.with_highlighter(Box::new(NuHighlighter {
|
||||
engine_state: engine_state.clone(),
|
||||
engine_state: engine_reference.clone(),
|
||||
config: config.clone(),
|
||||
}))
|
||||
.with_validator(Box::new(NuValidator {
|
||||
engine_state: engine_state.clone(),
|
||||
engine_state: engine_reference.clone(),
|
||||
}))
|
||||
.with_completer(Box::new(NuCompleter::new(
|
||||
engine_reference.clone(),
|
||||
@ -188,10 +215,13 @@ pub fn evaluate_repl(
|
||||
)))
|
||||
.with_quick_completions(config.quick_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);
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
line_editor = if config.use_ansi_coloring {
|
||||
line_editor.with_hinter(Box::new({
|
||||
// As of Nov 2022, "hints" color_config closures only get `null` passed in.
|
||||
@ -201,16 +231,23 @@ pub fn evaluate_repl(
|
||||
} else {
|
||||
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) {
|
||||
Ok(line_editor) => line_editor,
|
||||
Err(e) => {
|
||||
start_time = std::time::Instant::now();
|
||||
line_editor = add_menus(line_editor, engine_reference, stack, config).unwrap_or_else(|e| {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
report_error(&working_set, &e);
|
||||
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() {
|
||||
Some(config.buffer_editor.clone())
|
||||
} else {
|
||||
@ -231,17 +268,23 @@ pub fn evaluate_repl(
|
||||
} else {
|
||||
line_editor
|
||||
};
|
||||
perf(
|
||||
"reedline buffer_editor",
|
||||
start_time,
|
||||
file!(),
|
||||
line!(),
|
||||
column!(),
|
||||
);
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
if config.sync_history_on_enter {
|
||||
info!("sync history {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
if let Err(e) = line_editor.sync_history() {
|
||||
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
|
||||
line_editor = match create_keybindings(config) {
|
||||
Ok(keybindings) => match keybindings {
|
||||
@ -263,9 +306,9 @@ pub fn evaluate_repl(
|
||||
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,
|
||||
// fire the "pre_prompt" hook
|
||||
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
||||
@ -273,7 +316,9 @@ pub fn evaluate_repl(
|
||||
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
|
||||
// fire the "env_change" hook
|
||||
let config = engine_state.get_config();
|
||||
@ -282,19 +327,23 @@ pub fn evaluate_repl(
|
||||
{
|
||||
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 prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
|
||||
perf("update_prompt", start_time, file!(), line!(), column!());
|
||||
|
||||
entry_num += 1;
|
||||
|
||||
info!(
|
||||
"finished setup, starting repl {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
if entry_num == 1 && show_banner {
|
||||
println!(
|
||||
"Startup Time: {}",
|
||||
format_duration(entire_start_time.elapsed().as_nanos() as i64)
|
||||
);
|
||||
}
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
let input = line_editor.read_line(prompt);
|
||||
let shell_integration = config.shell_integration;
|
||||
|
||||
@ -421,7 +470,7 @@ pub fn evaluate_repl(
|
||||
engine_state,
|
||||
stack,
|
||||
s.as_bytes(),
|
||||
&format!("entry #{}", entry_num),
|
||||
&format!("entry #{entry_num}"),
|
||||
PipelineData::empty(),
|
||||
);
|
||||
}
|
||||
@ -479,7 +528,7 @@ pub fn evaluate_repl(
|
||||
// ESC]0;stringBEL -- Set icon name and window title to string
|
||||
// ESC]1;stringBEL -- Set icon name 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)?;
|
||||
}
|
||||
@ -519,7 +568,7 @@ pub fn evaluate_repl(
|
||||
Err(err) => {
|
||||
let message = err.to_string();
|
||||
if !message.contains("duration") {
|
||||
eprintln!("Error: {:?}", err);
|
||||
eprintln!("Error: {err:?}");
|
||||
// TODO: Identify possible error cases where a hard failure is preferable
|
||||
// Ignoring and reporting could hide bigger problems
|
||||
// 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(())
|
||||
}
|
||||
|
||||
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 {
|
||||
let age = match eval_string_with_input(
|
||||
engine_state,
|
||||
@ -558,15 +630,7 @@ Our {}Documentation{} is located at {}http://nushell.sh{}
|
||||
{}Tweet{} us at {}@nu_shell{}
|
||||
|
||||
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 2
|
||||
@ -598,10 +662,6 @@ let-env config = {{
|
||||
"\x1b[32m", //before Nushell
|
||||
"\x1b[0m", //after Nushell
|
||||
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
|
||||
);
|
||||
|
||||
@ -960,9 +1020,9 @@ fn run_hook_block(
|
||||
}
|
||||
}
|
||||
|
||||
match eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)
|
||||
{
|
||||
Ok(pipeline_data) => {
|
||||
let pipeline_data =
|
||||
eval_block_with_early_return(engine_state, &mut callee_stack, block, input, false, false)?;
|
||||
|
||||
if let PipelineData::Value(Value::Error { error }, _) = pipeline_data {
|
||||
return Err(error);
|
||||
}
|
||||
@ -983,24 +1043,18 @@ fn run_hook_block(
|
||||
stack.add_env_var(var, value);
|
||||
}
|
||||
Ok(pipeline_data)
|
||||
}
|
||||
Err(err) => Err(err),
|
||||
}
|
||||
}
|
||||
|
||||
fn run_ansi_sequence(seq: &str) -> Result<(), ShellError> {
|
||||
match io::stdout().write_all(seq.as_bytes()) {
|
||||
Ok(it) => it,
|
||||
Err(err) => {
|
||||
return Err(ShellError::GenericError(
|
||||
io::stdout().write_all(seq.as_bytes()).map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Error writing ansi sequence".into(),
|
||||
err.to_string(),
|
||||
e.to_string(),
|
||||
Some(Span::unknown()),
|
||||
None,
|
||||
Vec::new(),
|
||||
));
|
||||
}
|
||||
};
|
||||
)
|
||||
})?;
|
||||
io::stdout().flush().map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"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::{Config, Span};
|
||||
use reedline::{Highlighter, StyledText};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct NuHighlighter {
|
||||
pub engine_state: EngineState,
|
||||
pub engine_state: Arc<EngineState>,
|
||||
pub config: Config,
|
||||
}
|
||||
|
||||
@ -233,7 +234,8 @@ fn find_matching_block_end_in_block(
|
||||
PipelineElement::Expression(_, e)
|
||||
| PipelineElement::Redirection(_, _, e)
|
||||
| PipelineElement::And(_, e)
|
||||
| PipelineElement::Or(_, e) => {
|
||||
| PipelineElement::Or(_, e)
|
||||
| PipelineElement::SeparateRedirection { out: (_, e), .. } => {
|
||||
if e.span.contains(global_cursor_offset) {
|
||||
if let Some(pos) = find_matching_block_end_in_expr(
|
||||
line,
|
||||
|
@ -9,6 +9,7 @@ use nu_protocol::{
|
||||
};
|
||||
#[cfg(windows)]
|
||||
use nu_utils::enable_vt_processing;
|
||||
use nu_utils::utils::perf;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
// This will collect environment variables from std::env and adds them to a stack.
|
||||
@ -43,7 +44,7 @@ fn gather_env_vars(
|
||||
report_error(
|
||||
&working_set,
|
||||
&ShellError::GenericError(
|
||||
format!("Environment variable was not captured: {}", env_str),
|
||||
format!("Environment variable was not captured: {env_str}"),
|
||||
"".to_string(),
|
||||
None,
|
||||
Some(msg.into()),
|
||||
@ -79,8 +80,7 @@ fn gather_env_vars(
|
||||
"".to_string(),
|
||||
None,
|
||||
Some(format!(
|
||||
"Retrieving current directory failed: {:?} not a valid utf-8 path",
|
||||
init_cwd
|
||||
"Retrieving current directory failed: {init_cwd:?} not a valid utf-8 path"
|
||||
)),
|
||||
Vec::new(),
|
||||
),
|
||||
@ -204,6 +204,8 @@ pub fn eval_source(
|
||||
fname: &str,
|
||||
input: PipelineData,
|
||||
) -> bool {
|
||||
let start_time = std::time::Instant::now();
|
||||
|
||||
let (block, delta) = {
|
||||
let mut working_set = StateWorkingSet::new(engine_state);
|
||||
let (output, err) = parse(
|
||||
@ -282,6 +284,13 @@ pub fn eval_source(
|
||||
return false;
|
||||
}
|
||||
}
|
||||
perf(
|
||||
&format!("eval_source {}", &fname),
|
||||
start_time,
|
||||
file!(),
|
||||
line!(),
|
||||
column!(),
|
||||
);
|
||||
|
||||
true
|
||||
}
|
||||
@ -315,27 +324,19 @@ pub fn report_error_new(
|
||||
}
|
||||
|
||||
pub fn get_init_cwd() -> PathBuf {
|
||||
match std::env::current_dir() {
|
||||
Ok(cwd) => cwd,
|
||||
Err(_) => match std::env::var("PWD") {
|
||||
Ok(cwd) => PathBuf::from(cwd),
|
||||
Err(_) => match nu_path::home_dir() {
|
||||
Some(cwd) => cwd,
|
||||
None => PathBuf::new(),
|
||||
},
|
||||
},
|
||||
}
|
||||
std::env::current_dir().unwrap_or_else(|_| {
|
||||
std::env::var("PWD")
|
||||
.map(Into::into)
|
||||
.unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
||||
match nu_engine::env::current_dir(engine_state, stack) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
nu_engine::env::current_dir(engine_state, stack).unwrap_or_else(|e| {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
report_error(&working_set, &e);
|
||||
get_init_cwd()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -1,9 +1,10 @@
|
||||
use nu_parser::{parse, ParseError};
|
||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||
use reedline::{ValidationResult, Validator};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct NuValidator {
|
||||
pub engine_state: EngineState,
|
||||
pub engine_state: Arc<EngineState>,
|
||||
}
|
||||
|
||||
impl Validator for NuValidator {
|
||||
|
@ -173,7 +173,7 @@ fn file_completions() {
|
||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||
|
||||
// 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());
|
||||
|
||||
// Create the expected values
|
||||
@ -494,7 +494,7 @@ fn folder_with_directorycompletions() {
|
||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||
|
||||
// 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());
|
||||
|
||||
// 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()];
|
||||
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"
|
||||
license = "MIT"
|
||||
name = "nu-color-config"
|
||||
version = "0.74.0"
|
||||
version = "0.75.0"
|
||||
|
||||
[dependencies]
|
||||
serde = { version="1.0.123", features=["derive"] }
|
||||
# used only for text_style Alignments
|
||||
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-utils = { path = "../nu-utils", version = "0.74.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.74.0" }
|
||||
nu-json = { path="../nu-json", version = "0.74.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.75.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.75.0" }
|
||||
nu-json = { path="../nu-json", version = "0.75.0" }
|
||||
|
||||
[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::LightGray => Some(String::from("light_gray")),
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
@ -226,10 +226,10 @@ fn test_computable_style_static() {
|
||||
let style2 = Style::default().underline();
|
||||
// Create a "dummy" style_computer for this test.
|
||||
let dummy_engine_state = EngineState::new();
|
||||
let mut dummy_stack = Stack::new();
|
||||
let dummy_stack = Stack::new();
|
||||
let style_computer = StyleComputer::new(
|
||||
&dummy_engine_state,
|
||||
&mut dummy_stack,
|
||||
&dummy_stack,
|
||||
HashMap::from([
|
||||
("string".into(), ComputableStyle::Static(style1)),
|
||||
("row_index".into(), ComputableStyle::Static(style2)),
|
||||
|
@ -5,52 +5,53 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-command"
|
||||
version = "0.74.0"
|
||||
version = "0.75.0"
|
||||
build = "build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.74.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.74.0" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.74.0" }
|
||||
nu-json = { path = "../nu-json", version = "0.74.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.74.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.74.0" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.74.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.74.0" }
|
||||
nu-system = { path = "../nu-system", version = "0.74.0" }
|
||||
nu-table = { path = "../nu-table", version = "0.74.0" }
|
||||
nu-term-grid = { path = "../nu-term-grid", version = "0.74.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.74.0" }
|
||||
nu-explore = { path = "../nu-explore", version = "0.74.0" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.75.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.75.0" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.75.0" }
|
||||
nu-json = { path = "../nu-json", version = "0.75.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.75.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.75.0" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.75.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.75.0" }
|
||||
nu-system = { path = "../nu-system", version = "0.75.0" }
|
||||
nu-table = { path = "../nu-table", version = "0.75.0" }
|
||||
nu-term-grid = { path = "../nu-term-grid", version = "0.75.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.75.0" }
|
||||
nu-explore = { path = "../nu-explore", version = "0.75.0" }
|
||||
nu-ansi-term = "0.46.0"
|
||||
num-format = { version = "0.4.3" }
|
||||
|
||||
# Potential dependencies for extras
|
||||
alphanumeric-sort = "1.4.4"
|
||||
atty = "0.2.14"
|
||||
base64 = "0.13.0"
|
||||
base64 = "0.21.0"
|
||||
byteorder = "1.4.3"
|
||||
bytesize = "1.1.0"
|
||||
calamine = "0.19.1"
|
||||
chrono = { version = "0.4.23", features = ["unstable-locales", "std"], default-features = false }
|
||||
chrono-humanize = "0.2.1"
|
||||
chrono-tz = "0.6.3"
|
||||
chrono-tz = "0.8.1"
|
||||
crossterm = "0.24.0"
|
||||
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" }
|
||||
dtparse = "1.2.0"
|
||||
eml-parser = "0.1.0"
|
||||
encoding_rs = "0.8.30"
|
||||
fancy-regex = "0.10.0"
|
||||
fancy-regex = "0.11.0"
|
||||
filesize = "0.2.0"
|
||||
filetime = "0.2.15"
|
||||
fs_extra = "1.2.0"
|
||||
htmlescape = "0.3.1"
|
||||
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"
|
||||
is-root = "0.1.2"
|
||||
itertools = "0.10.0"
|
||||
@ -62,26 +63,26 @@ mime_guess = "2.0.4"
|
||||
notify = "4.0.17"
|
||||
num = { version = "0.4.0", optional = true }
|
||||
num-traits = "0.2.14"
|
||||
once_cell = "1.0"
|
||||
once_cell = "1.17"
|
||||
open = "3.2.0"
|
||||
pathdiff = "0.2.1"
|
||||
powierza-coefficient = "1.0.2"
|
||||
quick-xml = "0.25"
|
||||
quick-xml = "0.27"
|
||||
rand = "0.8"
|
||||
rayon = "1.5.1"
|
||||
regex = "1.6.0"
|
||||
reqwest = {version = "0.11", features = ["blocking", "json"] }
|
||||
roxmltree = "0.16.0"
|
||||
rayon = "1.6.1"
|
||||
regex = "1.7.1"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
roxmltree = "0.17.0"
|
||||
rust-embed = "6.3.0"
|
||||
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_urlencoded = "0.7.0"
|
||||
serde_yaml = "0.9.4"
|
||||
sha2 = "0.10.0"
|
||||
# Disable default features b/c the default features build Git (very slow to compile)
|
||||
shadow-rs = { version = "0.16.1", default-features = false }
|
||||
sysinfo = "0.26.2"
|
||||
shadow-rs = { version = "0.20.0", default-features = false }
|
||||
sysinfo = "0.27.7"
|
||||
terminal_size = "0.2.1"
|
||||
thiserror = "1.0.31"
|
||||
titlecase = "2.0.0"
|
||||
@ -89,12 +90,12 @@ toml = "0.5.8"
|
||||
unicode-segmentation = "1.8.0"
|
||||
url = "2.2.1"
|
||||
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 }
|
||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||
reedline = { version = "0.15.0", features = ["bashisms", "sqlite"] }
|
||||
wax = { version = "0.5.0" }
|
||||
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]
|
||||
winreg = "0.10.1"
|
||||
@ -105,11 +106,11 @@ users = "0.11.0"
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
||||
version = "3.0.0"
|
||||
version = "3.0.1"
|
||||
optional = true
|
||||
|
||||
[dependencies.polars]
|
||||
version = "0.25.0"
|
||||
version = "0.26.1"
|
||||
optional = true
|
||||
features = [
|
||||
"arg_where",
|
||||
@ -140,12 +141,8 @@ features = [
|
||||
]
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.43.0"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_SystemServices",
|
||||
]
|
||||
version = "0.44.0"
|
||||
features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_SystemServices"]
|
||||
|
||||
[features]
|
||||
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?
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = { version = "0.16.1", default-features = false }
|
||||
shadow-rs = { version = "0.20.0", default-features = false }
|
||||
|
||||
[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"
|
||||
dirs-next = "2.0.0"
|
||||
proptest = "1.0.0"
|
||||
quickcheck = "1.0.3"
|
||||
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,
|
||||
// because shadow_rs does it in a really slow-to-compile way (it builds libgit2)
|
||||
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()
|
||||
}
|
||||
|
@ -106,8 +106,7 @@ where
|
||||
error: ShellError::GenericError(
|
||||
"Rotate left result beyond the range of 64 bit signed number".to_string(),
|
||||
format!(
|
||||
"{} of the specified number of bytes rotate left {} bits exceed limit",
|
||||
val, bits
|
||||
"{val} of the specified number of bytes rotate left {bits} bits exceed limit"
|
||||
),
|
||||
Some(span),
|
||||
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),
|
||||
SignedTwo => get_rotate_left(val as i16, 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.
|
||||
|
@ -110,8 +110,7 @@ where
|
||||
error: ShellError::GenericError(
|
||||
"Rotate right result beyond the range of 64 bit signed number".to_string(),
|
||||
format!(
|
||||
"{} of the specified number of bytes rotate right {} bits exceed limit",
|
||||
val, bits
|
||||
"{val} of the specified number of bytes rotate right {bits} bits exceed limit"
|
||||
),
|
||||
Some(span),
|
||||
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),
|
||||
SignedTwo => get_rotate_right(val as i16, 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.
|
||||
|
@ -118,8 +118,7 @@ where
|
||||
error: ShellError::GenericError(
|
||||
"Shift left result beyond the range of 64 bit signed number".to_string(),
|
||||
format!(
|
||||
"{} of the specified number of bytes shift left {} bits exceed limit",
|
||||
val, bits
|
||||
"{val} of the specified number of bytes shift left {bits} bits exceed limit"
|
||||
),
|
||||
Some(span),
|
||||
None,
|
||||
@ -131,10 +130,7 @@ where
|
||||
None => Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
"Shift left failed".to_string(),
|
||||
format!(
|
||||
"{} shift left {} bits failed, you may shift too many bits",
|
||||
val, bits
|
||||
),
|
||||
format!("{val} shift left {bits} bits failed, you may shift too many bits"),
|
||||
Some(span),
|
||||
None,
|
||||
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),
|
||||
SignedTwo => get_shift_left(val as i16, 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.
|
||||
|
@ -108,8 +108,7 @@ where
|
||||
error: ShellError::GenericError(
|
||||
"Shift right result beyond the range of 64 bit signed number".to_string(),
|
||||
format!(
|
||||
"{} of the specified number of bytes shift right {} bits exceed limit",
|
||||
val, bits
|
||||
"{val} of the specified number of bytes shift right {bits} bits exceed limit"
|
||||
),
|
||||
Some(span),
|
||||
None,
|
||||
@ -121,10 +120,7 @@ where
|
||||
None => Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
"Shift right failed".to_string(),
|
||||
format!(
|
||||
"{} shift right {} bits failed, you may shift too many bits",
|
||||
val, bits
|
||||
),
|
||||
format!("{val} shift right {bits} bits failed, you may shift too many bits"),
|
||||
Some(span),
|
||||
None,
|
||||
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),
|
||||
SignedTwo => get_shift_right(val as i16, 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.
|
||||
|
@ -70,8 +70,8 @@ fn parse_range(range: Value, head: Span) -> Result<(isize, isize, Span), ShellEr
|
||||
}
|
||||
}
|
||||
Value::String { val, span } => {
|
||||
let splitted_result = val.split_once(',');
|
||||
match splitted_result {
|
||||
let split_result = val.split_once(',');
|
||||
match split_result {
|
||||
Some((start, end)) => (start.to_string(), end.to_string(), span),
|
||||
None => {
|
||||
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 == "_" {
|
||||
0
|
||||
} else {
|
||||
match start.trim().parse() {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
start.trim().parse().map_err(|_| {
|
||||
ShellError::UnsupportedInput(
|
||||
"could not perform subbytes".to_string(),
|
||||
"with this range".to_string(),
|
||||
head,
|
||||
span,
|
||||
))
|
||||
}
|
||||
}
|
||||
)
|
||||
})?
|
||||
};
|
||||
let end: isize = if end.is_empty() || end == "_" {
|
||||
isize::max_value()
|
||||
} else {
|
||||
match end.trim().parse() {
|
||||
Ok(s) => s,
|
||||
Err(_) => {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
end.trim().parse().map_err(|_| {
|
||||
ShellError::UnsupportedInput(
|
||||
"could not perform subbytes".to_string(),
|
||||
"with this range".to_string(),
|
||||
head,
|
||||
span,
|
||||
))
|
||||
}
|
||||
}
|
||||
)
|
||||
})?
|
||||
};
|
||||
Ok((start, end, span))
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
|
||||
// Note:
|
||||
// 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 {
|
||||
let (mut left, mut right) = (
|
||||
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;
|
||||
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.
|
||||
let mut remain = input[..left as usize].iter().copied().rev().collect();
|
||||
result.append(&mut remain);
|
||||
|
@ -129,18 +129,15 @@ impl Command for Histogram {
|
||||
let span = call.head;
|
||||
let data_as_value = input.into_value(span);
|
||||
// `input` is not a list, here we can return an error.
|
||||
match data_as_value.as_list() {
|
||||
Ok(list_value) => run_histogram(
|
||||
list_value.to_vec(),
|
||||
run_histogram(
|
||||
data_as_value.as_list()?.to_vec(),
|
||||
column_name,
|
||||
frequency_column_name,
|
||||
calc_method,
|
||||
span,
|
||||
// Note that as_list() filters out Value::Error here.
|
||||
data_as_value.expect_span(),
|
||||
),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -169,7 +166,7 @@ fn run_histogram(
|
||||
ShellError::UnsupportedInput(
|
||||
"Since --column-name was not provided, only lists of hashable values are supported.".to_string(),
|
||||
format!(
|
||||
"input type: {:?}", t
|
||||
"input type: {t:?}"
|
||||
),
|
||||
head_span,
|
||||
span,
|
||||
|
@ -103,31 +103,31 @@ fn fmt_it(num: i64, span: Span) -> Value {
|
||||
let mut vals = vec![];
|
||||
|
||||
cols.push("binary".into());
|
||||
vals.push(Value::string(format!("{:#b}", num), span));
|
||||
vals.push(Value::string(format!("{num:#b}"), span));
|
||||
|
||||
cols.push("debug".into());
|
||||
vals.push(Value::string(format!("{:#?}", num), span));
|
||||
vals.push(Value::string(format!("{num:#?}"), span));
|
||||
|
||||
cols.push("display".into());
|
||||
vals.push(Value::string(format!("{}", num), span));
|
||||
vals.push(Value::string(format!("{num}"), span));
|
||||
|
||||
cols.push("lowerexp".into());
|
||||
vals.push(Value::string(format!("{:#e}", num), span));
|
||||
vals.push(Value::string(format!("{num:#e}"), span));
|
||||
|
||||
cols.push("lowerhex".into());
|
||||
vals.push(Value::string(format!("{:#x}", num), span));
|
||||
vals.push(Value::string(format!("{num:#x}"), span));
|
||||
|
||||
cols.push("octal".into());
|
||||
vals.push(Value::string(format!("{:#o}", num), span));
|
||||
vals.push(Value::string(format!("{num:#o}"), span));
|
||||
|
||||
// cols.push("pointer".into());
|
||||
// vals.push(Value::string(format!("{:#p}", &num), span));
|
||||
|
||||
cols.push("upperexp".into());
|
||||
vals.push(Value::string(format!("{:#E}", num), span));
|
||||
vals.push(Value::string(format!("{num:#E}"), span));
|
||||
|
||||
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 }
|
||||
}
|
||||
|
@ -232,7 +232,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
return Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
"timestamp is out of range; it should between -8e+12 and 8e+12".to_string(),
|
||||
format!("timestamp is {:?}", ts),
|
||||
format!("timestamp is {ts:?}"),
|
||||
head,
|
||||
// Again, can safely unwrap this from here on
|
||||
input.expect_span(),
|
||||
|
@ -367,8 +367,7 @@ fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> {
|
||||
"string".to_string(),
|
||||
span,
|
||||
Some(format!(
|
||||
r#"string "{}" does not represent a valid integer"#,
|
||||
trimmed
|
||||
r#"string "{trimmed}" does not represent a valid integer"#
|
||||
)),
|
||||
)),
|
||||
},
|
||||
|
@ -206,7 +206,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
if decimals {
|
||||
let decimal_value = digits.unwrap_or(2) as usize;
|
||||
Value::String {
|
||||
val: format!("{:.*}", decimal_value, val),
|
||||
val: format!("{val:.decimal_value$}"),
|
||||
span,
|
||||
}
|
||||
} else {
|
||||
@ -234,12 +234,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
span,
|
||||
},
|
||||
Value::Error { error } => Value::String {
|
||||
val: {
|
||||
match into_code(error) {
|
||||
Some(code) => code,
|
||||
None => "".to_string(),
|
||||
}
|
||||
},
|
||||
val: into_code(error).unwrap_or_default(),
|
||||
span,
|
||||
},
|
||||
Value::Nothing { .. } => Value::String {
|
||||
|
@ -41,7 +41,7 @@ impl Command for Ast {
|
||||
let mut working_set = StateWorkingSet::new(engine_state);
|
||||
|
||||
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())
|
||||
}
|
||||
|
@ -1,8 +1,11 @@
|
||||
use std::thread;
|
||||
|
||||
use nu_engine::{eval_block_with_early_return, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Closure, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Type, Value,
|
||||
Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, SyntaxShape,
|
||||
Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -106,7 +109,7 @@ impl Command for Do {
|
||||
block,
|
||||
input,
|
||||
call.redirect_stdout,
|
||||
capture_errors || ignore_shell_errors || ignore_program_errors,
|
||||
call.redirect_stdout,
|
||||
);
|
||||
|
||||
match result {
|
||||
@ -118,6 +121,58 @@ impl Command for Do {
|
||||
metadata,
|
||||
trim_end_newline,
|
||||
}) 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 exit_code: Vec<Value> = match exit_code {
|
||||
None => vec![],
|
||||
@ -128,11 +183,6 @@ impl Command for Do {
|
||||
};
|
||||
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
|
||||
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(
|
||||
"External command failed".to_string(),
|
||||
stderr_msg,
|
||||
@ -143,7 +193,12 @@ impl Command for Do {
|
||||
|
||||
Ok(PipelineData::ExternalStream {
|
||||
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.into_iter(),
|
||||
exit_code_ctrlc,
|
||||
@ -168,10 +223,9 @@ impl Command for Do {
|
||||
metadata,
|
||||
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())
|
||||
}
|
||||
Err(_) if ignore_shell_errors => Ok(PipelineData::empty()),
|
||||
r => r,
|
||||
}
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ pub fn highlight_search_string(
|
||||
needle: &str,
|
||||
string_style: &Style,
|
||||
) -> Result<String, ShellError> {
|
||||
let regex_string = format!("(?i){}", needle);
|
||||
let regex_string = format!("(?i){needle}");
|
||||
let regex = match Regex::new(®ex_string) {
|
||||
Ok(regex) => regex,
|
||||
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 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 usage = sig.usage;
|
||||
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());
|
||||
vals.push(if search_terms.is_empty() {
|
||||
Value::nothing(span)
|
||||
} else {
|
||||
Value::String {
|
||||
vals.push(Value::String {
|
||||
val: search_terms.join(", "),
|
||||
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("\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 mut module_commands: Vec<(&[u8], DeclId)> = module
|
||||
.decls
|
||||
.iter()
|
||||
.map(|(name, id)| (name.as_ref(), *id))
|
||||
.collect();
|
||||
module_commands.sort_by(|a, b| a.0.cmp(b.0));
|
||||
let mut module_commands = module.decls();
|
||||
module_commands.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
let commands_str = module_commands
|
||||
.iter()
|
||||
|
@ -123,7 +123,6 @@ pub fn version(
|
||||
// Get a list of command names and check for plugins
|
||||
let installed_plugins = engine_state
|
||||
.plugin_decls()
|
||||
.into_iter()
|
||||
.filter(|x| x.is_plugin().is_some())
|
||||
.map(|x| x.name())
|
||||
.collect::<Vec<_>>();
|
||||
|
@ -117,7 +117,7 @@ fn action(
|
||||
|
||||
let table_columns_creation = columns
|
||||
.iter()
|
||||
.map(|(name, sql_type)| format!("{} {}", name, sql_type))
|
||||
.map(|(name, sql_type)| format!("{name} {sql_type}"))
|
||||
.join(",");
|
||||
|
||||
// get the values
|
||||
@ -156,10 +156,8 @@ fn action(
|
||||
let conn = open_sqlite_db(Path::new(&file.item), file.span)?;
|
||||
|
||||
// create a string for sql table creation
|
||||
let create_statement = format!(
|
||||
"CREATE TABLE IF NOT EXISTS {} ({})",
|
||||
table_name, table_columns_creation
|
||||
);
|
||||
let create_statement =
|
||||
format!("CREATE TABLE IF NOT EXISTS {table_name} ({table_columns_creation})");
|
||||
|
||||
// prepare the string as a sqlite statement
|
||||
let mut stmt = conn.prepare(&create_statement).map_err(|e| {
|
||||
@ -191,7 +189,7 @@ fn action(
|
||||
// ('dd', 'ee', 'ff')
|
||||
|
||||
// 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
|
||||
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(), ", ")))
|
||||
.collect::<Vec<_>>()
|
||||
.join(separator),
|
||||
Value::Block { val, .. } => format!("<Block {}>", val),
|
||||
Value::Closure { val, .. } => format!("<Closure {}>", val),
|
||||
Value::LazyRecord { val, .. } => match val.collect() {
|
||||
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::Error { error } => format!("{:?}", error),
|
||||
Value::Binary { val, .. } => format!("{:?}", val),
|
||||
Value::Error { error } => format!("{error:?}"),
|
||||
Value::Binary { val, .. } => format!("{val:?}"),
|
||||
Value::CellPath { val, .. } => val.into_string(),
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
}
|
||||
|
@ -107,12 +107,7 @@ impl SQLiteDatabase {
|
||||
}
|
||||
|
||||
pub fn open_connection(&self) -> Result<Connection, rusqlite::Error> {
|
||||
let conn = match Connection::open(&self.path) {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
Ok(conn)
|
||||
Connection::open(&self.path)
|
||||
}
|
||||
|
||||
pub fn get_tables(&self, conn: &Connection) -> Result<Vec<DbTable>, rusqlite::Error> {
|
||||
@ -369,7 +364,7 @@ fn read_single_table(
|
||||
call_span: Span,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> 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)
|
||||
}
|
||||
|
||||
@ -431,7 +426,7 @@ fn read_entire_sqlite_db(
|
||||
let table_name: String = row?;
|
||||
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())?;
|
||||
tables.push(rows);
|
||||
}
|
||||
@ -582,18 +577,13 @@ mod test {
|
||||
}
|
||||
|
||||
pub fn open_connection_in_memory() -> Result<Connection, ShellError> {
|
||||
let db = match Connection::open_in_memory() {
|
||||
Ok(conn) => conn,
|
||||
Err(err) => {
|
||||
return Err(ShellError::GenericError(
|
||||
Connection::open_in_memory().map_err(|err| {
|
||||
ShellError::GenericError(
|
||||
"Failed to open SQLite connection in memory".into(),
|
||||
err.to_string(),
|
||||
Some(Span::test_data()),
|
||||
None,
|
||||
Vec::new(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
Ok(db)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ fn command(
|
||||
let df = NuDataFrame::try_from_pipeline(input, call.head)?;
|
||||
|
||||
df.as_ref()
|
||||
.select(&col_string)
|
||||
.select(col_string)
|
||||
.map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Error selecting columns".into(),
|
||||
|
@ -51,9 +51,8 @@ impl Command for ListDF {
|
||||
|
||||
let vals = vals
|
||||
.into_iter()
|
||||
.filter_map(|(name, value)| match NuDataFrame::try_from_value(value) {
|
||||
Ok(df) => Some((name, df)),
|
||||
Err(_) => None,
|
||||
.filter_map(|(name, value)| {
|
||||
NuDataFrame::try_from_value(value).ok().map(|df| (name, df))
|
||||
})
|
||||
.map(|(name, df)| {
|
||||
let name = Value::String {
|
||||
|
@ -103,14 +103,13 @@ fn command(
|
||||
|
||||
let type_id = match &type_option {
|
||||
Some(ref t) => Some((t.item.to_owned(), "Invalid type", t.span)),
|
||||
None => match file.item.extension() {
|
||||
Some(e) => Some((
|
||||
None => file.item.extension().map(|e| {
|
||||
(
|
||||
e.to_string_lossy().into_owned(),
|
||||
"Invalid extension",
|
||||
file.span,
|
||||
)),
|
||||
None => None,
|
||||
},
|
||||
)
|
||||
}),
|
||||
};
|
||||
|
||||
match type_id {
|
||||
@ -120,10 +119,7 @@ fn command(
|
||||
"ipc" | "arrow" => from_ipc(engine_state, stack, call),
|
||||
"json" => from_json(engine_state, stack, call),
|
||||
_ => Err(ShellError::FileNotFoundCustom(
|
||||
format!(
|
||||
"{}. Supported values: csv, tsv, parquet, ipc, arrow, json",
|
||||
msg
|
||||
),
|
||||
format!("{msg}. Supported values: csv, tsv, parquet, ipc, arrow, json"),
|
||||
blamed,
|
||||
)),
|
||||
},
|
||||
@ -155,7 +151,7 @@ fn from_parquet(
|
||||
.map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Parquet reader error".into(),
|
||||
format!("{:?}", e),
|
||||
format!("{e:?}"),
|
||||
Some(call.head),
|
||||
None,
|
||||
Vec::new(),
|
||||
@ -189,7 +185,7 @@ fn from_parquet(
|
||||
.map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Parquet reader error".into(),
|
||||
format!("{:?}", e),
|
||||
format!("{e:?}"),
|
||||
Some(call.head),
|
||||
None,
|
||||
Vec::new(),
|
||||
@ -220,7 +216,7 @@ fn from_ipc(
|
||||
.map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"IPC reader error".into(),
|
||||
format!("{:?}", e),
|
||||
format!("{e:?}"),
|
||||
Some(call.head),
|
||||
None,
|
||||
Vec::new(),
|
||||
@ -254,7 +250,7 @@ fn from_ipc(
|
||||
.map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"IPC reader error".into(),
|
||||
format!("{:?}", e),
|
||||
format!("{e:?}"),
|
||||
Some(call.head),
|
||||
None,
|
||||
Vec::new(),
|
||||
@ -290,7 +286,7 @@ fn from_json(
|
||||
.map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Json reader error".into(),
|
||||
format!("{:?}", e),
|
||||
format!("{e:?}"),
|
||||
Some(call.head),
|
||||
None,
|
||||
Vec::new(),
|
||||
@ -354,7 +350,7 @@ fn from_csv(
|
||||
.map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Parquet reader error".into(),
|
||||
format!("{:?}", e),
|
||||
format!("{e:?}"),
|
||||
Some(call.head),
|
||||
None,
|
||||
Vec::new(),
|
||||
@ -420,7 +416,7 @@ fn from_csv(
|
||||
.map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Parquet reader error".into(),
|
||||
format!("{:?}", e),
|
||||
format!("{e:?}"),
|
||||
Some(call.head),
|
||||
None,
|
||||
Vec::new(),
|
||||
|
@ -77,15 +77,15 @@ impl SQLContext {
|
||||
Ok(match select_item {
|
||||
SelectItem::UnnamedExpr(expr) => {
|
||||
let expr = parse_sql_expr(expr)?;
|
||||
raw_projection_before_alias.insert(format!("{:?}", expr), i);
|
||||
raw_projection_before_alias.insert(format!("{expr:?}"), i);
|
||||
expr
|
||||
}
|
||||
SelectItem::ExprWithAlias { expr, alias } => {
|
||||
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)
|
||||
}
|
||||
SelectItem::QualifiedWildcard(_) | SelectItem::Wildcard => {
|
||||
SelectItem::QualifiedWildcard(_, _) | SelectItem::Wildcard(_) => {
|
||||
contain_wildcard = true;
|
||||
col("*")
|
||||
}
|
||||
@ -133,7 +133,7 @@ impl SQLContext {
|
||||
// and its projections columns, keeping the original index
|
||||
let (exclude_expr, groupby_pos): (Vec<_>, Vec<_>) = group_by
|
||||
.iter()
|
||||
.map(|expr| raw_projection_before_alias.get(&format!("{:?}", expr)))
|
||||
.map(|expr| raw_projection_before_alias.get(&format!("{expr:?}")))
|
||||
.enumerate()
|
||||
.filter(|(_, proj_p)| proj_p.is_some())
|
||||
.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> {
|
||||
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 {
|
||||
Err(PolarsError::ComputeError(
|
||||
"One and only one statement at a time please".into(),
|
||||
@ -196,7 +196,7 @@ impl SQLContext {
|
||||
Some(SqlExpr::Value(SQLValue::Number(nrow, _))) => {
|
||||
let nrow = nrow.parse().map_err(|err| {
|
||||
PolarsError::ComputeError(
|
||||
format!("Conversion Error: {:?}", err).into(),
|
||||
format!("Conversion Error: {err:?}").into(),
|
||||
)
|
||||
})?;
|
||||
rs.limit(nrow)
|
||||
@ -211,7 +211,7 @@ impl SQLContext {
|
||||
}
|
||||
_ => {
|
||||
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::Date => DataType::Date,
|
||||
SQLDataType::Time => DataType::Time,
|
||||
SQLDataType::Timestamp => DataType::Datetime(TimeUnit::Milliseconds, None),
|
||||
SQLDataType::Time(_, _) => DataType::Time,
|
||||
SQLDataType::Timestamp(_, _) => DataType::Datetime(TimeUnit::Milliseconds, None),
|
||||
SQLDataType::Interval => DataType::Duration(TimeUnit::Milliseconds),
|
||||
SQLDataType::Array(inner_type) => {
|
||||
DataType::List(Box::new(map_sql_polars_datatype(inner_type)?))
|
||||
SQLDataType::Array(inner_type) => match 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(
|
||||
format!(
|
||||
"SQL Datatype {:?} was not supported in polars-sql yet!",
|
||||
data_type
|
||||
)
|
||||
.into(),
|
||||
format!("SQL Datatype {data_type:?} was not supported in polars-sql yet!").into(),
|
||||
))
|
||||
}
|
||||
})
|
||||
@ -70,7 +71,7 @@ fn binary_op_(left: Expr, right: Expr, op: &SQLBinaryOperator) -> Result<Expr> {
|
||||
SQLBinaryOperator::Xor => left.xor(right),
|
||||
_ => {
|
||||
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
|
||||
if s.contains('.') {
|
||||
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 {
|
||||
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),
|
||||
_ => {
|
||||
return Err(PolarsError::ComputeError(
|
||||
format!(
|
||||
"Parsing SQL Value {:?} was not supported in polars-sql yet!",
|
||||
value
|
||||
)
|
||||
.into(),
|
||||
format!("Parsing SQL Value {value:?} was not supported in polars-sql yet!").into(),
|
||||
))
|
||||
}
|
||||
})
|
||||
@ -122,11 +119,7 @@ pub fn parse_sql_expr(expr: &SqlExpr) -> Result<Expr> {
|
||||
SqlExpr::Value(value) => literal_expr(value)?,
|
||||
_ => {
|
||||
return Err(PolarsError::ComputeError(
|
||||
format!(
|
||||
"Expression: {:?} was not supported in polars-sql yet!",
|
||||
expr
|
||||
)
|
||||
.into(),
|
||||
format!("Expression: {expr:?} was not supported in polars-sql yet!").into(),
|
||||
))
|
||||
}
|
||||
})
|
||||
@ -180,8 +173,7 @@ fn parse_sql_function(sql_function: &SQLFunction) -> Result<Expr> {
|
||||
_ => {
|
||||
return Err(PolarsError::ComputeError(
|
||||
format!(
|
||||
"Function {:?} with args {:?} was not supported in polars-sql yet!",
|
||||
function_name, args
|
||||
"Function {function_name:?} with args {args:?} was not supported in polars-sql yet!"
|
||||
)
|
||||
.into(),
|
||||
))
|
||||
|
@ -185,22 +185,22 @@ fn command(
|
||||
.cast(&DataType::Float64)
|
||||
.ok()
|
||||
.and_then(|ca| match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
Ok(AnyValue::Float64(v)) => Some(v),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
let mean = match col.mean_as_series().get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
Ok(AnyValue::Float64(v)) => Some(v),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let median = match col.median_as_series().get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
Ok(AnyValue::Float64(v)) => Some(v),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let std = match col.std_as_series(0).get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
Ok(AnyValue::Float64(v)) => Some(v),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
@ -209,7 +209,7 @@ fn command(
|
||||
.cast(&DataType::Float64)
|
||||
.ok()
|
||||
.and_then(|ca| match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
Ok(AnyValue::Float64(v)) => Some(v),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
@ -221,7 +221,7 @@ fn command(
|
||||
.ok()
|
||||
.and_then(|ca| ca.cast(&DataType::Float64).ok())
|
||||
.and_then(|ca| match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
Ok(AnyValue::Float64(v)) => Some(v),
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
@ -232,7 +232,7 @@ fn command(
|
||||
.cast(&DataType::Float64)
|
||||
.ok()
|
||||
.and_then(|ca| match ca.get(0) {
|
||||
AnyValue::Float64(v) => Some(v),
|
||||
Ok(AnyValue::Float64(v)) => Some(v),
|
||||
_ => None,
|
||||
});
|
||||
|
||||
|
@ -59,6 +59,10 @@ impl Command for ExprAlias {
|
||||
}]
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["aka", "abbr", "otherwise"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
|
@ -43,6 +43,10 @@ impl Command for ExprArgWhere {
|
||||
}]
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["condition", "match", "if"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
|
@ -37,6 +37,10 @@ impl Command for ExprAsNu {
|
||||
}]
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["convert", "conversion"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
|
@ -43,6 +43,10 @@ impl Command for ExprCol {
|
||||
}]
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["create"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
|
@ -69,6 +69,10 @@ impl Command for ExprConcatStr {
|
||||
}]
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["join", "connect", "update"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
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(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
|
@ -42,6 +42,10 @@ impl Command for ExprLit {
|
||||
}]
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["string", "literal", "expression"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
|
@ -23,7 +23,7 @@ impl Command for ExprOtherwise {
|
||||
.required(
|
||||
"otherwise expression",
|
||||
SyntaxShape::Any,
|
||||
"expressioini to apply when no when predicate matches",
|
||||
"expression to apply when no when predicate matches",
|
||||
)
|
||||
.input_type(Type::Any)
|
||||
.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(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
|
@ -5,7 +5,7 @@ use nu_protocol::{
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
use polars::prelude::QuantileInterpolOptions;
|
||||
use polars::prelude::{lit, QuantileInterpolOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ExprQuantile;
|
||||
@ -55,6 +55,10 @@ impl Command for ExprQuantile {
|
||||
}]
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["statistics", "percentile", "distribution"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -68,7 +72,7 @@ impl Command for ExprQuantile {
|
||||
let expr = NuExpression::try_from_value(value)?;
|
||||
let expr: NuExpression = expr
|
||||
.into_polars()
|
||||
.quantile(quantile, QuantileInterpolOptions::default())
|
||||
.quantile(lit(quantile), QuantileInterpolOptions::default())
|
||||
.into();
|
||||
|
||||
Ok(PipelineData::Value(
|
||||
|
@ -85,6 +85,10 @@ impl Command for ExprWhen {
|
||||
]
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["condition", "match", "if", "else"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
|
@ -128,7 +128,7 @@ impl Command for LazyAggregate {
|
||||
if matches!(dtype, Some(DataType::Object(..))) {
|
||||
return Err(ShellError::GenericError(
|
||||
"Object type column not supported for aggregation".into(),
|
||||
format!("Column '{}' is type Object", name),
|
||||
format!("Column '{name}' is type Object"),
|
||||
Some(call.head),
|
||||
Some("Aggregations cannot be performed on Object type columns. Use dtype command to check column types".into()),
|
||||
Vec::new(),
|
||||
|
@ -5,7 +5,7 @@ use nu_protocol::{
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Type, Value,
|
||||
};
|
||||
use polars::prelude::QuantileInterpolOptions;
|
||||
use polars::prelude::{lit, QuantileInterpolOptions};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct LazyQuantile;
|
||||
@ -60,7 +60,7 @@ impl Command for LazyQuantile {
|
||||
let lazy = NuLazyFrame::new(
|
||||
lazy.from_eager,
|
||||
lazy.into_polars()
|
||||
.quantile(quantile, QuantileInterpolOptions::default()),
|
||||
.quantile(lit(quantile), QuantileInterpolOptions::default()),
|
||||
);
|
||||
|
||||
Ok(PipelineData::Value(lazy.into_value(call.head)?, None))
|
||||
|
@ -79,7 +79,7 @@ fn command(
|
||||
let res = if not_exact {
|
||||
casted.as_date_not_exact(Some(format.as_str()))
|
||||
} else {
|
||||
casted.as_date(Some(format.as_str()))
|
||||
casted.as_date(Some(format.as_str()), false)
|
||||
};
|
||||
|
||||
let mut res = res
|
||||
|
@ -112,7 +112,7 @@ fn command(
|
||||
let res = if not_exact {
|
||||
casted.as_datetime_not_exact(Some(format.as_str()), TimeUnit::Milliseconds)
|
||||
} 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
|
||||
|
@ -35,7 +35,7 @@ impl Command for GetWeekDay {
|
||||
result: Some(
|
||||
NuDataFrame::try_from_columns(vec![Column::new(
|
||||
"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")
|
||||
.into_value(Span::test_data()),
|
||||
|
@ -71,19 +71,19 @@ pub fn test_dataframe(cmds: Vec<Box<dyn Command + 'static>>) {
|
||||
|
||||
let mut stack = Stack::new();
|
||||
|
||||
match eval_block(
|
||||
let result = eval_block(
|
||||
&engine_state,
|
||||
&mut stack,
|
||||
&block,
|
||||
PipelineData::empty(),
|
||||
true,
|
||||
true,
|
||||
) {
|
||||
Err(err) => panic!("test eval error in `{}`: {:?}", example.example, err),
|
||||
Ok(result) => {
|
||||
let result = result.into_value(Span::test_data());
|
||||
)
|
||||
.unwrap_or_else(|err| panic!("test eval error in `{}`: {:?}", example.example, err))
|
||||
.into_value(Span::test_data());
|
||||
|
||||
println!("input: {}", example.example);
|
||||
println!("result: {:?}", result);
|
||||
println!("result: {result:?}");
|
||||
println!("done: {:?}", start.elapsed());
|
||||
|
||||
// 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 result != expected {
|
||||
panic!(
|
||||
"the example result is different to expected value: {:?} != {:?}",
|
||||
result, expected
|
||||
"the example result is different to expected value: {result:?} != {expected:?}"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -307,10 +307,7 @@ pub fn create_column(
|
||||
.skip(from_row)
|
||||
.take(size)
|
||||
.map(|v| match v {
|
||||
Some(a) => Value::Int {
|
||||
val: a as i64,
|
||||
span,
|
||||
},
|
||||
Some(a) => Value::Int { val: a, span },
|
||||
None => Value::Nothing { span },
|
||||
})
|
||||
.collect::<Vec<Value>>();
|
||||
@ -423,7 +420,7 @@ pub fn create_column(
|
||||
"Error casting object from series".into(),
|
||||
"".to_string(),
|
||||
None,
|
||||
Some(format!("Object not supported for conversion: {}", x)),
|
||||
Some(format!("Object not supported for conversion: {x}")),
|
||||
Vec::new(),
|
||||
)),
|
||||
Some(ca) => {
|
||||
@ -467,7 +464,7 @@ pub fn create_column(
|
||||
error: ShellError::UnsupportedInput(
|
||||
"The given local datetime representation is invalid."
|
||||
.to_string(),
|
||||
format!("timestamp is {:?}", a),
|
||||
format!("timestamp is {a:?}"),
|
||||
span,
|
||||
Span::unknown(),
|
||||
),
|
||||
@ -482,7 +479,7 @@ pub fn create_column(
|
||||
error: ShellError::UnsupportedInput(
|
||||
"The given local datetime representation is invalid."
|
||||
.to_string(),
|
||||
format!("timestamp is {:?}", a),
|
||||
format!("timestamp is {a:?}"),
|
||||
span,
|
||||
Span::unknown(),
|
||||
),
|
||||
@ -532,7 +529,7 @@ pub fn create_column(
|
||||
error: ShellError::UnsupportedInput(
|
||||
"The given local datetime representation is invalid."
|
||||
.to_string(),
|
||||
format!("timestamp is {:?}", a),
|
||||
format!("timestamp is {a:?}"),
|
||||
span,
|
||||
Span::unknown(),
|
||||
),
|
||||
@ -547,7 +544,7 @@ pub fn create_column(
|
||||
error: ShellError::UnsupportedInput(
|
||||
"The given local datetime representation is invalid."
|
||||
.to_string(),
|
||||
format!("timestamp is {:?}", a),
|
||||
format!("timestamp is {a:?}"),
|
||||
span,
|
||||
Span::unknown(),
|
||||
),
|
||||
@ -597,7 +594,7 @@ pub fn create_column(
|
||||
"Error creating Dataframe".into(),
|
||||
"".to_string(),
|
||||
None,
|
||||
Some(format!("Value not supported in nushell: {}", e)),
|
||||
Some(format!("Value not supported in nushell: {e}")),
|
||||
Vec::new(),
|
||||
)),
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ impl NuDataFrame {
|
||||
Value::CustomValue { .. } => return Self::try_from_value(value),
|
||||
Value::List { vals, .. } => {
|
||||
let cols = (0..vals.len())
|
||||
.map(|i| format!("{}", i))
|
||||
.map(|i| format!("{i}"))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
conversion::insert_record(&mut column_values, &cols, &vals)?
|
||||
@ -181,7 +181,7 @@ impl NuDataFrame {
|
||||
let dataframe = DataFrame::new(columns).map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Error creating dataframe".into(),
|
||||
format!("Unable to create DataFrame: {}", e),
|
||||
format!("Unable to create DataFrame: {e}"),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
@ -426,7 +426,6 @@ impl NuDataFrame {
|
||||
.collect::<Vec<(String, std::vec::IntoIter<Value>)>>();
|
||||
|
||||
let values = (0..size)
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
let mut cols = vec![];
|
||||
let mut vals = vec![];
|
||||
|
@ -187,7 +187,7 @@ impl NuDataFrame {
|
||||
Err(e) => Err({
|
||||
ShellError::GenericError(
|
||||
"Error appending dataframe".into(),
|
||||
format!("Unable to append: {}", e),
|
||||
format!("Unable to append: {e}"),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
@ -200,7 +200,7 @@ impl NuDataFrame {
|
||||
let df_new = DataFrame::new(new_cols).map_err(|e| {
|
||||
ShellError::GenericError(
|
||||
"Error appending dataframe".into(),
|
||||
format!("Unable to append dataframes: {}", e),
|
||||
format!("Unable to append dataframes: {e}"),
|
||||
Some(span),
|
||||
None,
|
||||
Vec::new(),
|
||||
|
@ -227,7 +227,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
span,
|
||||
};
|
||||
let value = Value::String {
|
||||
val: format!("{:?}", literal),
|
||||
val: format!("{literal:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
@ -239,7 +239,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
let right_val = expr_to_value(right, span);
|
||||
|
||||
let operator = Value::String {
|
||||
val: format!("{:?}", op),
|
||||
val: format!("{op:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
@ -289,12 +289,9 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
interpol,
|
||||
} => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let quantile = Value::Float {
|
||||
val: *quantile,
|
||||
span,
|
||||
};
|
||||
let quantile = expr_to_value(quantile.as_ref(), span);
|
||||
let interpol = Value::String {
|
||||
val: format!("{:?}", interpol),
|
||||
val: format!("{interpol:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
@ -376,7 +373,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
let vals = dtypes
|
||||
.iter()
|
||||
.map(|d| Value::String {
|
||||
val: format!("{}", d),
|
||||
val: format!("{d}"),
|
||||
span,
|
||||
})
|
||||
.collect();
|
||||
@ -386,7 +383,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
Expr::Sort { expr, options } => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let options = Value::String {
|
||||
val: format!("{:?}", options),
|
||||
val: format!("{options:?}"),
|
||||
span,
|
||||
};
|
||||
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 dtype = Value::String {
|
||||
val: format!("{:?}", data_type),
|
||||
val: format!("{data_type:?}"),
|
||||
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
|
||||
.iter()
|
||||
.map(|e| Value::String {
|
||||
val: format!("{:?}", e),
|
||||
val: format!("{e:?}"),
|
||||
span,
|
||||
})
|
||||
.collect();
|
||||
@ -505,7 +502,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
Expr::RenameAlias { expr, function } => {
|
||||
let expr = expr_to_value(expr.as_ref(), span);
|
||||
let function = Value::String {
|
||||
val: format!("{:?}", function),
|
||||
val: format!("{function:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
@ -527,15 +524,15 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
let input = Value::List { vals: input, span };
|
||||
|
||||
let function = Value::String {
|
||||
val: format!("{:?}", function),
|
||||
val: format!("{function:?}"),
|
||||
span,
|
||||
};
|
||||
let output_type = Value::String {
|
||||
val: format!("{:?}", output_type),
|
||||
val: format!("{output_type:?}"),
|
||||
span,
|
||||
};
|
||||
let options = Value::String {
|
||||
val: format!("{:?}", options),
|
||||
val: format!("{options:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
@ -561,11 +558,11 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
let input = Value::List { vals: input, span };
|
||||
|
||||
let function = Value::String {
|
||||
val: format!("{:?}", function),
|
||||
val: format!("{function:?}"),
|
||||
span,
|
||||
};
|
||||
let options = Value::String {
|
||||
val: format!("{:?}", options),
|
||||
val: format!("{options:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
@ -600,7 +597,7 @@ pub fn expr_to_value(expr: &Expr, span: Span) -> Value {
|
||||
.unwrap_or_else(|| Value::nothing(span));
|
||||
|
||||
let options = Value::String {
|
||||
val: format!("{:?}", options),
|
||||
val: format!("{options:?}"),
|
||||
span,
|
||||
};
|
||||
|
||||
|
@ -126,7 +126,7 @@ where
|
||||
.unwrap_or(Locale::en_US);
|
||||
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 {
|
||||
val: formatter_buf,
|
||||
span,
|
||||
|
@ -46,7 +46,6 @@ impl Command for SubCommand {
|
||||
}];
|
||||
Value::Record { cols, vals, span }
|
||||
})
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
}
|
||||
|
||||
|
@ -95,6 +95,7 @@ pub fn create_default_context() -> EngineState {
|
||||
Each,
|
||||
EachWhile,
|
||||
Empty,
|
||||
Enumerate,
|
||||
Every,
|
||||
Filter,
|
||||
Find,
|
||||
@ -288,6 +289,7 @@ pub fn create_default_context() -> EngineState {
|
||||
Ansi,
|
||||
AnsiGradient,
|
||||
AnsiStrip,
|
||||
AnsiLink,
|
||||
Clear,
|
||||
Du,
|
||||
KeybindingsDefault,
|
||||
@ -354,7 +356,6 @@ pub fn create_default_context() -> EngineState {
|
||||
Use,
|
||||
Upsert,
|
||||
Where,
|
||||
ToUrl,
|
||||
ToXml,
|
||||
ToYaml,
|
||||
};
|
||||
@ -433,10 +434,13 @@ pub fn create_default_context() -> EngineState {
|
||||
|
||||
// Network
|
||||
bind_command! {
|
||||
Fetch,
|
||||
Post,
|
||||
Http,
|
||||
HttpGet,
|
||||
HttpPost,
|
||||
Url,
|
||||
UrlBuildQuery,
|
||||
UrlEncode,
|
||||
UrlJoin,
|
||||
UrlParse,
|
||||
Port,
|
||||
}
|
||||
@ -491,7 +495,7 @@ pub fn create_default_context() -> EngineState {
|
||||
};
|
||||
|
||||
if let Err(err) = engine_state.merge_delta(delta) {
|
||||
eprintln!("Error creating default context: {:?}", err);
|
||||
eprintln!("Error creating default context: {err:?}");
|
||||
}
|
||||
|
||||
engine_state
|
||||
|
@ -18,5 +18,7 @@ pub fn deprecated_commands() -> HashMap<String, String> {
|
||||
"build-string".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 writeln!(&mut file, "{}", config_file).is_err() {
|
||||
if writeln!(&mut file, "{config_file}").is_err() {
|
||||
return Err(ShellError::FileNotFoundCustom(
|
||||
"config.nu could not be written to".into(),
|
||||
span,
|
||||
@ -102,7 +102,7 @@ impl Command for ConfigReset {
|
||||
}
|
||||
}
|
||||
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(
|
||||
"env.nu could not be written to".into(),
|
||||
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));
|
||||
|
||||
cols.push("type".into());
|
||||
vals.push(Value::string(format!("{}", val_type), span));
|
||||
vals.push(Value::string(format!("{val_type}"), span));
|
||||
|
||||
cols.push("value".into());
|
||||
vals.push(val);
|
||||
|
@ -20,7 +20,7 @@ mod test_examples {
|
||||
engine::{Command, EngineState, Stack, StateDelta, StateWorkingSet},
|
||||
Example, PipelineData, Signature, Span, Type, Value,
|
||||
};
|
||||
use std::{collections::HashSet, path::PathBuf};
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn test_examples(cmd: impl Command + 'static) {
|
||||
let examples = cmd.examples();
|
||||
@ -45,7 +45,7 @@ mod test_examples {
|
||||
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(
|
||||
@ -104,7 +104,7 @@ mod test_examples {
|
||||
|
||||
fn check_example_input_and_output_types_match_command_signature(
|
||||
example: &Example,
|
||||
cwd: &PathBuf,
|
||||
cwd: &std::path::Path,
|
||||
engine_state: &mut Box<EngineState>,
|
||||
signature_input_output_types: &Vec<(Type, Type)>,
|
||||
signature_operates_on_cell_paths: bool,
|
||||
@ -204,7 +204,7 @@ mod test_examples {
|
||||
|
||||
fn check_example_evaluates_to_expected_output(
|
||||
example: &Example,
|
||||
cwd: &PathBuf,
|
||||
cwd: &std::path::Path,
|
||||
engine_state: &mut Box<EngineState>,
|
||||
) {
|
||||
let mut stack = Stack::new();
|
||||
@ -251,7 +251,7 @@ mod test_examples {
|
||||
{:?}",
|
||||
declared_type_transformations
|
||||
.difference(&witnessed_type_transformations)
|
||||
.map(|(s1, s2)| format!("{} -> {}", s1, s2))
|
||||
.map(|(s1, s2)| format!("{s1} -> {s2}"))
|
||||
.join(", ")
|
||||
);
|
||||
}
|
||||
@ -260,7 +260,7 @@ mod test_examples {
|
||||
fn eval(
|
||||
contents: &str,
|
||||
input: PipelineData,
|
||||
cwd: &PathBuf,
|
||||
cwd: &std::path::Path,
|
||||
engine_state: &mut Box<EngineState>,
|
||||
) -> Value {
|
||||
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, &[]);
|
||||
|
||||
if let Some(err) = err {
|
||||
panic!("test parse error in `{}`: {:?}", contents, err)
|
||||
panic!("test parse error in `{contents}`: {err:?}")
|
||||
}
|
||||
|
||||
(output, working_set.render())
|
||||
@ -282,7 +282,7 @@ mod test_examples {
|
||||
fn eval_block(
|
||||
block: Block,
|
||||
input: PipelineData,
|
||||
cwd: &PathBuf,
|
||||
cwd: &std::path::Path,
|
||||
engine_state: &mut Box<EngineState>,
|
||||
delta: StateDelta,
|
||||
) -> Value {
|
||||
@ -302,12 +302,11 @@ mod test_examples {
|
||||
|
||||
fn eval_pipeline_without_terminal_expression(
|
||||
src: &str,
|
||||
cwd: &PathBuf,
|
||||
cwd: &std::path::Path,
|
||||
engine_state: &mut Box<EngineState>,
|
||||
) -> Option<Value> {
|
||||
let (mut block, delta) = parse(src, engine_state);
|
||||
match block.pipelines.len() {
|
||||
1 => {
|
||||
if block.pipelines.len() == 1 {
|
||||
let n_expressions = block.pipelines[0].elements.len();
|
||||
block.pipelines[0].elements.truncate(&n_expressions - 1);
|
||||
|
||||
@ -317,11 +316,9 @@ mod test_examples {
|
||||
} else {
|
||||
Some(Value::nothing(Span::test_data()))
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
} else {
|
||||
// E.g. multiple semicolon-separated statements
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::filesystem::cd_query::query;
|
||||
use crate::{get_current_shell, get_shells};
|
||||
#[cfg(unix)]
|
||||
use libc::gid_t;
|
||||
use nu_engine::{current_dir, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
@ -8,22 +10,16 @@ use nu_protocol::{
|
||||
};
|
||||
use std::path::Path;
|
||||
|
||||
// when the file under the fold executable
|
||||
// For checking whether we have permission to cd to a directory
|
||||
#[cfg(unix)]
|
||||
mod permission_mods {
|
||||
mod file_permissions {
|
||||
pub type Mode = u32;
|
||||
pub mod unix {
|
||||
use super::Mode;
|
||||
pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode;
|
||||
pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
|
||||
pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
|
||||
}
|
||||
}
|
||||
|
||||
// use to return the message of the result of change director
|
||||
// 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
|
||||
// The result of checking whether we have permission to cd to a directory
|
||||
#[derive(Debug)]
|
||||
enum PermissionResult<'a> {
|
||||
PermissionOk,
|
||||
@ -97,14 +93,14 @@ impl Command for Cd {
|
||||
Err(e) => {
|
||||
return Err(ShellError::DirectoryNotFound(
|
||||
v.span,
|
||||
Some(format!("IO Error: {:?}", e)),
|
||||
Some(format!("IO Error: {e:?}")),
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(ShellError::DirectoryNotFound(
|
||||
v.span,
|
||||
Some(format!("IO Error: {:?}", e1)),
|
||||
Some(format!("IO Error: {e1:?}")),
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -127,7 +123,7 @@ impl Command for Cd {
|
||||
Err(e) => {
|
||||
return Err(ShellError::DirectoryNotFound(
|
||||
v.span,
|
||||
Some(format!("IO Error: {:?}", e)),
|
||||
Some(format!("IO Error: {e:?}")),
|
||||
))
|
||||
}
|
||||
};
|
||||
@ -146,14 +142,14 @@ impl Command for Cd {
|
||||
Err(e) => {
|
||||
return Err(ShellError::DirectoryNotFound(
|
||||
v.span,
|
||||
Some(format!("IO Error: {:?}", e)),
|
||||
Some(format!("IO Error: {e:?}")),
|
||||
))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Err(ShellError::DirectoryNotFound(
|
||||
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 { val: path, span };
|
||||
let path_value = Value::String {
|
||||
val: path.clone(),
|
||||
span,
|
||||
};
|
||||
let cwd = Value::string(cwd.to_string_lossy(), call.head);
|
||||
|
||||
let mut shells = get_shells(engine_state, stack, cwd);
|
||||
@ -191,16 +189,15 @@ impl Command for Cd {
|
||||
stack.add_env_var("OLDPWD".into(), oldpwd)
|
||||
}
|
||||
|
||||
match have_permission(&path) {
|
||||
//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
|
||||
match have_permission(&path_tointo) {
|
||||
PermissionResult::PermissionOk => {
|
||||
stack.add_env_var("PWD".into(), path_value);
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError(format!(
|
||||
"Cannot change directory to {}: {}",
|
||||
path_tointo, reason
|
||||
"Cannot change directory to {path}: {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)]
|
||||
fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
|
||||
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;
|
||||
let bits = metadata.mode();
|
||||
let has_bit = |bit| bits & bit == bit;
|
||||
let current_user = users::get_current_uid();
|
||||
if current_user == 0 {
|
||||
let current_user_uid = users::get_current_uid();
|
||||
if current_user_uid == 0 {
|
||||
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_group = metadata.gid();
|
||||
match (current_user == owner_user, current_group == owner_group) {
|
||||
match (
|
||||
current_user_uid == owner_user,
|
||||
current_user_gid == owner_group,
|
||||
) {
|
||||
(true, _) => {
|
||||
if has_bit(permission_mods::unix::USER_EXECUTE) {
|
||||
if has_bit(file_permissions::USER_EXECUTE) {
|
||||
PermissionResult::PermissionOk
|
||||
} else {
|
||||
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) => {
|
||||
if has_bit(permission_mods::unix::GROUP_EXECUTE) {
|
||||
if has_bit(file_permissions::GROUP_EXECUTE) {
|
||||
PermissionResult::PermissionOk
|
||||
} else {
|
||||
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) => {
|
||||
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
|
||||
} else {
|
||||
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);
|
||||
let res = if src == dst {
|
||||
let message = format!(
|
||||
"src {:?} and dst {:?} are identical(not copied)",
|
||||
source, destination
|
||||
"src {source:?} and dst {destination:?} are identical(not copied)"
|
||||
);
|
||||
|
||||
return Err(ShellError::GenericError(
|
||||
|
@ -26,6 +26,21 @@ impl Command for Glob {
|
||||
"directory depth to search",
|
||||
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)
|
||||
}
|
||||
|
||||
@ -81,6 +96,11 @@ impl Command for Glob {
|
||||
example: "glob <[a-d]:1,10>",
|
||||
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 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() {
|
||||
return Err(ShellError::GenericError(
|
||||
"glob pattern must not be empty".to_string(),
|
||||
@ -121,7 +145,7 @@ impl Command for Glob {
|
||||
Err(e) => {
|
||||
return Err(ShellError::GenericError(
|
||||
"error with glob pattern".to_string(),
|
||||
format!("{}", e),
|
||||
format!("{e}"),
|
||||
Some(glob_pattern.span),
|
||||
None,
|
||||
Vec::new(),
|
||||
@ -139,6 +163,13 @@ impl Command for Glob {
|
||||
},
|
||||
)
|
||||
.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 {
|
||||
val: entry.into_path().to_string_lossy().to_string(),
|
||||
span,
|
||||
|
@ -178,7 +178,6 @@ impl Command for Ls {
|
||||
let mut hidden_dirs = vec![];
|
||||
|
||||
Ok(paths_peek
|
||||
.into_iter()
|
||||
.filter_map(move |x| match x {
|
||||
Ok(path) => {
|
||||
let metadata = match std::fs::symlink_metadata(&path) {
|
||||
|
@ -70,7 +70,7 @@ impl Command for Mkdir {
|
||||
|
||||
if let Err(reason) = dir_res {
|
||||
return Err(ShellError::CreateNotPossible(
|
||||
format!("failed to create directory: {}", reason),
|
||||
format!("failed to create directory: {reason}"),
|
||||
call.positional_nth(i)
|
||||
.expect("already checked through directories")
|
||||
.span,
|
||||
|
@ -290,7 +290,7 @@ fn move_file(
|
||||
);
|
||||
if let Err(e) = interaction {
|
||||
return Err(ShellError::GenericError(
|
||||
format!("Error during interaction: {:}", e),
|
||||
format!("Error during interaction: {e:}"),
|
||||
"could not move".into(),
|
||||
None,
|
||||
None,
|
||||
@ -325,10 +325,10 @@ fn move_item(from: &Path, from_span: Span, to: &Path) -> Result<(), ShellError>
|
||||
Err(e) => {
|
||||
let error_kind = match e.kind {
|
||||
fs_extra::error::ErrorKind::Io(io) => {
|
||||
format!("I/O error: {}", io)
|
||||
format!("I/O error: {io}")
|
||||
}
|
||||
fs_extra::error::ErrorKind::StripPrefix(sp) => {
|
||||
format!("Strip prefix error: {}", sp)
|
||||
format!("Strip prefix error: {sp}")
|
||||
}
|
||||
fs_extra::error::ErrorKind::OsString(os) => {
|
||||
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(),
|
||||
};
|
||||
Err(ShellError::GenericError(
|
||||
format!(
|
||||
"Could not move {:?} to {:?}. Error Kind: {}",
|
||||
from, to, error_kind
|
||||
),
|
||||
format!("Could not move {from:?} to {to:?}. Error Kind: {error_kind}"),
|
||||
"could not move".into(),
|
||||
Some(from_span),
|
||||
None,
|
||||
|
@ -139,6 +139,7 @@ impl Command for Open {
|
||||
Box::new(BufferedReader { input: buf_reader }),
|
||||
ctrlc,
|
||||
call_span,
|
||||
None,
|
||||
)),
|
||||
stderr: None,
|
||||
exit_code: None,
|
||||
@ -155,7 +156,7 @@ impl Command for Open {
|
||||
};
|
||||
|
||||
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) => {
|
||||
let decl = engine_state.get_decl(converter_id);
|
||||
if let Some(block_id) = decl.get_block_id() {
|
||||
@ -166,7 +167,7 @@ impl Command for Open {
|
||||
}
|
||||
.map_err(|inner| {
|
||||
ShellError::GenericError(
|
||||
format!("Error while parsing as {}", ext),
|
||||
format!("Error while parsing as {ext}"),
|
||||
format!("Could not parse '{}' with `from {}`", path.display(), ext),
|
||||
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())),
|
||||
|
@ -318,7 +318,7 @@ fn rm(
|
||||
);
|
||||
if let Err(e) = interaction {
|
||||
return Err(ShellError::GenericError(
|
||||
format!("Error during interaction: {:}", e),
|
||||
format!("Error during interaction: {e:}"),
|
||||
"could not move".into(),
|
||||
None,
|
||||
None,
|
||||
@ -375,7 +375,7 @@ fn rm(
|
||||
Ok(())
|
||||
} else if trash || (rm_always_trash && !permanent) {
|
||||
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 {
|
||||
std::fs::remove_file(&f)
|
||||
@ -403,7 +403,7 @@ fn rm(
|
||||
}
|
||||
|
||||
if let Err(e) = result {
|
||||
let msg = format!("Could not delete because: {:}", e);
|
||||
let msg = format!("Could not delete because: {e:}");
|
||||
Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
msg,
|
||||
|
@ -8,6 +8,9 @@ use nu_protocol::{
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::Path;
|
||||
use std::thread;
|
||||
|
||||
use crate::progress_bar;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Save;
|
||||
@ -47,6 +50,7 @@ impl Command for Save {
|
||||
.switch("raw", "save file as raw binary", Some('r'))
|
||||
.switch("append", "append input to the end of the file", Some('a'))
|
||||
.switch("force", "overwrite the destination", Some('f'))
|
||||
.switch("progress", "enable progress bar", Some('p'))
|
||||
.category(Category::FileSystem)
|
||||
}
|
||||
|
||||
@ -60,6 +64,7 @@ impl Command for Save {
|
||||
let raw = call.has_flag("raw");
|
||||
let append = call.has_flag("append");
|
||||
let force = call.has_flag("force");
|
||||
let progress = call.has_flag("progress");
|
||||
|
||||
let span = call.head;
|
||||
|
||||
@ -81,16 +86,20 @@ impl Command for Save {
|
||||
|
||||
// delegate a thread to redirect stderr to result.
|
||||
let handler = stderr.map(|stderr_stream| match stderr_file {
|
||||
Some(stderr_file) => {
|
||||
std::thread::spawn(move || stream_to_file(stderr_stream, stderr_file, span))
|
||||
}
|
||||
None => std::thread::spawn(move || {
|
||||
Some(stderr_file) => thread::Builder::new()
|
||||
.name("stderr redirector".to_string())
|
||||
.spawn(move || stream_to_file(stderr_stream, stderr_file, span, progress))
|
||||
.expect("Failed to create thread"),
|
||||
None => thread::Builder::new()
|
||||
.name("stderr redirector".to_string())
|
||||
.spawn(move || {
|
||||
let _ = stderr_stream.into_bytes();
|
||||
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 {
|
||||
h.join().map_err(|err| {
|
||||
ShellError::ExternalCommand(
|
||||
@ -104,6 +113,20 @@ impl Command for Save {
|
||||
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 => {
|
||||
let bytes =
|
||||
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)
|
||||
} else {
|
||||
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,
|
||||
span: Span,
|
||||
) -> 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 {
|
||||
Some(converter_id) => {
|
||||
@ -208,13 +231,13 @@ fn convert_to_extension(
|
||||
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
|
||||
///
|
||||
/// 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 {
|
||||
Value::String { val, .. } => Ok(val.into_bytes()),
|
||||
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.
|
||||
Value::Error { error } => Err(error),
|
||||
other => Err(ShellError::OnlySupportsThisInputType(
|
||||
"string, binary or list".into(),
|
||||
other.get_type().to_string(),
|
||||
span,
|
||||
// This line requires the Value::Error match above.
|
||||
other.expect_span(),
|
||||
)),
|
||||
other => Ok(other.as_string()?.into_bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,10 +349,29 @@ fn stream_to_file(
|
||||
mut stream: RawStream,
|
||||
file: File,
|
||||
span: Span,
|
||||
progress: bool,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
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| {
|
||||
let buf = match result {
|
||||
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) {
|
||||
*process_failed_p = true;
|
||||
return Err(ShellError::IOError(err.to_string()));
|
||||
}
|
||||
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