Merge branch 'main' into fuzzy-select-underline

This commit is contained in:
Yash Thakur 2024-07-17 21:23:53 -04:00 committed by GitHub
commit 422b9a245f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
666 changed files with 24324 additions and 7111 deletions

View File

@ -26,6 +26,13 @@ updates:
patterns: patterns:
- "polars" - "polars"
- "polars-*" - "polars-*"
# uutils/coreutils also versions all their workspace crates the same at the moment
# Most of them have bleeding edge version requirements (some not)
# see: https://github.com/uutils/coreutils/blob/main/Cargo.toml
uutils:
patterns:
- "uucore"
- "uu_*"
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
directory: "/" directory: "/"
schedule: schedule:

View File

@ -19,7 +19,7 @@ jobs:
# Prevent sudden announcement of a new advisory from failing ci: # Prevent sudden announcement of a new advisory from failing ci:
continue-on-error: true continue-on-error: true
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.7
- uses: rustsec/audit-check@v1.4.1 - uses: rustsec/audit-check@v1.4.1
with: with:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}

View File

@ -33,10 +33,10 @@ jobs:
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0 uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
- name: cargo fmt - name: cargo fmt
run: cargo fmt --all -- --check run: cargo fmt --all -- --check
@ -66,10 +66,10 @@ jobs:
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0 uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
- name: Tests - name: Tests
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.default-flags }} run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.default-flags }}
@ -95,10 +95,10 @@ jobs:
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0 uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
- name: Install Nushell - name: Install Nushell
run: cargo install --path . --locked --no-default-features run: cargo install --path . --locked --no-default-features
@ -146,10 +146,10 @@ jobs:
runs-on: ${{ matrix.platform }} runs-on: ${{ matrix.platform }}
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.7
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0 uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
- name: Clippy - name: Clippy
run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS

View File

@ -27,7 +27,7 @@ jobs:
# if: github.repository == 'nushell/nightly' # if: github.repository == 'nushell/nightly'
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.1.6 uses: actions/checkout@v4.1.7
if: github.repository == 'nushell/nightly' if: github.repository == 'nushell/nightly'
with: with:
ref: main ref: main
@ -36,10 +36,10 @@ jobs:
token: ${{ secrets.WORKFLOW_TOKEN }} token: ${{ secrets.WORKFLOW_TOKEN }}
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v3.11 uses: hustcer/setup-nu@v3.12
if: github.repository == 'nushell/nightly' if: github.repository == 'nushell/nightly'
with: with:
version: 0.93.0 version: 0.95.0
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo # Synchronize the main branch of nightly repo with the main branch of Nushell official repo
- name: Prepare for Nightly Release - name: Prepare for Nightly Release
@ -99,20 +99,20 @@ jobs:
extra: msi extra: msi
os: windows-latest os: windows-latest
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
os: ubuntu-20.04 os: ubuntu-22.04
- target: x86_64-unknown-linux-musl - target: x86_64-unknown-linux-musl
os: ubuntu-20.04 os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu - target: aarch64-unknown-linux-gnu
os: ubuntu-20.04 os: ubuntu-22.04
- target: armv7-unknown-linux-gnueabihf - target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04 os: ubuntu-22.04
- target: riscv64gc-unknown-linux-gnu - target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-latest
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.7
with: with:
ref: main ref: main
fetch-depth: 0 fetch-depth: 0
@ -122,15 +122,15 @@ jobs:
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
- name: Setup Rust toolchain and cache - name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0 uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135` # WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with: with:
rustflags: '' rustflags: ''
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v3.11 uses: hustcer/setup-nu@v3.12
with: with:
version: 0.93.0 version: 0.95.0
- name: Release Nu Binary - name: Release Nu Binary
id: nu id: nu
@ -161,7 +161,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release # REF: https://github.com/marketplace/actions/gh-release
# Create a release only in nushell/nightly repo # Create a release only in nushell/nightly repo
- name: Publish Archive - name: Publish Archive
uses: softprops/action-gh-release@v2.0.5 uses: softprops/action-gh-release@v2.0.6
if: ${{ startsWith(github.repository, 'nushell/nightly') }} if: ${{ startsWith(github.repository, 'nushell/nightly') }}
with: with:
prerelease: true prerelease: true
@ -181,14 +181,14 @@ jobs:
- name: Waiting for Release - name: Waiting for Release
run: sleep 1800 run: sleep 1800
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.7
with: with:
ref: main ref: main
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v3.11 uses: hustcer/setup-nu@v3.12
with: with:
version: 0.93.0 version: 0.95.0
# Keep the last a few releases # Keep the last a few releases
- name: Delete Older Releases - name: Delete Older Releases

View File

@ -49,36 +49,36 @@ jobs:
extra: msi extra: msi
os: windows-latest os: windows-latest
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
os: ubuntu-20.04 os: ubuntu-22.04
- target: x86_64-unknown-linux-musl - target: x86_64-unknown-linux-musl
os: ubuntu-20.04 os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu - target: aarch64-unknown-linux-gnu
os: ubuntu-20.04 os: ubuntu-22.04
- target: armv7-unknown-linux-gnueabihf - target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04 os: ubuntu-22.04
- target: riscv64gc-unknown-linux-gnu - target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-latest
runs-on: ${{matrix.os}} runs-on: ${{matrix.os}}
steps: steps:
- uses: actions/checkout@v4.1.6 - uses: actions/checkout@v4.1.7
- name: Update Rust Toolchain Target - name: Update Rust Toolchain Target
run: | run: |
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
- name: Setup Rust toolchain - name: Setup Rust toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0 uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135` # WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
with: with:
cache: false cache: false
rustflags: '' rustflags: ''
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v3.11 uses: hustcer/setup-nu@v3.12
with: with:
version: 0.93.0 version: 0.95.0
- name: Release Nu Binary - name: Release Nu Binary
id: nu id: nu
@ -91,7 +91,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release # REF: https://github.com/marketplace/actions/gh-release
- name: Publish Archive - name: Publish Archive
uses: softprops/action-gh-release@v2.0.5 uses: softprops/action-gh-release@v2.0.6
if: ${{ startsWith(github.ref, 'refs/tags/') }} if: ${{ startsWith(github.ref, 'refs/tags/') }}
with: with:
draft: true draft: true

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout Actions Repository - name: Checkout Actions Repository
uses: actions/checkout@v4.1.6 uses: actions/checkout@v4.1.7
- name: Check spelling - name: Check spelling
uses: crate-ci/typos@v1.22.1 uses: crate-ci/typos@v1.23.2

514
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,7 @@ license = "MIT"
name = "nu" name = "nu"
repository = "https://github.com/nushell/nushell" repository = "https://github.com/nushell/nushell"
rust-version = "1.77.2" rust-version = "1.77.2"
version = "0.94.3" version = "0.95.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -39,6 +39,7 @@ members = [
"crates/nu-lsp", "crates/nu-lsp",
"crates/nu-pretty-hex", "crates/nu-pretty-hex",
"crates/nu-protocol", "crates/nu-protocol",
"crates/nu-derive-value",
"crates/nu-plugin", "crates/nu-plugin",
"crates/nu-plugin-core", "crates/nu-plugin-core",
"crates/nu-plugin-engine", "crates/nu-plugin-engine",
@ -74,13 +75,15 @@ chardetng = "0.1.17"
chrono = { default-features = false, version = "0.4.34" } chrono = { default-features = false, version = "0.4.34" }
chrono-humanize = "0.2.3" chrono-humanize = "0.2.3"
chrono-tz = "0.8" chrono-tz = "0.8"
convert_case = "0.6"
crossbeam-channel = "0.5.8" crossbeam-channel = "0.5.8"
crossterm = "0.27" crossterm = "0.27"
csv = "1.3" csv = "1.3"
ctrlc = "3.4" ctrlc = "3.4"
deunicode = "1.6.0"
dialoguer = { default-features = false, version = "0.11" } dialoguer = { default-features = false, version = "0.11" }
digest = { default-features = false, version = "0.10" } digest = { default-features = false, version = "0.10" }
dirs-next = "2.0" dirs = "5.0"
dtparse = "2.0" dtparse = "2.0"
encoding_rs = "0.8" encoding_rs = "0.8"
fancy-regex = "0.13" fancy-regex = "0.13"
@ -93,7 +96,7 @@ heck = "0.5.0"
human-date-parser = "0.1.1" human-date-parser = "0.1.1"
indexmap = "2.2" indexmap = "2.2"
indicatif = "0.17" indicatif = "0.17"
interprocess = "2.1.0" interprocess = "2.2.0"
is_executable = "1.0" is_executable = "1.0"
itertools = "0.12" itertools = "0.12"
libc = "0.2" libc = "0.2"
@ -117,17 +120,20 @@ num-format = "0.4"
num-traits = "0.2" num-traits = "0.2"
omnipath = "0.1" omnipath = "0.1"
once_cell = "1.18" once_cell = "1.18"
open = "5.1" open = "5.3"
os_pipe = { version = "1.2", features = ["io_safety"] } os_pipe = { version = "1.2", features = ["io_safety"] }
pathdiff = "0.2" pathdiff = "0.2"
percent-encoding = "2" percent-encoding = "2"
pretty_assertions = "1.4" pretty_assertions = "1.4"
print-positions = "0.6" print-positions = "0.6"
proc-macro-error = { version = "1.0", default-features = false }
proc-macro2 = "1.0"
procfs = "0.16.0" procfs = "0.16.0"
pwd = "1.3" pwd = "1.3"
quick-xml = "0.31.0" quick-xml = "0.31.0"
quickcheck = "1.0" quickcheck = "1.0"
quickcheck_macros = "1.0" quickcheck_macros = "1.0"
quote = "1.0"
rand = "0.8" rand = "0.8"
ratatui = "0.26" ratatui = "0.26"
rayon = "1.10" rayon = "1.10"
@ -139,7 +145,7 @@ ropey = "1.6.1"
roxmltree = "0.19" roxmltree = "0.19"
rstest = { version = "0.18", default-features = false } rstest = { version = "0.18", default-features = false }
rusqlite = "0.31" rusqlite = "0.31"
rust-embed = "8.4.0" rust-embed = "8.5.0"
same-file = "1.0" same-file = "1.0"
serde = { version = "1.0", default-features = false } serde = { version = "1.0", default-features = false }
serde_json = "1.0" serde_json = "1.0"
@ -147,6 +153,7 @@ serde_urlencoded = "0.7.1"
serde_yaml = "0.9" serde_yaml = "0.9"
sha2 = "0.10" sha2 = "0.10"
strip-ansi-escapes = "0.2.0" strip-ansi-escapes = "0.2.0"
syn = "2.0"
sysinfo = "0.30" sysinfo = "0.30"
tabled = { version = "0.14.0", default-features = false } tabled = { version = "0.14.0", default-features = false }
tempfile = "3.10" tempfile = "3.10"
@ -157,16 +164,16 @@ trash = "3.3"
umask = "2.1" umask = "2.1"
unicode-segmentation = "1.11" unicode-segmentation = "1.11"
unicode-width = "0.1" unicode-width = "0.1"
ureq = { version = "2.9", default-features = false } ureq = { version = "2.10", default-features = false }
url = "2.2" url = "2.2"
uu_cp = "0.0.25" uu_cp = "0.0.27"
uu_mkdir = "0.0.25" uu_mkdir = "0.0.27"
uu_mktemp = "0.0.25" uu_mktemp = "0.0.27"
uu_mv = "0.0.25" uu_mv = "0.0.27"
uu_whoami = "0.0.25" uu_whoami = "0.0.27"
uu_uname = "0.0.25" uu_uname = "0.0.27"
uucore = "0.0.25" uucore = "0.0.27"
uuid = "1.8.0" uuid = "1.10.0"
v_htmlescape = "0.15.0" v_htmlescape = "0.15.0"
wax = "0.6" wax = "0.6"
which = "6.0.0" which = "6.0.0"
@ -174,27 +181,27 @@ windows = "0.54"
winreg = "0.52" winreg = "0.52"
[dependencies] [dependencies]
nu-cli = { path = "./crates/nu-cli", version = "0.94.3" } nu-cli = { path = "./crates/nu-cli", version = "0.95.1" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.94.3" } nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.95.1" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.94.3" } nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.95.1" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.94.3", optional = true } nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.95.1", optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.94.3" } nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.95.1" }
nu-command = { path = "./crates/nu-command", version = "0.94.3" } nu-command = { path = "./crates/nu-command", version = "0.95.1" }
nu-engine = { path = "./crates/nu-engine", version = "0.94.3" } nu-engine = { path = "./crates/nu-engine", version = "0.95.1" }
nu-explore = { path = "./crates/nu-explore", version = "0.94.3" } nu-explore = { path = "./crates/nu-explore", version = "0.95.1" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.94.3" } nu-lsp = { path = "./crates/nu-lsp/", version = "0.95.1" }
nu-parser = { path = "./crates/nu-parser", version = "0.94.3" } nu-parser = { path = "./crates/nu-parser", version = "0.95.1" }
nu-path = { path = "./crates/nu-path", version = "0.94.3" } nu-path = { path = "./crates/nu-path", version = "0.95.1" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.94.3" } nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.95.1" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.94.3" } nu-protocol = { path = "./crates/nu-protocol", version = "0.95.1" }
nu-std = { path = "./crates/nu-std", version = "0.94.3" } nu-std = { path = "./crates/nu-std", version = "0.95.1" }
nu-system = { path = "./crates/nu-system", version = "0.94.3" } nu-system = { path = "./crates/nu-system", version = "0.95.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.94.3" } nu-utils = { path = "./crates/nu-utils", version = "0.95.1" }
reedline = { workspace = true, features = ["bashisms", "sqlite"] } reedline = { workspace = true, features = ["bashisms", "sqlite"] }
crossterm = { workspace = true } crossterm = { workspace = true }
ctrlc = { workspace = true } ctrlc = { workspace = true }
dirs = { workspace = true }
log = { workspace = true } log = { workspace = true }
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] } miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
mimalloc = { version = "0.1.42", default-features = false, optional = true } mimalloc = { version = "0.1.42", default-features = false, optional = true }
@ -218,13 +225,14 @@ nix = { workspace = true, default-features = false, features = [
] } ] }
[dev-dependencies] [dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.94.3" } nu-test-support = { path = "./crates/nu-test-support", version = "0.95.1" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.94.3" } nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.95.1" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.94.3" } nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.95.1" }
assert_cmd = "2.0" assert_cmd = "2.0"
dirs-next = { workspace = true } dirs = { workspace = true }
tango-bench = "0.5" tango-bench = "0.5"
pretty_assertions = { workspace = true } pretty_assertions = { workspace = true }
regex = { workspace = true }
rstest = { workspace = true, default-features = false } rstest = { workspace = true, default-features = false }
serial_test = "3.1" serial_test = "3.1"
tempfile = { workspace = true } tempfile = { workspace = true }
@ -244,7 +252,6 @@ default = ["default-no-clipboard", "system-clipboard"]
# See https://github.com/nushell/nushell/pull/11535 # See https://github.com/nushell/nushell/pull/11535
default-no-clipboard = [ default-no-clipboard = [
"plugin", "plugin",
"which-support",
"trash-support", "trash-support",
"sqlite", "sqlite",
"mimalloc", "mimalloc",
@ -264,7 +271,6 @@ system-clipboard = [
] ]
# Stable (Default) # Stable (Default)
which-support = ["nu-command/which-support", "nu-cmd-lang/which-support"]
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"] trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
# SQLite commands for nushell # SQLite commands for nushell
@ -298,7 +304,7 @@ bench = false
# To use a development version of a dependency please use a global override here # To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious # changing versions in each sub-crate of the workspace is tedious
[patch.crates-io] [patch.crates-io]
reedline = { git = "https://github.com/ysthakur/reedline", branch = "fuzzy-select-underline" } reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"} # nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
# Run all benchmarks with `cargo bench` # Run all benchmarks with `cargo bench`

View File

@ -52,7 +52,7 @@ To use `Nu` in GitHub Action, check [setup-nu](https://github.com/marketplace/ac
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers: Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
[![Packaging status](https://repology.org/badge/vertical-allrepos/nushell.svg)](https://repology.org/project/nushell/versions) [![Packaging status](https://repology.org/badge/vertical-allrepos/nushell.svg?columns=3)](https://repology.org/project/nushell/versions)
For details about which platforms the Nushell team actively supports, see [our platform support policy](devdocs/PLATFORM_SUPPORT.md). For details about which platforms the Nushell team actively supports, see [our platform support policy](devdocs/PLATFORM_SUPPORT.md).
@ -222,6 +222,7 @@ Please submit an issue or PR to be added to this list.
- [clap](https://github.com/clap-rs/clap/tree/master/clap_complete_nushell) - [clap](https://github.com/clap-rs/clap/tree/master/clap_complete_nushell)
- [Dorothy](http://github.com/bevry/dorothy) - [Dorothy](http://github.com/bevry/dorothy)
- [Direnv](https://github.com/direnv/direnv/blob/master/docs/hook.md#nushell) - [Direnv](https://github.com/direnv/direnv/blob/master/docs/hook.md#nushell)
- [x-cmd](https://x-cmd.com/mod/nu)
## Contributing ## Contributing

View File

@ -4,11 +4,14 @@ use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
PipelineData, Span, Spanned, Value, PipelineData, Signals, Span, Spanned, Value,
}; };
use nu_std::load_standard_library; use nu_std::load_standard_library;
use nu_utils::{get_default_config, get_default_env}; use nu_utils::{get_default_config, get_default_env};
use std::rc::Rc; use std::{
rc::Rc,
sync::{atomic::AtomicBool, Arc},
};
use std::hint::black_box; use std::hint::black_box;
@ -42,13 +45,16 @@ fn setup_stack_and_engine_from_command(command: &str) -> (Stack, EngineState) {
}; };
let mut stack = Stack::new(); let mut stack = Stack::new();
// Support running benchmarks with IR mode
stack.use_ir = std::env::var_os("NU_USE_IR").is_some();
evaluate_commands( evaluate_commands(
&commands, &commands,
&mut engine, &mut engine,
&mut stack, &mut stack,
PipelineData::empty(), PipelineData::empty(),
None, Default::default(),
false,
) )
.unwrap(); .unwrap();
@ -90,8 +96,7 @@ fn bench_command(
&mut engine, &mut engine,
&mut stack, &mut stack,
PipelineData::empty(), PipelineData::empty(),
None, Default::default(),
false,
) )
.unwrap(), .unwrap(),
); );
@ -250,14 +255,12 @@ fn bench_eval_interleave(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_eval_interleave_with_ctrlc(n: i32) -> impl IntoBenchmarks { fn bench_eval_interleave_with_interrupt(n: i32) -> impl IntoBenchmarks {
let mut engine = setup_engine(); let mut engine = setup_engine();
engine.ctrlc = Some(std::sync::Arc::new(std::sync::atomic::AtomicBool::new( engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
false,
)));
let stack = Stack::new(); let stack = Stack::new();
bench_command( bench_command(
&format!("eval_interleave_with_ctrlc_{n}"), &format!("eval_interleave_with_interrupt_{n}"),
&format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"), &format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
stack, stack,
engine, engine,
@ -445,9 +448,9 @@ tango_benchmarks!(
bench_eval_interleave(100), bench_eval_interleave(100),
bench_eval_interleave(1_000), bench_eval_interleave(1_000),
bench_eval_interleave(10_000), bench_eval_interleave(10_000),
bench_eval_interleave_with_ctrlc(100), bench_eval_interleave_with_interrupt(100),
bench_eval_interleave_with_ctrlc(1_000), bench_eval_interleave_with_interrupt(1_000),
bench_eval_interleave_with_ctrlc(10_000), bench_eval_interleave_with_interrupt(10_000),
// For // For
bench_eval_for(1), bench_eval_for(1),
bench_eval_for(10), bench_eval_for(10),

View File

@ -5,27 +5,27 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cli" name = "nu-cli"
version = "0.94.3" version = "0.95.1"
[lib] [lib]
bench = false bench = false
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.3" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.95.1" }
nu-command = { path = "../nu-command", version = "0.94.3" } nu-command = { path = "../nu-command", version = "0.95.1" }
nu-test-support = { path = "../nu-test-support", version = "0.94.3" } nu-test-support = { path = "../nu-test-support", version = "0.95.1" }
rstest = { workspace = true, default-features = false } rstest = { workspace = true, default-features = false }
tempfile = { workspace = true } tempfile = { workspace = true }
[dependencies] [dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.94.3" } nu-cmd-base = { path = "../nu-cmd-base", version = "0.95.1" }
nu-engine = { path = "../nu-engine", version = "0.94.3" } nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-path = { path = "../nu-path", version = "0.94.3" } nu-path = { path = "../nu-path", version = "0.95.1" }
nu-parser = { path = "../nu-parser", version = "0.94.3" } nu-parser = { path = "../nu-parser", version = "0.95.1" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.94.3", optional = true } nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.95.1", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.94.3" } nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
nu-utils = { path = "../nu-utils", version = "0.94.3" } nu-utils = { path = "../nu-utils", version = "0.95.1" }
nu-color-config = { path = "../nu-color-config", version = "0.94.3" } nu-color-config = { path = "../nu-color-config", version = "0.95.1" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
reedline = { workspace = true, features = ["bashisms", "sqlite"] } reedline = { workspace = true, features = ["bashisms", "sqlite"] }
@ -46,4 +46,4 @@ which = { workspace = true }
[features] [features]
plugin = ["nu-plugin-engine"] plugin = ["nu-plugin-engine"]
system-clipboard = ["reedline/system_clipboard"] system-clipboard = ["reedline/system_clipboard"]

7
crates/nu-cli/README.md Normal file
View File

@ -0,0 +1,7 @@
This crate implements the core functionality of the interactive Nushell REPL and interfaces with `reedline`.
Currently implements the syntax highlighting and completions logic.
Furthermore includes a few commands that are specific to `reedline`
## Internal Nushell crate
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.

View File

@ -47,7 +47,7 @@ impl Command for History {
if let Some(config_path) = nu_path::config_dir() { if let Some(config_path) = nu_path::config_dir() {
let clear = call.has_flag(engine_state, stack, "clear")?; let clear = call.has_flag(engine_state, stack, "clear")?;
let long = call.has_flag(engine_state, stack, "long")?; let long = call.has_flag(engine_state, stack, "long")?;
let ctrlc = engine_state.ctrlc.clone(); let signals = engine_state.signals().clone();
let mut history_path = config_path; let mut history_path = config_path;
history_path.push("nushell"); history_path.push("nushell");
@ -107,7 +107,7 @@ impl Command for History {
file: history_path.display().to_string(), file: history_path.display().to_string(),
span: head, span: head,
})? })?
.into_pipeline_data(head, ctrlc)), .into_pipeline_data(head, signals)),
HistoryFileFormat::Sqlite => Ok(history_reader HistoryFileFormat::Sqlite => Ok(history_reader
.and_then(|h| { .and_then(|h| {
h.search(SearchQuery::everything(SearchDirection::Forward, None)) h.search(SearchQuery::everything(SearchDirection::Forward, None))
@ -122,7 +122,7 @@ impl Command for History {
file: history_path.display().to_string(), file: history_path.display().to_string(),
span: head, span: head,
})? })?
.into_pipeline_data(head, ctrlc)), .into_pipeline_data(head, signals)),
} }
} }
} else { } else {

View File

@ -49,22 +49,24 @@ impl Command for KeybindingsList {
fn run( fn run(
&self, &self,
_engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let records = if call.named_len() == 0 { let all_options = ["modifiers", "keycodes", "edits", "modes", "events"];
let all_options = ["modifiers", "keycodes", "edits", "modes", "events"];
all_options let presence = all_options
.iter() .iter()
.flat_map(|argument| get_records(argument, call.head)) .map(|option| call.has_flag(engine_state, stack, option))
.collect() .collect::<Result<Vec<_>, ShellError>>()?;
} else {
call.named_iter() let records = all_options
.flat_map(|(argument, _, _)| get_records(argument.item.as_str(), call.head)) .iter()
.collect() .zip(presence)
}; .filter(|(_, present)| *present)
.flat_map(|(option, _)| get_records(option, call.head))
.collect();
Ok(Value::list(records, call.head).into_pipeline_data()) Ok(Value::list(records, call.head).into_pipeline_data())
} }

View File

@ -1,13 +1,12 @@
use crate::completions::{CompletionOptions, SortBy}; use crate::completions::CompletionOptions;
use nu_protocol::{ use nu_protocol::{
engine::{Stack, StateWorkingSet}, engine::{Stack, StateWorkingSet},
levenshtein_distance, Span, Span,
}; };
use reedline::Suggestion; use reedline::Suggestion;
// Completer trait represents the three stages of the completion
// fetch, filter and sort
pub trait Completer { pub trait Completer {
/// Fetch, filter, and sort completions
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn fetch( fn fetch(
&mut self, &mut self,
@ -19,32 +18,6 @@ pub trait Completer {
pos: usize, pos: usize,
options: &CompletionOptions, options: &CompletionOptions,
) -> Vec<SemanticSuggestion>; ) -> Vec<SemanticSuggestion>;
fn get_sort_by(&self) -> SortBy {
SortBy::Ascending
}
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
let mut filtered_items = items;
// Sort items
match self.get_sort_by() {
SortBy::LevenshteinDistance => {
filtered_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
a_distance.cmp(&b_distance)
});
}
SortBy::Ascending => {
filtered_items.sort_by(|a, b| a.suggestion.value.cmp(&b.suggestion.value));
}
SortBy::None => {}
};
filtered_items
}
} }
#[derive(Debug, Default, PartialEq)] #[derive(Debug, Default, PartialEq)]

View File

@ -9,7 +9,7 @@ use nu_protocol::{
}; };
use reedline::Suggestion; use reedline::Suggestion;
use super::SemanticSuggestion; use super::{completion_common::sort_suggestions, SemanticSuggestion};
pub struct CommandCompletion { pub struct CommandCompletion {
flattened: Vec<(Span, FlatShape)>, flattened: Vec<(Span, FlatShape)>,
@ -164,7 +164,7 @@ impl Completer for CommandCompletion {
&mut self, &mut self,
working_set: &StateWorkingSet, working_set: &StateWorkingSet,
_stack: &Stack, _stack: &Stack,
_prefix: Vec<u8>, prefix: Vec<u8>,
span: Span, span: Span,
offset: usize, offset: usize,
pos: usize, pos: usize,
@ -201,7 +201,11 @@ impl Completer for CommandCompletion {
}; };
if !subcommands.is_empty() { if !subcommands.is_empty() {
return subcommands; return sort_suggestions(
&String::from_utf8_lossy(&prefix),
subcommands,
SortBy::LevenshteinDistance,
);
} }
let config = working_set.get_config(); let config = working_set.get_config();
@ -226,11 +230,11 @@ impl Completer for CommandCompletion {
vec![] vec![]
}; };
subcommands.into_iter().chain(commands).collect::<Vec<_>>() sort_suggestions(
} &String::from_utf8_lossy(&prefix),
commands,
fn get_sort_by(&self) -> SortBy { SortBy::LevenshteinDistance,
SortBy::LevenshteinDistance )
} }
} }

View File

@ -51,8 +51,7 @@ impl NuCompleter {
..Default::default() ..Default::default()
}; };
// Fetch completer.fetch(
let mut suggestions = completer.fetch(
working_set, working_set,
&self.stack, &self.stack,
prefix.clone(), prefix.clone(),
@ -60,12 +59,7 @@ impl NuCompleter {
offset, offset,
pos, pos,
&options, &options,
); )
// Sort
suggestions = completer.sort(suggestions, prefix);
suggestions
} }
fn external_completion( fn external_completion(

View File

@ -1,16 +1,21 @@
use crate::completions::{matches, CompletionOptions}; use crate::{
completions::{matches, CompletionOptions},
SemanticSuggestion,
};
use nu_ansi_term::Style; use nu_ansi_term::Style;
use nu_engine::env_to_string; use nu_engine::env_to_string;
use nu_path::{expand_to_real_path, home_dir}; use nu_path::{expand_to_real_path, home_dir};
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
Span, levenshtein_distance, Span,
}; };
use nu_utils::get_ls_colors; use nu_utils::get_ls_colors;
use std::path::{ use std::path::{
is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR, is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR,
}; };
use super::SortBy;
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct PathBuiltFromString { pub struct PathBuiltFromString {
parts: Vec<String>, parts: Vec<String>,
@ -45,6 +50,7 @@ fn complete_rec(
return completions; return completions;
}; };
let mut entries = Vec::new();
for entry in result.filter_map(|e| e.ok()) { for entry in result.filter_map(|e| e.ok()) {
let entry_name = entry.file_name().to_string_lossy().into_owned(); let entry_name = entry.file_name().to_string_lossy().into_owned();
let entry_isdir = entry.path().is_dir(); let entry_isdir = entry.path().is_dir();
@ -53,20 +59,26 @@ fn complete_rec(
built.isdir = entry_isdir; built.isdir = entry_isdir;
if !dir || entry_isdir { if !dir || entry_isdir {
match partial.split_first() { entries.push((entry_name, built));
Some((base, rest)) => { }
if matches(base, &entry_name, options) { }
if !rest.is_empty() || isdir {
completions let prefix = partial.first().unwrap_or(&"");
.extend(complete_rec(rest, &built, cwd, options, dir, isdir)); let sorted_entries = sort_completions(prefix, entries, SortBy::Ascending, |(entry, _)| entry);
} else {
completions.push(built); for (entry_name, built) in sorted_entries {
} match partial.split_first() {
Some((base, rest)) => {
if matches(base, &entry_name, options) {
if !rest.is_empty() || isdir {
completions.extend(complete_rec(rest, &built, cwd, options, dir, isdir));
} else {
completions.push(built);
} }
} }
None => { }
completions.push(built); None => {
} completions.push(built);
} }
} }
} }
@ -256,3 +268,38 @@ pub fn adjust_if_intermediate(
readjusted, readjusted,
} }
} }
/// Convenience function to sort suggestions using [`sort_completions`]
pub fn sort_suggestions(
prefix: &str,
items: Vec<SemanticSuggestion>,
sort_by: SortBy,
) -> Vec<SemanticSuggestion> {
sort_completions(prefix, items, sort_by, |it| &it.suggestion.value)
}
/// # Arguments
/// * `prefix` - What the user's typed, for sorting by Levenshtein distance
pub fn sort_completions<T>(
prefix: &str,
mut items: Vec<T>,
sort_by: SortBy,
get_value: fn(&T) -> &str,
) -> Vec<T> {
// Sort items
match sort_by {
SortBy::LevenshteinDistance => {
items.sort_by(|a, b| {
let a_distance = levenshtein_distance(prefix, get_value(a));
let b_distance = levenshtein_distance(prefix, get_value(b));
a_distance.cmp(&b_distance)
});
}
SortBy::Ascending => {
items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
}
SortBy::None => {}
};
items
}

View File

@ -12,6 +12,8 @@ use nu_protocol::{
use nu_utils::IgnoreCaseExt; use nu_utils::IgnoreCaseExt;
use std::collections::HashMap; use std::collections::HashMap;
use super::completion_common::sort_suggestions;
pub struct CustomCompletion { pub struct CustomCompletion {
stack: Stack, stack: Stack,
decl_id: usize, decl_id: usize,
@ -122,15 +124,12 @@ impl Completer for CustomCompletion {
}) })
.unwrap_or_default(); .unwrap_or_default();
if let Some(custom_completion_options) = custom_completion_options { let suggestions = if let Some(custom_completion_options) = custom_completion_options {
filter(&prefix, suggestions, &custom_completion_options) filter(&prefix, suggestions, &custom_completion_options)
} else { } else {
filter(&prefix, suggestions, completion_options) filter(&prefix, suggestions, completion_options)
} };
} sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, self.sort_by)
fn get_sort_by(&self) -> SortBy {
self.sort_by
} }
} }

View File

@ -1,14 +1,14 @@
use crate::completions::{ use crate::completions::{
completion_common::{adjust_if_intermediate, complete_item, AdjustView}, completion_common::{adjust_if_intermediate, complete_item, AdjustView},
Completer, CompletionOptions, SortBy, Completer, CompletionOptions,
}; };
use nu_ansi_term::Style; use nu_ansi_term::Style;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
levenshtein_distance, Span, Span,
}; };
use reedline::Suggestion; use reedline::Suggestion;
use std::path::{Path, MAIN_SEPARATOR as SEP}; use std::path::Path;
use super::SemanticSuggestion; use super::SemanticSuggestion;
@ -36,7 +36,7 @@ impl Completer for DirectoryCompletion {
// Filter only the folders // Filter only the folders
#[allow(deprecated)] #[allow(deprecated)]
let output: Vec<_> = directory_completion( let items: Vec<_> = directory_completion(
span, span,
&prefix, &prefix,
&working_set.permanent_state.current_work_dir(), &working_set.permanent_state.current_work_dir(),
@ -63,41 +63,11 @@ impl Completer for DirectoryCompletion {
}) })
.collect(); .collect();
output
}
// Sort results prioritizing the non hidden folders
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
// Sort items
let mut sorted_items = items;
match self.get_sort_by() {
SortBy::Ascending => {
sorted_items.sort_by(|a, b| {
// Ignore trailing slashes in folder names when sorting
a.suggestion
.value
.trim_end_matches(SEP)
.cmp(b.suggestion.value.trim_end_matches(SEP))
});
}
SortBy::LevenshteinDistance => {
sorted_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
a_distance.cmp(&b_distance)
});
}
_ => (),
}
// Separate the results between hidden and non hidden // Separate the results between hidden and non hidden
let mut hidden: Vec<SemanticSuggestion> = vec![]; let mut hidden: Vec<SemanticSuggestion> = vec![];
let mut non_hidden: Vec<SemanticSuggestion> = vec![]; let mut non_hidden: Vec<SemanticSuggestion> = vec![];
for item in sorted_items.into_iter() { for item in items.into_iter() {
let item_path = Path::new(&item.suggestion.value); let item_path = Path::new(&item.suggestion.value);
if let Some(value) = item_path.file_name() { if let Some(value) = item_path.file_name() {

View File

@ -1,4 +1,4 @@
use crate::completions::{file_path_completion, Completer, CompletionOptions, SortBy}; use crate::completions::{file_path_completion, Completer, CompletionOptions};
use nu_protocol::{ use nu_protocol::{
engine::{Stack, StateWorkingSet}, engine::{Stack, StateWorkingSet},
Span, Span,
@ -6,7 +6,7 @@ use nu_protocol::{
use reedline::Suggestion; use reedline::Suggestion;
use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR}; use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
use super::SemanticSuggestion; use super::{completion_common::sort_suggestions, SemanticSuggestion, SortBy};
#[derive(Clone, Default)] #[derive(Clone, Default)]
pub struct DotNuCompletion {} pub struct DotNuCompletion {}
@ -132,10 +132,6 @@ impl Completer for DotNuCompletion {
}) })
.collect(); .collect();
output sort_suggestions(&prefix_str, output, SortBy::Ascending)
}
fn get_sort_by(&self) -> SortBy {
SortBy::LevenshteinDistance
} }
} }

View File

@ -1,15 +1,15 @@
use crate::completions::{ use crate::completions::{
completion_common::{adjust_if_intermediate, complete_item, AdjustView}, completion_common::{adjust_if_intermediate, complete_item, AdjustView},
Completer, CompletionOptions, SortBy, Completer, CompletionOptions,
}; };
use nu_ansi_term::Style; use nu_ansi_term::Style;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
levenshtein_distance, Span, Span,
}; };
use nu_utils::IgnoreCaseExt; use nu_utils::IgnoreCaseExt;
use reedline::Suggestion; use reedline::Suggestion;
use std::path::{Path, MAIN_SEPARATOR as SEP}; use std::path::Path;
use super::SemanticSuggestion; use super::SemanticSuggestion;
@ -40,7 +40,7 @@ impl Completer for FileCompletion {
} = adjust_if_intermediate(&prefix, working_set, span); } = adjust_if_intermediate(&prefix, working_set, span);
#[allow(deprecated)] #[allow(deprecated)]
let output: Vec<_> = complete_item( let items: Vec<_> = complete_item(
readjusted, readjusted,
span, span,
&prefix, &prefix,
@ -68,41 +68,13 @@ impl Completer for FileCompletion {
}) })
.collect(); .collect();
output // Sort results prioritizing the non hidden folders
}
// Sort results prioritizing the non hidden folders
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
// Sort items
let mut sorted_items = items;
match self.get_sort_by() {
SortBy::Ascending => {
sorted_items.sort_by(|a, b| {
// Ignore trailing slashes in folder names when sorting
a.suggestion
.value
.trim_end_matches(SEP)
.cmp(b.suggestion.value.trim_end_matches(SEP))
});
}
SortBy::LevenshteinDistance => {
sorted_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
a_distance.cmp(&b_distance)
});
}
_ => (),
}
// Separate the results between hidden and non hidden // Separate the results between hidden and non hidden
let mut hidden: Vec<SemanticSuggestion> = vec![]; let mut hidden: Vec<SemanticSuggestion> = vec![];
let mut non_hidden: Vec<SemanticSuggestion> = vec![]; let mut non_hidden: Vec<SemanticSuggestion> = vec![];
for item in sorted_items.into_iter() { for item in items.into_iter() {
let item_path = Path::new(&item.suggestion.value); let item_path = Path::new(&item.suggestion.value);
if let Some(value) = item_path.file_name() { if let Some(value) = item_path.file_name() {

View File

@ -1,4 +1,6 @@
use crate::completions::{Completer, CompletionOptions}; use crate::completions::{
completion_common::sort_suggestions, Completer, CompletionOptions, SortBy,
};
use nu_protocol::{ use nu_protocol::{
ast::{Expr, Expression}, ast::{Expr, Expression},
engine::{Stack, StateWorkingSet}, engine::{Stack, StateWorkingSet},
@ -92,7 +94,7 @@ impl Completer for FlagCompletion {
} }
} }
return output; return sort_suggestions(&String::from_utf8_lossy(&prefix), output, SortBy::Ascending);
} }
vec![] vec![]

View File

@ -9,6 +9,8 @@ use nu_protocol::{
use reedline::Suggestion; use reedline::Suggestion;
use std::str; use std::str;
use super::{completion_common::sort_suggestions, SortBy};
#[derive(Clone)] #[derive(Clone)]
pub struct VariableCompletion { pub struct VariableCompletion {
var_context: (Vec<u8>, Vec<Vec<u8>>), // tuple with $var and the sublevels (.b.c.d) var_context: (Vec<u8>, Vec<Vec<u8>>), // tuple with $var and the sublevels (.b.c.d)
@ -40,6 +42,7 @@ impl Completer for VariableCompletion {
end: span.end - offset, end: span.end - offset,
}; };
let sublevels_count = self.var_context.1.len(); let sublevels_count = self.var_context.1.len();
let prefix_str = String::from_utf8_lossy(&prefix);
// Completions for the given variable // Completions for the given variable
if !var_str.is_empty() { if !var_str.is_empty() {
@ -69,7 +72,7 @@ impl Completer for VariableCompletion {
} }
} }
return output; return sort_suggestions(&prefix_str, output, SortBy::Ascending);
} }
} else { } else {
// No nesting provided, return all env vars // No nesting provided, return all env vars
@ -94,7 +97,7 @@ impl Completer for VariableCompletion {
} }
} }
return output; return sort_suggestions(&prefix_str, output, SortBy::Ascending);
} }
} }
@ -118,7 +121,7 @@ impl Completer for VariableCompletion {
} }
} }
return output; return sort_suggestions(&prefix_str, output, SortBy::Ascending);
} }
} }
@ -140,7 +143,7 @@ impl Completer for VariableCompletion {
} }
} }
return output; return sort_suggestions(&prefix_str, output, SortBy::Ascending);
} }
} }
} }
@ -230,6 +233,8 @@ impl Completer for VariableCompletion {
} }
} }
output = sort_suggestions(&prefix_str, output, SortBy::Ascending);
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended? output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
output output

View File

@ -8,7 +8,7 @@ use nu_protocol::{
report_error_new, HistoryFileFormat, PipelineData, report_error_new, HistoryFileFormat, PipelineData,
}; };
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
use nu_utils::utils::perf; use nu_utils::perf;
use std::path::PathBuf; use std::path::PathBuf;
#[cfg(feature = "plugin")] #[cfg(feature = "plugin")]
@ -53,13 +53,10 @@ pub fn read_plugin_file(
// Reading signatures from plugin registry file // Reading signatures from plugin registry file
// The plugin.msgpackz file stores the parsed signature collected from each registered plugin // The plugin.msgpackz file stores the parsed signature collected from each registered plugin
add_plugin_file(engine_state, plugin_file.clone(), storage_path); add_plugin_file(engine_state, plugin_file.clone(), storage_path);
perf( perf!(
"add plugin file to engine_state", "add plugin file to engine_state",
start_time, start_time,
file!(), engine_state.get_config().use_ansi_coloring
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
); );
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
@ -137,13 +134,10 @@ pub fn read_plugin_file(
} }
}; };
perf( perf!(
&format!("read plugin file {}", plugin_path.display()), &format!("read plugin file {}", plugin_path.display()),
start_time, start_time,
file!(), engine_state.get_config().use_ansi_coloring
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
); );
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
@ -156,13 +150,10 @@ pub fn read_plugin_file(
return; return;
} }
perf( perf!(
&format!("load plugin file {}", plugin_path.display()), &format!("load plugin file {}", plugin_path.display()),
start_time, start_time,
file!(), engine_state.get_config().use_ansi_coloring
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
); );
} }
} }
@ -344,7 +335,10 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState, storage_path: &str) -
name: identity.name().to_owned(), name: identity.name().to_owned(),
filename: identity.filename().to_owned(), filename: identity.filename().to_owned(),
shell: identity.shell().map(|p| p.to_owned()), shell: identity.shell().map(|p| p.to_owned()),
data: PluginRegistryItemData::Valid { commands }, data: PluginRegistryItemData::Valid {
metadata: Default::default(),
commands,
},
}); });
} }
@ -378,13 +372,10 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState, storage_path: &str) -
); );
} }
perf( perf!(
"migrate old plugin file", "migrate old plugin file",
start_time, start_time,
file!(), engine_state.get_config().use_ansi_coloring
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
); );
true true
} }

View File

@ -8,24 +8,53 @@ use nu_protocol::{
}; };
use std::sync::Arc; use std::sync::Arc;
#[derive(Default)]
pub struct EvaluateCommandsOpts {
pub table_mode: Option<Value>,
pub error_style: Option<Value>,
pub no_newline: bool,
}
/// Run a command (or commands) given to us by the user /// Run a command (or commands) given to us by the user
pub fn evaluate_commands( pub fn evaluate_commands(
commands: &Spanned<String>, commands: &Spanned<String>,
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
input: PipelineData, input: PipelineData,
table_mode: Option<Value>, opts: EvaluateCommandsOpts,
no_newline: bool,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
let EvaluateCommandsOpts {
table_mode,
error_style,
no_newline,
} = opts;
// Handle the configured error style early
if let Some(e_style) = error_style {
match e_style.coerce_str()?.parse() {
Ok(e_style) => {
Arc::make_mut(&mut engine_state.config).error_style = e_style;
}
Err(err) => {
return Err(ShellError::GenericError {
error: "Invalid value for `--error-style`".into(),
msg: err.into(),
span: Some(e_style.span()),
help: None,
inner: vec![],
});
}
}
}
// Translate environment variables from Strings to Values // Translate environment variables from Strings to Values
convert_env_values(engine_state, stack)?; convert_env_values(engine_state, stack)?;
// Parse the source code // Parse the source code
let (block, delta) = { let (block, delta) = {
if let Some(ref t_mode) = table_mode { if let Some(ref t_mode) = table_mode {
let mut config = engine_state.get_config().clone(); Arc::make_mut(&mut engine_state.config).table_mode =
config.table_mode = t_mode.coerce_str()?.parse().unwrap_or_default(); t_mode.coerce_str()?.parse().unwrap_or_default();
engine_state.set_config(config);
} }
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
@ -40,6 +69,11 @@ pub fn evaluate_commands(
std::process::exit(1); std::process::exit(1);
} }
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
(output, working_set.render()) (output, working_set.render())
}; };

View File

@ -76,12 +76,21 @@ pub fn evaluate_file(
trace!("parsing file: {}", file_path_str); trace!("parsing file: {}", file_path_str);
let block = parse(&mut working_set, Some(file_path_str), &file, false); let block = parse(&mut working_set, Some(file_path_str), &file, false);
if let Some(warning) = working_set.parse_warnings.first() {
report_error(&working_set, warning);
}
// If any parse errors were found, report the first error and exit. // If any parse errors were found, report the first error and exit.
if let Some(err) = working_set.parse_errors.first() { if let Some(err) = working_set.parse_errors.first() {
report_error(&working_set, err); report_error(&working_set, err);
std::process::exit(1); std::process::exit(1);
} }
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
// Look for blocks whose name starts with "main" and replace it with the filename. // Look for blocks whose name starts with "main" and replace it with the filename.
for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) { for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) {
if block.signature.name == "main" { if block.signature.name == "main" {

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod commands; mod commands;
mod completions; mod completions;
mod config_files; mod config_files;
@ -17,7 +18,7 @@ mod validation;
pub use commands::add_cli_context; pub use commands::add_cli_context;
pub use completions::{FileCompletion, NuCompleter, SemanticSuggestion, SuggestionKind}; pub use completions::{FileCompletion, NuCompleter, SemanticSuggestion, SuggestionKind};
pub use config_files::eval_config_contents; pub use config_files::eval_config_contents;
pub use eval_cmds::evaluate_commands; pub use eval_cmds::{evaluate_commands, EvaluateCommandsOpts};
pub use eval_file::evaluate_file; pub use eval_file::evaluate_file;
pub use menus::NuHelpCompleter; pub use menus::NuHelpCompleter;
pub use nu_cmd_base::util::get_init_cwd; pub use nu_cmd_base::util::get_init_cwd;

View File

@ -1,25 +1,31 @@
use nu_engine::documentation::get_flags_section; use nu_engine::documentation::get_flags_section;
use nu_protocol::{engine::EngineState, levenshtein_distance}; use nu_protocol::{engine::EngineState, levenshtein_distance, Config};
use nu_utils::IgnoreCaseExt; use nu_utils::IgnoreCaseExt;
use reedline::{Completer, Suggestion}; use reedline::{Completer, Suggestion};
use std::{fmt::Write, sync::Arc}; use std::{fmt::Write, sync::Arc};
pub struct NuHelpCompleter(Arc<EngineState>); pub struct NuHelpCompleter {
engine_state: Arc<EngineState>,
config: Arc<Config>,
}
impl NuHelpCompleter { impl NuHelpCompleter {
pub fn new(engine_state: Arc<EngineState>) -> Self { pub fn new(engine_state: Arc<EngineState>, config: Arc<Config>) -> Self {
Self(engine_state) Self {
engine_state,
config,
}
} }
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> { fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
let folded_line = line.to_folded_case(); let folded_line = line.to_folded_case();
let mut commands = self let mut commands = self
.0 .engine_state
.get_decls_sorted(false) .get_decls_sorted(false)
.into_iter() .into_iter()
.filter_map(|(_, decl_id)| { .filter_map(|(_, decl_id)| {
let decl = self.0.get_decl(decl_id); let decl = self.engine_state.get_decl(decl_id);
(decl.name().to_folded_case().contains(&folded_line) (decl.name().to_folded_case().contains(&folded_line)
|| decl.usage().to_folded_case().contains(&folded_line) || decl.usage().to_folded_case().contains(&folded_line)
|| decl || decl
@ -54,9 +60,12 @@ impl NuHelpCompleter {
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature()); let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
if !sig.named.is_empty() { if !sig.named.is_empty() {
long_desc.push_str(&get_flags_section(Some(&*self.0.clone()), &sig, |v| { long_desc.push_str(&get_flags_section(
v.to_parsable_string(", ", &self.0.config) Some(&self.engine_state),
})) Some(&self.config),
&sig,
|v| v.to_parsable_string(", ", &self.config),
))
} }
if !sig.required_positional.is_empty() if !sig.required_positional.is_empty()
@ -71,7 +80,7 @@ impl NuHelpCompleter {
let opt_suffix = if let Some(value) = &positional.default_value { let opt_suffix = if let Some(value) = &positional.default_value {
format!( format!(
" (optional, default: {})", " (optional, default: {})",
&value.to_parsable_string(", ", &self.0.config), &value.to_parsable_string(", ", &self.config),
) )
} else { } else {
(" (optional)").to_string() (" (optional)").to_string()
@ -139,7 +148,8 @@ mod test {
) { ) {
let engine_state = let engine_state =
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context()); nu_command::add_shell_command_context(nu_cmd_lang::create_default_context());
let mut completer = NuHelpCompleter::new(engine_state.into()); let config = engine_state.get_config().clone();
let mut completer = NuHelpCompleter::new(engine_state.into(), config);
let suggestions = completer.complete(line, end); let suggestions = completer.complete(line, end);
assert_eq!( assert_eq!(

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use reedline::{Highlighter, StyledText}; use reedline::{Highlighter, StyledText};
@ -32,14 +34,11 @@ impl Command for NuHighlight {
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
let ctrlc = engine_state.ctrlc.clone(); let signals = engine_state.signals();
let engine_state = std::sync::Arc::new(engine_state.clone());
let config = engine_state.get_config().clone();
let highlighter = crate::NuHighlighter { let highlighter = crate::NuHighlighter {
engine_state, engine_state: Arc::new(engine_state.clone()),
stack: std::sync::Arc::new(stack.clone()), stack: Arc::new(stack.clone()),
config,
}; };
input.map( input.map(
@ -50,7 +49,7 @@ impl Command for NuHighlight {
} }
Err(err) => Value::error(err, head), Err(err) => Value::error(err, head),
}, },
ctrlc, signals,
) )
} }

View File

@ -75,15 +75,21 @@ const DEFAULT_HELP_MENU: &str = r#"
// Adds all menus to line editor // Adds all menus to line editor
pub(crate) fn add_menus( pub(crate) fn add_menus(
mut line_editor: Reedline, mut line_editor: Reedline,
engine_state: Arc<EngineState>, engine_state_ref: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
//log::trace!("add_menus: config: {:#?}", &config); //log::trace!("add_menus: config: {:#?}", &config);
line_editor = line_editor.clear_menus(); line_editor = line_editor.clear_menus();
for menu in &config.menus { for menu in &config.menus {
line_editor = add_menu(line_editor, menu, engine_state.clone(), stack, config)? line_editor = add_menu(
line_editor,
menu,
engine_state_ref.clone(),
stack,
config.clone(),
)?
} }
// Checking if the default menus have been added from the config file // Checking if the default menus have been added from the config file
@ -93,13 +99,16 @@ pub(crate) fn add_menus(
("help_menu", DEFAULT_HELP_MENU), ("help_menu", DEFAULT_HELP_MENU),
]; ];
let mut engine_state = (*engine_state_ref).clone();
let mut menu_eval_results = vec![];
for (name, definition) in default_menus { for (name, definition) in default_menus {
if !config if !config
.menus .menus
.iter() .iter()
.any(|menu| menu.name.to_expanded_string("", config) == name) .any(|menu| menu.name.to_expanded_string("", &config) == name)
{ {
let (block, _) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(&engine_state); let mut working_set = StateWorkingSet::new(&engine_state);
let output = parse( let output = parse(
&mut working_set, &mut working_set,
@ -111,15 +120,31 @@ pub(crate) fn add_menus(
(output, working_set.render()) (output, working_set.render())
}; };
engine_state.merge_delta(delta)?;
let mut temp_stack = Stack::new().capture(); let mut temp_stack = Stack::new().capture();
let input = PipelineData::Empty; let input = PipelineData::Empty;
let res = eval_block::<WithoutDebug>(&engine_state, &mut temp_stack, &block, input)?; menu_eval_results.push(eval_block::<WithoutDebug>(
&engine_state,
&mut temp_stack,
&block,
input,
)?);
}
}
if let PipelineData::Value(value, None) = res { let new_engine_state_ref = Arc::new(engine_state);
for menu in create_menus(&value)? {
line_editor = for res in menu_eval_results.into_iter() {
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?; if let PipelineData::Value(value, None) = res {
} for menu in create_menus(&value)? {
line_editor = add_menu(
line_editor,
&menu,
new_engine_state_ref.clone(),
stack,
config.clone(),
)?;
} }
} }
} }
@ -132,27 +157,27 @@ fn add_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let span = menu.menu_type.span(); let span = menu.menu_type.span();
if let Value::Record { val, .. } = &menu.menu_type { if let Value::Record { val, .. } = &menu.menu_type {
let layout = extract_value("layout", val, span)?.to_expanded_string("", config); let layout = extract_value("layout", val, span)?.to_expanded_string("", &config);
match layout.as_str() { match layout.as_str() {
"columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, config), "columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, &config),
"list" => add_list_menu(line_editor, menu, engine_state, stack, config), "list" => add_list_menu(line_editor, menu, engine_state, stack, config),
"ide" => add_ide_menu(line_editor, menu, engine_state, stack, config), "ide" => add_ide_menu(line_editor, menu, engine_state, stack, config),
"description" => add_description_menu(line_editor, menu, engine_state, stack, config), "description" => add_description_menu(line_editor, menu, engine_state, stack, config),
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "columnar, list, ide or description".to_string(), expected: "columnar, list, ide or description".to_string(),
value: menu.menu_type.to_abbreviated_string(config), value: menu.menu_type.to_abbreviated_string(&config),
span: menu.menu_type.span(), span: menu.menu_type.span(),
}), }),
} }
} else { } else {
Err(ShellError::UnsupportedConfigValue { Err(ShellError::UnsupportedConfigValue {
expected: "only record type".to_string(), expected: "only record type".to_string(),
value: menu.menu_type.to_abbreviated_string(config), value: menu.menu_type.to_abbreviated_string(&config),
span: menu.menu_type.span(), span: menu.menu_type.span(),
}) })
} }
@ -263,9 +288,9 @@ pub(crate) fn add_list_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let name = menu.name.to_expanded_string("", config); let name = menu.name.to_expanded_string("", &config);
let mut list_menu = ListMenu::default().with_name(&name); let mut list_menu = ListMenu::default().with_name(&name);
let span = menu.menu_type.span(); let span = menu.menu_type.span();
@ -292,7 +317,7 @@ pub(crate) fn add_list_menu(
} }
} }
let marker = menu.marker.to_expanded_string("", config); let marker = menu.marker.to_expanded_string("", &config);
list_menu = list_menu.with_marker(&marker); list_menu = list_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?; let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -318,7 +343,7 @@ pub(crate) fn add_list_menu(
} }
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "block or omitted value".to_string(), expected: "block or omitted value".to_string(),
value: menu.source.to_abbreviated_string(config), value: menu.source.to_abbreviated_string(&config),
span: menu.source.span(), span: menu.source.span(),
}), }),
} }
@ -330,10 +355,10 @@ pub(crate) fn add_ide_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let span = menu.menu_type.span(); let span = menu.menu_type.span();
let name = menu.name.to_expanded_string("", config); let name = menu.name.to_expanded_string("", &config);
let mut ide_menu = IdeMenu::default().with_name(&name); let mut ide_menu = IdeMenu::default().with_name(&name);
if let Value::Record { val, .. } = &menu.menu_type { if let Value::Record { val, .. } = &menu.menu_type {
@ -398,7 +423,7 @@ pub(crate) fn add_ide_menu(
} else { } else {
return Err(ShellError::UnsupportedConfigValue { return Err(ShellError::UnsupportedConfigValue {
expected: "bool or record".to_string(), expected: "bool or record".to_string(),
value: border.to_abbreviated_string(config), value: border.to_abbreviated_string(&config),
span: border.span(), span: border.span(),
}); });
} }
@ -422,7 +447,7 @@ pub(crate) fn add_ide_menu(
_ => { _ => {
return Err(ShellError::UnsupportedConfigValue { return Err(ShellError::UnsupportedConfigValue {
expected: "\"left\", \"right\" or \"prefer_right\"".to_string(), expected: "\"left\", \"right\" or \"prefer_right\"".to_string(),
value: description_mode.to_abbreviated_string(config), value: description_mode.to_abbreviated_string(&config),
span: description_mode.span(), span: description_mode.span(),
}); });
} }
@ -490,7 +515,7 @@ pub(crate) fn add_ide_menu(
} }
} }
let marker = menu.marker.to_expanded_string("", config); let marker = menu.marker.to_expanded_string("", &config);
ide_menu = ide_menu.with_marker(&marker); ide_menu = ide_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?; let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -516,7 +541,7 @@ pub(crate) fn add_ide_menu(
} }
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "block or omitted value".to_string(), expected: "block or omitted value".to_string(),
value: menu.source.to_abbreviated_string(config), value: menu.source.to_abbreviated_string(&config),
span, span,
}), }),
} }
@ -528,9 +553,9 @@ pub(crate) fn add_description_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let name = menu.name.to_expanded_string("", config); let name = menu.name.to_expanded_string("", &config);
let mut description_menu = DescriptionMenu::default().with_name(&name); let mut description_menu = DescriptionMenu::default().with_name(&name);
let span = menu.menu_type.span(); let span = menu.menu_type.span();
@ -589,7 +614,7 @@ pub(crate) fn add_description_menu(
} }
} }
let marker = menu.marker.to_expanded_string("", config); let marker = menu.marker.to_expanded_string("", &config);
description_menu = description_menu.with_marker(&marker); description_menu = description_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?; let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -598,7 +623,7 @@ pub(crate) fn add_description_menu(
let span = menu.source.span(); let span = menu.source.span();
match &menu.source { match &menu.source {
Value::Nothing { .. } => { Value::Nothing { .. } => {
let completer = Box::new(NuHelpCompleter::new(engine_state)); let completer = Box::new(NuHelpCompleter::new(engine_state, config));
Ok(line_editor.with_menu(ReedlineMenu::WithCompleter { Ok(line_editor.with_menu(ReedlineMenu::WithCompleter {
menu: Box::new(description_menu), menu: Box::new(description_menu),
completer, completer,
@ -619,7 +644,7 @@ pub(crate) fn add_description_menu(
} }
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "closure or omitted value".to_string(), expected: "closure or omitted value".to_string(),
value: menu.source.to_abbreviated_string(config), value: menu.source.to_abbreviated_string(&config),
span: menu.source.span(), span: menu.source.span(),
}), }),
} }

View File

@ -31,7 +31,7 @@ use nu_protocol::{
}; };
use nu_utils::{ use nu_utils::{
filesystem::{have_permission, PermissionResult}, filesystem::{have_permission, PermissionResult},
utils::perf, perf,
}; };
use reedline::{ use reedline::{
CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory, CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory,
@ -43,7 +43,7 @@ use std::{
io::{self, IsTerminal, Write}, io::{self, IsTerminal, Write},
panic::{catch_unwind, AssertUnwindSafe}, panic::{catch_unwind, AssertUnwindSafe},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{atomic::Ordering, Arc}, sync::Arc,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use sysinfo::System; use sysinfo::System;
@ -89,14 +89,7 @@ pub fn evaluate_repl(
if let Err(e) = convert_env_values(engine_state, &unique_stack) { if let Err(e) = convert_env_values(engine_state, &unique_stack) {
report_error_new(engine_state, &e); report_error_new(engine_state, &e);
} }
perf( perf!("translate env vars", start_time, use_color);
"translate env vars",
start_time,
file!(),
line!(),
column!(),
use_color,
);
// seed env vars // seed env vars
unique_stack.add_env_var( unique_stack.add_env_var(
@ -225,28 +218,14 @@ fn get_line_editor(
// Now that reedline is created, get the history session id and store it in engine_state // Now that reedline is created, get the history session id and store it in engine_state
store_history_id_in_engine(engine_state, &line_editor); store_history_id_in_engine(engine_state, &line_editor);
perf( perf!("setup reedline", start_time, use_color);
"setup reedline",
start_time,
file!(),
line!(),
column!(),
use_color,
);
if let Some(history) = engine_state.history_config() { if let Some(history) = engine_state.history_config() {
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
line_editor = setup_history(nushell_path, engine_state, line_editor, history)?; line_editor = setup_history(nushell_path, engine_state, line_editor, history)?;
perf( perf!("setup history", start_time, use_color);
"setup history",
start_time,
file!(),
line!(),
column!(),
use_color,
);
} }
Ok(line_editor) Ok(line_editor)
} }
@ -289,28 +268,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
if let Err(err) = engine_state.merge_env(&mut stack, cwd) { if let Err(err) = engine_state.merge_env(&mut stack, cwd) {
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
perf( // Check whether $env.NU_USE_IR is set, so that the user can change it in the REPL
"merge env", // Temporary while IR eval is optional
start_time, stack.use_ir = stack.has_env_var(engine_state, "NU_USE_IR");
file!(), perf!("merge env", start_time, use_color);
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Reset the ctrl-c handler engine_state.reset_signals();
if let Some(ctrlc) = &mut engine_state.ctrlc { perf!("reset signals", start_time, use_color);
ctrlc.store(false, Ordering::SeqCst);
}
perf(
"reset ctrlc",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Right before we start our prompt and take input from the user, // Right before we start our prompt and take input from the user,
@ -320,14 +285,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
} }
perf( perf!("pre-prompt hook", start_time, use_color);
"pre-prompt hook",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Next, check all the environment variables they ask for // Next, check all the environment variables they ask for
@ -336,17 +294,10 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
if let Err(error) = hook::eval_env_change_hook(env_change, engine_state, &mut stack) { if let Err(error) = hook::eval_env_change_hook(env_change, engine_state, &mut stack) {
report_error_new(engine_state, &error) report_error_new(engine_state, &error)
} }
perf( perf!("env-change hook", start_time, use_color);
"env-change hook",
start_time,
file!(),
line!(),
column!(),
use_color,
);
let engine_reference = Arc::new(engine_state.clone()); let engine_reference = Arc::new(engine_state.clone());
let config = engine_state.get_config(); let config = stack.get_config(engine_state);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Find the configured cursor shapes for each mode // Find the configured cursor shapes for each mode
@ -355,14 +306,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal), vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal),
emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs), emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs),
}; };
perf( perf!("get config/cursor config", start_time, use_color);
"get config/cursor config",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// at this line we have cloned the state for the completer and the transient prompt // at this line we have cloned the state for the completer and the transient prompt
@ -379,7 +323,6 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
engine_state: engine_reference.clone(), engine_state: engine_reference.clone(),
// STACK-REFERENCE 1 // STACK-REFERENCE 1
stack: stack_arc.clone(), stack: stack_arc.clone(),
config: config.clone(),
})) }))
.with_validator(Box::new(NuValidator { .with_validator(Box::new(NuValidator {
engine_state: engine_reference.clone(), engine_state: engine_reference.clone(),
@ -392,16 +335,17 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
.with_quick_completions(config.quick_completions) .with_quick_completions(config.quick_completions)
.with_partial_completions(config.partial_completions) .with_partial_completions(config.partial_completions)
.with_ansi_colors(config.use_ansi_coloring) .with_ansi_colors(config.use_ansi_coloring)
.with_cwd(Some(
engine_state
.cwd(None)
.map(|cwd| cwd.into_std_path_buf())
.unwrap_or_default()
.to_string_lossy()
.to_string(),
))
.with_cursor_config(cursor_config); .with_cursor_config(cursor_config);
perf( perf!("reedline builder", start_time, use_color);
"reedline builder",
start_time,
file!(),
line!(),
column!(),
use_color,
);
let style_computer = StyleComputer::from_config(engine_state, &stack_arc); let style_computer = StyleComputer::from_config(engine_state, &stack_arc);
@ -416,14 +360,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
line_editor.disable_hints() line_editor.disable_hints()
}; };
perf( perf!("reedline coloring/style_computer", start_time, use_color);
"reedline coloring/style_computer",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
trace!("adding menus"); trace!("adding menus");
@ -433,14 +370,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
Reedline::create() Reedline::create()
}); });
perf( perf!("reedline adding menus", start_time, use_color);
"reedline adding menus",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
let buffer_editor = get_editor(engine_state, &stack_arc, Span::unknown()); let buffer_editor = get_editor(engine_state, &stack_arc, Span::unknown());
@ -457,14 +387,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
line_editor line_editor
}; };
perf( perf!("reedline buffer_editor", start_time, use_color);
"reedline buffer_editor",
start_time,
file!(),
line!(),
column!(),
use_color,
);
if let Some(history) = engine_state.history_config() { if let Some(history) = engine_state.history_config() {
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
@ -474,28 +397,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
} }
} }
perf( perf!("sync_history", start_time, use_color);
"sync_history",
start_time,
file!(),
line!(),
column!(),
use_color,
);
} }
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Changing the line editor based on the found keybindings // Changing the line editor based on the found keybindings
line_editor = setup_keybindings(engine_state, line_editor); line_editor = setup_keybindings(engine_state, line_editor);
perf( perf!("keybindings", start_time, use_color);
"keybindings",
start_time,
file!(),
line!(),
column!(),
use_color,
);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
let config = &engine_state.get_config().clone(); let config = &engine_state.get_config().clone();
@ -512,14 +421,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
nu_prompt, nu_prompt,
); );
perf( perf!("update_prompt", start_time, use_color);
"update_prompt",
start_time,
file!(),
line!(),
column!(),
use_color,
);
*entry_num += 1; *entry_num += 1;
@ -546,14 +448,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
// so we should avoid it or making stack cheaper to clone. // so we should avoid it or making stack cheaper to clone.
let mut stack = Arc::unwrap_or_clone(stack_arc); let mut stack = Arc::unwrap_or_clone(stack_arc);
perf( perf!("line_editor setup", start_time, use_color);
"line_editor setup",
start_time,
file!(),
line!(),
column!(),
use_color,
);
let line_editor_input_time = std::time::Instant::now(); let line_editor_input_time = std::time::Instant::now();
match input { match input {
@ -590,14 +485,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
} }
} }
perf( perf!("pre_execution_hook", start_time, use_color);
"pre_execution_hook",
start_time,
file!(),
line!(),
column!(),
use_color,
);
let mut repl = engine_state.repl_state.lock().expect("repl state mutex"); let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
repl.cursor_pos = line_editor.current_insertion_point(); repl.cursor_pos = line_editor.current_insertion_point();
@ -612,26 +500,20 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
run_ansi_sequence(VSCODE_PRE_EXECUTION_MARKER); run_ansi_sequence(VSCODE_PRE_EXECUTION_MARKER);
perf( perf!(
"pre_execute_marker (633;C) ansi escape sequence", "pre_execute_marker (633;C) ansi escape sequence",
start_time, start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
} else if shell_integration_osc133 { } else if shell_integration_osc133 {
start_time = Instant::now(); start_time = Instant::now();
run_ansi_sequence(PRE_EXECUTION_MARKER); run_ansi_sequence(PRE_EXECUTION_MARKER);
perf( perf!(
"pre_execute_marker (133;C) ansi escape sequence", "pre_execute_marker (133;C) ansi escape sequence",
start_time, start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
} }
} else if shell_integration_osc133 { } else if shell_integration_osc133 {
@ -639,13 +521,10 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
run_ansi_sequence(PRE_EXECUTION_MARKER); run_ansi_sequence(PRE_EXECUTION_MARKER);
perf( perf!(
"pre_execute_marker (133;C) ansi escape sequence", "pre_execute_marker (133;C) ansi escape sequence",
start_time, start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
} }
@ -769,22 +648,16 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
); );
} }
} }
perf( perf!(
"processing line editor input", "processing line editor input",
line_editor_input_time, line_editor_input_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
perf( perf!(
"time between prompts in line editor loop", "time between prompts in line editor loop",
loop_start_time, loop_start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
(true, stack, line_editor) (true, stack, line_editor)
@ -800,13 +673,14 @@ fn prepare_history_metadata(
line_editor: &mut Reedline, line_editor: &mut Reedline,
) { ) {
if !s.is_empty() && line_editor.has_last_command_context() { if !s.is_empty() && line_editor.has_last_command_context() {
#[allow(deprecated)]
let result = line_editor let result = line_editor
.update_last_command_context(&|mut c| { .update_last_command_context(&|mut c| {
c.start_timestamp = Some(chrono::Utc::now()); c.start_timestamp = Some(chrono::Utc::now());
c.hostname = hostname.map(str::to_string); c.hostname = hostname.map(str::to_string);
c.cwd = engine_state
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd()); .cwd(None)
.ok()
.map(|path| path.to_string_lossy().to_string());
c c
}) })
.into_diagnostic(); .into_diagnostic();
@ -1061,14 +935,7 @@ fn run_shell_integration_osc2(
// ESC]2;stringBEL -- Set window title to string // ESC]2;stringBEL -- Set window title to string
run_ansi_sequence(&format!("\x1b]2;{title}\x07")); run_ansi_sequence(&format!("\x1b]2;{title}\x07"));
perf( perf!("set title with command osc2", start_time, use_color);
"set title with command osc2",
start_time,
file!(),
line!(),
column!(),
use_color,
);
} }
} }
@ -1093,13 +960,10 @@ fn run_shell_integration_osc7(
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS) percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
)); ));
perf( perf!(
"communicate path to terminal with osc7", "communicate path to terminal with osc7",
start_time, start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
} }
} }
@ -1116,13 +980,10 @@ fn run_shell_integration_osc9_9(engine_state: &EngineState, stack: &mut Stack, u
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS) percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
)); ));
perf( perf!(
"communicate path to terminal with osc9;9", "communicate path to terminal with osc9;9",
start_time, start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
} }
} }
@ -1142,13 +1003,10 @@ fn run_shell_integration_osc633(engine_state: &EngineState, stack: &mut Stack, u
VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX
)); ));
perf( perf!(
"communicate path to terminal with osc633;P", "communicate path to terminal with osc633;P",
start_time, start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
} }
} }
@ -1371,13 +1229,10 @@ fn run_finaliziation_ansi_sequence(
shell_integration_osc133, shell_integration_osc133,
)); ));
perf( perf!(
"post_execute_marker (633;D) ansi escape sequences", "post_execute_marker (633;D) ansi escape sequences",
start_time, start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
} else if shell_integration_osc133 { } else if shell_integration_osc133 {
let start_time = Instant::now(); let start_time = Instant::now();
@ -1389,13 +1244,10 @@ fn run_finaliziation_ansi_sequence(
shell_integration_osc133, shell_integration_osc133,
)); ));
perf( perf!(
"post_execute_marker (133;D) ansi escape sequences", "post_execute_marker (133;D) ansi escape sequences",
start_time, start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
} }
} else if shell_integration_osc133 { } else if shell_integration_osc133 {
@ -1408,13 +1260,10 @@ fn run_finaliziation_ansi_sequence(
shell_integration_osc133, shell_integration_osc133,
)); ));
perf( perf!(
"post_execute_marker (133;D) ansi escape sequences", "post_execute_marker (133;D) ansi escape sequences",
start_time, start_time,
file!(), use_color
line!(),
column!(),
use_color,
); );
} }
} }

View File

@ -6,7 +6,7 @@ use nu_parser::{flatten_block, parse, FlatShape};
use nu_protocol::{ use nu_protocol::{
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem}, ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
Config, Span, Span,
}; };
use reedline::{Highlighter, StyledText}; use reedline::{Highlighter, StyledText};
use std::sync::Arc; use std::sync::Arc;
@ -14,15 +14,14 @@ use std::sync::Arc;
pub struct NuHighlighter { pub struct NuHighlighter {
pub engine_state: Arc<EngineState>, pub engine_state: Arc<EngineState>,
pub stack: Arc<Stack>, pub stack: Arc<Stack>,
pub config: Config,
} }
impl Highlighter for NuHighlighter { impl Highlighter for NuHighlighter {
fn highlight(&self, line: &str, _cursor: usize) -> StyledText { fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
trace!("highlighting: {}", line); trace!("highlighting: {}", line);
let highlight_resolved_externals = let config = self.stack.get_config(&self.engine_state);
self.engine_state.get_config().highlight_resolved_externals; let highlight_resolved_externals = config.highlight_resolved_externals;
let mut working_set = StateWorkingSet::new(&self.engine_state); let mut working_set = StateWorkingSet::new(&self.engine_state);
let block = parse(&mut working_set, None, line.as_bytes(), false); let block = parse(&mut working_set, None, line.as_bytes(), false);
let (shapes, global_span_offset) = { let (shapes, global_span_offset) = {
@ -88,7 +87,7 @@ impl Highlighter for NuHighlighter {
.to_string(); .to_string();
let mut add_colored_token = |shape: &FlatShape, text: String| { let mut add_colored_token = |shape: &FlatShape, text: String| {
output.push((get_shape_color(shape.as_str(), &self.config), text)); output.push((get_shape_color(shape.as_str(), &config), text));
}; };
match shape.1 { match shape.1 {
@ -128,9 +127,9 @@ impl Highlighter for NuHighlighter {
let start = part.start - span.start; let start = part.start - span.start;
let end = part.end - span.start; let end = part.end - span.start;
let text = next_token[start..end].to_string(); let text = next_token[start..end].to_string();
let mut style = get_shape_color(shape.as_str(), &self.config); let mut style = get_shape_color(shape.as_str(), &config);
if highlight { if highlight {
style = get_matching_brackets_style(style, &self.config); style = get_matching_brackets_style(style, &config);
} }
output.push((style, text)); output.push((style, text));
} }
@ -138,6 +137,7 @@ impl Highlighter for NuHighlighter {
FlatShape::Filepath => 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::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::GlobPattern => add_colored_token(&shape.1, next_token),
FlatShape::Variable(_) | FlatShape::VarDecl(_) => { FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
add_colored_token(&shape.1, next_token) add_colored_token(&shape.1, next_token)
@ -429,6 +429,14 @@ fn find_matching_block_end_in_expr(
) )
}), }),
Expr::Collect(_, expr) => find_matching_block_end_in_expr(
line,
working_set,
expr,
global_span_offset,
global_cursor_offset,
),
Expr::Block(block_id) Expr::Block(block_id)
| Expr::Closure(block_id) | Expr::Closure(block_id)
| Expr::RowCondition(block_id) | Expr::RowCondition(block_id)
@ -452,15 +460,17 @@ fn find_matching_block_end_in_expr(
} }
} }
Expr::StringInterpolation(exprs) => exprs.iter().find_map(|expr| { Expr::StringInterpolation(exprs) | Expr::GlobInterpolation(exprs, _) => {
find_matching_block_end_in_expr( exprs.iter().find_map(|expr| {
line, find_matching_block_end_in_expr(
working_set, line,
expr, working_set,
global_span_offset, expr,
global_cursor_offset, global_span_offset,
) global_cursor_offset,
}), )
})
}
Expr::List(list) => { Expr::List(list) => {
if expr_last == global_cursor_offset { if expr_last == global_cursor_offset {

View File

@ -8,7 +8,7 @@ use nu_protocol::{
}; };
#[cfg(windows)] #[cfg(windows)]
use nu_utils::enable_vt_processing; use nu_utils::enable_vt_processing;
use nu_utils::utils::perf; use nu_utils::perf;
use std::path::Path; use std::path::Path;
// This will collect environment variables from std::env and adds them to a stack. // This will collect environment variables from std::env and adds them to a stack.
@ -228,13 +228,10 @@ pub fn eval_source(
let _ = enable_vt_processing(); let _ = enable_vt_processing();
} }
perf( perf!(
&format!("eval_source {}", &fname), &format!("eval_source {}", &fname),
start_time, start_time,
file!(), engine_state.get_config().use_ansi_coloring
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
); );
exit_code exit_code
@ -265,6 +262,11 @@ fn evaluate_source(
return Ok(Some(1)); return Ok(Some(1));
} }
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
(output, working_set.render()) (output, working_set.render())
}; };

View File

@ -11,7 +11,7 @@ use std::{
sync::Arc, sync::Arc,
}; };
use support::{ use support::{
completions_helpers::{new_partial_engine, new_quote_engine}, completions_helpers::{new_dotnu_engine, new_partial_engine, new_quote_engine},
file, folder, match_suggestions, new_engine, file, folder, match_suggestions, new_engine,
}; };
@ -85,8 +85,29 @@ fn custom_completer() -> NuCompleter {
NuCompleter::new(Arc::new(engine), Arc::new(stack)) NuCompleter::new(Arc::new(engine), Arc::new(stack))
} }
#[fixture]
fn subcommand_completer() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Use fuzzy matching, because subcommands are sorted by Levenshtein distance,
// and that's not very useful with prefix matching
let commands = r#"
$env.config.completions.algorithm = "fuzzy"
def foo [] {}
def "foo bar" [] {}
def "foo abaz" [] {}
def "foo aabrr" [] {}
def food [] {}
"#;
assert!(support::merge_input(commands.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(Arc::new(engine), Arc::new(stack))
}
#[test] #[test]
fn variables_dollar_sign_with_varialblecompletion() { fn variables_dollar_sign_with_variablecompletion() {
let (_, _, engine, stack) = new_engine(); let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
@ -138,43 +159,42 @@ fn variables_customcompletion_subcommands_with_customcompletion_2(
#[test] #[test]
fn dotnu_completions() { fn dotnu_completions() {
// Create a new engine // Create a new engine
let (_, _, engine, stack) = new_engine(); let (_, _, engine, stack) = new_dotnu_engine();
// Instantiate a new completer // Instantiate a new completer
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
let expected = vec![
"asdf.nu".into(),
"bar.nu".into(),
"bat.nu".into(),
"baz.nu".into(),
#[cfg(windows)]
"dir_module\\".into(),
#[cfg(not(windows))]
"dir_module/".into(),
"foo.nu".into(),
"spam.nu".into(),
"xyzzy.nu".into(),
];
// Test source completion // Test source completion
let completion_str = "source-env ".to_string(); let completion_str = "source-env ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len()); let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len()); match_suggestions(expected.clone(), suggestions);
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
// Test use completion // Test use completion
let completion_str = "use ".to_string(); let completion_str = "use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len()); let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len()); match_suggestions(expected.clone(), suggestions);
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
// Test overlay use completion // Test overlay use completion
let completion_str = "overlay use ".to_string(); let completion_str = "overlay use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len()); let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len()); match_suggestions(expected, suggestions);
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
} }
#[test] #[test]
@ -276,9 +296,10 @@ fn partial_completions() {
// Create the expected values // Create the expected values
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
folder(dir.join("partial_a")), folder(dir.join("partial")),
folder(dir.join("partial_b")), folder(dir.join("partial-a")),
folder(dir.join("partial_c")), folder(dir.join("partial-b")),
folder(dir.join("partial-c")),
]; ];
// Match the results // Match the results
@ -292,13 +313,14 @@ fn partial_completions() {
// Create the expected values // Create the expected values
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("have_ext.exe")), file(dir.join("partial").join("hello.txt")),
file(dir.join("partial_a").join("have_ext.txt")), file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial_a").join("hello")), file(dir.join("partial-a").join("have_ext.txt")),
file(dir.join("partial_a").join("hola")), file(dir.join("partial-a").join("hello")),
file(dir.join("partial_b").join("hello_b")), file(dir.join("partial-a").join("hola")),
file(dir.join("partial_b").join("hi_b")), file(dir.join("partial-b").join("hello_b")),
file(dir.join("partial_c").join("hello_c")), file(dir.join("partial-b").join("hi_b")),
file(dir.join("partial-c").join("hello_c")),
]; ];
// Match the results // Match the results
@ -311,14 +333,15 @@ fn partial_completions() {
// Create the expected values // Create the expected values
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("anotherfile")), file(dir.join("partial").join("hello.txt")),
file(dir.join("partial_a").join("have_ext.exe")), file(dir.join("partial-a").join("anotherfile")),
file(dir.join("partial_a").join("have_ext.txt")), file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial_a").join("hello")), file(dir.join("partial-a").join("have_ext.txt")),
file(dir.join("partial_a").join("hola")), file(dir.join("partial-a").join("hello")),
file(dir.join("partial_b").join("hello_b")), file(dir.join("partial-a").join("hola")),
file(dir.join("partial_b").join("hi_b")), file(dir.join("partial-b").join("hello_b")),
file(dir.join("partial_c").join("hello_c")), file(dir.join("partial-b").join("hi_b")),
file(dir.join("partial-c").join("hello_c")),
]; ];
// Match the results // Match the results
@ -343,19 +366,25 @@ fn partial_completions() {
// Create the expected values // Create the expected values
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
file( file(
dir.join("partial_a") dir.join("partial")
.join("..") .join("..")
.join("final_partial") .join("final_partial")
.join("somefile"), .join("somefile"),
), ),
file( file(
dir.join("partial_b") dir.join("partial-a")
.join("..") .join("..")
.join("final_partial") .join("final_partial")
.join("somefile"), .join("somefile"),
), ),
file( file(
dir.join("partial_c") dir.join("partial-b")
.join("..")
.join("final_partial")
.join("somefile"),
),
file(
dir.join("partial-c")
.join("..") .join("..")
.join("final_partial") .join("final_partial")
.join("somefile"), .join("somefile"),
@ -366,28 +395,28 @@ fn partial_completions() {
match_suggestions(expected_paths, suggestions); match_suggestions(expected_paths, suggestions);
// Test completion for all files under directories whose names begin with "pa" // Test completion for all files under directories whose names begin with "pa"
let file_str = file(dir.join("partial_a").join("have")); let file_str = file(dir.join("partial-a").join("have"));
let target_file = format!("rm {file_str}"); let target_file = format!("rm {file_str}");
let suggestions = completer.complete(&target_file, target_file.len()); let suggestions = completer.complete(&target_file, target_file.len());
// Create the expected values // Create the expected values
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("have_ext.exe")), file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")), file(dir.join("partial-a").join("have_ext.txt")),
]; ];
// Match the results // Match the results
match_suggestions(expected_paths, suggestions); match_suggestions(expected_paths, suggestions);
// Test completion for all files under directories whose names begin with "pa" // Test completion for all files under directories whose names begin with "pa"
let file_str = file(dir.join("partial_a").join("have_ext.")); let file_str = file(dir.join("partial-a").join("have_ext."));
let file_dir = format!("rm {file_str}"); let file_dir = format!("rm {file_str}");
let suggestions = completer.complete(&file_dir, file_dir.len()); let suggestions = completer.complete(&file_dir, file_dir.len());
// Create the expected values // Create the expected values
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("have_ext.exe")), file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")), file(dir.join("partial-a").join("have_ext.txt")),
]; ];
// Match the results // Match the results
@ -652,6 +681,27 @@ fn command_watch_with_filecompletion() {
match_suggestions(expected_paths, suggestions) match_suggestions(expected_paths, suggestions)
} }
#[rstest]
fn subcommand_completions(mut subcommand_completer: NuCompleter) {
let prefix = "foo br";
let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions(
vec!["foo bar".to_string(), "foo aabrr".to_string()],
suggestions,
);
let prefix = "foo b";
let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions(
vec![
"foo bar".to_string(),
"foo abaz".to_string(),
"foo aabrr".to_string(),
],
suggestions,
);
}
#[test] #[test]
fn file_completion_quoted() { fn file_completion_quoted() {
let (_, _, engine, stack) = new_quote_engine(); let (_, _, engine, stack) = new_quote_engine();
@ -662,16 +712,16 @@ fn file_completion_quoted() {
let suggestions = completer.complete(target_dir, target_dir.len()); let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec![ let expected_paths: Vec<String> = vec![
"\'[a] bc.txt\'".to_string(),
"`--help`".to_string(), "`--help`".to_string(),
"`-42`".to_string(), "`-42`".to_string(),
"`-inf`".to_string(), "`-inf`".to_string(),
"`4.2`".to_string(), "`4.2`".to_string(),
"\'[a] bc.txt\'".to_string(),
"`te st.txt`".to_string(), "`te st.txt`".to_string(),
"`te#st.txt`".to_string(), "`te#st.txt`".to_string(),
"`te'st.txt`".to_string(), "`te'st.txt`".to_string(),
"`te(st).txt`".to_string(), "`te(st).txt`".to_string(),
format!("`{}`", folder("test dir".into())), format!("`{}`", folder("test dir")),
]; ];
match_suggestions(expected_paths, suggestions); match_suggestions(expected_paths, suggestions);
@ -763,11 +813,13 @@ fn variables_completions() {
// Test completions for $nu // Test completions for $nu
let suggestions = completer.complete("$nu.", 4); let suggestions = completer.complete("$nu.", 4);
assert_eq!(15, suggestions.len()); assert_eq!(18, suggestions.len());
let expected: Vec<String> = vec![ let expected: Vec<String> = vec![
"cache-dir".into(),
"config-path".into(), "config-path".into(),
"current-exe".into(), "current-exe".into(),
"data-dir".into(),
"default-config-dir".into(), "default-config-dir".into(),
"env-path".into(), "env-path".into(),
"history-enabled".into(), "history-enabled".into(),
@ -781,6 +833,7 @@ fn variables_completions() {
"plugin-path".into(), "plugin-path".into(),
"startup-time".into(), "startup-time".into(),
"temp-path".into(), "temp-path".into(),
"vendor-autoload-dir".into(),
]; ];
// Match results // Match results
@ -854,6 +907,11 @@ fn variables_completions() {
// Match results // Match results
match_suggestions(expected, suggestions); match_suggestions(expected, suggestions);
let suggestions = completer.complete("$", 1);
let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()];
match_suggestions(expected, suggestions);
} }
#[test] #[test]

View File

@ -1,5 +1,6 @@
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::parse; use nu_parser::parse;
use nu_path::{AbsolutePathBuf, PathBuf};
use nu_protocol::{ use nu_protocol::{
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
@ -7,14 +8,14 @@ use nu_protocol::{
}; };
use nu_test_support::fs; use nu_test_support::fs;
use reedline::Suggestion; use reedline::Suggestion;
use std::path::{PathBuf, MAIN_SEPARATOR}; use std::path::MAIN_SEPARATOR;
fn create_default_context() -> EngineState { fn create_default_context() -> EngineState {
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context()) nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
} }
// creates a new engine with the current path into the completions fixtures folder // creates a new engine with the current path into the completions fixtures folder
pub fn new_engine() -> (PathBuf, String, EngineState, Stack) { pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("completions"); let dir = fs::fixtures().join("completions");
let dir_str = dir let dir_str = dir
@ -68,7 +69,53 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack) (dir, dir_str, engine_state, stack)
} }
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) { // creates a new engine with the current path into the completions fixtures folder
pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("dotnu_completions");
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
let dir_span = nu_protocol::Span::new(0, dir_str.len());
// Create a new engine with default context
let mut engine_state = create_default_context();
// Add $nu
engine_state.generate_nu_constant();
// New stack
let mut stack = Stack::new();
// Add pwd as env var
stack.add_env_var("PWD".to_string(), Value::string(dir_str.clone(), dir_span));
stack.add_env_var(
"TEST".to_string(),
Value::string("NUSHELL".to_string(), dir_span),
);
stack.add_env_var(
"NU_LIB_DIRS".to_string(),
Value::List {
vals: vec![
Value::string(file(dir.join("lib-dir1")), dir_span),
Value::string(file(dir.join("lib-dir2")), dir_span),
Value::string(file(dir.join("lib-dir3")), dir_span),
],
internal_span: dir_span,
},
);
// Merge environment into the permanent state
let merge_result = engine_state.merge_env(&mut stack, &dir);
assert!(merge_result.is_ok());
(dir, dir_str, engine_state, stack)
}
pub fn new_quote_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("quoted_completions"); let dir = fs::fixtures().join("quoted_completions");
let dir_str = dir let dir_str = dir
@ -103,7 +150,7 @@ pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack) (dir, dir_str, engine_state, stack)
} }
pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) { pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("partial_completions"); let dir = fs::fixtures().join("partial_completions");
let dir_str = dir let dir_str = dir
@ -149,22 +196,25 @@ pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
Expected: {expected:#?}\n" Expected: {expected:#?}\n"
) )
} }
expected.iter().zip(suggestions).for_each(|it| { assert_eq!(
assert_eq!(it.0, &it.1.value); expected,
}); suggestions
.into_iter()
.map(|it| it.value)
.collect::<Vec<_>>()
);
} }
// append the separator to the converted path // append the separator to the converted path
pub fn folder(path: PathBuf) -> String { pub fn folder(path: impl Into<PathBuf>) -> String {
let mut converted_path = file(path); let mut converted_path = file(path);
converted_path.push(MAIN_SEPARATOR); converted_path.push(MAIN_SEPARATOR);
converted_path converted_path
} }
// convert a given path to string // convert a given path to string
pub fn file(path: PathBuf) -> String { pub fn file(path: impl Into<PathBuf>) -> String {
path.into_os_string().into_string().unwrap_or_default() path.into().into_os_string().into_string().unwrap()
} }
// merge_input executes the given input into the engine // merge_input executes the given input into the engine
@ -173,7 +223,7 @@ pub fn merge_input(
input: &[u8], input: &[u8],
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
dir: PathBuf, dir: AbsolutePathBuf,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
let (block, delta) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);

View File

@ -5,17 +5,17 @@ edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-base" name = "nu-cmd-base"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base" repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
version = "0.94.3" version = "0.95.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.94.3" } nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-parser = { path = "../nu-parser", version = "0.94.3" } nu-parser = { path = "../nu-parser", version = "0.95.1" }
nu-path = { path = "../nu-path", version = "0.94.3" } nu-path = { path = "../nu-path", version = "0.95.1" }
nu-protocol = { path = "../nu-protocol", version = "0.94.3" } nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
indexmap = { workspace = true } indexmap = { workspace = true }
miette = { workspace = true } miette = { workspace = true }
[dev-dependencies] [dev-dependencies]

View File

@ -0,0 +1,5 @@
Utilities used by the different `nu-command`/`nu-cmd-*` crates, should not contain any full `Command` implementations.
## Internal Nushell crate
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.

View File

@ -194,7 +194,7 @@ pub fn eval_hook(
let Some(follow) = val.get("code") else { let Some(follow) = val.get("code") else {
return Err(ShellError::CantFindColumn { return Err(ShellError::CantFindColumn {
col_name: "code".into(), col_name: "code".into(),
span, span: Some(span),
src_span: span, src_span: span,
}); });
}; };

View File

@ -1,5 +1,5 @@
use nu_protocol::{ast::CellPath, PipelineData, ShellError, Span, Value}; use nu_protocol::{ast::CellPath, PipelineData, ShellError, Signals, Span, Value};
use std::sync::{atomic::AtomicBool, Arc}; use std::sync::Arc;
pub trait CmdArgument { pub trait CmdArgument {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>>; fn take_cell_paths(&mut self) -> Option<Vec<CellPath>>;
@ -40,7 +40,7 @@ pub fn operate<C, A>(
mut arg: A, mut arg: A,
input: PipelineData, input: PipelineData,
span: Span, span: Span,
ctrlc: Option<Arc<AtomicBool>>, signals: &Signals,
) -> Result<PipelineData, ShellError> ) -> Result<PipelineData, ShellError>
where where
A: CmdArgument + Send + Sync + 'static, A: CmdArgument + Send + Sync + 'static,
@ -55,7 +55,7 @@ where
_ => cmd(&v, &arg, span), _ => cmd(&v, &arg, span),
} }
}, },
ctrlc, signals,
), ),
Some(column_paths) => { Some(column_paths) => {
let arg = Arc::new(arg); let arg = Arc::new(arg);
@ -79,7 +79,7 @@ where
} }
v v
}, },
ctrlc, signals,
) )
} }
} }

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
pub mod formats; pub mod formats;
pub mod hook; pub mod hook;
pub mod input_handler; pub mod input_handler;

View File

@ -1,3 +1,4 @@
use nu_path::AbsolutePathBuf;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
Range, ShellError, Span, Value, Range, ShellError, Span, Value,
@ -15,18 +16,20 @@ pub fn get_init_cwd() -> PathBuf {
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf { pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
engine_state engine_state
.cwd(Some(stack)) .cwd(Some(stack))
.map(AbsolutePathBuf::into_std_path_buf)
.unwrap_or(crate::util::get_init_cwd()) .unwrap_or(crate::util::get_init_cwd())
} }
type MakeRangeError = fn(&str, Span) -> ShellError; type MakeRangeError = fn(&str, Span) -> ShellError;
/// Returns a inclusive pair of boundary in given `range`.
pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> { pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> {
match range { match range {
Range::IntRange(range) => { Range::IntRange(range) => {
let start = range.start().try_into().unwrap_or(0); let start = range.start().try_into().unwrap_or(0);
let end = match range.end() { let end = match range.end() {
Bound::Included(v) => (v + 1) as isize, Bound::Included(v) => v as isize,
Bound::Excluded(v) => v as isize, Bound::Excluded(v) => (v - 1) as isize,
Bound::Unbounded => isize::MAX, Bound::Unbounded => isize::MAX,
}; };
Ok((start, end)) Ok((start, end))

View File

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

View File

@ -79,7 +79,7 @@ impl Command for BitsAnd {
input.map( input.map(
move |value| binary_op(&value, &target, little_endian, |(l, r)| l & r, head), move |value| binary_op(&value, &target, little_endian, |(l, r)| l & r, head),
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -1,6 +1,9 @@
use std::io::{self, Read, Write};
use nu_cmd_base::input_handler::{operate, CmdArgument}; use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::Signals;
use num_traits::ToPrimitive; use num_traits::ToPrimitive;
pub struct Arguments { pub struct Arguments {
@ -118,12 +121,43 @@ fn into_bits(
let cell_paths = call.rest(engine_state, stack, 0)?; let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths); let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
if let PipelineData::ByteStream(stream, ..) = input { if let PipelineData::ByteStream(stream, metadata) = input {
// TODO: in the future, we may want this to stream out, converting each to bytes Ok(PipelineData::ByteStream(
Ok(Value::binary(stream.into_bytes()?, head).into_pipeline_data()) byte_stream_to_bits(stream, head),
metadata,
))
} else { } else {
let args = Arguments { cell_paths }; let args = Arguments { cell_paths };
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.signals())
}
}
fn byte_stream_to_bits(stream: ByteStream, head: Span) -> ByteStream {
if let Some(mut reader) = stream.reader() {
let mut is_first = true;
ByteStream::from_fn(
head,
Signals::empty(),
ByteStreamType::String,
move |buffer| {
let mut byte = [0];
if reader.read(&mut byte[..]).err_span(head)? > 0 {
// Format the byte as bits
if is_first {
is_first = false;
} else {
buffer.push(b' ');
}
write!(buffer, "{:08b}", byte[0]).expect("format failed");
Ok(true)
} else {
// EOF
Ok(false)
}
},
)
} else {
ByteStream::read(io::empty(), head, Signals::empty(), ByteStreamType::String)
} }
} }

View File

@ -82,7 +82,7 @@ impl Command for BitsNot {
number_size, number_size,
}; };
operate(action, args, input, head, engine_state.ctrlc.clone()) operate(action, args, input, head, engine_state.signals())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -80,7 +80,7 @@ impl Command for BitsOr {
input.map( input.map(
move |value| binary_op(&value, &target, little_endian, |(l, r)| l | r, head), move |value| binary_op(&value, &target, little_endian, |(l, r)| l | r, head),
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -86,7 +86,7 @@ impl Command for BitsRol {
bits, bits,
}; };
operate(action, args, input, head, engine_state.ctrlc.clone()) operate(action, args, input, head, engine_state.signals())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -86,7 +86,7 @@ impl Command for BitsRor {
bits, bits,
}; };
operate(action, args, input, head, engine_state.ctrlc.clone()) operate(action, args, input, head, engine_state.signals())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -88,7 +88,7 @@ impl Command for BitsShl {
bits, bits,
}; };
operate(action, args, input, head, engine_state.ctrlc.clone()) operate(action, args, input, head, engine_state.signals())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -88,7 +88,7 @@ impl Command for BitsShr {
bits, bits,
}; };
operate(action, args, input, head, engine_state.ctrlc.clone()) operate(action, args, input, head, engine_state.signals())
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -80,7 +80,7 @@ impl Command for BitsXor {
input.map( input.map(
move |value| binary_op(&value, &target, little_endian, |(l, r)| l ^ r, head), move |value| binary_op(&value, &target, little_endian, |(l, r)| l ^ r, head),
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -59,7 +59,7 @@ fn fmt(
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths); let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.signals())
} }
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value { fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {

View File

@ -89,7 +89,7 @@ impl Command for EachWhile {
} }
}) })
.fuse() .fuse()
.into_pipeline_data(head, engine_state.ctrlc.clone())) .into_pipeline_data(head, engine_state.signals().clone()))
} }
PipelineData::ByteStream(stream, ..) => { PipelineData::ByteStream(stream, ..) => {
let span = stream.span(); let span = stream.span();
@ -107,7 +107,7 @@ impl Command for EachWhile {
} }
}) })
.fuse() .fuse()
.into_pipeline_data(head, engine_state.ctrlc.clone())) .into_pipeline_data(head, engine_state.signals().clone()))
} else { } else {
Ok(PipelineData::Empty) Ok(PipelineData::Empty)
} }

View File

@ -108,7 +108,7 @@ impl Command for UpdateCells {
columns, columns,
span: head, span: head,
} }
.into_pipeline_data(head, engine_state.ctrlc.clone()) .into_pipeline_data(head, engine_state.signals().clone())
.set_metadata(metadata)) .set_metadata(metadata))
} }
} }

View File

@ -239,7 +239,7 @@ fn to_html(
let partial = call.has_flag(engine_state, stack, "partial")?; let partial = call.has_flag(engine_state, stack, "partial")?;
let list = call.has_flag(engine_state, stack, "list")?; let list = call.has_flag(engine_state, stack, "list")?;
let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?; let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
let config = engine_state.get_config(); let config = &stack.get_config(engine_state);
let vec_of_values = input.into_iter().collect::<Vec<Value>>(); let vec_of_values = input.into_iter().collect::<Vec<Value>>();
let headers = merge_descriptors(&vec_of_values); let headers = merge_descriptors(&vec_of_values);
@ -368,6 +368,7 @@ fn theme_demo(span: Span) -> PipelineData {
.collect(); .collect();
Value::list(result, span).into_pipeline_data_with_metadata(PipelineMetadata { Value::list(result, span).into_pipeline_data_with_metadata(PipelineMetadata {
data_source: DataSource::HtmlThemes, data_source: DataSource::HtmlThemes,
content_type: None,
}) })
} }

View File

@ -45,7 +45,7 @@ impl Command for SubCommand {
} }
input.map( input.map(
move |value| operate(value, head, use_degrees), move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); return Err(ShellError::PipelineEmpty { dst_span: head });
} }
input.map( input.map(move |value| operate(value, head), engine_state.signals())
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -45,7 +45,7 @@ impl Command for SubCommand {
} }
input.map( input.map(
move |value| operate(value, head, use_degrees), move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); return Err(ShellError::PipelineEmpty { dst_span: head });
} }
input.map( input.map(move |value| operate(value, head), engine_state.signals())
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -45,7 +45,7 @@ impl Command for SubCommand {
} }
input.map( input.map(
move |value| operate(value, head, use_degrees), move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); return Err(ShellError::PipelineEmpty { dst_span: head });
} }
input.map( input.map(move |value| operate(value, head), engine_state.signals())
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -44,7 +44,7 @@ impl Command for SubCommand {
} }
input.map( input.map(
move |value| operate(value, head, use_degrees), move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); return Err(ShellError::PipelineEmpty { dst_span: head });
} }
input.map( input.map(move |value| operate(value, head), engine_state.signals())
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); return Err(ShellError::PipelineEmpty { dst_span: head });
} }
input.map( input.map(move |value| operate(value, head), engine_state.signals())
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); return Err(ShellError::PipelineEmpty { dst_span: head });
} }
input.map( input.map(move |value| operate(value, head), engine_state.signals())
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -44,7 +44,7 @@ impl Command for SubCommand {
} }
input.map( input.map(
move |value| operate(value, head, use_degrees), move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); return Err(ShellError::PipelineEmpty { dst_span: head });
} }
input.map( input.map(move |value| operate(value, head), engine_state.signals())
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -44,7 +44,7 @@ impl Command for SubCommand {
} }
input.map( input.map(
move |value| operate(value, head, use_degrees), move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) { if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head }); return Err(ShellError::PipelineEmpty { dst_span: head });
} }
input.map( input.map(move |value| operate(value, head), engine_state.signals())
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {

View File

@ -140,7 +140,7 @@ fn operate(
ret ret
} }
}, },
engine_state.ctrlc.clone(), engine_state.signals(),
) )
} }

View File

@ -88,7 +88,7 @@ pub fn operate(
cell_paths, cell_paths,
}; };
general_operate(action, args, input, call.head, engine_state.ctrlc.clone()) general_operate(action, args, input, call.head, engine_state.signals())
} }
fn action( fn action(

View File

@ -1,5 +1,5 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::{ast::PathMember, engine::StateWorkingSet, ListStream}; use nu_protocol::{ast::PathMember, engine::StateWorkingSet, Config, ListStream};
#[derive(Clone)] #[derive(Clone)]
pub struct FormatPattern; pub struct FormatPattern;
@ -43,6 +43,8 @@ impl Command for FormatPattern {
let it_id = working_set.add_variable(b"$it".to_vec(), call.head, Type::Any, false); let it_id = working_set.add_variable(b"$it".to_vec(), call.head, Type::Any, false);
stack.add_var(it_id, input_val.clone()); stack.add_var(it_id, input_val.clone());
let config = stack.get_config(engine_state);
match specified_pattern { match specified_pattern {
Err(e) => Err(e), Err(e) => Err(e),
Ok(pattern) => { Ok(pattern) => {
@ -56,7 +58,7 @@ impl Command for FormatPattern {
string_span.start + 1, string_span.start + 1,
)?; )?;
format(input_val, &ops, engine_state, call.head) format(input_val, &ops, engine_state, &config, call.head)
} }
} }
} }
@ -181,33 +183,30 @@ fn format(
input_data: Value, input_data: Value,
format_operations: &[FormatOperation], format_operations: &[FormatOperation],
engine_state: &EngineState, engine_state: &EngineState,
config: &Config,
head_span: Span, head_span: Span,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let data_as_value = input_data; let data_as_value = input_data;
// We can only handle a Record or a List of Records // We can only handle a Record or a List of Records
match data_as_value { match data_as_value {
Value::Record { .. } => { Value::Record { .. } => match format_record(format_operations, &data_as_value, config) {
match format_record(format_operations, &data_as_value, engine_state) { Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)),
Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)), Err(value) => Err(value),
Err(value) => Err(value), },
}
}
Value::List { vals, .. } => { Value::List { vals, .. } => {
let mut list = vec![]; let mut list = vec![];
for val in vals.iter() { for val in vals.iter() {
match val { match val {
Value::Record { .. } => { Value::Record { .. } => match format_record(format_operations, val, config) {
match format_record(format_operations, val, engine_state) { Ok(value) => {
Ok(value) => { list.push(Value::string(value, head_span));
list.push(Value::string(value, head_span));
}
Err(value) => {
return Err(value);
}
} }
} Err(value) => {
return Err(value);
}
},
Value::Error { error, .. } => return Err(*error.clone()), Value::Error { error, .. } => return Err(*error.clone()),
_ => { _ => {
return Err(ShellError::OnlySupportsThisInputType { return Err(ShellError::OnlySupportsThisInputType {
@ -220,7 +219,7 @@ fn format(
} }
} }
Ok(ListStream::new(list.into_iter(), head_span, engine_state.ctrlc.clone()).into()) Ok(ListStream::new(list.into_iter(), head_span, engine_state.signals().clone()).into())
} }
// Unwrapping this ShellError is a bit unfortunate. // Unwrapping this ShellError is a bit unfortunate.
// Ideally, its Span would be preserved. // Ideally, its Span would be preserved.
@ -237,9 +236,8 @@ fn format(
fn format_record( fn format_record(
format_operations: &[FormatOperation], format_operations: &[FormatOperation],
data_as_value: &Value, data_as_value: &Value,
engine_state: &EngineState, config: &Config,
) -> Result<String, ShellError> { ) -> Result<String, ShellError> {
let config = engine_state.get_config();
let mut output = String::new(); let mut output = String::new();
for op in format_operations { for op in format_operations {

View File

@ -44,7 +44,7 @@ where
case_operation, case_operation,
cell_paths, cell_paths,
}; };
general_operate(action, args, input, call.head, engine_state.ctrlc.clone()) general_operate(action, args, input, call.head, engine_state.signals())
} }
fn action<F>(input: &Value, args: &Arguments<F>, head: Span) -> Value fn action<F>(input: &Value, args: &Arguments<F>, head: Span) -> Value

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod example_test; mod example_test;
pub mod extra; pub mod extra;
pub use extra::*; pub use extra::*;

View File

@ -0,0 +1,13 @@
use nu_test_support::nu;
#[test]
fn byte_stream_into_bits() {
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits");
assert_eq!("00000001 00000010 00000011", result.out);
}
#[test]
fn byte_stream_into_bits_is_stream() {
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits | describe");
assert_eq!("string (stream)", result.out);
}

View File

@ -0,0 +1 @@
mod into;

View File

@ -1 +1,2 @@
mod bits;
mod bytes; mod bytes;

View File

@ -6,27 +6,26 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-cmd-lang" name = "nu-cmd-lang"
version = "0.94.3" version = "0.95.1"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.94.3" } nu-engine = { path = "../nu-engine", version = "0.95.1" }
nu-parser = { path = "../nu-parser", version = "0.94.3" } nu-parser = { path = "../nu-parser", version = "0.95.1" }
nu-protocol = { path = "../nu-protocol", version = "0.94.3" } nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
nu-utils = { path = "../nu-utils", version = "0.94.3" } nu-utils = { path = "../nu-utils", version = "0.95.1" }
itertools = { workspace = true } itertools = { workspace = true }
shadow-rs = { version = "0.28", default-features = false } shadow-rs = { version = "0.29", default-features = false }
[build-dependencies] [build-dependencies]
shadow-rs = { version = "0.28", default-features = false } shadow-rs = { version = "0.29", default-features = false }
[features] [features]
mimalloc = [] mimalloc = []
which-support = []
trash-support = [] trash-support = []
sqlite = [] sqlite = []
static-link-openssl = [] static-link-openssl = []
system-clipboard = [] system-clipboard = []

View File

@ -1,4 +1,5 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::engine::CommandType;
#[derive(Clone)] #[derive(Clone)]
pub struct Break; pub struct Break;
@ -18,6 +19,15 @@ impl Command for Break {
.category(Category::Core) .category(Category::Core)
} }
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn run( fn run(
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,

View File

@ -50,6 +50,7 @@ is particularly large, this can cause high memory usage."#
// check where some input came from. // check where some input came from.
Some(PipelineMetadata { Some(PipelineMetadata {
data_source: DataSource::FilePath(_), data_source: DataSource::FilePath(_),
content_type: None,
}) => None, }) => None,
other => other, other => other,
}; };

View File

@ -46,6 +46,9 @@ impl Command for Const {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let var_id = if let Some(id) = call.positional_nth(0).and_then(|pos| pos.as_var()) { let var_id = if let Some(id) = call.positional_nth(0).and_then(|pos| pos.as_var()) {
id id
} else { } else {

View File

@ -1,4 +1,5 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::engine::CommandType;
#[derive(Clone)] #[derive(Clone)]
pub struct Continue; pub struct Continue;
@ -18,6 +19,14 @@ impl Command for Continue {
.category(Category::Core) .category(Category::Core)
} }
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn run( fn run(
&self, &self,
_engine_state: &EngineState, _engine_state: &EngineState,

View File

@ -67,8 +67,8 @@ impl Command for Def {
}, },
Example { Example {
description: "Define a custom wrapper for an external command", description: "Define a custom wrapper for an external command",
example: r#"def --wrapped my-echo [...rest] { echo $rest }; my-echo spam"#, example: r#"def --wrapped my-echo [...rest] { ^echo ...$rest }; my-echo -e 'spam\tspam'"#,
result: Some(Value::test_list(vec![Value::test_string("spam")])), result: Some(Value::test_string("spam\tspam")),
}, },
] ]
} }

View File

@ -23,11 +23,7 @@ impl Command for Do {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("do") Signature::build("do")
.required( .required("closure", SyntaxShape::Closure(None), "The closure to run.")
"closure",
SyntaxShape::OneOf(vec![SyntaxShape::Closure(None), SyntaxShape::Any]),
"The closure to run.",
)
.input_output_types(vec![(Type::Any, Type::Any)]) .input_output_types(vec![(Type::Any, Type::Any)])
.switch( .switch(
"ignore-errors", "ignore-errors",
@ -85,6 +81,10 @@ impl Command for Do {
bind_args_to(&mut callee_stack, &block.signature, rest, head)?; bind_args_to(&mut callee_stack, &block.signature, rest, head)?;
let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); let eval_block_with_early_return = get_eval_block_with_early_return(engine_state);
// Applies to all block evaluation once set true
callee_stack.use_ir = caller_stack.has_env_var(engine_state, "NU_USE_IR");
let result = eval_block_with_early_return(engine_state, &mut callee_stack, block, input); let result = eval_block_with_early_return(engine_state, &mut callee_stack, block, input);
if has_env { if has_env {
@ -229,14 +229,24 @@ impl Command for Do {
result: None, result: None,
}, },
Example { Example {
description: "Run the closure, with a positional parameter", description: "Run the closure with a positional, type-checked parameter",
example: r#"do {|x| 100 + $x } 77"#, example: r#"do {|x:int| 100 + $x } 77"#,
result: Some(Value::test_int(177)), result: Some(Value::test_int(177)),
}, },
Example { Example {
description: "Run the closure, with input", description: "Run the closure with pipeline input",
example: r#"77 | do {|x| 100 + $in }"#, example: r#"77 | do { 100 + $in }"#,
result: None, // TODO: returns 177 result: Some(Value::test_int(177)),
},
Example {
description: "Run the closure with a default parameter value",
example: r#"77 | do {|x=100| $x + $in }"#,
result: Some(Value::test_int(177)),
},
Example {
description: "Run the closure with two positional parameters",
example: r#"do {|x,y| $x + $y } 77 100"#,
result: Some(Value::test_int(177)),
}, },
Example { Example {
description: "Run the closure and keep changes to the environment", description: "Run the closure and keep changes to the environment",

View File

@ -56,16 +56,7 @@ impl Command for ErrorMake {
Example { Example {
description: "Create a simple custom error", description: "Create a simple custom error",
example: r#"error make {msg: "my custom error message"}"#, example: r#"error make {msg: "my custom error message"}"#,
result: Some(Value::error( result: None,
ShellError::GenericError {
error: "my custom error message".into(),
msg: "".into(),
span: None,
help: None,
inner: vec![],
},
Span::unknown(),
)),
}, },
Example { Example {
description: "Create a more complex custom error", description: "Create a more complex custom error",
@ -82,16 +73,7 @@ impl Command for ErrorMake {
} }
help: "A help string, suggesting a fix to the user" # optional help: "A help string, suggesting a fix to the user" # optional
}"#, }"#,
result: Some(Value::error( result: None,
ShellError::GenericError {
error: "my custom error message".into(),
msg: "my custom label text".into(),
span: Some(Span::new(123, 456)),
help: Some("A help string, suggesting a fix to the user".into()),
inner: vec![],
},
Span::unknown(),
)),
}, },
Example { Example {
description: description:

View File

@ -1,5 +1,5 @@
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression}; use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
use nu_protocol::engine::CommandType; use nu_protocol::{engine::CommandType, Signals};
#[derive(Clone)] #[derive(Clone)]
pub struct For; pub struct For;
@ -28,11 +28,6 @@ impl Command for For {
"Range of the loop.", "Range of the loop.",
) )
.required("block", SyntaxShape::Block, "The block to run.") .required("block", SyntaxShape::Block, "The block to run.")
.switch(
"numbered",
"return a numbered item ($it.index and $it.item)",
Some('n'),
)
.creates_scope() .creates_scope()
.category(Category::Core) .category(Category::Core)
} }
@ -53,6 +48,9 @@ impl Command for For {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head; let head = call.head;
let var_id = call let var_id = call
.positional_nth(0) .positional_nth(0)
@ -77,9 +75,6 @@ impl Command for For {
let value = eval_expression(engine_state, stack, keyword_expr)?; let value = eval_expression(engine_state, stack, keyword_expr)?;
let numbered = call.has_flag(engine_state, stack, "numbered")?;
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone(); let engine_state = engine_state.clone();
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
@ -88,29 +83,14 @@ impl Command for For {
let span = value.span(); let span = value.span();
match value { match value {
Value::List { vals, .. } => { Value::List { vals, .. } => {
for (idx, x) in vals.into_iter().enumerate() { for x in vals.into_iter() {
if nu_utils::ctrl_c::was_pressed(&ctrlc) { engine_state.signals().check(head)?;
break;
}
// with_env() is used here to ensure that each iteration uses // with_env() is used here to ensure that each iteration uses
// a different set of environment variables. // a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop. // Hence, a 'cd' in the first loop won't affect the next loop.
stack.add_var( stack.add_var(var_id, x);
var_id,
if numbered {
Value::record(
record! {
"index" => Value::int(idx as i64, head),
"item" => x,
},
head,
)
} else {
x
},
);
match eval_block(&engine_state, stack, block, PipelineData::empty()) { match eval_block(&engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => { Err(ShellError::Break { .. }) => {
@ -136,21 +116,9 @@ impl Command for For {
} }
} }
Value::Range { val, .. } => { Value::Range { val, .. } => {
for (idx, x) in val.into_range_iter(span, ctrlc).enumerate() { for x in val.into_range_iter(span, Signals::empty()) {
stack.add_var( engine_state.signals().check(head)?;
var_id, stack.add_var(var_id, x);
if numbered {
Value::record(
record! {
"index" => Value::int(idx as i64, head),
"item" => x,
},
head,
)
} else {
x
},
);
match eval_block(&engine_state, stack, block, PipelineData::empty()) { match eval_block(&engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => { Err(ShellError::Break { .. }) => {
@ -198,8 +166,7 @@ impl Command for For {
}, },
Example { Example {
description: "Number each item and print a message", description: "Number each item and print a message",
example: example: r#"for $it in (['bob' 'fred'] | enumerate) { print $"($it.index) is ($it.item)" }"#,
"for $it in ['bob' 'fred'] --numbered { print $\"($it.index) is ($it.item)\" }",
result: None, result: None,
}, },
] ]

View File

@ -2,7 +2,7 @@ use nu_engine::{
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input, command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
}; };
use nu_protocol::{ use nu_protocol::{
engine::StateWorkingSet, engine::{CommandType, StateWorkingSet},
eval_const::{eval_const_subexpression, eval_constant, eval_constant_with_input}, eval_const::{eval_const_subexpression, eval_constant, eval_constant_with_input},
}; };
@ -41,6 +41,15 @@ impl Command for If {
.category(Category::Core) .category(Category::Core)
} }
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn is_const(&self) -> bool { fn is_const(&self) -> bool {
true true
} }
@ -51,6 +60,9 @@ impl Command for If {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let cond = call.positional_nth(0).expect("checked through parser"); let cond = call.positional_nth(0).expect("checked through parser");
let then_block = call let then_block = call
.positional_nth(1) .positional_nth(1)
@ -90,6 +102,9 @@ impl Command for If {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let cond = call.positional_nth(0).expect("checked through parser"); let cond = call.positional_nth(0).expect("checked through parser");
let then_block = call let then_block = call
.positional_nth(1) .positional_nth(1)
@ -122,6 +137,10 @@ impl Command for If {
} }
} }
fn search_terms(&self) -> Vec<&str> {
vec!["else", "conditional"]
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -46,6 +46,9 @@ impl Command for Let {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let var_id = call let var_id = call
.positional_nth(0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")

View File

@ -1,4 +1,5 @@
use nu_engine::{command_prelude::*, get_eval_block}; use nu_engine::{command_prelude::*, get_eval_block};
use nu_protocol::engine::CommandType;
#[derive(Clone)] #[derive(Clone)]
pub struct Loop; pub struct Loop;
@ -20,6 +21,15 @@ impl Command for Loop {
.category(Category::Core) .category(Category::Core)
} }
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -27,6 +37,10 @@ impl Command for Loop {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head;
let block_id = call let block_id = call
.positional_nth(0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")
@ -39,9 +53,7 @@ impl Command for Loop {
let stack = &mut stack.push_redirection(None, None); let stack = &mut stack.push_redirection(None, None);
loop { loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) { engine_state.signals().check(head)?;
break;
}
match eval_block(engine_state, stack, block, PipelineData::empty()) { match eval_block(engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => { Err(ShellError::Break { .. }) => {

View File

@ -1,7 +1,7 @@
use nu_engine::{ use nu_engine::{
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input, command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
}; };
use nu_protocol::engine::Matcher; use nu_protocol::engine::{CommandType, Matcher};
#[derive(Clone)] #[derive(Clone)]
pub struct Match; pub struct Match;
@ -27,6 +27,15 @@ impl Command for Match {
.category(Category::Core) .category(Category::Core)
} }
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -34,6 +43,9 @@ impl Command for Match {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let value: Value = call.req(engine_state, stack, 0)?; let value: Value = call.req(engine_state, stack, 0)?;
let matches = call let matches = call
.positional_nth(1) .positional_nth(1)

View File

@ -46,6 +46,9 @@ impl Command for Mut {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let var_id = call let var_id = call
.positional_nth(0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")

View File

@ -65,9 +65,9 @@ impl Command for OverlayUse {
name_arg.item = trim_quotes_str(&name_arg.item).to_string(); name_arg.item = trim_quotes_str(&name_arg.item).to_string();
let maybe_origin_module_id = let maybe_origin_module_id =
if let Some(overlay_expr) = call.get_parser_info("overlay_expr") { if let Some(overlay_expr) = call.get_parser_info(caller_stack, "overlay_expr") {
if let Expr::Overlay(module_id) = &overlay_expr.expr { if let Expr::Overlay(module_id) = &overlay_expr.expr {
module_id *module_id
} else { } else {
return Err(ShellError::NushellFailedSpanned { return Err(ShellError::NushellFailedSpanned {
msg: "Not an overlay".to_string(), msg: "Not an overlay".to_string(),
@ -110,7 +110,7 @@ impl Command for OverlayUse {
// a) adding a new overlay // a) adding a new overlay
// b) refreshing an active overlay (the origin module changed) // b) refreshing an active overlay (the origin module changed)
let module = engine_state.get_module(*module_id); let module = engine_state.get_module(module_id);
// Evaluate the export-env block (if any) and keep its environment // Evaluate the export-env block (if any) and keep its environment
if let Some(block_id) = module.env_block { if let Some(block_id) = module.env_block {
@ -118,7 +118,7 @@ impl Command for OverlayUse {
&name_arg.item, &name_arg.item,
engine_state, engine_state,
caller_stack, caller_stack,
get_dirs_var_from_call(call), get_dirs_var_from_call(caller_stack, call),
)?; )?;
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);

View File

@ -1,5 +1,4 @@
use nu_engine::{command_prelude::*, get_full_help}; use nu_engine::{command_prelude::*, get_full_help};
use nu_protocol::engine::CommandType;
#[derive(Clone)] #[derive(Clone)]
pub struct Scope; pub struct Scope;
@ -20,10 +19,6 @@ impl Command for Scope {
"Commands for getting info about what is in scope." "Commands for getting info about what is in scope."
} }
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,

View File

@ -1,5 +1,5 @@
use nu_engine::{command_prelude::*, get_eval_block, EvalBlockFn}; use nu_engine::{command_prelude::*, get_eval_block, EvalBlockFn};
use nu_protocol::engine::Closure; use nu_protocol::engine::{Closure, CommandType};
#[derive(Clone)] #[derive(Clone)]
pub struct Try; pub struct Try;
@ -10,7 +10,7 @@ impl Command for Try {
} }
fn usage(&self) -> &str { fn usage(&self) -> &str {
"Try to run a block, if it fails optionally run a catch block." "Try to run a block, if it fails optionally run a catch closure."
} }
fn signature(&self) -> nu_protocol::Signature { fn signature(&self) -> nu_protocol::Signature {
@ -18,7 +18,7 @@ impl Command for Try {
.input_output_types(vec![(Type::Any, Type::Any)]) .input_output_types(vec![(Type::Any, Type::Any)])
.required("try_block", SyntaxShape::Block, "Block to run.") .required("try_block", SyntaxShape::Block, "Block to run.")
.optional( .optional(
"catch_block", "catch_closure",
SyntaxShape::Keyword( SyntaxShape::Keyword(
b"catch".to_vec(), b"catch".to_vec(),
Box::new(SyntaxShape::OneOf(vec![ Box::new(SyntaxShape::OneOf(vec![
@ -26,11 +26,20 @@ impl Command for Try {
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])), SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
])), ])),
), ),
"Block to run if try block fails.", "Closure to run if try block fails.",
) )
.category(Category::Core) .category(Category::Core)
} }
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -38,6 +47,9 @@ impl Command for Try {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let try_block = call let try_block = call
.positional_nth(0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")
@ -85,9 +97,14 @@ impl Command for Try {
}, },
Example { Example {
description: "Try to run a missing command", description: "Try to run a missing command",
example: "try { asdfasdf } catch { 'missing' } ", example: "try { asdfasdf } catch { 'missing' }",
result: Some(Value::test_string("missing")), result: Some(Value::test_string("missing")),
}, },
Example {
description: "Try to run a missing command and report the message",
example: "try { asdfasdf } catch { |err| $err.msg }",
result: None,
},
] ]
} }
} }

View File

@ -57,7 +57,7 @@ This command is a parser keyword. For details, check:
let Some(Expression { let Some(Expression {
expr: Expr::ImportPattern(import_pattern), expr: Expr::ImportPattern(import_pattern),
.. ..
}) = call.get_parser_info("import_pattern") }) = call.get_parser_info(caller_stack, "import_pattern")
else { else {
return Err(ShellError::GenericError { return Err(ShellError::GenericError {
error: "Unexpected import".into(), error: "Unexpected import".into(),
@ -68,6 +68,9 @@ This command is a parser keyword. For details, check:
}); });
}; };
// Necessary so that we can modify the stack.
let import_pattern = import_pattern.clone();
if let Some(module_id) = import_pattern.head.id { if let Some(module_id) = import_pattern.head.id {
// Add constants // Add constants
for var_id in &import_pattern.constants { for var_id in &import_pattern.constants {
@ -99,7 +102,7 @@ This command is a parser keyword. For details, check:
&module_arg_str, &module_arg_str,
engine_state, engine_state,
caller_stack, caller_stack,
get_dirs_var_from_call(call), get_dirs_var_from_call(caller_stack, call),
)?; )?;
let maybe_parent = maybe_file_path let maybe_parent = maybe_file_path
.as_ref() .as_ref()

View File

@ -116,11 +116,18 @@ pub fn version(engine_state: &EngineState, span: Span) -> Result<PipelineData, S
Value::string(features_enabled().join(", "), span), Value::string(features_enabled().join(", "), span),
); );
// Get a list of plugin names // Get a list of plugin names and versions if present
let installed_plugins = engine_state let installed_plugins = engine_state
.plugins() .plugins()
.iter() .iter()
.map(|x| x.identity().name()) .map(|x| {
let name = x.identity().name();
if let Some(version) = x.metadata().and_then(|m| m.version) {
format!("{name} {version}")
} else {
name.into()
}
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
record.push( record.push(
@ -160,11 +167,6 @@ fn features_enabled() -> Vec<String> {
// NOTE: There should be another way to know features on. // NOTE: There should be another way to know features on.
#[cfg(feature = "which-support")]
{
names.push("which".to_string());
}
#[cfg(feature = "trash-support")] #[cfg(feature = "trash-support")]
{ {
names.push("trash".to_string()); names.push("trash".to_string());

View File

@ -1,4 +1,5 @@
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression}; use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
use nu_protocol::engine::CommandType;
#[derive(Clone)] #[derive(Clone)]
pub struct While; pub struct While;
@ -29,6 +30,15 @@ impl Command for While {
vec!["loop"] vec!["loop"]
} }
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -36,6 +46,10 @@ impl Command for While {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head;
let cond = call.positional_nth(0).expect("checked through parser"); let cond = call.positional_nth(0).expect("checked through parser");
let block_id = call let block_id = call
.positional_nth(1) .positional_nth(1)
@ -49,9 +63,7 @@ impl Command for While {
let stack = &mut stack.push_redirection(None, None); let stack = &mut stack.push_redirection(None, None);
loop { loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) { engine_state.signals().check(head)?;
break;
}
let result = eval_expression(engine_state, stack, cond)?; let result = eval_expression(engine_state, stack, cond)?;

View File

@ -4,7 +4,7 @@ use nu_protocol::{
ast::Block, ast::Block,
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{StateDelta, StateWorkingSet}, engine::{StateDelta, StateWorkingSet},
Range, report_error_new, Range,
}; };
use std::{ use std::{
sync::Arc, sync::Arc,
@ -124,7 +124,10 @@ pub fn eval_block(
nu_engine::eval_block::<WithoutDebug>(engine_state, &mut stack, &block, input) nu_engine::eval_block::<WithoutDebug>(engine_state, &mut stack, &block, input)
.and_then(|data| data.into_value(Span::test_data())) .and_then(|data| data.into_value(Span::test_data()))
.unwrap_or_else(|err| panic!("test eval error in `{}`: {:?}", "TODO", err)) .unwrap_or_else(|err| {
report_error_new(engine_state, &err);
panic!("test eval error in `{}`: {:?}", "TODO", err)
})
} }
pub fn check_example_evaluates_to_expected_output( pub fn check_example_evaluates_to_expected_output(

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod core_commands; mod core_commands;
mod default_context; mod default_context;
pub mod example_support; pub mod example_support;

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