Compare commits

..

1 Commits

Author SHA1 Message Date
4f67310681 Revert "Allow polars schema --datatype-list to be used without pipeline inp…"
This reverts commit fb691c0da5.
2025-06-13 12:38:53 -07:00
353 changed files with 2548 additions and 5329 deletions

View File

@ -1,25 +0,0 @@
name: Comment on changes to the config
on:
pull_request_target:
paths:
- 'crates/nu-protocol/src/config/**'
jobs:
comment:
runs-on: ubuntu-latest
steps:
- name: Check if there is already a bot comment
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Hey, just a bot checking in!
- name: Create comment if there is not
if: steps.fc.outputs.comment-id == ''
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.pull_request.number }}
body: |
Hey, just a bot checking in! You edited files related to the configuration.
If you changed any of the default values or added a new config option, don't forget to update the [`doc_config.nu`](https://github.com/nushell/nushell/blob/main/crates/nu-utils/src/default_files/doc_config.nu) which documents the options for our users including the defaults provided by the Rust implementation.
If you didn't make a change here, you can just ignore me.

View File

@ -46,7 +46,7 @@ jobs:
uses: hustcer/setup-nu@v3
if: github.repository == 'nushell/nightly'
with:
version: 0.105.1
version: 0.103.0
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
- name: Prepare for Nightly Release
@ -127,7 +127,6 @@ jobs:
- armv7-unknown-linux-musleabihf
- riscv64gc-unknown-linux-gnu
- loongarch64-unknown-linux-gnu
- loongarch64-unknown-linux-musl
include:
- target: aarch64-apple-darwin
os: macos-latest
@ -153,8 +152,6 @@ jobs:
os: ubuntu-22.04
- target: loongarch64-unknown-linux-gnu
os: ubuntu-22.04
- target: loongarch64-unknown-linux-musl
os: ubuntu-22.04
runs-on: ${{matrix.os}}
steps:
@ -182,22 +179,36 @@ jobs:
uses: actions-rust-lang/setup-rust-toolchain@v1
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with:
cache: false
rustflags: ''
- name: Setup Nushell
uses: hustcer/setup-nu@v3
if: ${{ matrix.os != 'windows-11-arm' }}
with:
version: 0.105.1
version: 0.103.0
- name: Release Nu Binary
id: nu
if: ${{ matrix.os != 'windows-11-arm' }}
run: nu .github/workflows/release-pkg.nu
env:
OS: ${{ matrix.os }}
REF: ${{ github.ref }}
TARGET: ${{ matrix.target }}
- name: Build Nu for Windows ARM64
id: nu0
shell: pwsh
if: ${{ matrix.os == 'windows-11-arm' }}
run: |
$env:OS = 'windows'
$env:REF = '${{ github.ref }}'
$env:TARGET = '${{ matrix.target }}'
cargo build --release --all --target aarch64-pc-windows-msvc
cp ./target/${{ matrix.target }}/release/nu.exe .
./nu.exe -c 'version'
./nu.exe ${{github.workspace}}/.github/workflows/release-pkg.nu
- name: Create an Issue for Release Failure
if: ${{ failure() }}
uses: JasonEtco/create-an-issue@v2
@ -217,7 +228,9 @@ jobs:
prerelease: true
files: |
${{ steps.nu.outputs.msi }}
${{ steps.nu0.outputs.msi }}
${{ steps.nu.outputs.archive }}
${{ steps.nu0.outputs.archive }}
tag_name: ${{ needs.prepare.outputs.nightly_tag }}
name: ${{ needs.prepare.outputs.build_date }}-${{ needs.prepare.outputs.nightly_tag }}
env:
@ -263,7 +276,7 @@ jobs:
- name: Setup Nushell
uses: hustcer/setup-nu@v3
with:
version: 0.105.1
version: 0.103.0
# Keep the last a few releases
- name: Delete Older Releases

View File

@ -58,7 +58,7 @@ jobs:
- name: Setup Nushell
uses: hustcer/setup-nu@v3
with:
version: 0.105.1
version: nightly
- name: Release MSI Packages
id: nu

View File

@ -99,14 +99,6 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER = 'loongarch64-unknown-linux-gnu-gcc'
cargo-build-nu
}
'loongarch64-unknown-linux-musl' => {
print $"(ansi g)Downloading LoongArch64 musl cross-compilation toolchain...(ansi reset)"
aria2c -q https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/releases/download/loongarch64-linux-musl-cross-gcc-13.2.0/loongarch64-linux-musl-cross.tgz
tar -xf loongarch64-linux-musl-cross.tgz
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.PWD)/loongarch64-linux-musl-cross/bin')
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_MUSL_LINKER = "loongarch64-linux-musl-gcc"
cargo-build-nu
}
_ => {
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
# Actually just for x86_64-unknown-linux-musl target

View File

@ -35,7 +35,6 @@ jobs:
- armv7-unknown-linux-musleabihf
- riscv64gc-unknown-linux-gnu
- loongarch64-unknown-linux-gnu
- loongarch64-unknown-linux-musl
include:
- target: aarch64-apple-darwin
os: macos-latest
@ -61,8 +60,6 @@ jobs:
os: ubuntu-22.04
- target: loongarch64-unknown-linux-gnu
os: ubuntu-22.04
- target: loongarch64-unknown-linux-musl
os: ubuntu-22.04
runs-on: ${{matrix.os}}
@ -93,17 +90,32 @@ jobs:
- name: Setup Nushell
uses: hustcer/setup-nu@v3
if: ${{ matrix.os != 'windows-11-arm' }}
with:
version: 0.105.1
version: 0.103.0
- name: Release Nu Binary
id: nu
if: ${{ matrix.os != 'windows-11-arm' }}
run: nu .github/workflows/release-pkg.nu
env:
OS: ${{ matrix.os }}
REF: ${{ github.ref }}
TARGET: ${{ matrix.target }}
- name: Build Nu for Windows ARM64
id: nu0
shell: pwsh
if: ${{ matrix.os == 'windows-11-arm' }}
run: |
$env:OS = 'windows'
$env:REF = '${{ github.ref }}'
$env:TARGET = '${{ matrix.target }}'
cargo build --release --all --target aarch64-pc-windows-msvc
cp ./target/${{ matrix.target }}/release/nu.exe .
./nu.exe -c 'version'
./nu.exe ${{github.workspace}}/.github/workflows/release-pkg.nu
# WARN: Don't upgrade this action due to the release per asset issue.
# See: https://github.com/softprops/action-gh-release/issues/445
- name: Publish Archive
@ -113,7 +125,9 @@ jobs:
draft: true
files: |
${{ steps.nu.outputs.msi }}
${{ steps.nu0.outputs.msi }}
${{ steps.nu.outputs.archive }}
${{ steps.nu0.outputs.archive }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -10,4 +10,4 @@ jobs:
uses: actions/checkout@v4.1.7
- name: Check spelling
uses: crate-ci/typos@v1.34.0
uses: crate-ci/typos@v1.33.1

6
.gitignore vendored
View File

@ -32,17 +32,11 @@ unstable_cargo_features.txt
# Helix configuration folder
.helix/*
.helix
wix/bin/
wix/obj/
wix/nu/
# Coverage tools
lcov.info
tarpaulin-report.html
# benchmarking
/tango
# Visual Studio
.vs/*
*.rsproj

606
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,8 @@ homepage = "https://www.nushell.sh"
license = "MIT"
name = "nu"
repository = "https://github.com/nushell/nushell"
rust-version = "1.86.0"
version = "0.106.1"
rust-version = "1.85.1"
version = "0.105.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -24,37 +24,36 @@ pkg-fmt = "zip"
[workspace]
members = [
"crates/nu_plugin_custom_values",
"crates/nu_plugin_example",
"crates/nu_plugin_formats",
"crates/nu_plugin_gstat",
"crates/nu_plugin_inc",
"crates/nu_plugin_polars",
"crates/nu_plugin_query",
"crates/nu_plugin_stress_internals",
"crates/nu-cli",
"crates/nu-engine",
"crates/nu-parser",
"crates/nu-system",
"crates/nu-cmd-base",
"crates/nu-cmd-extra",
"crates/nu-cmd-lang",
"crates/nu-cmd-plugin",
"crates/nu-color-config",
"crates/nu-command",
"crates/nu-derive-value",
"crates/nu-engine",
"crates/nu-experimental",
"crates/nu-color-config",
"crates/nu-explore",
"crates/nu-json",
"crates/nu-lsp",
"crates/nu-parser",
"crates/nu-pretty-hex",
"crates/nu-protocol",
"crates/nu-derive-value",
"crates/nu-plugin",
"crates/nu-plugin-core",
"crates/nu-plugin-engine",
"crates/nu-plugin-protocol",
"crates/nu-plugin-test-support",
"crates/nu-plugin",
"crates/nu-pretty-hex",
"crates/nu-protocol",
"crates/nu_plugin_inc",
"crates/nu_plugin_gstat",
"crates/nu_plugin_example",
"crates/nu_plugin_query",
"crates/nu_plugin_custom_values",
"crates/nu_plugin_formats",
"crates/nu_plugin_polars",
"crates/nu_plugin_stress_internals",
"crates/nu-std",
"crates/nu-system",
"crates/nu-table",
"crates/nu-term-grid",
"crates/nu-test-support",
@ -72,7 +71,7 @@ brotli = "7.0"
byteorder = "1.5"
bytes = "1"
bytesize = "1.3.3"
calamine = "0.28"
calamine = "0.26"
chardetng = "0.1.17"
chrono = { default-features = false, version = "0.4.34" }
chrono-humanize = "0.2.3"
@ -93,7 +92,7 @@ filesize = "0.2"
filetime = "0.2"
heck = "0.5.0"
human-date-parser = "0.3.0"
indexmap = "2.10"
indexmap = "2.9"
indicatif = "0.17"
interprocess = "2.2.0"
is_executable = "1.0"
@ -132,7 +131,7 @@ proc-macro-error2 = "2.0"
proc-macro2 = "1.0"
procfs = "0.17.0"
pwd = "1.3"
quick-xml = "0.37.5"
quick-xml = "0.37.0"
quickcheck = "1.0"
quickcheck_macros = "1.1"
quote = "1.0"
@ -141,7 +140,7 @@ getrandom = "0.2" # pick same version that rand requires
rand_chacha = "0.9"
ratatui = "0.29"
rayon = "1.10"
reedline = "0.41.0"
reedline = "0.40.0"
rmp = "0.8"
rmp-serde = "1.3"
roxmltree = "0.20"
@ -157,18 +156,17 @@ serde_json = "1.0.97"
serde_urlencoded = "0.7.1"
serde_yaml = "0.9.33"
sha2 = "0.10"
strip-ansi-escapes = "0.2.1"
strip-ansi-escapes = "0.2.0"
strum = "0.26"
strum_macros = "0.26"
syn = "2.0"
sysinfo = "0.36"
sysinfo = "0.33"
tabled = { version = "0.20", default-features = false }
tempfile = "3.20"
thiserror = "2.0.12"
titlecase = "3.6"
toml = "0.8"
trash = "5.2"
update-informer = { version = "1.3.0", default-features = false, features = ["github", "ureq"] }
update-informer = { version = "1.2.0", default-features = false, features = ["github", "ureq"] }
umask = "2.1"
unicode-segmentation = "1.12"
unicode-width = "0.2"
@ -186,7 +184,7 @@ uuid = "1.16.0"
v_htmlescape = "0.15.0"
wax = "0.6"
web-time = "1.1.0"
which = "8.0.0"
which = "7.0.3"
windows = "0.56"
windows-sys = "0.48"
winreg = "0.52"
@ -197,29 +195,27 @@ webpki-roots = "1.0"
# Warning: workspace lints affect library code as well as tests, so don't enable lints that would be too noisy in tests like that.
# todo = "warn"
unchecked_duration_subtraction = "warn"
used_underscore_binding = "warn"
[lints]
workspace = true
[dependencies]
nu-cli = { path = "./crates/nu-cli", version = "0.106.1" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.106.1" }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.106.1" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.106.1" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.106.1", optional = true }
nu-command = { path = "./crates/nu-command", version = "0.106.1", default-features = false, features = ["os"] }
nu-engine = { path = "./crates/nu-engine", version = "0.106.1" }
nu-experimental = { path = "./crates/nu-experimental", version = "0.106.1" }
nu-explore = { path = "./crates/nu-explore", version = "0.106.1" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.106.1" }
nu-parser = { path = "./crates/nu-parser", version = "0.106.1" }
nu-path = { path = "./crates/nu-path", version = "0.106.1" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.106.1" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.106.1" }
nu-std = { path = "./crates/nu-std", version = "0.106.1" }
nu-system = { path = "./crates/nu-system", version = "0.106.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.106.1" }
nu-cli = { path = "./crates/nu-cli", version = "0.105.2" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.105.2" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.105.2" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.105.2", optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.105.2" }
nu-command = { path = "./crates/nu-command", version = "0.105.2", default-features = false, features = ["os"] }
nu-engine = { path = "./crates/nu-engine", version = "0.105.2" }
nu-explore = { path = "./crates/nu-explore", version = "0.105.2" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.105.2" }
nu-parser = { path = "./crates/nu-parser", version = "0.105.2" }
nu-path = { path = "./crates/nu-path", version = "0.105.2" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.105.2" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.105.2" }
nu-std = { path = "./crates/nu-std", version = "0.105.2" }
nu-system = { path = "./crates/nu-system", version = "0.105.2" }
nu-utils = { path = "./crates/nu-utils", version = "0.105.2" }
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
crossterm = { workspace = true }
@ -248,9 +244,9 @@ nix = { workspace = true, default-features = false, features = [
] }
[dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.106.1" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.106.1" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.106.1" }
nu-test-support = { path = "./crates/nu-test-support", version = "0.105.2" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.105.2" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.105.2" }
assert_cmd = "2.0"
dirs = { workspace = true }
tango-bench = "0.6"
@ -261,14 +257,10 @@ serial_test = "3.2"
tempfile = { workspace = true }
[features]
# Enable all features while still avoiding mutually exclusive features.
# Use this if `--all-features` fails.
full = ["plugin", "rustls-tls", "system-clipboard", "trash-support", "sqlite"]
plugin = [
# crates
"dep:nu-cmd-plugin",
"dep:nu-plugin-engine",
"nu-cmd-plugin",
"nu-plugin-engine",
# features
"nu-cli/plugin",
@ -294,20 +286,21 @@ stable = ["default"]
# Enable to statically link OpenSSL (perl is required, to build OpenSSL https://docs.rs/openssl/latest/openssl/);
# otherwise the system version will be used. Not enabled by default because it takes a while to build
static-link-openssl = ["dep:openssl"]
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
# Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems.
# Missing X server/ Wayland can cause issues
system-clipboard = [
"reedline/system_clipboard",
"nu-cli/system-clipboard",
"nu-cmd-lang/system-clipboard",
]
# Stable (Default)
trash-support = ["nu-command/trash-support"]
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
# SQLite commands for nushell
sqlite = ["nu-command/sqlite", "nu-std/sqlite"]
sqlite = ["nu-command/sqlite", "nu-cmd-lang/sqlite", "nu-std/sqlite"]
[profile.release]
opt-level = "s" # Optimize for size

View File

@ -199,7 +199,7 @@ fn bench_record_nested_access(n: usize) -> impl IntoBenchmarks {
let nested_access = ".col".repeat(n);
bench_command(
format!("record_nested_access_{n}"),
format!("$record{nested_access} | ignore"),
format!("$record{} | ignore", nested_access),
stack,
engine,
)
@ -319,7 +319,7 @@ fn bench_eval_par_each(n: usize) -> impl IntoBenchmarks {
let stack = Stack::new();
bench_command(
format!("eval_par_each_{n}"),
format!("(1..{n}) | par-each -t 2 {{|_| 1 }} | ignore"),
format!("(1..{}) | par-each -t 2 {{|_| 1 }} | ignore", n),
stack,
engine,
)
@ -357,7 +357,7 @@ fn encode_json(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
let encoder = Rc::new(EncodingType::try_from_bytes(b"json").unwrap());
[benchmark_fn(
format!("encode_json_{row_cnt}_{col_cnt}"),
format!("encode_json_{}_{}", row_cnt, col_cnt),
move |b| {
let encoder = encoder.clone();
let test_data = test_data.clone();
@ -377,7 +377,7 @@ fn encode_msgpack(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
let encoder = Rc::new(EncodingType::try_from_bytes(b"msgpack").unwrap());
[benchmark_fn(
format!("encode_msgpack_{row_cnt}_{col_cnt}"),
format!("encode_msgpack_{}_{}", row_cnt, col_cnt),
move |b| {
let encoder = encoder.clone();
let test_data = test_data.clone();
@ -399,7 +399,7 @@ fn decode_json(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
encoder.encode(&test_data, &mut res).unwrap();
[benchmark_fn(
format!("decode_json_{row_cnt}_{col_cnt}"),
format!("decode_json_{}_{}", row_cnt, col_cnt),
move |b| {
let res = res.clone();
b.iter(move || {
@ -422,7 +422,7 @@ fn decode_msgpack(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
encoder.encode(&test_data, &mut res).unwrap();
[benchmark_fn(
format!("decode_msgpack_{row_cnt}_{col_cnt}"),
format!("decode_msgpack_{}_{}", row_cnt, col_cnt),
move |b| {
let res = res.clone();
b.iter(move || {

View File

@ -5,29 +5,29 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2024"
license = "MIT"
name = "nu-cli"
version = "0.106.1"
version = "0.105.2"
[lib]
bench = false
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.1" }
nu-command = { path = "../nu-command", version = "0.106.1" }
nu-std = { path = "../nu-std", version = "0.106.1" }
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.105.2" }
nu-command = { path = "../nu-command", version = "0.105.2" }
nu-std = { path = "../nu-std", version = "0.105.2" }
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }
rstest = { workspace = true, default-features = false }
tempfile = { workspace = true }
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
nu-engine = { path = "../nu-engine", version = "0.106.1", features = ["os"] }
nu-glob = { path = "../nu-glob", version = "0.106.1" }
nu-path = { path = "../nu-path", version = "0.106.1" }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.106.1", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", features = ["os"] }
nu-utils = { path = "../nu-utils", version = "0.106.1" }
nu-color-config = { path = "../nu-color-config", version = "0.106.1" }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
nu-engine = { path = "../nu-engine", version = "0.105.2", features = ["os"] }
nu-glob = { path = "../nu-glob", version = "0.105.2" }
nu-path = { path = "../nu-path", version = "0.105.2" }
nu-parser = { path = "../nu-parser", version = "0.105.2" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.105.2", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.105.2", features = ["os"] }
nu-utils = { path = "../nu-utils", version = "0.105.2" }
nu-color-config = { path = "../nu-color-config", version = "0.105.2" }
nu-ansi-term = { workspace = true }
reedline = { workspace = true, features = ["bashisms", "sqlite"] }

View File

@ -57,7 +57,7 @@ Note that history item IDs are ignored when importing from file."#
result: None,
},
Example {
example: "[[ command cwd ]; [ foo /home ]] | history import",
example: "[[ command_line cwd ]; [ foo /home ]] | history import",
description: "Append `foo` ran from `/home` to the current history",
result: None,
},

View File

@ -118,7 +118,7 @@ fn get_suggestions_by_value(
|| s.chars()
.any(|c: char| !(c.is_ascii_alphabetic() || ['_', '-'].contains(&c)))
{
format!("{s:?}")
format!("{:?}", s)
} else {
s
};

View File

@ -52,7 +52,7 @@ impl CommandCompletion {
continue;
};
let value = if matched_internal(&name) {
format!("^{name}")
format!("^{}", name)
} else {
name.clone()
};

View File

@ -176,7 +176,7 @@ impl NuCompleter {
&mut working_set,
Some("completer"),
// Add a placeholder `a` to the end
format!("{line}a").as_bytes(),
format!("{}a", line).as_bytes(),
false,
);
self.fetch_completions_by_block(block, &working_set, pos, offset, line, true)
@ -370,8 +370,7 @@ impl NuCompleter {
FileCompletion,
);
// Prioritize argument completions over (sub)commands
suggestions.splice(0..0, self.process_completion(&mut completer, &ctx));
suggestions.extend(self.process_completion(&mut completer, &ctx));
break;
}
@ -385,39 +384,33 @@ impl NuCompleter {
};
self.process_completion(&mut flag_completions, &ctx)
};
// Prioritize argument completions over (sub)commands
suggestions.splice(
0..0,
match arg {
// flags
Argument::Named(_) | Argument::Unknown(_)
if prefix.starts_with(b"-") =>
{
flag_completion_helper()
}
// only when `strip` == false
Argument::Positional(_) if prefix == b"-" => {
flag_completion_helper()
}
// complete according to expression type and command head
Argument::Positional(expr) => {
let command_head = working_set.get_decl(call.decl_id).name();
positional_arg_indices.push(arg_idx);
self.argument_completion_helper(
PositionalArguments {
command_head,
positional_arg_indices,
arguments: &call.arguments,
expr,
},
pos,
&ctx,
suggestions.is_empty(),
)
}
_ => vec![],
},
);
suggestions.extend(match arg {
// flags
Argument::Named(_) | Argument::Unknown(_)
if prefix.starts_with(b"-") =>
{
flag_completion_helper()
}
// only when `strip` == false
Argument::Positional(_) if prefix == b"-" => flag_completion_helper(),
// complete according to expression type and command head
Argument::Positional(expr) => {
let command_head = working_set.get_decl(call.decl_id).name();
positional_arg_indices.push(arg_idx);
self.argument_completion_helper(
PositionalArguments {
command_head,
positional_arg_indices,
arguments: &call.arguments,
expr,
},
pos,
&ctx,
suggestions.is_empty(),
)
}
_ => vec![],
});
break;
} else if !matches!(arg, Argument::Named(_)) {
positional_arg_indices.push(arg_idx);
@ -469,18 +462,10 @@ impl NuCompleter {
if let Some(external_result) =
self.external_completion(closure, &text_spans, offset, new_span)
{
// Prioritize external results over (sub)commands
suggestions.splice(0..0, external_result);
suggestions.extend(external_result);
return suggestions;
}
}
// for external path arguments with spaces, please check issue #15790
if suggestions.is_empty() {
let (new_span, prefix) =
strip_placeholder_if_any(working_set, &span, strip);
let ctx = Context::new(working_set, new_span, prefix, offset);
return self.process_completion(&mut FileCompletion, &ctx);
}
break;
}
}
@ -857,7 +842,7 @@ mod completer_tests {
for (line, has_result, begins_with, expected_values) in dataset {
let result = completer.fetch_completions_at(line, line.len());
// Test whether the result is empty or not
assert_eq!(!result.is_empty(), has_result, "line: {line}");
assert_eq!(!result.is_empty(), has_result, "line: {}", line);
// Test whether the result begins with the expected value
result
@ -872,7 +857,8 @@ mod completer_tests {
.filter(|x| *x)
.count(),
expected_values.len(),
"line: {line}"
"line: {}",
line
);
}
}

View File

@ -314,7 +314,7 @@ pub fn escape_path(path: String) -> String {
if path.contains('\'') {
// decide to use double quotes
// Path as Debug will do the escaping for `"`, `\`
format!("{path:?}")
format!("{:?}", path)
} else {
format!("'{path}'")
}

View File

@ -102,11 +102,7 @@ impl<T> NuMatcher<'_, T> {
options,
needle: needle.to_owned(),
state: State::Fuzzy {
matcher: Matcher::new({
let mut cfg = Config::DEFAULT;
cfg.prefer_prefix = true;
cfg
}),
matcher: Matcher::new(Config::DEFAULT),
atom,
items: Vec::new(),
},

View File

@ -129,7 +129,7 @@ impl Completer for DotNuCompletion {
.take_while(|c| "`'\"".contains(*c))
.collect::<String>();
for path in ["std", "std-rfc"] {
let path = format!("{surround_prefix}{path}");
let path = format!("{}{}", surround_prefix, path);
matcher.add(
path.clone(),
FileSuggestion {
@ -146,7 +146,7 @@ impl Completer for DotNuCompletion {
for sub_vp_id in sub_paths {
let (path, sub_vp) = working_set.get_virtual_path(*sub_vp_id);
let path = path
.strip_prefix(&format!("{base_dir}/"))
.strip_prefix(&format!("{}/", base_dir))
.unwrap_or(path)
.to_string();
matcher.add(

View File

@ -3,9 +3,9 @@ use nu_engine::eval_block;
use nu_parser::parse;
use nu_protocol::{
PipelineData, ShellError, Spanned, Value,
cli_error::report_compile_error,
debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet},
report_error::report_compile_error,
report_parse_error, report_parse_warning,
};
use std::sync::Arc;

View File

@ -5,9 +5,9 @@ use nu_parser::parse;
use nu_path::canonicalize_with;
use nu_protocol::{
PipelineData, ShellError, Span, Value,
cli_error::report_compile_error,
debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet},
report_error::report_compile_error,
report_parse_error, report_parse_warning,
shell_error::io::*,
};

View File

@ -1,4 +1,4 @@
use nu_engine::documentation::{FormatterValue, HelpStyle, get_flags_section};
use nu_engine::documentation::{HelpStyle, get_flags_section};
use nu_protocol::{Config, engine::EngineState, levenshtein_distance};
use nu_utils::IgnoreCaseExt;
use reedline::{Completer, Suggestion};
@ -66,11 +66,8 @@ impl NuHelpCompleter {
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
if !sig.named.is_empty() {
long_desc.push_str(&get_flags_section(&sig, &help_style, |v| match v {
FormatterValue::DefaultValue(value) => {
value.to_parsable_string(", ", &self.config)
}
FormatterValue::CodeString(text) => text.to_string(),
long_desc.push_str(&get_flags_section(&sig, &help_style, |v| {
v.to_parsable_string(", ", &self.config)
}))
}

View File

@ -3,8 +3,6 @@ use std::sync::Arc;
use nu_engine::command_prelude::*;
use reedline::{Highlighter, StyledText};
use crate::syntax_highlight::highlight_syntax;
#[derive(Clone)]
pub struct NuHighlight;
@ -16,11 +14,6 @@ impl Command for NuHighlight {
fn signature(&self) -> Signature {
Signature::build("nu-highlight")
.category(Category::Strings)
.switch(
"reject-garbage",
"Return an error if invalid syntax (garbage) was encountered",
Some('r'),
)
.input_output_types(vec![(Type::String, Type::String)])
}
@ -39,33 +32,19 @@ impl Command for NuHighlight {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let reject_garbage = call.has_flag(engine_state, stack, "reject-garbage")?;
let head = call.head;
let signals = engine_state.signals();
let engine_state = Arc::new(engine_state.clone());
let stack = Arc::new(stack.clone());
let highlighter = crate::NuHighlighter {
engine_state: Arc::new(engine_state.clone()),
stack: Arc::new(stack.clone()),
};
input.map(
move |x| match x.coerce_into_string() {
Ok(line) => {
let result = highlight_syntax(&engine_state, &stack, &line, line.len());
let highlights = match (reject_garbage, result.found_garbage) {
(false, _) => result.text,
(true, None) => result.text,
(true, Some(span)) => {
let error = ShellError::OutsideSpannedLabeledError {
src: line,
error: "encountered invalid syntax while highlighting".into(),
msg: "invalid syntax".into(),
span,
};
return Value::error(error, head);
}
};
let highlights = highlighter.highlight(&line, line.len());
Value::string(highlights.render_simple(), head)
}
Err(err) => Value::error(err, head),

View File

@ -1172,7 +1172,6 @@ fn edit_from_record(
"cutfromlinestart" => EditCommand::CutFromLineStart,
"cuttoend" => EditCommand::CutToEnd,
"cuttolineend" => EditCommand::CutToLineEnd,
"killline" => EditCommand::KillLine,
"cutwordleft" => EditCommand::CutWordLeft,
"cutbigwordleft" => EditCommand::CutBigWordLeft,
"cutwordright" => EditCommand::CutWordRight,

View File

@ -22,8 +22,8 @@ use nu_color_config::StyleComputer;
use nu_engine::env_to_strings;
use nu_engine::exit::cleanup_exit;
use nu_parser::{lex, parse, trim_quotes_str};
use nu_protocol::shell_error;
use nu_protocol::shell_error::io::IoError;
use nu_protocol::{BannerKind, shell_error};
use nu_protocol::{
HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, Value,
config::NuCursorShape,
@ -145,8 +145,8 @@ pub fn evaluate_repl(
if load_std_lib.is_none() {
match engine_state.get_config().show_banner {
BannerKind::None => {}
BannerKind::Short => {
Value::Bool { val: false, .. } => {}
Value::String { ref val, .. } if val == "short" => {
eval_source(
engine_state,
&mut unique_stack,
@ -156,7 +156,7 @@ pub fn evaluate_repl(
false,
);
}
BannerKind::Full => {
_ => {
eval_source(
engine_state,
&mut unique_stack,
@ -239,7 +239,7 @@ fn escape_special_vscode_bytes(input: &str) -> Result<String, ShellError> {
match byte {
// Escape bytes below 0x20
b if b < 0x20 => format!("\\x{byte:02X}").into_bytes(),
b if b < 0x20 => format!("\\x{:02X}", byte).into_bytes(),
// Escape semicolon as \x3B
b';' => "\\x3B".to_string().into_bytes(),
// Escape backslash as \\
@ -1097,7 +1097,8 @@ fn run_shell_integration_osc633(
// If we're in vscode, run their specific ansi escape sequence.
// This is helpful for ctrl+g to change directories in the terminal.
run_ansi_sequence(&format!(
"{VSCODE_CWD_PROPERTY_MARKER_PREFIX}{path}{VSCODE_CWD_PROPERTY_MARKER_SUFFIX}"
"{}{}{}",
VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX
));
perf!(
@ -1113,7 +1114,10 @@ fn run_shell_integration_osc633(
//OSC 633 ; E ; <commandline> [; <nonce] ST - Explicitly set the command line with an optional nonce.
run_ansi_sequence(&format!(
"{VSCODE_COMMANDLINE_MARKER_PREFIX}{replaced_cmd_text}{VSCODE_COMMANDLINE_MARKER_SUFFIX}"
"{}{}{}",
VSCODE_COMMANDLINE_MARKER_PREFIX,
replaced_cmd_text,
VSCODE_COMMANDLINE_MARKER_SUFFIX
));
}
}
@ -1489,7 +1493,7 @@ mod test_auto_cd {
// Parse the input. It must be an auto-cd operation.
let op = parse_operation(input.to_string(), &engine_state, &stack).unwrap();
let ReplOperation::AutoCd { cwd, target, span } = op else {
panic!("'{input}' was not parsed into an auto-cd operation")
panic!("'{}' was not parsed into an auto-cd operation", input)
};
// Perform the auto-cd operation.

View File

@ -17,173 +17,147 @@ pub struct NuHighlighter {
}
impl Highlighter for NuHighlighter {
fn highlight(&self, line: &str, cursor: usize) -> StyledText {
let result = highlight_syntax(&self.engine_state, &self.stack, line, cursor);
result.text
}
}
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
trace!("highlighting: {}", line);
/// Result of a syntax highlight operation
#[derive(Default)]
pub(crate) struct HighlightResult {
/// The highlighted text
pub(crate) text: StyledText,
/// The span of any garbage that was highlighted
pub(crate) found_garbage: Option<Span>,
}
let config = self.stack.get_config(&self.engine_state);
let highlight_resolved_externals = config.highlight_resolved_externals;
let mut working_set = StateWorkingSet::new(&self.engine_state);
let block = parse(&mut working_set, None, line.as_bytes(), false);
let (shapes, global_span_offset) = {
let mut shapes = flatten_block(&working_set, &block);
// Highlighting externals has a config point because of concerns that using which to resolve
// externals may slow down things too much.
if highlight_resolved_externals {
for (span, shape) in shapes.iter_mut() {
if *shape == FlatShape::External {
let str_contents =
working_set.get_span_contents(Span::new(span.start, span.end));
pub(crate) fn highlight_syntax(
engine_state: &EngineState,
stack: &Stack,
line: &str,
cursor: usize,
) -> HighlightResult {
trace!("highlighting: {}", line);
let config = stack.get_config(engine_state);
let highlight_resolved_externals = config.highlight_resolved_externals;
let mut working_set = StateWorkingSet::new(engine_state);
let block = parse(&mut working_set, None, line.as_bytes(), false);
let (shapes, global_span_offset) = {
let mut shapes = flatten_block(&working_set, &block);
// Highlighting externals has a config point because of concerns that using which to resolve
// externals may slow down things too much.
if highlight_resolved_externals {
for (span, shape) in shapes.iter_mut() {
if *shape == FlatShape::External {
let str_contents =
working_set.get_span_contents(Span::new(span.start, span.end));
let str_word = String::from_utf8_lossy(str_contents).to_string();
let paths = env::path_str(engine_state, stack, *span).ok();
let res = if let Ok(cwd) = engine_state.cwd(Some(stack)) {
which::which_in(str_word, paths.as_ref(), cwd).ok()
} else {
which::which_in_global(str_word, paths.as_ref())
.ok()
.and_then(|mut i| i.next())
};
if res.is_some() {
*shape = FlatShape::ExternalResolved;
let str_word = String::from_utf8_lossy(str_contents).to_string();
let paths = env::path_str(&self.engine_state, &self.stack, *span).ok();
#[allow(deprecated)]
let res = if let Ok(cwd) =
env::current_dir_str(&self.engine_state, &self.stack)
{
which::which_in(str_word, paths.as_ref(), cwd).ok()
} else {
which::which_in_global(str_word, paths.as_ref())
.ok()
.and_then(|mut i| i.next())
};
if res.is_some() {
*shape = FlatShape::ExternalResolved;
}
}
}
}
}
(shapes, engine_state.next_span_start())
};
let mut result = HighlightResult::default();
let mut last_seen_span = global_span_offset;
let global_cursor_offset = cursor + global_span_offset;
let matching_brackets_pos = find_matching_brackets(
line,
&working_set,
&block,
global_span_offset,
global_cursor_offset,
);
for shape in &shapes {
if shape.0.end <= last_seen_span
|| last_seen_span < global_span_offset
|| shape.0.start < global_span_offset
{
// We've already output something for this span
// so just skip this one
continue;
}
if shape.0.start > last_seen_span {
let gap = line
[(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)]
.to_string();
result.text.push((Style::new(), gap));
}
let next_token = line
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
.to_string();
let mut add_colored_token = |shape: &FlatShape, text: String| {
result
.text
.push((get_shape_color(shape.as_str(), &config), text));
(shapes, self.engine_state.next_span_start())
};
match shape.1 {
FlatShape::Garbage => {
result.found_garbage.get_or_insert_with(|| {
Span::new(
shape.0.start - global_span_offset,
shape.0.end - global_span_offset,
)
});
add_colored_token(&shape.1, next_token)
let mut output = StyledText::default();
let mut last_seen_span = global_span_offset;
let global_cursor_offset = _cursor + global_span_offset;
let matching_brackets_pos = find_matching_brackets(
line,
&working_set,
&block,
global_span_offset,
global_cursor_offset,
);
for shape in &shapes {
if shape.0.end <= last_seen_span
|| last_seen_span < global_span_offset
|| shape.0.start < global_span_offset
{
// We've already output something for this span
// so just skip this one
continue;
}
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
FlatShape::Binary => add_colored_token(&shape.1, next_token),
FlatShape::Bool => add_colored_token(&shape.1, next_token),
FlatShape::Int => add_colored_token(&shape.1, next_token),
FlatShape::Float => add_colored_token(&shape.1, next_token),
FlatShape::Range => add_colored_token(&shape.1, next_token),
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
FlatShape::External => add_colored_token(&shape.1, next_token),
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
FlatShape::ExternalResolved => add_colored_token(&shape.1, next_token),
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
FlatShape::Literal => add_colored_token(&shape.1, next_token),
FlatShape::Operator => add_colored_token(&shape.1, next_token),
FlatShape::Signature => add_colored_token(&shape.1, next_token),
FlatShape::String => add_colored_token(&shape.1, next_token),
FlatShape::RawString => add_colored_token(&shape.1, next_token),
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
FlatShape::List
| FlatShape::Table
| FlatShape::Record
| FlatShape::Block
| FlatShape::Closure => {
let span = shape.0;
let shape = &shape.1;
let spans = split_span_by_highlight_positions(
line,
span,
&matching_brackets_pos,
global_span_offset,
);
for (part, highlight) in spans {
let start = part.start - span.start;
let end = part.end - span.start;
let text = next_token[start..end].to_string();
let mut style = get_shape_color(shape.as_str(), &config);
if highlight {
style = get_matching_brackets_style(style, &config);
if shape.0.start > last_seen_span {
let gap = line
[(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)]
.to_string();
output.push((Style::new(), gap));
}
let next_token = line
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
.to_string();
let mut add_colored_token = |shape: &FlatShape, text: String| {
output.push((get_shape_color(shape.as_str(), &config), text));
};
match shape.1 {
FlatShape::Garbage => add_colored_token(&shape.1, next_token),
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
FlatShape::Binary => add_colored_token(&shape.1, next_token),
FlatShape::Bool => add_colored_token(&shape.1, next_token),
FlatShape::Int => add_colored_token(&shape.1, next_token),
FlatShape::Float => add_colored_token(&shape.1, next_token),
FlatShape::Range => add_colored_token(&shape.1, next_token),
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
FlatShape::External => add_colored_token(&shape.1, next_token),
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
FlatShape::ExternalResolved => add_colored_token(&shape.1, next_token),
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
FlatShape::Literal => add_colored_token(&shape.1, next_token),
FlatShape::Operator => add_colored_token(&shape.1, next_token),
FlatShape::Signature => add_colored_token(&shape.1, next_token),
FlatShape::String => add_colored_token(&shape.1, next_token),
FlatShape::RawString => add_colored_token(&shape.1, next_token),
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
FlatShape::List
| FlatShape::Table
| FlatShape::Record
| FlatShape::Block
| FlatShape::Closure => {
let span = shape.0;
let shape = &shape.1;
let spans = split_span_by_highlight_positions(
line,
span,
&matching_brackets_pos,
global_span_offset,
);
for (part, highlight) in spans {
let start = part.start - span.start;
let end = part.end - span.start;
let text = next_token[start..end].to_string();
let mut style = get_shape_color(shape.as_str(), &config);
if highlight {
style = get_matching_brackets_style(style, &config);
}
output.push((style, text));
}
result.text.push((style, text));
}
}
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
FlatShape::Directory => add_colored_token(&shape.1, next_token),
FlatShape::GlobInterpolation => add_colored_token(&shape.1, next_token),
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
add_colored_token(&shape.1, next_token)
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
FlatShape::Directory => add_colored_token(&shape.1, next_token),
FlatShape::GlobInterpolation => add_colored_token(&shape.1, next_token),
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
add_colored_token(&shape.1, next_token)
}
FlatShape::Flag => add_colored_token(&shape.1, next_token),
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
}
FlatShape::Flag => add_colored_token(&shape.1, next_token),
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
last_seen_span = shape.0.end;
}
last_seen_span = shape.0.end;
}
let remainder = line[(last_seen_span - global_span_offset)..].to_string();
if !remainder.is_empty() {
result.text.push((Style::new(), remainder));
}
let remainder = line[(last_seen_span - global_span_offset)..].to_string();
if !remainder.is_empty() {
output.push((Style::new(), remainder));
}
result
output
}
}
fn split_span_by_highlight_positions(

View File

@ -5,9 +5,9 @@ use nu_engine::{eval_block, eval_block_with_early_return};
use nu_parser::{Token, TokenContents, lex, parse, unescape_unquote_string};
use nu_protocol::{
PipelineData, ShellError, Span, Value,
cli_error::report_compile_error,
debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet},
report_error::report_compile_error,
report_parse_error, report_parse_warning, report_shell_error,
};
#[cfg(windows)]

View File

@ -5,9 +5,3 @@ fn nu_highlight_not_expr() {
let actual = nu!("'not false' | nu-highlight | ansi strip");
assert_eq!(actual.out, "not false");
}
#[test]
fn nu_highlight_where_row_condition() {
let actual = nu!("'ls | where a b 12345(' | nu-highlight | ansi strip");
assert_eq!(actual.out, "ls | where a b 12345(");
}

View File

@ -9,16 +9,14 @@ use std::{
use nu_cli::NuCompleter;
use nu_engine::eval_block;
use nu_parser::parse;
use nu_path::{AbsolutePathBuf, expand_tilde};
use nu_path::expand_tilde;
use nu_protocol::{Config, PipelineData, debugger::WithoutDebug, engine::StateWorkingSet};
use nu_std::load_standard_library;
use nu_test_support::fs;
use reedline::{Completer, Suggestion};
use rstest::{fixture, rstest};
use support::{
completions_helpers::{
new_dotnu_engine, new_engine_helper, new_external_engine, new_partial_engine,
new_quote_engine,
new_dotnu_engine, new_external_engine, new_partial_engine, new_quote_engine,
},
file, folder, match_suggestions, match_suggestions_by_string, new_engine,
};
@ -125,7 +123,7 @@ fn custom_completer_with_options(
global_opts,
completions
.iter()
.map(|comp| format!("'{comp}'"))
.map(|comp| format!("'{}'", comp))
.collect::<Vec<_>>()
.join(", "),
completer_opts,
@ -309,10 +307,10 @@ fn custom_arguments_and_subcommands() {
let suggestions = completer.complete(completion_str, completion_str.len());
// including both subcommand and directory completions
let expected = [
"foo test bar".into(),
folder("test_a"),
file("test_a_symlink"),
folder("test_b"),
"foo test bar".into(),
];
match_suggestions_by_string(&expected, &suggestions);
}
@ -330,7 +328,7 @@ fn custom_flags_and_subcommands() {
let completion_str = "foo --test";
let suggestions = completer.complete(completion_str, completion_str.len());
// including both flag and directory completions
let expected: Vec<_> = vec!["--test", "foo --test bar"];
let expected: Vec<_> = vec!["foo --test bar", "--test"];
match_suggestions(&expected, &suggestions);
}
@ -718,16 +716,6 @@ fn external_completer_fallback() {
let expected = [folder("test_a"), file("test_a_symlink"), folder("test_b")];
let suggestions = run_external_completion(block, input);
match_suggestions_by_string(&expected, &suggestions);
// issue #15790
let input = "foo `dir with space/`";
let expected = vec!["`dir with space/bar baz`", "`dir with space/foo`"];
let suggestions = run_external_completion_within_pwd(
block,
input,
fs::fixtures().join("external_completions"),
);
match_suggestions(&expected, &suggestions);
}
/// Fallback to external completions for flags of `sudo`
@ -1480,12 +1468,11 @@ fn command_watch_with_filecompletion() {
match_suggestions(&expected_paths, &suggestions)
}
#[test]
fn subcommand_vs_external_completer() {
#[rstest]
fn subcommand_completions() {
let (_, _, mut engine, mut stack) = new_engine();
let commands = r#"
$env.config.completions.algorithm = "fuzzy"
$env.config.completions.external.completer = {|spans| ["external"]}
def foo-test-command [] {}
def "foo-test-command bar" [] {}
def "foo-test-command aagap bcr" [] {}
@ -1498,7 +1485,6 @@ fn subcommand_vs_external_completer() {
let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions(
&vec![
"external",
"food bar",
"foo-test-command bar",
"foo-test-command aagap bcr",
@ -1508,7 +1494,7 @@ fn subcommand_vs_external_completer() {
let prefix = "foot bar";
let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions(&vec!["external", "foo-test-command bar"], &suggestions);
match_suggestions(&vec!["foo-test-command bar"], &suggestions);
}
#[test]
@ -2117,15 +2103,11 @@ fn alias_of_another_alias() {
match_suggestions(&expected_paths, &suggestions)
}
fn run_external_completion_within_pwd(
completer: &str,
input: &str,
pwd: AbsolutePathBuf,
) -> Vec<Suggestion> {
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
let completer = format!("$env.config.completions.external.completer = {completer}");
// Create a new engine
let (_, _, mut engine_state, mut stack) = new_engine_helper(pwd);
let (_, _, mut engine_state, mut stack) = new_engine();
let (block, delta) = {
let mut working_set = StateWorkingSet::new(&engine_state);
let block = parse(&mut working_set, None, completer.as_bytes(), false);
@ -2149,10 +2131,6 @@ fn run_external_completion_within_pwd(
completer.complete(input, input.len())
}
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
run_external_completion_within_pwd(completer, input, fs::fixtures().join("completions"))
}
#[test]
fn unknown_command_completion() {
let (_, _, engine, stack) = new_engine();

View File

@ -5,7 +5,7 @@ edition = "2024"
license = "MIT"
name = "nu-cmd-base"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
version = "0.106.1"
version = "0.105.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,10 +13,10 @@ version = "0.106.1"
workspace = true
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-path = { path = "../nu-path", version = "0.106.1" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.105.2" }
nu-path = { path = "../nu-path", version = "0.105.2" }
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
indexmap = { workspace = true }
miette = { workspace = true }

View File

@ -1,11 +1,11 @@
use miette::Result;
use nu_engine::{eval_block, eval_block_with_early_return, redirect_env};
use nu_engine::{eval_block, eval_block_with_early_return};
use nu_parser::parse;
use nu_protocol::{
PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
cli_error::{report_parse_error, report_shell_error},
debugger::WithoutDebug,
engine::{Closure, EngineState, Stack, StateWorkingSet},
report_error::{report_parse_error, report_shell_error},
};
use std::{collections::HashMap, sync::Arc};
@ -325,7 +325,19 @@ fn run_hook(
}
// If all went fine, preserve the environment of the called block
redirect_env(engine_state, stack, &callee_stack);
let caller_env_vars = stack.get_env_var_names(engine_state);
// remove env vars that are present in the caller but not in the callee
// (the callee hid them)
for var in caller_env_vars.iter() {
if !callee_stack.has_env_var(engine_state, var) {
stack.remove_env_var(engine_state, var);
}
}
// add new env vars from callee to caller
for (var, value) in callee_stack.get_stack_env_vars() {
stack.add_env_var(var, value);
}
Ok(pipeline_data)
}

View File

@ -5,7 +5,7 @@ edition = "2024"
license = "MIT"
name = "nu-cmd-extra"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
version = "0.106.1"
version = "0.105.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -16,13 +16,13 @@ bench = false
workspace = true
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-json = { version = "0.106.1", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-pretty-hex = { version = "0.106.1", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.106.1", default-features = false }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
nu-json = { version = "0.105.2", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.105.2" }
nu-pretty-hex = { version = "0.105.2", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.105.2", default-features = false }
# Potential dependencies for extras
heck = { workspace = true }
@ -37,6 +37,6 @@ itertools = { workspace = true }
mime = { workspace = true }
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.1" }
nu-command = { path = "../nu-command", version = "0.106.1" }
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.105.2" }
nu-command = { path = "../nu-command", version = "0.105.2" }
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }

View File

@ -12,10 +12,7 @@ impl Command for UpdateCells {
fn signature(&self) -> Signature {
Signature::build("update cells")
.input_output_types(vec![
(Type::table(), Type::table()),
(Type::record(), Type::record()),
])
.input_output_types(vec![(Type::table(), Type::table())])
.required(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
@ -80,15 +77,6 @@ impl Command for UpdateCells {
"2021-11-18" => Value::test_string(""),
})])),
},
Example {
example: r#"{a: 1, b: 2, c: 3} | update cells { $in + 10 }"#,
description: "Update each value in a record.",
result: Some(Value::test_record(record! {
"a" => Value::test_int(11),
"b" => Value::test_int(12),
"c" => Value::test_int(13),
})),
},
]
}
@ -97,7 +85,7 @@ impl Command for UpdateCells {
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
mut input: PipelineData,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let closure: Closure = call.req(engine_state, stack, 0)?;
@ -114,51 +102,14 @@ impl Command for UpdateCells {
let metadata = input.metadata();
match input {
PipelineData::Value(
Value::Record {
ref mut val,
internal_span,
},
..,
) => {
let val = val.to_mut();
update_record(
val,
&mut ClosureEval::new(engine_state, stack, closure),
internal_span,
columns.as_ref(),
);
Ok(input)
}
_ => Ok(UpdateCellIterator {
iter: input.into_iter(),
closure: ClosureEval::new(engine_state, stack, closure),
columns,
span: head,
}
.into_pipeline_data(head, engine_state.signals().clone())
.set_metadata(metadata)),
}
}
}
fn update_record(
record: &mut Record,
closure: &mut ClosureEval,
span: Span,
cols: Option<&HashSet<String>>,
) {
if let Some(columns) = cols {
for (col, val) in record.iter_mut() {
if columns.contains(col) {
*val = eval_value(closure, span, std::mem::take(val));
}
}
} else {
for (_, val) in record.iter_mut() {
*val = eval_value(closure, span, std::mem::take(val))
Ok(UpdateCellIterator {
iter: input.into_iter(),
closure: ClosureEval::new(engine_state, stack, closure),
columns,
span: head,
}
.into_pipeline_data(head, engine_state.signals().clone())
.set_metadata(metadata))
}
}
@ -177,7 +128,18 @@ impl Iterator for UpdateCellIterator {
let value = if let Value::Record { val, .. } = &mut value {
let val = val.to_mut();
update_record(val, &mut self.closure, self.span, self.columns.as_ref());
if let Some(columns) = &self.columns {
for (col, val) in val.iter_mut() {
if columns.contains(col) {
*val = eval_value(&mut self.closure, self.span, std::mem::take(val));
}
}
} else {
for (_, val) in val.iter_mut() {
*val = eval_value(&mut self.closure, self.span, std::mem::take(val))
}
}
value
} else {
eval_value(&mut self.closure, self.span, value)

View File

@ -188,7 +188,7 @@ fn get_theme_from_asset_file(
Some(t) => t,
None => {
return Err(ShellError::TypeMismatch {
err_message: format!("Unknown HTML theme '{theme_name}'"),
err_message: format!("Unknown HTML theme '{}'", theme_name),
span: theme_span,
});
}
@ -774,7 +774,8 @@ mod tests {
for key in required_keys {
assert!(
theme_map.contains_key(key),
"Expected theme to contain key '{key}'"
"Expected theme to contain key '{}'",
key
);
}
}
@ -791,13 +792,15 @@ mod tests {
if let Err(err) = result {
assert!(
matches!(err, ShellError::TypeMismatch { .. }),
"Expected TypeMismatch error, got: {err:?}"
"Expected TypeMismatch error, got: {:?}",
err
);
if let ShellError::TypeMismatch { err_message, span } = err {
assert!(
err_message.contains("doesnt-exist"),
"Error message should mention theme name, got: {err_message}"
"Error message should mention theme name, got: {}",
err_message
);
assert_eq!(span.start, 0);
assert_eq!(span.end, 13);

View File

@ -161,28 +161,28 @@ fn convert_to_smallest_number_type(num: i64, span: Span) -> Value {
let bytes = v.to_ne_bytes();
let mut raw_string = "".to_string();
for ch in bytes {
raw_string.push_str(&format!("{ch:08b} "));
raw_string.push_str(&format!("{:08b} ", ch));
}
Value::string(raw_string.trim(), span)
} else if let Some(v) = num.to_i16() {
let bytes = v.to_ne_bytes();
let mut raw_string = "".to_string();
for ch in bytes {
raw_string.push_str(&format!("{ch:08b} "));
raw_string.push_str(&format!("{:08b} ", ch));
}
Value::string(raw_string.trim(), span)
} else if let Some(v) = num.to_i32() {
let bytes = v.to_ne_bytes();
let mut raw_string = "".to_string();
for ch in bytes {
raw_string.push_str(&format!("{ch:08b} "));
raw_string.push_str(&format!("{:08b} ", ch));
}
Value::string(raw_string.trim(), span)
} else {
let bytes = num.to_ne_bytes();
let mut raw_string = "".to_string();
for ch in bytes {
raw_string.push_str(&format!("{ch:08b} "));
raw_string.push_str(&format!("{:08b} ", ch));
}
Value::string(raw_string.trim(), span)
}
@ -193,7 +193,7 @@ fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
Value::Binary { val, .. } => {
let mut raw_string = "".to_string();
for ch in val {
raw_string.push_str(&format!("{ch:08b} "));
raw_string.push_str(&format!("{:08b} ", ch));
}
Value::string(raw_string.trim(), span)
}
@ -204,7 +204,7 @@ fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
let raw_bytes = val.as_bytes();
let mut raw_string = "".to_string();
for ch in raw_bytes {
raw_string.push_str(&format!("{ch:08b} "));
raw_string.push_str(&format!("{:08b} ", ch));
}
Value::string(raw_string.trim(), span)
}

View File

@ -16,11 +16,6 @@ impl Command for FormatNumber {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("format number")
.input_output_types(vec![(Type::Number, Type::record())])
.switch(
"no-prefix",
"don't include the binary, hex or octal prefixes",
Some('n'),
)
.category(Category::Conversions)
}
@ -29,36 +24,20 @@ impl Command for FormatNumber {
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Get a record containing multiple formats for the number 42",
example: "42 | format number",
result: Some(Value::test_record(record! {
"debug" => Value::test_string("42"),
"display" => Value::test_string("42"),
"binary" => Value::test_string("0b101010"),
"lowerexp" => Value::test_string("4.2e1"),
"upperexp" => Value::test_string("4.2E1"),
"lowerhex" => Value::test_string("0x2a"),
"upperhex" => Value::test_string("0x2A"),
"octal" => Value::test_string("0o52"),
})),
},
Example {
description: "Format float without prefixes",
example: "3.14 | format number --no-prefix",
result: Some(Value::test_record(record! {
"debug" => Value::test_string("3.14"),
"display" => Value::test_string("3.14"),
"binary" => Value::test_string("100000000001001000111101011100001010001111010111000010100011111"),
"lowerexp" => Value::test_string("3.14e0"),
"upperexp" => Value::test_string("3.14E0"),
"lowerhex" => Value::test_string("40091eb851eb851f"),
"upperhex" => Value::test_string("40091EB851EB851F"),
"octal" => Value::test_string("400110753412172702437"),
})),
},
]
vec![Example {
description: "Get a record containing multiple formats for the number 42",
example: "42 | format number",
result: Some(Value::test_record(record! {
"binary" => Value::test_string("0b101010"),
"debug" => Value::test_string("42"),
"display" => Value::test_string("42"),
"lowerexp" => Value::test_string("4.2e1"),
"lowerhex" => Value::test_string("0x2a"),
"octal" => Value::test_string("0o52"),
"upperexp" => Value::test_string("4.2E1"),
"upperhex" => Value::test_string("0x2A"),
})),
}]
}
fn run(
@ -80,24 +59,14 @@ pub(crate) fn format_number(
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
if call.has_flag(engine_state, stack, "no-prefix")? {
operate(
action_no_prefix,
args,
input,
call.head,
engine_state.signals(),
)
} else {
operate(action, args, input, call.head, engine_state.signals())
}
operate(action, args, input, call.head, engine_state.signals())
}
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input {
Value::Float { val, .. } => format_f64(*val, false, span),
Value::Int { val, .. } => format_i64(*val, false, span),
Value::Filesize { val, .. } => format_i64(val.get(), false, span),
Value::Float { val, .. } => format_f64(*val, span),
Value::Int { val, .. } => format_i64(*val, span),
Value::Filesize { val, .. } => format_i64(val.get(), span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::error(
@ -112,80 +81,33 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
}
}
fn action_no_prefix(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input {
Value::Float { val, .. } => format_f64(*val, true, span),
Value::Int { val, .. } => format_i64(*val, true, span),
Value::Filesize { val, .. } => format_i64(val.get(), true, span),
// Propagate errors by explicitly matching them before the final case.
Value::Error { .. } => input.clone(),
other => Value::error(
ShellError::OnlySupportsThisInputType {
exp_input_type: "float, int, or filesize".into(),
wrong_type: other.get_type().to_string(),
dst_span: span,
src_span: other.span(),
},
span,
),
}
}
fn format_i64(num: i64, no_prefix: bool, span: Span) -> Value {
fn format_i64(num: i64, span: Span) -> Value {
Value::record(
record! {
"binary" => Value::string(format!("{num:#b}"), span),
"debug" => Value::string(format!("{num:#?}"), span),
"display" => Value::string(format!("{num}"), span),
"binary" => Value::string(
if no_prefix { format!("{num:b}") } else { format!("{num:#b}") },
span,
),
"lowerexp" => Value::string(format!("{num:#e}"), span),
"lowerhex" => Value::string(format!("{num:#x}"), span),
"octal" => Value::string(format!("{num:#o}"), span),
"upperexp" => Value::string(format!("{num:#E}"), span),
"lowerhex" => Value::string(
if no_prefix { format!("{num:x}") } else { format!("{num:#x}") },
span,
),
"upperhex" => Value::string(
if no_prefix { format!("{num:X}") } else { format!("{num:#X}") },
span,
),
"octal" => Value::string(
if no_prefix { format!("{num:o}") } else { format!("{num:#o}") },
span,
)
"upperhex" => Value::string(format!("{num:#X}"), span),
},
span,
)
}
fn format_f64(num: f64, no_prefix: bool, span: Span) -> Value {
fn format_f64(num: f64, span: Span) -> Value {
Value::record(
record! {
"binary" => Value::string(format!("{:b}", num.to_bits()), span),
"debug" => Value::string(format!("{num:#?}"), span),
"display" => Value::string(format!("{num}"), span),
"binary" => Value::string(
if no_prefix {
format!("{:b}", num.to_bits())
} else {
format!("{:#b}", num.to_bits())
},
span,
),
"lowerexp" => Value::string(format!("{num:#e}"), span),
"lowerhex" => Value::string(format!("{:0x}", num.to_bits()), span),
"octal" => Value::string(format!("{:0o}", num.to_bits()), span),
"upperexp" => Value::string(format!("{num:#E}"), span),
"lowerhex" => Value::string(
if no_prefix { format!("{:x}", num.to_bits()) } else { format!("{:#x}", num.to_bits()) },
span,
),
"upperhex" => Value::string(
if no_prefix { format!("{:X}", num.to_bits()) } else { format!("{:#X}", num.to_bits()) },
span,
),
"octal" => Value::string(
if no_prefix { format!("{:o}", num.to_bits()) } else { format!("{:#o}", num.to_bits()) },
span,
)
"upperhex" => Value::string(format!("{:0X}", num.to_bits()), span),
},
span,
)

View File

@ -6,7 +6,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
edition = "2024"
license = "MIT"
name = "nu-cmd-lang"
version = "0.106.1"
version = "0.105.2"
[lib]
bench = false
@ -15,18 +15,17 @@ bench = false
workspace = true
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-experimental = { path = "../nu-experimental", version = "0.106.1" }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.106.1", default-features = false }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
nu-parser = { path = "../nu-parser", version = "0.105.2" }
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
nu-utils = { path = "../nu-utils", version = "0.105.2", default-features = false }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
itertools = { workspace = true }
shadow-rs = { version = "1.2", default-features = false }
shadow-rs = { version = "1.1", default-features = false }
[build-dependencies]
shadow-rs = { version = "1.2", default-features = false, features = ["build"] }
shadow-rs = { version = "1.1", default-features = false, features = ["build"] }
[dev-dependencies]
quickcheck = { workspace = true }
@ -44,3 +43,8 @@ plugin = [
"nu-protocol/plugin",
"os",
]
trash-support = []
sqlite = []
static-link-openssl = []
system-clipboard = []

View File

@ -296,7 +296,7 @@ fn run(
} else {
let value = stream.into_value();
let base_description = value.get_type().to_string();
Value::string(format!("{base_description} (stream)"), head)
Value::string(format!("{} (stream)", base_description), head)
}
}
PipelineData::Value(value, ..) => {

View File

@ -264,7 +264,7 @@ fn bind_args_to(
.expect("internal error: all custom parameters must have var_ids");
if let Some(result) = val_iter.next() {
let param_type = param.shape.to_type();
if !result.is_subtype_of(&param_type) {
if required && !result.is_subtype_of(&param_type) {
return Err(ShellError::CantConvert {
to_type: param.shape.to_type().to_string(),
from_type: result.get_type().to_string(),

View File

@ -229,7 +229,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
error: "invalid error format.".into(),
msg: "`$.label.start` should be smaller than `$.label.end`".into(),
span: Some(label_span),
help: Some(format!("{span_start} > {span_end}")),
help: Some(format!("{} > {}", span_start, span_end)),
inner: vec![],
};
}

View File

@ -60,13 +60,11 @@ impl Command for If {
) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?;
let cond = call.positional_nth(0).expect("checked through parser");
let then_expr = call.positional_nth(1).expect("checked through parser");
let then_block = then_expr
let then_block = call
.positional_nth(1)
.expect("checked through parser")
.as_block()
.ok_or_else(|| ShellError::TypeMismatch {
err_message: "expected block".into(),
span: then_expr.span,
})?;
.expect("internal error: missing block");
let else_case = call.positional_nth(2);
if eval_constant(working_set, cond)?.as_bool()? {

View File

@ -69,5 +69,5 @@ pub use return_::Return;
pub use scope::*;
pub use try_::Try;
pub use use_::Use;
pub use version::{VERSION_NU_FEATURES, Version};
pub use version::Version;
pub use while_::While;

View File

@ -96,7 +96,6 @@ impl Command for OverlayHide {
for (name, val) in env_vars_to_keep {
stack.add_env_var(name, val);
}
stack.update_config(engine_state)?;
Ok(PipelineData::empty())
}

View File

@ -158,7 +158,7 @@ impl Command for OverlayUse {
}
let eval_block = get_eval_block(engine_state);
let _ = eval_block(engine_state, &mut callee_stack, block, input)?;
let _ = eval_block(engine_state, &mut callee_stack, block, input);
// The export-env block should see the env vars *before* activating this overlay
caller_stack.add_overlay(overlay_name);
@ -178,7 +178,6 @@ impl Command for OverlayUse {
}
} else {
caller_stack.add_overlay(overlay_name);
caller_stack.update_config(engine_state)?;
}
Ok(PipelineData::empty())

View File

@ -1,48 +1,11 @@
use std::{borrow::Cow, sync::OnceLock};
use std::sync::OnceLock;
use itertools::Itertools;
use nu_engine::command_prelude::*;
use nu_protocol::engine::StateWorkingSet;
use shadow_rs::shadow;
shadow!(build);
/// Static container for the cargo features used by the `version` command.
///
/// This `OnceLock` holds the features from `nu`.
/// When you build `nu_cmd_lang`, Cargo doesn't pass along the same features that `nu` itself uses.
/// By setting this static before calling `version`, you make it show `nu`'s features instead
/// of `nu_cmd_lang`'s.
///
/// Embedders can set this to any feature list they need, but in most cases you'll probably want to
/// pass the cargo features of your host binary.
///
/// # How to get cargo features in your build script
///
/// In your binary's build script:
/// ```rust,ignore
/// // Re-export CARGO_CFG_FEATURE to the main binary.
/// // It holds all the features that cargo sets for your binary as a comma-separated list.
/// println!(
/// "cargo:rustc-env=NU_FEATURES={}",
/// std::env::var("CARGO_CFG_FEATURE").expect("set by cargo")
/// );
/// ```
///
/// Then, before you call `version`:
/// ```rust,ignore
/// // This uses static strings, but since we're using `Cow`, you can also pass owned strings.
/// let features = env!("NU_FEATURES")
/// .split(',')
/// .map(Cow::Borrowed)
/// .collect();
///
/// nu_cmd_lang::VERSION_NU_FEATURES
/// .set(features)
/// .expect("couldn't set VERSION_NU_FEATURES");
/// ```
pub static VERSION_NU_FEATURES: OnceLock<Vec<Cow<'static, str>>> = OnceLock::new();
#[derive(Clone)]
pub struct Version;
@ -150,17 +113,7 @@ pub fn version(engine_state: &EngineState, span: Span) -> Result<PipelineData, S
record.push(
"features",
Value::string(
VERSION_NU_FEATURES
.get()
.as_ref()
.map(|v| v.as_slice())
.unwrap_or_default()
.iter()
.filter(|f| !f.starts_with("dep:"))
.join(", "),
span,
),
Value::string(features_enabled().join(", "), span),
);
#[cfg(not(feature = "plugin"))]
@ -188,17 +141,6 @@ pub fn version(engine_state: &EngineState, span: Span) -> Result<PipelineData, S
);
}
record.push(
"experimental_options",
Value::string(
nu_experimental::ALL
.iter()
.map(|option| format!("{}={}", option.identifier(), option.get()))
.join(", "),
span,
),
);
Ok(Value::record(record, span).into_pipeline_data())
}
@ -222,12 +164,42 @@ fn global_allocator() -> &'static str {
"standard"
}
fn features_enabled() -> Vec<String> {
let mut names = vec!["default".to_string()];
// NOTE: There should be another way to know features on.
#[cfg(feature = "trash-support")]
{
names.push("trash".to_string());
}
#[cfg(feature = "sqlite")]
{
names.push("sqlite".to_string());
}
#[cfg(feature = "static-link-openssl")]
{
names.push("static-link-openssl".to_string());
}
#[cfg(feature = "system-clipboard")]
{
names.push("system-clipboard".to_string());
}
names.sort();
names
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::Version;
use crate::test_examples;
test_examples(Version)
test_examples(Version {})
}
}

View File

@ -221,23 +221,23 @@ impl std::fmt::Debug for DebuggableValue<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.0 {
Value::Bool { val, .. } => {
write!(f, "{val:?}")
write!(f, "{:?}", val)
}
Value::Int { val, .. } => {
write!(f, "{val:?}")
write!(f, "{:?}", val)
}
Value::Float { val, .. } => {
write!(f, "{val:?}f")
write!(f, "{:?}f", val)
}
Value::Filesize { val, .. } => {
write!(f, "Filesize({val:?})")
write!(f, "Filesize({:?})", val)
}
Value::Duration { val, .. } => {
let duration = std::time::Duration::from_nanos(*val as u64);
write!(f, "Duration({duration:?})")
write!(f, "Duration({:?})", duration)
}
Value::Date { val, .. } => {
write!(f, "Date({val:?})")
write!(f, "Date({:?})", val)
}
Value::Range { val, .. } => match **val {
Range::IntRange(range) => match range.end() {
@ -280,7 +280,7 @@ impl std::fmt::Debug for DebuggableValue<'_> {
},
},
Value::String { val, .. } | Value::Glob { val, .. } => {
write!(f, "{val:?}")
write!(f, "{:?}", val)
}
Value::Record { val, .. } => {
write!(f, "{{")?;
@ -305,22 +305,22 @@ impl std::fmt::Debug for DebuggableValue<'_> {
write!(f, "]")
}
Value::Closure { val, .. } => {
write!(f, "Closure({val:?})")
write!(f, "Closure({:?})", val)
}
Value::Nothing { .. } => {
write!(f, "Nothing")
}
Value::Error { error, .. } => {
write!(f, "Error({error:?})")
write!(f, "Error({:?})", error)
}
Value::Binary { val, .. } => {
write!(f, "Binary({val:?})")
write!(f, "Binary({:?})", val)
}
Value::CellPath { val, .. } => {
write!(f, "CellPath({:?})", val.to_string())
}
Value::Custom { val, .. } => {
write!(f, "CustomValue({val:?})")
write!(f, "CustomValue({:?})", val)
}
}
}

View File

@ -5,7 +5,7 @@ edition = "2024"
license = "MIT"
name = "nu-cmd-plugin"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-plugin"
version = "0.106.1"
version = "0.105.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -13,10 +13,10 @@ version = "0.106.1"
workspace = true
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.106.1" }
nu-path = { path = "../nu-path", version = "0.106.1" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.106.1" }
nu-engine = { path = "../nu-engine", version = "0.105.2" }
nu-path = { path = "../nu-path", version = "0.105.2" }
nu-protocol = { path = "../nu-protocol", version = "0.105.2", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.105.2" }
itertools = { workspace = true }

View File

@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2024"
license = "MIT"
name = "nu-color-config"
version = "0.106.1"
version = "0.105.2"
[lib]
bench = false
@ -14,12 +14,12 @@ bench = false
workspace = true
[dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-json = { path = "../nu-json", version = "0.106.1" }
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
nu-json = { path = "../nu-json", version = "0.105.2" }
nu-ansi-term = { workspace = true }
serde = { workspace = true, features = ["derive"] }
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }

View File

@ -5,7 +5,7 @@ edition = "2024"
license = "MIT"
name = "nu-command"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
version = "0.106.1"
version = "0.105.2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -16,22 +16,21 @@ bench = false
workspace = true
[dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
nu-color-config = { path = "../nu-color-config", version = "0.105.2" }
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
nu-glob = { path = "../nu-glob", version = "0.105.2" }
nu-json = { path = "../nu-json", version = "0.105.2" }
nu-parser = { path = "../nu-parser", version = "0.105.2" }
nu-path = { path = "../nu-path", version = "0.105.2" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.105.2" }
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
nu-system = { path = "../nu-system", version = "0.105.2" }
nu-table = { path = "../nu-table", version = "0.105.2" }
nu-term-grid = { path = "../nu-term-grid", version = "0.105.2" }
nu-utils = { path = "../nu-utils", version = "0.105.2", default-features = false }
nu-ansi-term = { workspace = true }
nu-cmd-base = { path = "../nu-cmd-base", version = "0.106.1" }
nu-color-config = { path = "../nu-color-config", version = "0.106.1" }
nu-engine = { path = "../nu-engine", version = "0.106.1", default-features = false }
nu-experimental = { path = "../nu-experimental", version = "0.106.1" }
nu-glob = { path = "../nu-glob", version = "0.106.1" }
nu-json = { path = "../nu-json", version = "0.106.1" }
nu-parser = { path = "../nu-parser", version = "0.106.1" }
nu-path = { path = "../nu-path", version = "0.106.1" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.106.1" }
nu-protocol = { path = "../nu-protocol", version = "0.106.1", default-features = false }
nu-system = { path = "../nu-system", version = "0.106.1" }
nu-table = { path = "../nu-table", version = "0.106.1" }
nu-term-grid = { path = "../nu-term-grid", version = "0.106.1" }
nu-utils = { path = "../nu-utils", version = "0.106.1", default-features = false }
nuon = { path = "../nuon", version = "0.106.1" }
nuon = { path = "../nuon", version = "0.105.2" }
alphanumeric-sort = { workspace = true }
base64 = { workspace = true }
@ -227,8 +226,8 @@ sqlite = ["rusqlite"]
trash-support = ["trash"]
[dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.106.1" }
nu-test-support = { path = "../nu-test-support", version = "0.106.1" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.105.2" }
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }
dirs = { workspace = true }
mockito = { workspace = true, default-features = false }

View File

@ -89,7 +89,7 @@ impl Command for Histogram {
"frequency-column-name can't be {}",
forbidden_column_names
.iter()
.map(|val| format!("'{val}'"))
.map(|val| format!("'{}'", val))
.collect::<Vec<_>>()
.join(", ")
),

View File

@ -142,7 +142,7 @@ fn into_binary(
}
}
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
let value = match input {
Value::Binary { .. } => input.clone(),
Value::Int { val, .. } => Value::binary(val.to_ne_bytes().to_vec(), span),
@ -168,7 +168,7 @@ fn action(input: &Value, args: &Arguments, span: Span) -> Value {
),
};
if args.compact {
if _args.compact {
let val_span = value.span();
if let Value::Binary { val, .. } = value {
let val = if cfg!(target_endian = "little") {

View File

@ -678,7 +678,7 @@ fn parse_value_from_record_as_u32(
Value::Int { val, .. } => {
if *val < 0 || *val > u32::MAX as i64 {
return Err(ShellError::IncorrectValue {
msg: format!("incorrect value for {col}"),
msg: format!("incorrect value for {}", col),
val_span: *head,
call_span: *span,
});

View File

@ -368,7 +368,8 @@ fn merge_record(record: &Record, head: Span, span: Span) -> Result<Value, ShellE
if !ALLOWED_SIGNS.contains(&val.as_str()) {
let allowed_signs = ALLOWED_SIGNS.join(", ");
return Err(ShellError::IncorrectValue {
msg: format!("Invalid sign. Allowed signs are {allowed_signs}").to_string(),
msg: format!("Invalid sign. Allowed signs are {}", allowed_signs)
.to_string(),
val_span: sign.span(),
call_span: head,
});

View File

@ -122,8 +122,8 @@ impl Table {
.conn
.query_row(&table_exists_query, [], |row| row.get(0))
.map_err(|err| ShellError::GenericError {
error: format!("{err:#?}"),
msg: format!("{err:#?}"),
error: format!("{:#?}", err),
msg: format!("{:#?}", err),
span: None,
help: None,
inner: Vec::new(),
@ -241,7 +241,7 @@ fn insert_in_transaction(
let tx = table.try_init(&first_val)?;
for stream_value in stream {
if let Err(err) = signals.check(&span) {
if let Err(err) = signals.check(span) {
tx.rollback().map_err(|e| ShellError::GenericError {
error: "Failed to rollback SQLite transaction".into(),
msg: e.to_string(),
@ -257,7 +257,7 @@ fn insert_in_transaction(
let insert_statement = format!(
"INSERT INTO [{}] ({}) VALUES ({})",
table_name,
Itertools::intersperse(val.columns().map(|c| format!("`{c}`")), ", ".to_string())
Itertools::intersperse(val.columns().map(|c| format!("`{}`", c)), ", ".to_string())
.collect::<String>(),
Itertools::intersperse(itertools::repeat_n("?", val.len()), ", ").collect::<String>(),
);
@ -353,7 +353,6 @@ fn nu_value_to_sqlite_type(val: &Value) -> Result<&'static str, ShellError> {
// intentionally enumerated so that any future types get handled
Type::Any
| Type::Block
| Type::CellPath
| Type::Closure
| Type::Custom(_)
@ -382,7 +381,7 @@ fn get_columns_with_sqlite_types(
.map(|name| (format!("`{}`", name.0), name.1))
.any(|(name, _)| name == *c)
{
columns.push((format!("`{c}`"), nu_value_to_sqlite_type(v)?));
columns.push((format!("`{}`", c), nu_value_to_sqlite_type(v)?));
}
}

View File

@ -112,31 +112,16 @@ impl SQLiteDatabase {
if self.path == PathBuf::from(MEMORY_DB) {
open_connection_in_memory_custom()
} else {
let conn = Connection::open(&self.path).map_err(|e| ShellError::GenericError {
Connection::open(&self.path).map_err(|e| ShellError::GenericError {
error: "Failed to open SQLite database from open_connection".into(),
msg: e.to_string(),
span: None,
help: None,
inner: vec![],
})?;
conn.busy_handler(Some(SQLiteDatabase::sleeper))
.map_err(|e| ShellError::GenericError {
error: "Failed to set busy handler for SQLite database".into(),
msg: e.to_string(),
span: None,
help: None,
inner: vec![],
})?;
Ok(conn)
})
}
}
fn sleeper(attempts: i32) -> bool {
log::warn!("SQLITE_BUSY, retrying after 250ms (attempt {})", attempts);
std::thread::sleep(std::time::Duration::from_millis(250));
true
}
pub fn get_tables(&self, conn: &Connection) -> Result<Vec<DbTable>, SqliteError> {
let mut table_names =
conn.prepare("SELECT name FROM sqlite_master WHERE type = 'table'")?;
@ -173,7 +158,7 @@ impl SQLiteDatabase {
filename: String,
) -> Result<(), SqliteError> {
//vacuum main into 'c:\\temp\\foo.db'
conn.execute(&format!("vacuum main into '{filename}'"), [])?;
conn.execute(&format!("vacuum main into '{}'", filename), [])?;
Ok(())
}
@ -588,7 +573,7 @@ fn prepared_statement_to_nu_list(
let mut row_values = vec![];
for row_result in row_results {
signals.check(&call_span)?;
signals.check(call_span)?;
if let Ok(row_value) = row_result {
row_values.push(row_value);
}
@ -614,7 +599,7 @@ fn prepared_statement_to_nu_list(
let mut row_values = vec![];
for row_result in row_results {
signals.check(&call_span)?;
signals.check(call_span)?;
if let Ok(row_value) = row_result {
row_values.push(row_value);
}
@ -683,23 +668,13 @@ pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {
pub fn open_connection_in_memory_custom() -> Result<Connection, ShellError> {
let flags = OpenFlags::default();
let conn =
Connection::open_with_flags(MEMORY_DB, flags).map_err(|e| ShellError::GenericError {
error: "Failed to open SQLite custom connection in memory".into(),
msg: e.to_string(),
span: Some(Span::test_data()),
help: None,
inner: vec![],
})?;
conn.busy_handler(Some(SQLiteDatabase::sleeper))
.map_err(|e| ShellError::GenericError {
error: "Failed to set busy handler for SQLite custom connection in memory".into(),
msg: e.to_string(),
span: Some(Span::test_data()),
help: None,
inner: vec![],
})?;
Ok(conn)
Connection::open_with_flags(MEMORY_DB, flags).map_err(|e| ShellError::GenericError {
error: "Failed to open SQLite custom connection in memory".into(),
msg: e.to_string(),
span: Some(Span::test_data()),
help: None,
inner: vec![],
})
}
pub fn open_connection_in_memory() -> Result<Connection, ShellError> {

View File

@ -1,64 +0,0 @@
use nu_engine::command_prelude::*;
use nu_experimental::Status;
#[derive(Clone)]
pub struct DebugExperimentalOptions;
impl Command for DebugExperimentalOptions {
fn name(&self) -> &str {
"debug experimental-options"
}
fn signature(&self) -> Signature {
Signature::new(self.name())
.input_output_type(
Type::Nothing,
Type::Table(Box::from([
(String::from("identifier"), Type::String),
(String::from("enabled"), Type::Bool),
(String::from("status"), Type::String),
(String::from("description"), Type::String),
])),
)
.add_help()
.category(Category::Debug)
}
fn description(&self) -> &str {
"Show all experimental options."
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::Value(
Value::list(
nu_experimental::ALL
.iter()
.map(|option| {
Value::record(
nu_protocol::record! {
"identifier" => Value::string(option.identifier(), call.head),
"enabled" => Value::bool(option.get(), call.head),
"status" => Value::string(match option.status() {
Status::OptIn => "opt-in",
Status::OptOut => "opt-out",
Status::DeprecatedDiscard => "deprecated-discard",
Status::DeprecatedDefault => "deprecated-default"
}, call.head),
"description" => Value::string(option.description(), call.head),
},
call.head,
)
})
.collect(),
call.head,
),
None,
))
}
}

View File

@ -43,17 +43,6 @@ impl Command for Metadata {
let arg = call.positional_nth(stack, 0);
let head = call.head;
if !matches!(input, PipelineData::Empty) {
if let Some(arg_expr) = arg {
return Err(ShellError::IncompatibleParameters {
left_message: "pipeline input was provided".into(),
left_span: head,
right_message: "but a positional metadata expression was also given".into(),
right_span: arg_expr.span,
});
}
}
match arg {
Some(Expression {
expr: Expr::FullCellPath(full_cell_path),
@ -67,6 +56,7 @@ impl Command for Metadata {
..
} => {
let origin = stack.get_var_with_origin(*var_id, *span)?;
Ok(build_metadata_record_value(
&origin,
input.metadata().as_ref(),
@ -97,9 +87,10 @@ impl Command for Metadata {
.into_pipeline_data(),
)
}
None => {
Ok(Value::record(build_metadata_record(&input, head), head).into_pipeline_data())
}
None => Ok(
Value::record(build_metadata_record(input.metadata().as_ref(), head), head)
.into_pipeline_data(),
),
}
}
@ -125,7 +116,19 @@ fn build_metadata_record_value(
head: Span,
) -> Value {
let mut record = Record::new();
record.push("span", arg.span().into_value(head));
let span = arg.span();
record.push(
"span",
Value::record(
record! {
"start" => Value::int(span.start as i64,span),
"end" => Value::int(span.end as i64, span),
},
head,
),
);
Value::record(extend_record_with_metadata(record, metadata, head), head)
}

View File

@ -42,7 +42,10 @@ impl Command for MetadataAccess {
// `ClosureEvalOnce` is not used as it uses `Stack::captures_to_stack` rather than
// `Stack::captures_to_stack_preserve_out_dest`. This command shouldn't collect streams
let mut callee_stack = caller_stack.captures_to_stack_preserve_out_dest(closure.captures);
let metadata_record = Value::record(build_metadata_record(&input, call.head), call.head);
let metadata_record = Value::record(
build_metadata_record(input.metadata().as_ref(), call.head),
call.head,
);
if let Some(var_id) = block.signature.get_positional(0).and_then(|var| var.var_id) {
callee_stack.add_var(var_id, metadata_record)
@ -55,10 +58,12 @@ impl Command for MetadataAccess {
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Access metadata and data from a stream together",
example: r#"{foo: bar} | to json --raw | metadata access {|meta| {in: $in, content: $meta.content_type}}"#,
example: r#"{foo: bar} | to json --raw | metadata access {|meta| {in: $in, meta: $meta}}"#,
result: Some(Value::test_record(record! {
"in" => Value::test_string(r#"{"foo":"bar"}"#),
"content" => Value::test_string(r#"application/json"#)
"meta" => Value::test_record(record! {
"content_type" => Value::test_string(r#"application/json"#)
})
})),
}]
}

View File

@ -63,18 +63,7 @@ impl Command for MetadataSet {
match (ds_fp, ds_ls) {
(Some(path), false) => metadata.data_source = DataSource::FilePath(path.into()),
(None, true) => metadata.data_source = DataSource::Ls,
(Some(_), true) => {
return Err(ShellError::IncompatibleParameters {
left_message: "cannot use `--datasource-filepath`".into(),
left_span: call
.get_flag_span(stack, "datasource-filepath")
.expect("has flag"),
right_message: "with `--datasource-ls`".into(),
right_span: call
.get_flag_span(stack, "datasource-ls")
.expect("has flag"),
});
}
(Some(_), true) => (), // TODO: error here
(None, false) => (),
}
@ -90,13 +79,15 @@ impl Command for MetadataSet {
},
Example {
description: "Set the metadata of a file path",
example: "'crates' | metadata set --datasource-filepath $'(pwd)/crates'",
example: "'crates' | metadata set --datasource-filepath $'(pwd)/crates' | metadata",
result: None,
},
Example {
description: "Set the metadata of a file path",
example: "'crates' | metadata set --content-type text/plain | metadata | get content_type",
result: Some(Value::test_string("text/plain")),
example: "'crates' | metadata set --content-type text/plain | metadata",
result: Some(Value::test_record(record! {
"content_type" => Value::test_string("text/plain"),
})),
},
]
}

View File

@ -1,7 +1,6 @@
mod ast;
mod debug_;
mod env;
mod experimental_options;
mod explain;
mod info;
mod inspect;
@ -22,7 +21,6 @@ mod view_span;
pub use ast::Ast;
pub use debug_::Debug;
pub use env::DebugEnv;
pub use experimental_options::DebugExperimentalOptions;
pub use explain::Explain;
pub use info::DebugInfo;
pub use inspect::Inspect;

View File

@ -1,4 +1,4 @@
use nu_protocol::{DataSource, IntoValue, PipelineData, PipelineMetadata, Record, Span, Value};
use nu_protocol::{DataSource, PipelineMetadata, Record, Span, Value};
pub fn extend_record_with_metadata(
mut record: Record,
@ -29,10 +29,6 @@ pub fn extend_record_with_metadata(
record
}
pub fn build_metadata_record(pipeline: &PipelineData, head: Span) -> Record {
let mut record = Record::new();
if let Some(span) = pipeline.span() {
record.insert("span", span.into_value(head));
}
extend_record_with_metadata(record, pipeline.metadata().as_ref(), head)
pub fn build_metadata_record(metadata: Option<&PipelineMetadata>, head: Span) -> Record {
extend_record_with_metadata(Record::new(), metadata, head)
}

View File

@ -126,10 +126,10 @@ impl Command for ViewSource {
}
let _ = write!(&mut final_contents, "--{}", n.long);
if let Some(short) = n.short {
let _ = write!(&mut final_contents, "(-{short})");
let _ = write!(&mut final_contents, "(-{})", short);
}
if let Some(arg) = &n.arg {
let _ = write!(&mut final_contents, ": {arg}");
let _ = write!(&mut final_contents, ": {}", arg);
}
final_contents.push(' ');
}
@ -146,7 +146,7 @@ impl Command for ViewSource {
let mut c = 0;
for (insig, outsig) in type_signatures {
c += 1;
let s = format!("{insig} -> {outsig}");
let s = format!("{} -> {}", insig, outsig);
final_contents.push_str(&s);
if c != len {
final_contents.push_str(", ")

View File

@ -153,7 +153,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
Ast,
Debug,
DebugEnv,
DebugExperimentalOptions,
DebugInfo,
DebugProfile,
Explain,
@ -189,9 +188,6 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
// Strings
bind_command! {
Ansi,
AnsiLink,
AnsiStrip,
Char,
Decode,
Encode,
@ -254,6 +250,9 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
// Platform
#[cfg(feature = "os")]
bind_command! {
Ansi,
AnsiLink,
AnsiStrip,
Clear,
Du,
Input,

View File

@ -85,7 +85,7 @@ impl Command for Glob {
result: None,
},
Example {
description: "Search for files or folders that do not begin with c, C, b, M, or s",
description: "Search for files for folders that do not begin with c, C, b, M, or s",
example: r#"glob "[!cCbMs]*""#,
result: None,
},
@ -329,7 +329,7 @@ fn glob_to_value(
) -> ListStream {
let map_signals = signals.clone();
let result = glob_results.filter_map(move |entry| {
if let Err(err) = map_signals.check(&span) {
if let Err(err) = map_signals.check(span) {
return Some(Value::error(err, span));
};
let file_type = entry.file_type();

View File

@ -341,7 +341,7 @@ fn ls_for_one_pattern(
let mut paths_peek = paths.peekable();
let no_matches = paths_peek.peek().is_none();
signals.check(&call_span)?;
signals.check(call_span)?;
if no_matches {
return Err(ShellError::GenericError {
error: format!("No matches found for {:?}", path.item),
@ -979,14 +979,14 @@ fn read_dir(
.read_dir()
.map_err(|err| IoError::new(err, span, f.clone()))?
.map(move |d| {
signals_clone.check(&span)?;
signals_clone.check(span)?;
d.map(|r| r.path())
.map_err(|err| IoError::new(err, span, f.clone()))
.map_err(ShellError::from)
});
if !use_threads {
let mut collected = items.collect::<Vec<_>>();
signals.check(&span)?;
signals.check(span)?;
collected.sort_by(|a, b| match (a, b) {
(Ok(a), Ok(b)) => a.cmp(b),
(Ok(_), Err(_)) => Ordering::Greater,

View File

@ -112,8 +112,8 @@ impl Command for Mktemp {
.map_err(|_| ShellError::NonUtf8 { span })?,
Err(e) => {
return Err(ShellError::GenericError {
error: format!("{e}"),
msg: format!("{e}"),
error: format!("{}", e),
msg: format!("{}", e),
span: None,
help: None,
inner: vec![],

View File

@ -198,7 +198,7 @@ impl Command for Open {
let converter = exts_opt.and_then(|exts| {
exts.iter().find_map(|ext| {
engine_state
.find_decl(format!("from {ext}").as_bytes(), &[])
.find_decl(format!("from {}", ext).as_bytes(), &[])
.map(|id| (id, ext.to_string()))
})
});
@ -314,7 +314,7 @@ fn extract_extensions(filename: &str) -> Vec<String> {
if current_extension.is_empty() {
current_extension.push_str(part);
} else {
current_extension = format!("{part}.{current_extension}");
current_extension = format!("{}.{}", part, current_extension);
}
extensions.push(current_extension.clone());
}

View File

@ -454,7 +454,7 @@ fn rm(
});
for result in iter {
engine_state.signals().check(&call.head)?;
engine_state.signals().check(call.head)?;
match result {
Ok(None) => {}
Ok(Some(msg)) => eprintln!("{msg}"),

View File

@ -91,8 +91,7 @@ impl Command for Save {
PipelineData::ByteStream(stream, metadata) => {
check_saving_to_source_file(metadata.as_ref(), &path, stderr_path.as_ref())?;
let (file, stderr_file) =
get_files(engine_state, &path, stderr_path.as_ref(), append, force)?;
let (file, stderr_file) = get_files(&path, stderr_path.as_ref(), append, force)?;
let size = stream.known_size();
let signals = engine_state.signals();
@ -202,8 +201,7 @@ impl Command for Save {
stderr_path.as_ref(),
)?;
let (mut file, _) =
get_files(engine_state, &path, stderr_path.as_ref(), append, force)?;
let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?;
for val in ls {
file.write_all(&value_to_bytes(val)?)
.map_err(&from_io_error)?;
@ -228,8 +226,7 @@ impl Command for Save {
input_to_bytes(input, Path::new(&path.item), raw, engine_state, stack, span)?;
// Only open file after successful conversion
let (mut file, _) =
get_files(engine_state, &path, stderr_path.as_ref(), append, force)?;
let (mut file, _) = get_files(&path, stderr_path.as_ref(), append, force)?;
file.write_all(&bytes).map_err(&from_io_error)?;
file.flush().map_err(&from_io_error)?;
@ -425,14 +422,13 @@ fn prepare_path(
}
}
fn open_file(
engine_state: &EngineState,
path: &Path,
span: Span,
append: bool,
) -> Result<File, ShellError> {
let file: std::io::Result<File> = match (append, path.exists()) {
(true, true) => std::fs::OpenOptions::new().append(true).open(path),
fn open_file(path: &Path, span: Span, append: bool) -> Result<File, ShellError> {
let file: Result<File, nu_protocol::shell_error::io::ErrorKind> = match (append, path.exists())
{
(true, true) => std::fs::OpenOptions::new()
.append(true)
.open(path)
.map_err(|err| err.into()),
_ => {
// This is a temporary solution until `std::fs::File::create` is fixed on Windows (rust-lang/rust#134893)
// A TOCTOU problem exists here, which may cause wrong error message to be shown
@ -442,51 +438,22 @@ fn open_file(
deprecated,
reason = "we don't get a IsADirectory error, so we need to provide it"
)]
Err(std::io::ErrorKind::IsADirectory.into())
Err(nu_protocol::shell_error::io::ErrorKind::from_std(
std::io::ErrorKind::IsADirectory,
))
} else {
std::fs::File::create(path)
std::fs::File::create(path).map_err(|err| err.into())
}
#[cfg(not(target_os = "windows"))]
std::fs::File::create(path)
std::fs::File::create(path).map_err(|err| err.into())
}
};
match file {
Ok(file) => Ok(file),
Err(err) => {
// In caase of NotFound, search for the missing parent directory.
// This also presents a TOCTOU (or TOUTOC, technically?)
if err.kind() == std::io::ErrorKind::NotFound {
if let Some(missing_component) =
path.ancestors().skip(1).filter(|dir| !dir.exists()).last()
{
// By looking at the postfix to remove, rather than the prefix
// to keep, we are able to handle relative paths too.
let components_to_remove = path
.strip_prefix(missing_component)
.expect("Stripping ancestor from a path should never fail")
.as_os_str()
.as_encoded_bytes();
return Err(ShellError::Io(IoError::new(
ErrorKind::DirectoryNotFound,
engine_state
.span_match_postfix(span, components_to_remove)
.map(|(pre, _post)| pre)
.unwrap_or(span),
PathBuf::from(missing_component),
)));
}
}
Err(ShellError::Io(IoError::new(err, span, PathBuf::from(path))))
}
}
file.map_err(|err_kind| ShellError::Io(IoError::new(err_kind, span, PathBuf::from(path))))
}
/// Get output file and optional stderr file
fn get_files(
engine_state: &EngineState,
path: &Spanned<PathBuf>,
stderr_path: Option<&Spanned<PathBuf>>,
append: bool,
@ -500,7 +467,7 @@ fn get_files(
.transpose()?;
// Only if both files can be used open and possibly truncate them
let file = open_file(engine_state, path, path_span, append)?;
let file = open_file(path, path_span, append)?;
let stderr_file = stderr_path_and_span
.map(|(stderr_path, stderr_path_span)| {
@ -513,7 +480,7 @@ fn get_files(
inner: vec![],
})
} else {
open_file(engine_state, stderr_path, stderr_path_span, append)
open_file(stderr_path, stderr_path_span, append)
}
})
.transpose()?;
@ -543,7 +510,7 @@ fn stream_to_file(
let mut reader = BufReader::new(source);
let res = loop {
if let Err(err) = signals.check(&span) {
if let Err(err) = signals.check(span) {
bar.abandoned_msg("# Cancelled #".to_owned());
return Err(err);
}

View File

@ -49,8 +49,7 @@ impl Command for Start {
}
// If it's not a URL, treat it as a file path
let cwd = engine_state.cwd(Some(stack))?;
let full_path = nu_path::expand_path_with(path_no_whitespace, &cwd, true);
let full_path = cwd.join(path_no_whitespace);
// Check if the path exists or if it's a valid file/directory
if full_path.exists() {
open_path(full_path, engine_state, stack, path.span)?;

View File

@ -272,8 +272,8 @@ impl Command for UCp {
uu_cp::Error::NotAllFilesCopied => {}
_ => {
return Err(ShellError::GenericError {
error: format!("{error}"),
msg: format!("{error}"),
error: format!("{}", error),
msg: format!("{}", error),
span: None,
help: None,
inner: vec![],
@ -373,7 +373,7 @@ fn parse_and_set_attribute(
"xattr" => &mut attribute.xattr,
_ => {
return Err(ShellError::IncompatibleParametersSingle {
msg: format!("--preserve flag got an unexpected attribute \"{val}\""),
msg: format!("--preserve flag got an unexpected attribute \"{}\"", val),
span: value.span(),
});
}

View File

@ -77,8 +77,8 @@ impl Command for UMkdir {
for dir in directories {
if let Err(error) = mkdir(&dir, IS_RECURSIVE, get_mode(), is_verbose) {
return Err(ShellError::GenericError {
error: format!("{error}"),
msg: format!("{error}"),
error: format!("{}", error),
msg: format!("{}", error),
span: None,
help: None,
inner: vec![],

View File

@ -195,8 +195,8 @@ impl Command for UMv {
};
if let Err(error) = uu_mv::mv(&files, &options) {
return Err(ShellError::GenericError {
error: format!("{error}"),
msg: format!("{error}"),
error: format!("{}", error),
msg: format!("{}", error),
span: None,
help: None,
inner: Vec::new(),

View File

@ -220,7 +220,7 @@ impl Command for UTouch {
inner: Vec::new(),
},
TouchError::InvalidDateFormat(date) => ShellError::IncorrectValue {
msg: format!("Invalid date: {date}"),
msg: format!("Invalid date: {}", date),
val_span: date_span.expect("touch should've been given a date"),
call_span: call.head,
},

View File

@ -6,7 +6,11 @@ use notify_debouncer_full::{
},
};
use nu_engine::{ClosureEval, command_prelude::*};
use nu_protocol::{engine::Closure, report_shell_error, shell_error::io::IoError};
use nu_protocol::{
engine::{Closure, StateWorkingSet},
format_shell_error,
shell_error::io::IoError,
};
use std::{
path::PathBuf,
sync::mpsc::{RecvTimeoutError, channel},
@ -199,9 +203,14 @@ impl Command for Watch {
.run_with_input(PipelineData::Empty);
match result {
Ok(val) => val.print_table(engine_state, stack, false, false)?,
Err(err) => report_shell_error(engine_state, &err),
};
Ok(val) => {
val.print_table(engine_state, stack, false, false)?;
}
Err(err) => {
let working_set = StateWorkingSet::new(engine_state);
eprintln!("{}", format_shell_error(&working_set, &err));
}
}
}
Ok(())

View File

@ -2,7 +2,7 @@ use std::{borrow::Cow, ops::Deref};
use nu_engine::{ClosureEval, command_prelude::*};
use nu_protocol::{
ListStream, ReportMode, ShellWarning, Signals,
ListStream, Signals,
ast::{Expr, Expression},
report_shell_warning,
};
@ -82,7 +82,7 @@ impl Command for Default {
},
Example {
description: "Get the env value of `MY_ENV` with a default value 'abc' if not present",
example: "$env | get --optional MY_ENV | default 'abc'",
example: "$env | get --ignore-errors MY_ENV | default 'abc'",
result: Some(Value::test_string("abc")),
},
Example {
@ -213,7 +213,7 @@ fn default(
|| (default_when_empty
&& matches!(input, PipelineData::Value(ref value, _) if value.is_empty()))
{
default_value.single_run_pipeline_data()
default_value.pipeline_data()
} else if default_when_empty && matches!(input, PipelineData::ListStream(..)) {
let PipelineData::ListStream(ls, metadata) = input else {
unreachable!()
@ -221,7 +221,7 @@ fn default(
let span = ls.span();
let mut stream = ls.into_inner().peekable();
if stream.peek().is_none() {
return default_value.single_run_pipeline_data();
return default_value.pipeline_data();
}
// stream's internal state already preserves the original signals config, so if this
@ -278,14 +278,8 @@ impl DefaultValue {
}
}
/// Used when we know the value won't need to be cached to allow streaming.
fn single_run_pipeline_data(self) -> Result<PipelineData, ShellError> {
match self {
DefaultValue::Uncalculated(mut closure) => {
closure.item.run_with_input(PipelineData::Empty)
}
DefaultValue::Calculated(val) => Ok(val.into_pipeline_data()),
}
fn pipeline_data(&mut self) -> Result<PipelineData, ShellError> {
self.value().map(|x| x.into_pipeline_data())
}
}
@ -329,25 +323,24 @@ fn closure_variable_warning(
(Value::Closure { .. }, true) => {
let span_contents = String::from_utf8_lossy(engine_state.get_span_contents(span));
let carapace_suggestion = "re-run carapace init with version v1.3.3 or later\nor, change this to `{ $carapace_completer }`";
let label = match span_contents {
let suggestion = match span_contents {
Cow::Borrowed("$carapace_completer") => carapace_suggestion.to_string(),
Cow::Owned(s) if s.deref() == "$carapace_completer" => {
carapace_suggestion.to_string()
}
_ => format!("change this to {{ {span_contents} }}").to_string(),
_ => format!("change this to {{ {} }}", span_contents).to_string(),
};
report_shell_warning(
engine_state,
&ShellWarning::Deprecated {
dep_type: "Behavior".to_string(),
label,
&ShellError::DeprecationWarning {
deprecation_type: "Behavior",
suggestion,
span,
help: Some(
r"Since 0.105.0, closure literals passed to default are lazily evaluated, rather than returned as a value.
In a future release, closures passed by variable will also be lazily evaluated.".to_string(),
In a future release, closures passed by variable will also be lazily evaluated.",
),
report_mode: ReportMode::FirstUse,
},
);

View File

@ -1,6 +1,6 @@
use itertools::Either;
use nu_engine::command_prelude::*;
use nu_protocol::{PipelineIterator, Range};
use std::collections::VecDeque;
use std::ops::Bound;
#[derive(Clone)]
@ -18,11 +18,13 @@ impl Command for DropNth {
(Type::list(Type::Any), Type::list(Type::Any)),
])
.allow_variants_without_examples(true)
.rest(
"rest",
.required(
"row number or row range",
// FIXME: we can make this accept either Int or Range when we can compose SyntaxShapes
SyntaxShape::Any,
"The row numbers or ranges to drop.",
"The number of the row to drop or a range to drop consecutive rows.",
)
.rest("rest", SyntaxShape::Any, "The number of the row to drop.")
.category(Category::Filters)
}
@ -101,125 +103,110 @@ impl Command for DropNth {
) -> Result<PipelineData, ShellError> {
let head = call.head;
let metadata = input.metadata();
let number_or_range = extract_int_or_range(engine_state, stack, call)?;
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
if args.is_empty() {
return Ok(input);
}
let rows = match number_or_range.item {
Either::Left(row_number) => {
let and_rows: Vec<Spanned<i64>> = call.rest(engine_state, stack, 1)?;
let mut rows: Vec<_> = and_rows.into_iter().map(|x| x.item as usize).collect();
rows.push(row_number as usize);
rows.sort_unstable();
rows
}
Either::Right(Range::FloatRange(_)) => {
return Err(ShellError::UnsupportedInput {
msg: "float range".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: number_or_range.span,
});
}
Either::Right(Range::IntRange(range)) => {
// check for negative range inputs, e.g., (2..-5)
let end_negative = match range.end() {
Bound::Included(end) | Bound::Excluded(end) => end < 0,
Bound::Unbounded => false,
};
if range.start().is_negative() || end_negative {
return Err(ShellError::UnsupportedInput {
msg: "drop nth accepts only positive ints".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: number_or_range.span,
});
}
// check if the upper bound is smaller than the lower bound, e.g., do not accept 4..2
if range.step() < 0 {
return Err(ShellError::UnsupportedInput {
msg: "The upper bound needs to be equal or larger to the lower bound"
.into(),
input: "value originates from here".into(),
msg_span: head,
input_span: number_or_range.span,
});
}
let (rows_to_drop, min_unbounded_start) = get_rows_to_drop(&args, head)?;
let start = range.start() as usize;
let input = if let Some(cutoff) = min_unbounded_start {
input
.into_iter()
.take(cutoff)
.into_pipeline_data_with_metadata(
head,
engine_state.signals().clone(),
metadata.clone(),
)
} else {
input
let end = match range.end() {
Bound::Included(end) => end as usize,
Bound::Excluded(end) => (end - 1) as usize,
Bound::Unbounded => {
return Ok(input
.into_iter()
.take(start)
.into_pipeline_data_with_metadata(
head,
engine_state.signals().clone(),
metadata,
));
}
};
let end = if let PipelineData::Value(Value::List { vals, .. }, _) = &input {
end.min(vals.len() - 1)
} else {
end
};
(start..=end).collect()
}
};
Ok(DropNthIterator {
input: input.into_iter(),
rows: rows_to_drop,
rows,
current: 0,
}
.into_pipeline_data_with_metadata(head, engine_state.signals().clone(), metadata))
}
}
fn get_rows_to_drop(
args: &[Value],
head: Span,
) -> Result<(VecDeque<usize>, Option<usize>), ShellError> {
let mut rows_to_drop = Vec::new();
let mut min_unbounded_start: Option<usize> = None;
fn extract_int_or_range(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
) -> Result<Spanned<Either<i64, Range>>, ShellError> {
let value: Value = call.req(engine_state, stack, 0)?;
for value in args {
if let Ok(i) = value.as_int() {
if i < 0 {
return Err(ShellError::UnsupportedInput {
msg: "drop nth accepts only positive ints".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: value.span(),
});
}
rows_to_drop.push(i as usize);
} else if let Ok(range) = value.as_range() {
match range {
Range::IntRange(range) => {
let start = range.start();
if start < 0 {
return Err(ShellError::UnsupportedInput {
msg: "drop nth accepts only positive ints".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: value.span(),
});
}
let int_opt = value.as_int().map(Either::Left).ok();
let range_opt = value.as_range().map(Either::Right).ok();
match range.end() {
Bound::Included(end) => {
if end < start {
return Err(ShellError::UnsupportedInput {
msg: "The upper bound must be greater than or equal to the lower bound".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: value.span(),
});
}
rows_to_drop.extend((start as usize)..=(end as usize));
}
Bound::Excluded(end) => {
if end <= start {
return Err(ShellError::UnsupportedInput {
msg: "The upper bound must be greater than the lower bound"
.into(),
input: "value originates from here".into(),
msg_span: head,
input_span: value.span(),
});
}
rows_to_drop.extend((start as usize)..(end as usize));
}
Bound::Unbounded => {
let start_usize = start as usize;
min_unbounded_start = Some(
min_unbounded_start.map_or(start_usize, |s| s.min(start_usize)),
);
}
}
}
Range::FloatRange(_) => {
return Err(ShellError::UnsupportedInput {
msg: "float range not supported".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: value.span(),
});
}
}
} else {
return Err(ShellError::TypeMismatch {
err_message: "Expected int or range".into(),
span: value.span(),
});
}
}
rows_to_drop.sort_unstable();
rows_to_drop.dedup();
Ok((VecDeque::from(rows_to_drop), min_unbounded_start))
int_opt
.or(range_opt)
.ok_or_else(|| ShellError::TypeMismatch {
err_message: "int or range".into(),
span: value.span(),
})
.map(|either| Spanned {
item: either,
span: value.span(),
})
}
struct DropNthIterator {
input: PipelineIterator,
rows: VecDeque<usize>,
rows: Vec<usize>,
current: usize,
}
@ -228,9 +215,9 @@ impl Iterator for DropNthIterator {
fn next(&mut self) -> Option<Self::Item> {
loop {
if let Some(row) = self.rows.front() {
if let Some(row) = self.rows.first() {
if self.current == *row {
self.rows.pop_front();
self.rows.remove(0);
self.current += 1;
let _ = self.input.next();
continue;

View File

@ -47,7 +47,7 @@ impl Command for Find {
.named(
"columns",
SyntaxShape::List(Box::new(SyntaxShape::String)),
"column names to be searched",
"column names to be searched (with rest parameter, not regex yet)",
Some('c'),
)
.switch(
@ -163,12 +163,7 @@ impl Command for Find {
example: r#"[["Larry", "Moe"], ["Victor", "Marina"]] | find --regex "rr""#,
result: Some(Value::list(
vec![Value::list(
vec![
Value::test_string(
"\u{1b}[37mLa\u{1b}[0m\u{1b}[41;37mrr\u{1b}[0m\u{1b}[37my\u{1b}[0m",
),
Value::test_string("Moe"),
],
vec![Value::test_string("Larry"), Value::test_string("Moe")],
Span::test_data(),
)],
Span::test_data(),
@ -349,10 +344,7 @@ fn get_match_pattern_from_arguments(
// map functions
fn highlight_matches_in_string(pattern: &MatchPattern, val: String) -> String {
if !pattern.regex.is_match(&val).unwrap_or(false) {
return val;
}
// strip haystack to remove existing ansi style
let stripped_val = nu_utils::strip_ansi_string_unlikely(val);
let mut last_match_end = 0;
let mut highlighted = String::new();
@ -398,7 +390,7 @@ fn highlight_matches_in_string(pattern: &MatchPattern, val: String) -> String {
highlighted
}
fn highlight_matches_in_value(
fn highlight_matches_in_record_or_value(
pattern: &MatchPattern,
value: Value,
columns_to_search: &[String],
@ -420,16 +412,16 @@ fn highlight_matches_in_value(
continue;
}
*val = highlight_matches_in_value(pattern, std::mem::take(val), &[]);
if let Value::String { val: val_str, .. } = val {
if pattern.regex.is_match(val_str).unwrap_or(false) {
let val_str = std::mem::take(val_str);
*val = highlight_matches_in_string(pattern, val_str).into_value(span)
}
}
}
Value::record(record, span)
}
Value::List { vals, .. } => vals
.into_iter()
.map(|item| highlight_matches_in_value(pattern, item, &[]))
.collect::<Vec<Value>>()
.into_value(span),
Value::String { val, .. } => highlight_matches_in_string(pattern, val).into_value(span),
_ => value,
}
@ -452,22 +444,24 @@ fn find_in_pipelinedata(
PipelineData::Value(_, _) => input
.filter(
move |value| {
value_should_be_printed(&pattern, value, &columns_to_search, &config)
!= pattern.invert
record_or_value_should_be_printed(&pattern, value, &columns_to_search, &config)
},
engine_state.signals(),
)?
.map(
move |x| highlight_matches_in_value(&map_pattern, x, &map_columns_to_search),
move |x| {
highlight_matches_in_record_or_value(&map_pattern, x, &map_columns_to_search)
},
engine_state.signals(),
),
PipelineData::ListStream(stream, metadata) => {
let stream = stream.modify(|iter| {
iter.filter(move |value| {
value_should_be_printed(&pattern, value, &columns_to_search, &config)
!= pattern.invert
record_or_value_should_be_printed(&pattern, value, &columns_to_search, &config)
})
.map(move |x| {
highlight_matches_in_record_or_value(&map_pattern, x, &map_columns_to_search)
})
.map(move |x| highlight_matches_in_value(&map_pattern, x, &map_columns_to_search))
});
Ok(PipelineData::ListStream(stream, metadata))
@ -501,12 +495,7 @@ fn string_should_be_printed(pattern: &MatchPattern, value: &str) -> bool {
pattern.regex.is_match(value).unwrap_or(false)
}
fn value_should_be_printed(
pattern: &MatchPattern,
value: &Value,
columns_to_search: &[String],
config: &Config,
) -> bool {
fn value_should_be_printed(pattern: &MatchPattern, value: &Value, config: &Config) -> bool {
let lower_value = value.to_expanded_string("", config).to_lowercase();
match value {
@ -518,7 +507,8 @@ fn value_should_be_printed(
| Value::Range { .. }
| Value::Float { .. }
| Value::Closure { .. }
| Value::Nothing { .. } => {
| Value::Nothing { .. }
| Value::Error { .. } => {
if !pattern.lower_terms.is_empty() {
// look for exact match when searching with terms
pattern
@ -529,25 +519,37 @@ fn value_should_be_printed(
string_should_be_printed(pattern, &lower_value)
}
}
Value::Glob { .. } | Value::CellPath { .. } | Value::Custom { .. } => {
string_should_be_printed(pattern, &lower_value)
}
Value::Glob { .. }
| Value::List { .. }
| Value::CellPath { .. }
| Value::Record { .. }
| Value::Custom { .. } => string_should_be_printed(pattern, &lower_value),
Value::String { val, .. } => string_should_be_printed(pattern, val),
Value::List { vals, .. } => vals
.iter()
.any(|item| value_should_be_printed(pattern, item, &[], config)),
Value::Binary { .. } => false,
}
}
fn record_or_value_should_be_printed(
pattern: &MatchPattern,
value: &Value,
columns_to_search: &[String],
config: &Config,
) -> bool {
let match_found = match value {
Value::Record { val: record, .. } => {
// Only perform column selection if given columns.
let col_select = !columns_to_search.is_empty();
record.iter().any(|(col, val)| {
if col_select && !columns_to_search.contains(col) {
return false;
}
value_should_be_printed(pattern, val, &[], config)
value_should_be_printed(pattern, val, config)
})
}
Value::Binary { .. } => false,
Value::Error { .. } => true,
}
_ => value_should_be_printed(pattern, value, config),
};
match_found != pattern.invert
}
// utility
@ -572,46 +574,6 @@ fn split_string_if_multiline(input: PipelineData, head_span: Span) -> PipelineDa
}
}
/// function for using find from other commands
pub fn find_internal(
input: PipelineData,
engine_state: &EngineState,
stack: &mut Stack,
search_term: &str,
columns_to_search: &[&str],
highlight: bool,
) -> Result<PipelineData, ShellError> {
let span = input.span().unwrap_or(Span::unknown());
let style_computer = StyleComputer::from_config(engine_state, stack);
let string_style = style_computer.compute("string", &Value::string("search result", span));
let highlight_style =
style_computer.compute("search_result", &Value::string("search result", span));
let regex_str = format!("(?i){}", escape(search_term));
let regex = Regex::new(regex_str.as_str()).map_err(|e| ShellError::TypeMismatch {
err_message: format!("invalid regex: {e}"),
span: Span::unknown(),
})?;
let pattern = MatchPattern {
regex,
lower_terms: vec![search_term.to_lowercase()],
highlight,
invert: false,
string_style,
highlight_style,
};
let columns_to_search = columns_to_search
.iter()
.map(|str| String::from(*str))
.collect();
find_in_pipelinedata(pattern, columns_to_search, engine_state, stack, input)
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -40,14 +40,9 @@ If multiple cell paths are given, this will produce a list of values."#
"The cell path to the data.",
)
.rest("rest", SyntaxShape::CellPath, "Additional cell paths.")
.switch(
"optional",
"make all cell path members optional (returns `null` for missing values)",
Some('o'),
)
.switch(
"ignore-errors",
"ignore missing data (make all cell path members optional) (deprecated)",
"ignore missing data (make all cell path members optional)",
Some('i'),
)
.switch(
@ -114,14 +109,13 @@ If multiple cell paths are given, this will produce a list of values."#
) -> Result<PipelineData, ShellError> {
let cell_path: CellPath = call.req_const(working_set, 0)?;
let rest: Vec<CellPath> = call.rest_const(working_set, 1)?;
let optional = call.has_flag_const(working_set, "optional")?
|| call.has_flag_const(working_set, "ignore-errors")?;
let ignore_errors = call.has_flag_const(working_set, "ignore-errors")?;
let metadata = input.metadata();
action(
input,
cell_path,
rest,
optional,
ignore_errors,
working_set.permanent().signals().clone(),
call.head,
)
@ -137,14 +131,13 @@ If multiple cell paths are given, this will produce a list of values."#
) -> Result<PipelineData, ShellError> {
let cell_path: CellPath = call.req(engine_state, stack, 0)?;
let rest: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let optional = call.has_flag(engine_state, stack, "optional")?
|| call.has_flag(engine_state, stack, "ignore-errors")?;
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
let metadata = input.metadata();
action(
input,
cell_path,
rest,
optional,
ignore_errors,
engine_state.signals().clone(),
call.head,
)
@ -159,13 +152,6 @@ If multiple cell paths are given, this will produce a list of values."#
since: Some("0.105.0".into()),
expected_removal: None,
help: Some("Cell-paths are now case-sensitive by default.\nTo access fields case-insensitively, add `!` after the relevant path member.".into())
},
DeprecationEntry {
ty: DeprecationType::Flag("ignore-errors".into()),
report_mode: ReportMode::FirstUse,
since: Some("0.106.0".into()),
expected_removal: None,
help: Some("This flag has been renamed to `--optional (-o)` to better reflect its behavior.".into())
}
]
}
@ -175,19 +161,29 @@ fn action(
input: PipelineData,
mut cell_path: CellPath,
mut rest: Vec<CellPath>,
optional: bool,
ignore_errors: bool,
signals: Signals,
span: Span,
) -> Result<PipelineData, ShellError> {
if optional {
if ignore_errors {
cell_path.make_optional();
for path in &mut rest {
path.make_optional();
}
}
if let PipelineData::Empty = input {
return Err(ShellError::PipelineEmpty { dst_span: span });
match input {
PipelineData::Empty => return Err(ShellError::PipelineEmpty { dst_span: span }),
// Allow chaining of get -i
PipelineData::Value(val @ Value::Nothing { .. }, ..) if !ignore_errors => {
return Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "table or record".into(),
wrong_type: "nothing".into(),
dst_span: span,
src_span: val.span(),
});
}
_ => (),
}
if rest.is_empty() {

View File

@ -231,12 +231,7 @@ pub fn group_by(
let values: Vec<Value> = input.into_iter().collect();
if values.is_empty() {
let val = if to_table {
Value::list(Vec::new(), head)
} else {
Value::record(Record::new(), head)
};
return Ok(val.into_pipeline_data());
return Ok(Value::record(Record::new(), head).into_pipeline_data());
}
let grouped = match &groupers[..] {

View File

@ -379,7 +379,10 @@ fn merge_records(left: &Record, right: &Record, shared_key: Option<&str>) -> Rec
let k_shared = shared_key == Some(k.as_str());
// Do not output shared join key twice
if !(k_seen && k_shared) {
record.push(if k_seen { format!("{k}_") } else { k.clone() }, v.clone());
record.push(
if k_seen { format!("{}_", k) } else { k.clone() },
v.clone(),
);
}
}
record

View File

@ -100,7 +100,7 @@ impl Command for Last {
let mut buf = VecDeque::new();
for row in iterator {
engine_state.signals().check(&head)?;
engine_state.signals().check(head)?;
if buf.len() == rows {
buf.pop_front();
}

View File

@ -70,7 +70,7 @@ pub use empty::empty;
pub use enumerate::Enumerate;
pub use every::Every;
pub use filter::Filter;
pub use find::{Find, find_internal};
pub use find::Find;
pub use first::First;
pub use flatten::Flatten;
pub use get::Get;

View File

@ -119,7 +119,7 @@ impl Command for Reduce {
let mut closure = ClosureEval::new(engine_state, stack, closure);
for value in iter {
engine_state.signals().check(&head)?;
engine_state.signals().check(head)?;
acc = closure
.add_arg(value)
.add_arg(acc.clone())

View File

@ -1,5 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::{DeprecationEntry, DeprecationType, ReportMode, ast::PathMember, casing::Casing};
use nu_protocol::{ast::PathMember, casing::Casing};
use std::{cmp::Reverse, collections::HashSet};
#[derive(Clone)]
@ -17,10 +17,9 @@ impl Command for Reject {
(Type::table(), Type::table()),
(Type::list(Type::Any), Type::list(Type::Any)),
])
.switch("optional", "make all cell path members optional", Some('o'))
.switch(
"ignore-errors",
"ignore missing data (make all cell path members optional) (deprecated)",
"ignore missing data (make all cell path members optional)",
Some('i'),
)
.rest(
@ -91,9 +90,8 @@ impl Command for Reject {
}
let span = call.head;
let optional = call.has_flag(engine_state, stack, "optional")?
|| call.has_flag(engine_state, stack, "ignore-errors")?;
if optional {
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
if ignore_errors {
for cell_path in &mut new_columns {
cell_path.make_optional();
}
@ -102,19 +100,6 @@ impl Command for Reject {
reject(engine_state, span, input, new_columns)
}
fn deprecation_info(&self) -> Vec<DeprecationEntry> {
vec![DeprecationEntry {
ty: DeprecationType::Flag("ignore-errors".into()),
report_mode: ReportMode::FirstUse,
since: Some("0.106.0".into()),
expected_removal: None,
help: Some(
"This flag has been renamed to `--optional (-o)` to better reflect its behavior."
.into(),
),
}]
}
fn examples(&self) -> Vec<Example> {
vec![
Example {

View File

@ -1,8 +1,5 @@
use nu_engine::command_prelude::*;
use nu_protocol::{
DeprecationEntry, DeprecationType, PipelineIterator, ReportMode, ast::PathMember,
casing::Casing,
};
use nu_protocol::{PipelineIterator, ast::PathMember, casing::Casing};
use std::collections::BTreeSet;
#[derive(Clone)]
@ -21,14 +18,9 @@ impl Command for Select {
(Type::table(), Type::table()),
(Type::List(Box::new(Type::Any)), Type::Any),
])
.switch(
"optional",
"make all cell path members optional (returns `null` for missing values)",
Some('o'),
)
.switch(
"ignore-errors",
"ignore missing data (make all cell path members optional) (deprecated)",
"ignore missing data (make all cell path members optional)",
Some('i'),
)
.rest(
@ -108,11 +100,10 @@ produce a table, a list will produce a list, and a record will produce a record.
}
}
}
let optional = call.has_flag(engine_state, stack, "optional")?
|| call.has_flag(engine_state, stack, "ignore-errors")?;
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
let span = call.head;
if optional {
if ignore_errors {
for cell_path in &mut new_columns {
cell_path.make_optional();
}
@ -121,19 +112,6 @@ produce a table, a list will produce a list, and a record will produce a record.
select(engine_state, span, new_columns, input)
}
fn deprecation_info(&self) -> Vec<DeprecationEntry> {
vec![DeprecationEntry {
ty: DeprecationType::Flag("ignore-errors".into()),
report_mode: ReportMode::FirstUse,
since: Some("0.106.0".into()),
expected_removal: None,
help: Some(
"This flag has been renamed to `--optional (-o)` to better reflect its behavior."
.into(),
),
}]
}
fn examples(&self) -> Vec<Example> {
vec![
Example {

View File

@ -31,7 +31,7 @@ pub fn boolean_fold(
let mut closure = ClosureEval::new(engine_state, stack, closure);
for value in input {
engine_state.signals().check(&head)?;
engine_state.signals().check(head)?;
let pred = closure.run_with_value(value)?.into_value(head)?.is_true();
if pred == accumulator {

View File

@ -43,7 +43,10 @@ Row conditions cannot be stored in a variable. To pass a condition with a variab
])
.required(
"condition",
SyntaxShape::RowCondition,
SyntaxShape::OneOf(vec![
SyntaxShape::RowCondition,
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
]),
"Filter row condition or closure.",
)
.allow_variants_without_examples(true)

View File

@ -203,7 +203,6 @@ mod test {
use super::*;
use crate::Reject;
use crate::{Metadata, MetadataSet};
#[test]
@ -222,7 +221,6 @@ mod test {
working_set.add_decl(Box::new(FromCsv {}));
working_set.add_decl(Box::new(Metadata {}));
working_set.add_decl(Box::new(MetadataSet {}));
working_set.add_decl(Box::new(Reject {}));
working_set.render()
};
@ -231,7 +229,7 @@ mod test {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = r#""a,b\n1,2" | metadata set --content-type 'text/csv' --datasource-ls | from csv | metadata | reject span | $in"#;
let cmd = r#""a,b\n1,2" | metadata set --content-type 'text/csv' --datasource-ls | from csv | metadata | $in"#;
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),

View File

@ -248,7 +248,6 @@ fn convert_string_to_value_strict(string_input: &str, span: Span) -> Result<Valu
mod test {
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
use crate::Reject;
use crate::{Metadata, MetadataSet};
use super::*;
@ -269,7 +268,6 @@ mod test {
working_set.add_decl(Box::new(FromJson {}));
working_set.add_decl(Box::new(Metadata {}));
working_set.add_decl(Box::new(MetadataSet {}));
working_set.add_decl(Box::new(Reject {}));
working_set.render()
};
@ -278,7 +276,7 @@ mod test {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = r#"'{"a":1,"b":2}' | metadata set --content-type 'application/json' --datasource-ls | from json | metadata | reject span | $in"#;
let cmd = r#"'{"a":1,"b":2}' | metadata set --content-type 'application/json' --datasource-ls | from json | metadata | $in"#;
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),

View File

@ -212,7 +212,7 @@ impl From<ReadError> for ShellError {
},
ReadError::TypeMismatch(marker, span) => ShellError::GenericError {
error: "Invalid marker while reading MessagePack data".into(),
msg: format!("unexpected {marker:?} in data"),
msg: format!("unexpected {:?} in data", marker),
span: Some(span),
help: None,
inner: vec![],
@ -514,7 +514,6 @@ fn assert_eof(input: &mut impl io::Read, span: Span) -> Result<(), ShellError> {
mod test {
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
use crate::Reject;
use crate::{Metadata, MetadataSet, ToMsgpack};
use super::*;
@ -536,7 +535,6 @@ mod test {
working_set.add_decl(Box::new(FromMsgpack {}));
working_set.add_decl(Box::new(Metadata {}));
working_set.add_decl(Box::new(MetadataSet {}));
working_set.add_decl(Box::new(Reject {}));
working_set.render()
};
@ -545,7 +543,7 @@ mod test {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = r#"{a: 1 b: 2} | to msgpack | metadata set --datasource-ls | from msgpack | metadata | reject span | $in"#;
let cmd = r#"{a: 1 b: 2} | to msgpack | metadata set --datasource-ls | from msgpack | metadata | $in"#;
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),

View File

@ -74,7 +74,6 @@ impl Command for FromNuon {
mod test {
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
use crate::Reject;
use crate::{Metadata, MetadataSet};
use super::*;
@ -95,7 +94,6 @@ mod test {
working_set.add_decl(Box::new(FromNuon {}));
working_set.add_decl(Box::new(Metadata {}));
working_set.add_decl(Box::new(MetadataSet {}));
working_set.add_decl(Box::new(Reject {}));
working_set.render()
};
@ -104,7 +102,7 @@ mod test {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = r#"'[[a, b]; [1, 2]]' | metadata set --content-type 'application/x-nuon' --datasource-ls | from nuon | metadata | reject span | $in"#;
let cmd = r#"'[[a, b]; [1, 2]]' | metadata set --content-type 'application/x-nuon' --datasource-ls | from nuon | metadata | $in"#;
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),

View File

@ -167,7 +167,7 @@ fn parse_aligned_columns<'a>(
let headers: Vec<(String, usize)> = indices
.iter()
.enumerate()
.map(|(i, position)| (format!("column{i}"), *position))
.map(|(i, position)| (format!("column{}", i), *position))
.collect();
construct(ls.iter().map(|s| s.to_owned()), headers)

View File

@ -145,7 +145,6 @@ pub fn convert_string_to_value(string_input: String, span: Span) -> Result<Value
#[cfg(test)]
mod tests {
use crate::Reject;
use crate::{Metadata, MetadataSet};
use super::*;
@ -346,7 +345,6 @@ mod tests {
working_set.add_decl(Box::new(FromToml {}));
working_set.add_decl(Box::new(Metadata {}));
working_set.add_decl(Box::new(MetadataSet {}));
working_set.add_decl(Box::new(Reject {}));
working_set.render()
};
@ -355,7 +353,7 @@ mod tests {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = r#""[a]\nb = 1\nc = 1" | metadata set --content-type 'text/x-toml' --datasource-ls | from toml | metadata | reject span | $in"#;
let cmd = r#""[a]\nb = 1\nc = 1" | metadata set --content-type 'text/x-toml' --datasource-ls | from toml | metadata | $in"#;
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),

View File

@ -160,7 +160,6 @@ fn from_tsv(
mod test {
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
use crate::Reject;
use crate::{Metadata, MetadataSet};
use super::*;
@ -181,7 +180,6 @@ mod test {
working_set.add_decl(Box::new(FromTsv {}));
working_set.add_decl(Box::new(Metadata {}));
working_set.add_decl(Box::new(MetadataSet {}));
working_set.add_decl(Box::new(Reject {}));
working_set.render()
};
@ -190,7 +188,7 @@ mod test {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = r#""a\tb\n1\t2" | metadata set --content-type 'text/tab-separated-values' --datasource-ls | from tsv | metadata | reject span | $in"#;
let cmd = r#""a\tb\n1\t2" | metadata set --content-type 'text/tab-separated-values' --datasource-ls | from tsv | metadata | $in"#;
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),

View File

@ -252,7 +252,7 @@ fn process_xml_parse_error(source: String, err: roxmltree::Error, span: Span) ->
pos,
),
roxmltree::Error::UnknownNamespace(prefix, pos) => {
make_xml_error_spanned(format!("Unknown prefix {prefix}"), source, pos)
make_xml_error_spanned(format!("Unknown prefix {}", prefix), source, pos)
}
roxmltree::Error::UnexpectedCloseTag(expected, actual, pos) => make_xml_error_spanned(
format!("Unexpected close tag {actual}, expected {expected}"),
@ -370,7 +370,6 @@ fn make_xml_error_spanned(msg: impl Into<String>, src: String, pos: TextPos) ->
mod tests {
use crate::Metadata;
use crate::MetadataSet;
use crate::Reject;
use super::*;
@ -542,7 +541,6 @@ mod tests {
working_set.add_decl(Box::new(FromXml {}));
working_set.add_decl(Box::new(Metadata {}));
working_set.add_decl(Box::new(MetadataSet {}));
working_set.add_decl(Box::new(Reject {}));
working_set.render()
};
@ -554,7 +552,7 @@ mod tests {
let cmd = r#"'<?xml version="1.0" encoding="UTF-8"?>
<note>
<remember>Event</remember>
</note>' | metadata set --content-type 'application/xml' --datasource-ls | from xml | metadata | reject span | $in"#;
</note>' | metadata set --content-type 'application/xml' --datasource-ls | from xml | metadata | $in"#;
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),

View File

@ -158,26 +158,27 @@ fn convert_yaml_value_to_nu_value(
}
serde_yaml::Value::Tagged(t) => {
let tag = &t.tag;
match &t.value {
let value = match &t.value {
serde_yaml::Value::String(s) => {
let val = format!("{tag} {s}").trim().to_string();
let val = format!("{} {}", tag, s).trim().to_string();
Value::string(val, span)
}
serde_yaml::Value::Number(n) => {
let val = format!("{tag} {n}").trim().to_string();
let val = format!("{} {}", tag, n).trim().to_string();
Value::string(val, span)
}
serde_yaml::Value::Bool(b) => {
let val = format!("{tag} {b}").trim().to_string();
let val = format!("{} {}", tag, b).trim().to_string();
Value::string(val, span)
}
serde_yaml::Value::Null => {
let val = format!("{tag}").trim().to_string();
let val = format!("{}", tag).trim().to_string();
Value::string(val, span)
}
v => convert_yaml_value_to_nu_value(v, span, val_span)?,
}
};
value
}
serde_yaml::Value::Null => Value::nothing(span),
x => unimplemented!("Unsupported YAML case: {:?}", x),
@ -243,7 +244,6 @@ fn from_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError
#[cfg(test)]
mod test {
use crate::Reject;
use crate::{Metadata, MetadataSet};
use super::*;
@ -410,7 +410,6 @@ mod test {
working_set.add_decl(Box::new(FromYaml {}));
working_set.add_decl(Box::new(Metadata {}));
working_set.add_decl(Box::new(MetadataSet {}));
working_set.add_decl(Box::new(Reject {}));
working_set.render()
};
@ -419,7 +418,7 @@ mod test {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = r#""a: 1\nb: 2" | metadata set --content-type 'application/yaml' --datasource-ls | from yaml | metadata | reject span | $in"#;
let cmd = r#""a: 1\nb: 2" | metadata set --content-type 'application/yaml' --datasource-ls | from yaml | metadata | $in"#;
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),

View File

@ -166,14 +166,14 @@ mod test {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = "{a: 1 b: 2} | to csv | metadata | get content_type | $in";
let cmd = "{a: 1 b: 2} | to csv | metadata | get content_type";
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),
&mut engine_state,
);
assert_eq!(
Value::test_string("text/csv"),
Value::test_record(record!("content_type" => Value::test_string("text/csv"))),
result.expect("There should be a result")
);
}

View File

@ -50,7 +50,7 @@ fn make_unsupported_input_error(
) -> ShellError {
ShellError::UnsupportedInput {
msg: "expected table or record".to_string(),
input: format!("input type: {type}"),
input: format!("input type: {}", r#type),
msg_span: head,
input_span: span,
}

View File

@ -229,14 +229,14 @@ mod test {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = "{a: 1 b: 2} | to json | metadata | get content_type | $in";
let cmd = "{a: 1 b: 2} | to json | metadata | get content_type";
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),
&mut engine_state,
);
assert_eq!(
Value::test_string("application/json"),
Value::test_record(record!("content_type" => Value::test_string("application/json"))),
result.expect("There should be a result")
);
}

View File

@ -246,7 +246,7 @@ fn table(
escaped_rows.push(escaped_row);
}
if (column_widths.is_empty() || column_widths.iter().all(|x| *x == 0))
let output_string = if (column_widths.is_empty() || column_widths.iter().all(|x| *x == 0))
&& escaped_rows.is_empty()
{
String::from("")
@ -260,7 +260,9 @@ fn table(
)
.trim()
.to_string()
}
};
output_string
}
pub fn group_by(values: PipelineData, head: Span, config: &Config) -> (PipelineData, bool) {
@ -904,14 +906,14 @@ mod tests {
.merge_delta(delta)
.expect("Error merging delta");
let cmd = "{a: 1 b: 2} | to md | metadata | get content_type | $in";
let cmd = "{a: 1 b: 2} | to md | metadata | get content_type";
let result = eval_pipeline_without_terminal_expression(
cmd,
std::env::temp_dir().as_ref(),
&mut engine_state,
);
assert_eq!(
Value::test_string("text/markdown"),
Value::test_record(record!("content_type" => Value::test_string("text/markdown"))),
result.expect("There should be a result")
);
}

Some files were not shown because too many files have changed in this diff Show More