mirror of
https://github.com/nushell/nushell.git
synced 2025-05-06 07:52:57 +00:00
Compare commits
No commits in common. "main" and "0.102.0" have entirely different histories.
40
.github/labeler.yml
vendored
40
.github/labeler.yml
vendored
@ -1,40 +0,0 @@
|
||||
# A bot for automatically labelling pull requests
|
||||
# See https://github.com/actions/labeler
|
||||
|
||||
dataframe:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/nu_plugin_polars/**
|
||||
|
||||
std-library:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/nu-std/**
|
||||
|
||||
ci:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- .github/workflows/**
|
||||
|
||||
|
||||
LSP:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/nu-lsp/**
|
||||
|
||||
parser:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
- crates/nu-parser/**
|
||||
|
||||
pr:plugins:
|
||||
- changed-files:
|
||||
- any-glob-to-any-file:
|
||||
# plugins API
|
||||
- crates/nu-plugin/**
|
||||
- crates/nu-plugin-core/**
|
||||
- crates/nu-plugin-engine/**
|
||||
- crates/nu-plugin-protocol/**
|
||||
- crates/nu-plugin-test-support/**
|
||||
# specific plugins (like polars)
|
||||
- crates/nu_plugin_*/**
|
52
.github/workflows/beta-test.yml
vendored
52
.github/workflows/beta-test.yml
vendored
@ -1,52 +0,0 @@
|
||||
name: Test on Beta Toolchain
|
||||
# This workflow is made to run our tests on the beta toolchain to validate that
|
||||
# the beta toolchain works.
|
||||
# We do not intend to test here that we are working correctly but rather that
|
||||
# the beta toolchain works correctly.
|
||||
# The ci.yml handles our actual testing with our guarantees.
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# If this workflow fails, GitHub notifications will go to the last person
|
||||
# who edited this line.
|
||||
# See: https://docs.github.com/en/actions/monitoring-and-troubleshooting-workflows/monitoring-workflows/notifications-for-workflow-runs
|
||||
- cron: '0 0 * * *' # Runs daily at midnight UTC
|
||||
|
||||
env:
|
||||
NUSHELL_CARGO_PROFILE: ci
|
||||
NU_LOG_LEVEL: DEBUG
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref && github.ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
# this job is more for testing the beta toolchain and not our tests, so if
|
||||
# this fails but the tests of the regular ci pass, then this is fine
|
||||
continue-on-error: true
|
||||
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
platform: [windows-latest, macos-latest, ubuntu-22.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- run: rustup update beta
|
||||
|
||||
- name: Tests
|
||||
run: cargo +beta test --workspace --profile ci --exclude nu_plugin_*
|
||||
- name: Check for clean repo
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "there are changes";
|
||||
git status --porcelain
|
||||
exit 1
|
||||
else
|
||||
echo "no changes in working directory";
|
||||
fi
|
25
.github/workflows/ci.yml
vendored
25
.github/workflows/ci.yml
vendored
@ -3,7 +3,6 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'patch-release-*'
|
||||
|
||||
name: continuous-integration
|
||||
|
||||
@ -22,14 +21,14 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
# Pinning to Ubuntu 22.04 because building on newer Ubuntu versions causes linux-gnu
|
||||
# Pinning to Ubuntu 20.04 because building on newer Ubuntu versions causes linux-gnu
|
||||
# builds to link against a too-new-for-many-Linux-installs glibc version. Consider
|
||||
# revisiting this when 22.04 is closer to EOL (June 2027)
|
||||
# revisiting this when 20.04 is closer to EOL (April 2025)
|
||||
#
|
||||
# Using macOS 13 runner because 14 is based on the M1 and has half as much RAM (7 GB,
|
||||
# instead of 14 GB) which is too little for us right now. Revisit when `dfr` commands are
|
||||
# removed and we're only building the `polars` plugin instead
|
||||
platform: [windows-latest, macos-13, ubuntu-22.04]
|
||||
platform: [windows-latest, macos-13, ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
@ -37,7 +36,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1
|
||||
|
||||
- name: cargo fmt
|
||||
run: cargo fmt --all -- --check
|
||||
@ -57,7 +56,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
platform: [windows-latest, macos-latest, ubuntu-22.04]
|
||||
platform: [windows-latest, macos-latest, ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
@ -65,7 +64,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1
|
||||
|
||||
- name: Tests
|
||||
run: cargo test --workspace --profile ci --exclude nu_plugin_*
|
||||
@ -84,7 +83,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
platform: [ubuntu-22.04, macos-latest, windows-latest]
|
||||
platform: [ubuntu-20.04, macos-latest, windows-latest]
|
||||
py:
|
||||
- py
|
||||
|
||||
@ -94,10 +93,10 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1
|
||||
|
||||
- name: Install Nushell
|
||||
run: cargo install --path . --locked --force
|
||||
run: cargo install --path . --locked --no-default-features --force
|
||||
|
||||
- name: Standard library tests
|
||||
run: nu -c 'use crates/nu-std/testing.nu; testing run-tests --path crates/nu-std'
|
||||
@ -137,7 +136,7 @@ jobs:
|
||||
# instead of 14 GB) which is too little for us right now.
|
||||
#
|
||||
# Failure occurring with clippy for rust 1.77.2
|
||||
platform: [windows-latest, macos-13, ubuntu-22.04]
|
||||
platform: [windows-latest, macos-13, ubuntu-20.04]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
@ -145,7 +144,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1
|
||||
|
||||
- name: Clippy
|
||||
run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS
|
||||
@ -186,7 +185,7 @@ jobs:
|
||||
- uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1
|
||||
|
||||
- name: Add wasm32-unknown-unknown target
|
||||
run: rustup target add wasm32-unknown-unknown
|
||||
|
19
.github/workflows/labels.yml
vendored
19
.github/workflows/labels.yml
vendored
@ -1,19 +0,0 @@
|
||||
# Automatically labels PRs based on the configuration file
|
||||
# you are probably looking for 👉 `.github/labeler.yml`
|
||||
name: Label PRs
|
||||
|
||||
on:
|
||||
- pull_request_target
|
||||
|
||||
jobs:
|
||||
triage:
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'nushell'
|
||||
steps:
|
||||
- uses: actions/labeler@v5
|
||||
with:
|
||||
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
sync-labels: true
|
9
.github/workflows/nightly-build.yml
vendored
9
.github/workflows/nightly-build.yml
vendored
@ -8,7 +8,6 @@
|
||||
name: Nightly Build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- nightly # Just for test purpose only with the nightly repo
|
||||
@ -40,7 +39,7 @@ jobs:
|
||||
uses: hustcer/setup-nu@v3
|
||||
if: github.repository == 'nushell/nightly'
|
||||
with:
|
||||
version: 0.103.0
|
||||
version: 0.101.0
|
||||
|
||||
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
|
||||
- name: Prepare for Nightly Release
|
||||
@ -132,7 +131,7 @@ jobs:
|
||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1
|
||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||
with:
|
||||
rustflags: ''
|
||||
@ -140,7 +139,7 @@ jobs:
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3
|
||||
with:
|
||||
version: 0.103.0
|
||||
version: 0.101.0
|
||||
|
||||
- name: Release Nu Binary
|
||||
id: nu
|
||||
@ -198,7 +197,7 @@ jobs:
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3
|
||||
with:
|
||||
version: 0.103.0
|
||||
version: 0.101.0
|
||||
|
||||
# Keep the last a few releases
|
||||
- name: Delete Older Releases
|
||||
|
14
.github/workflows/release-pkg.nu
vendored
14
.github/workflows/release-pkg.nu
vendored
@ -117,14 +117,14 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
||||
# ----------------------------------------------------------------------------
|
||||
# Build for Windows without static-link-openssl feature
|
||||
# ----------------------------------------------------------------------------
|
||||
if $os =~ 'windows' {
|
||||
if $os in ['windows-latest'] {
|
||||
cargo-build-nu
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Prepare for the release archive
|
||||
# ----------------------------------------------------------------------------
|
||||
let suffix = if $os =~ 'windows' { '.exe' }
|
||||
let suffix = if $os == 'windows-latest' { '.exe' }
|
||||
# nu, nu_plugin_* were all included
|
||||
let executable = $'target/($target)/release/($bin)*($suffix)'
|
||||
print $'Current executable file: ($executable)'
|
||||
@ -148,10 +148,10 @@ For more information, refer to https://www.nushell.sh/book/plugins.html
|
||||
[LICENSE ...(glob $executable)] | each {|it| cp -rv $it $dist } | flatten
|
||||
|
||||
print $'(char nl)Check binary release version detail:'; hr-line
|
||||
let ver = if $os =~ 'windows' {
|
||||
(do -i { .\output\nu.exe -c 'version' }) | default '' | str join
|
||||
let ver = if $os == 'windows-latest' {
|
||||
(do -i { .\output\nu.exe -c 'version' }) | str join
|
||||
} else {
|
||||
(do -i { ./output/nu -c 'version' }) | default '' | str join
|
||||
(do -i { ./output/nu -c 'version' }) | str join
|
||||
}
|
||||
if ($ver | str trim | is-empty) {
|
||||
print $'(ansi r)Incompatible Nu binary: The binary cross compiled is not runnable on current arch...(ansi reset)'
|
||||
@ -177,7 +177,7 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
||||
# REF: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
|
||||
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
||||
|
||||
} else if $os =~ 'windows' {
|
||||
} else if $os == 'windows-latest' {
|
||||
|
||||
let releaseStem = $'($bin)-($version)-($target)'
|
||||
|
||||
@ -221,7 +221,7 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
||||
}
|
||||
|
||||
def 'cargo-build-nu' [] {
|
||||
if $os =~ 'windows' {
|
||||
if $os == 'windows-latest' {
|
||||
cargo build --release --all --target $target
|
||||
} else {
|
||||
cargo build --release --all --target $target --features=static-link-openssl
|
||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -80,7 +80,7 @@ jobs:
|
||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.10.1
|
||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||
with:
|
||||
cache: false
|
||||
@ -89,7 +89,7 @@ jobs:
|
||||
- name: Setup Nushell
|
||||
uses: hustcer/setup-nu@v3
|
||||
with:
|
||||
version: 0.103.0
|
||||
version: 0.101.0
|
||||
|
||||
- name: Release Nu Binary
|
||||
id: nu
|
||||
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@ -10,4 +10,4 @@ jobs:
|
||||
uses: actions/checkout@v4.1.7
|
||||
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@v1.31.1
|
||||
uses: crate-ci/typos@v1.29.4
|
||||
|
756
Cargo.lock
generated
756
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
101
Cargo.toml
101
Cargo.toml
@ -10,8 +10,8 @@ homepage = "https://www.nushell.sh"
|
||||
license = "MIT"
|
||||
name = "nu"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
rust-version = "1.84.1"
|
||||
version = "0.104.1"
|
||||
rust-version = "1.82.0"
|
||||
version = "0.102.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -66,12 +66,12 @@ alphanumeric-sort = "1.5"
|
||||
ansi-str = "0.8"
|
||||
anyhow = "1.0.82"
|
||||
base64 = "0.22.1"
|
||||
bracoxide = "0.1.6"
|
||||
bracoxide = "0.1.4"
|
||||
brotli = "7.0"
|
||||
byteorder = "1.5"
|
||||
bytes = "1"
|
||||
bytesize = "1.3.3"
|
||||
calamine = "0.27"
|
||||
bytesize = "1.3"
|
||||
calamine = "0.26.1"
|
||||
chardetng = "0.1.17"
|
||||
chrono = { default-features = false, version = "0.4.34" }
|
||||
chrono-humanize = "0.2.3"
|
||||
@ -91,8 +91,8 @@ fancy-regex = "0.14"
|
||||
filesize = "0.2"
|
||||
filetime = "0.2"
|
||||
heck = "0.5.0"
|
||||
human-date-parser = "0.3.0"
|
||||
indexmap = "2.9"
|
||||
human-date-parser = "0.2.0"
|
||||
indexmap = "2.7"
|
||||
indicatif = "0.17"
|
||||
interprocess = "2.2.0"
|
||||
is_executable = "1.0"
|
||||
@ -104,13 +104,13 @@ lru = "0.12"
|
||||
lscolors = { version = "0.17", default-features = false }
|
||||
lsp-server = "0.7.8"
|
||||
lsp-types = { version = "0.97.0", features = ["proposed"] }
|
||||
lsp-textdocument = "0.4.2"
|
||||
lsp-textdocument = "0.4.1"
|
||||
mach2 = "0.4"
|
||||
md5 = { version = "0.10", package = "md-5" }
|
||||
miette = "7.5"
|
||||
miette = "7.3"
|
||||
mime = "0.3.17"
|
||||
mime_guess = "2.0"
|
||||
mockito = { version = "1.7", default-features = false }
|
||||
mockito = { version = "1.6", default-features = false }
|
||||
multipart-rs = "0.1.13"
|
||||
native-tls = "0.2"
|
||||
nix = { version = "0.29", default-features = false }
|
||||
@ -127,7 +127,7 @@ pathdiff = "0.2"
|
||||
percent-encoding = "2"
|
||||
pretty_assertions = "1.4"
|
||||
print-positions = "0.6"
|
||||
proc-macro-error2 = "2.0"
|
||||
proc-macro-error = { version = "1.0", default-features = false }
|
||||
proc-macro2 = "1.0"
|
||||
procfs = "0.17.0"
|
||||
pwd = "1.3"
|
||||
@ -135,50 +135,48 @@ quick-xml = "0.37.0"
|
||||
quickcheck = "1.0"
|
||||
quickcheck_macros = "1.0"
|
||||
quote = "1.0"
|
||||
rand = "0.9"
|
||||
rand = "0.8"
|
||||
getrandom = "0.2" # pick same version that rand requires
|
||||
rand_chacha = "0.9"
|
||||
ratatui = "0.29"
|
||||
rand_chacha = "0.3.1"
|
||||
ratatui = "0.26"
|
||||
rayon = "1.10"
|
||||
reedline = "0.40.0"
|
||||
reedline = "0.38.0"
|
||||
rmp = "0.8"
|
||||
rmp-serde = "1.3"
|
||||
roxmltree = "0.20"
|
||||
rstest = { version = "0.23", default-features = false }
|
||||
rstest_reuse = "0.7"
|
||||
rusqlite = "0.31"
|
||||
rust-embed = "8.7.0"
|
||||
rust-embed = "8.5.0"
|
||||
scopeguard = { version = "1.2.0" }
|
||||
serde = { version = "1.0" }
|
||||
serde_json = "1.0.97"
|
||||
serde_json = "1.0"
|
||||
serde_urlencoded = "0.7.1"
|
||||
serde_yaml = "0.9.33"
|
||||
sha2 = "0.10"
|
||||
strip-ansi-escapes = "0.2.0"
|
||||
strum = "0.26"
|
||||
strum_macros = "0.26"
|
||||
syn = "2.0"
|
||||
sysinfo = "0.33"
|
||||
tabled = { version = "0.17.0", default-features = false }
|
||||
tempfile = "3.15"
|
||||
titlecase = "3.5"
|
||||
titlecase = "3.0"
|
||||
toml = "0.8"
|
||||
trash = "5.2"
|
||||
update-informer = { version = "1.2.0", default-features = false, features = ["github", "native-tls", "ureq"] }
|
||||
umask = "2.1"
|
||||
unicode-segmentation = "1.12"
|
||||
unicode-width = "0.2"
|
||||
ureq = { version = "2.12", default-features = false, features = ["socks-proxy"] }
|
||||
ureq = { version = "2.12", default-features = false }
|
||||
url = "2.2"
|
||||
uu_cp = "0.0.30"
|
||||
uu_mkdir = "0.0.30"
|
||||
uu_mktemp = "0.0.30"
|
||||
uu_mv = "0.0.30"
|
||||
uu_touch = "0.0.30"
|
||||
uu_whoami = "0.0.30"
|
||||
uu_uname = "0.0.30"
|
||||
uucore = "0.0.30"
|
||||
uuid = "1.16.0"
|
||||
uu_cp = "0.0.29"
|
||||
uu_mkdir = "0.0.29"
|
||||
uu_mktemp = "0.0.29"
|
||||
uu_mv = "0.0.29"
|
||||
uu_touch = "0.0.29"
|
||||
uu_whoami = "0.0.29"
|
||||
uu_uname = "0.0.29"
|
||||
uucore = "0.0.29"
|
||||
uuid = "1.12.0"
|
||||
v_htmlescape = "0.15.0"
|
||||
wax = "0.6"
|
||||
web-time = "1.1.0"
|
||||
@ -197,22 +195,22 @@ unchecked_duration_subtraction = "warn"
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-cli = { path = "./crates/nu-cli", version = "0.104.1" }
|
||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.104.1" }
|
||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.104.1" }
|
||||
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.104.1", optional = true }
|
||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.104.1" }
|
||||
nu-command = { path = "./crates/nu-command", version = "0.104.1" }
|
||||
nu-engine = { path = "./crates/nu-engine", version = "0.104.1" }
|
||||
nu-explore = { path = "./crates/nu-explore", version = "0.104.1" }
|
||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.104.1" }
|
||||
nu-parser = { path = "./crates/nu-parser", version = "0.104.1" }
|
||||
nu-path = { path = "./crates/nu-path", version = "0.104.1" }
|
||||
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.104.1" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.104.1" }
|
||||
nu-std = { path = "./crates/nu-std", version = "0.104.1" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.104.1" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.104.1" }
|
||||
nu-cli = { path = "./crates/nu-cli", version = "0.102.0" }
|
||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.102.0" }
|
||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.102.0" }
|
||||
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.102.0", optional = true }
|
||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.102.0" }
|
||||
nu-command = { path = "./crates/nu-command", version = "0.102.0" }
|
||||
nu-engine = { path = "./crates/nu-engine", version = "0.102.0" }
|
||||
nu-explore = { path = "./crates/nu-explore", version = "0.102.0" }
|
||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.102.0" }
|
||||
nu-parser = { path = "./crates/nu-parser", version = "0.102.0" }
|
||||
nu-path = { path = "./crates/nu-path", version = "0.102.0" }
|
||||
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.102.0" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.102.0" }
|
||||
nu-std = { path = "./crates/nu-std", version = "0.102.0" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.102.0" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.102.0" }
|
||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||
|
||||
crossterm = { workspace = true }
|
||||
@ -220,6 +218,7 @@ ctrlc = { workspace = true }
|
||||
dirs = { workspace = true }
|
||||
log = { workspace = true }
|
||||
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
|
||||
mimalloc = { version = "0.1.42", default-features = false, optional = true }
|
||||
multipart-rs = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
simplelog = "0.12"
|
||||
@ -241,9 +240,9 @@ nix = { workspace = true, default-features = false, features = [
|
||||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.104.1" }
|
||||
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.104.1" }
|
||||
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.104.1" }
|
||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.102.0" }
|
||||
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.102.0" }
|
||||
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.102.0" }
|
||||
assert_cmd = "2.0"
|
||||
dirs = { workspace = true }
|
||||
tango-bench = "0.6"
|
||||
@ -273,6 +272,7 @@ default = [
|
||||
"plugin",
|
||||
"trash-support",
|
||||
"sqlite",
|
||||
"mimalloc",
|
||||
]
|
||||
stable = ["default"]
|
||||
# NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command
|
||||
@ -281,6 +281,7 @@ stable = ["default"]
|
||||
# otherwise the system version will be used. Not enabled by default because it takes a while to build
|
||||
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
|
||||
|
||||
mimalloc = ["nu-cmd-lang/mimalloc", "dep:mimalloc"]
|
||||
# Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems.
|
||||
# Missing X server/ Wayland can cause issues
|
||||
system-clipboard = [
|
||||
@ -293,7 +294,7 @@ system-clipboard = [
|
||||
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
|
||||
|
||||
# SQLite commands for nushell
|
||||
sqlite = ["nu-command/sqlite", "nu-cmd-lang/sqlite", "nu-std/sqlite"]
|
||||
sqlite = ["nu-command/sqlite", "nu-cmd-lang/sqlite"]
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s" # Optimize for size
|
||||
|
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 - 2025 The Nushell Project Developers
|
||||
Copyright (c) 2019 - 2023 The Nushell Project Developers
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -4,6 +4,7 @@
|
||||
[](https://github.com/nushell/nushell/actions/workflows/nightly-build.yml)
|
||||
[](https://discord.gg/NtAbbGn)
|
||||
[](https://changelog.com/podcast/363)
|
||||
[](https://twitter.com/nu_shell)
|
||||
[](https://github.com/nushell/nushell/graphs/commit-activity)
|
||||
[](https://github.com/nushell/nushell/graphs/contributors)
|
||||
|
||||
@ -34,7 +35,7 @@ This project has reached a minimum-viable-product level of quality. Many people
|
||||
|
||||
The [Nushell book](https://www.nushell.sh/book/) is the primary source of Nushell documentation. You can find [a full list of Nu commands in the book](https://www.nushell.sh/commands/), and we have many examples of using Nu in our [cookbook](https://www.nushell.sh/cookbook/).
|
||||
|
||||
We're also active on [Discord](https://discord.gg/NtAbbGn); come and chat with us!
|
||||
We're also active on [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell); come and chat with us!
|
||||
|
||||
## Installation
|
||||
|
||||
@ -222,7 +223,6 @@ Please submit an issue or PR to be added to this list.
|
||||
- [Dorothy](http://github.com/bevry/dorothy)
|
||||
- [Direnv](https://github.com/direnv/direnv/blob/master/docs/hook.md#nushell)
|
||||
- [x-cmd](https://x-cmd.com/mod/nu)
|
||||
- [vfox](https://github.com/version-fox/vfox)
|
||||
|
||||
## Contributing
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
use nu_cli::{eval_source, evaluate_commands};
|
||||
use nu_plugin_core::{Encoder, EncodingType};
|
||||
use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
|
||||
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
PipelineData, Signals, Span, Spanned, Value,
|
||||
@ -8,11 +9,12 @@ use nu_protocol::{
|
||||
use nu_std::load_standard_library;
|
||||
use nu_utils::{get_default_config, get_default_env};
|
||||
use std::{
|
||||
fmt::Write,
|
||||
hint::black_box,
|
||||
rc::Rc,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
|
||||
use std::hint::black_box;
|
||||
|
||||
use tango_bench::{benchmark_fn, tango_benchmarks, tango_main, IntoBenchmarks};
|
||||
|
||||
fn load_bench_commands() -> EngineState {
|
||||
@ -139,16 +141,19 @@ fn bench_load_standard_lib() -> impl IntoBenchmarks {
|
||||
})]
|
||||
}
|
||||
|
||||
fn create_flat_record_string(n: usize) -> String {
|
||||
let mut s = String::from("let record = { ");
|
||||
fn create_flat_record_string(n: i32) -> String {
|
||||
let mut s = String::from("let record = {");
|
||||
for i in 0..n {
|
||||
write!(s, "col_{i}: {i}, ").unwrap();
|
||||
s.push_str(&format!("col_{}: {}", i, i));
|
||||
if i < n - 1 {
|
||||
s.push_str(", ");
|
||||
}
|
||||
}
|
||||
s.push('}');
|
||||
s
|
||||
}
|
||||
|
||||
fn create_nested_record_string(depth: usize) -> String {
|
||||
fn create_nested_record_string(depth: i32) -> String {
|
||||
let mut s = String::from("let record = {");
|
||||
for _ in 0..depth {
|
||||
s.push_str("col: {");
|
||||
@ -161,7 +166,7 @@ fn create_nested_record_string(depth: usize) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
fn create_example_table_nrows(n: usize) -> String {
|
||||
fn create_example_table_nrows(n: i32) -> String {
|
||||
let mut s = String::from("let table = [[foo bar baz]; ");
|
||||
for i in 0..n {
|
||||
s.push_str(&format!("[0, 1, {i}]"));
|
||||
@ -173,7 +178,7 @@ fn create_example_table_nrows(n: usize) -> String {
|
||||
s
|
||||
}
|
||||
|
||||
fn bench_record_create(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_record_create(n: i32) -> impl IntoBenchmarks {
|
||||
bench_command(
|
||||
&format!("record_create_{n}"),
|
||||
&create_flat_record_string(n),
|
||||
@ -182,7 +187,7 @@ fn bench_record_create(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_record_flat_access(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_record_flat_access(n: i32) -> impl IntoBenchmarks {
|
||||
let setup_command = create_flat_record_string(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
bench_command(
|
||||
@ -193,10 +198,10 @@ fn bench_record_flat_access(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_record_nested_access(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_record_nested_access(n: i32) -> impl IntoBenchmarks {
|
||||
let setup_command = create_nested_record_string(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
let nested_access = ".col".repeat(n);
|
||||
let nested_access = ".col".repeat(n as usize);
|
||||
bench_command(
|
||||
&format!("record_nested_access_{n}"),
|
||||
&format!("$record{} | ignore", nested_access),
|
||||
@ -205,18 +210,7 @@ fn bench_record_nested_access(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_record_insert(n: usize, m: usize) -> impl IntoBenchmarks {
|
||||
let setup_command = create_flat_record_string(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
let mut insert = String::from("$record");
|
||||
for i in n..(n + m) {
|
||||
write!(insert, " | insert col_{i} {i}").unwrap();
|
||||
}
|
||||
insert.push_str(" | ignore");
|
||||
bench_command(&format!("record_insert_{n}_{m}"), &insert, stack, engine)
|
||||
}
|
||||
|
||||
fn bench_table_create(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_table_create(n: i32) -> impl IntoBenchmarks {
|
||||
bench_command(
|
||||
&format!("table_create_{n}"),
|
||||
&create_example_table_nrows(n),
|
||||
@ -225,7 +219,7 @@ fn bench_table_create(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_table_get(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_table_get(n: i32) -> impl IntoBenchmarks {
|
||||
let setup_command = create_example_table_nrows(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
bench_command(
|
||||
@ -236,7 +230,7 @@ fn bench_table_get(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_table_select(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_table_select(n: i32) -> impl IntoBenchmarks {
|
||||
let setup_command = create_example_table_nrows(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
bench_command(
|
||||
@ -247,29 +241,7 @@ fn bench_table_select(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_table_insert_row(n: usize, m: usize) -> impl IntoBenchmarks {
|
||||
let setup_command = create_example_table_nrows(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
let mut insert = String::from("$table");
|
||||
for i in n..(n + m) {
|
||||
write!(insert, " | insert {i} {{ foo: 0, bar: 1, baz: {i} }}").unwrap();
|
||||
}
|
||||
insert.push_str(" | ignore");
|
||||
bench_command(&format!("table_insert_row_{n}_{m}"), &insert, stack, engine)
|
||||
}
|
||||
|
||||
fn bench_table_insert_col(n: usize, m: usize) -> impl IntoBenchmarks {
|
||||
let setup_command = create_example_table_nrows(n);
|
||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||
let mut insert = String::from("$table");
|
||||
for i in 0..m {
|
||||
write!(insert, " | insert col_{i} {i}").unwrap();
|
||||
}
|
||||
insert.push_str(" | ignore");
|
||||
bench_command(&format!("table_insert_col_{n}_{m}"), &insert, stack, engine)
|
||||
}
|
||||
|
||||
fn bench_eval_interleave(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_eval_interleave(n: i32) -> impl IntoBenchmarks {
|
||||
let engine = setup_engine();
|
||||
let stack = Stack::new();
|
||||
bench_command(
|
||||
@ -280,7 +252,7 @@ fn bench_eval_interleave(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_eval_interleave_with_interrupt(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_eval_interleave_with_interrupt(n: i32) -> impl IntoBenchmarks {
|
||||
let mut engine = setup_engine();
|
||||
engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
|
||||
let stack = Stack::new();
|
||||
@ -292,7 +264,7 @@ fn bench_eval_interleave_with_interrupt(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_eval_for(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_eval_for(n: i32) -> impl IntoBenchmarks {
|
||||
let engine = setup_engine();
|
||||
let stack = Stack::new();
|
||||
bench_command(
|
||||
@ -303,7 +275,7 @@ fn bench_eval_for(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_eval_each(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_eval_each(n: i32) -> impl IntoBenchmarks {
|
||||
let engine = setup_engine();
|
||||
let stack = Stack::new();
|
||||
bench_command(
|
||||
@ -314,7 +286,7 @@ fn bench_eval_each(n: usize) -> impl IntoBenchmarks {
|
||||
)
|
||||
}
|
||||
|
||||
fn bench_eval_par_each(n: usize) -> impl IntoBenchmarks {
|
||||
fn bench_eval_par_each(n: i32) -> impl IntoBenchmarks {
|
||||
let engine = setup_engine();
|
||||
let stack = Stack::new();
|
||||
bench_command(
|
||||
@ -455,14 +427,6 @@ tango_benchmarks!(
|
||||
bench_record_nested_access(32),
|
||||
bench_record_nested_access(64),
|
||||
bench_record_nested_access(128),
|
||||
bench_record_insert(1, 1),
|
||||
bench_record_insert(10, 1),
|
||||
bench_record_insert(100, 1),
|
||||
bench_record_insert(1000, 1),
|
||||
bench_record_insert(1, 10),
|
||||
bench_record_insert(10, 10),
|
||||
bench_record_insert(100, 10),
|
||||
bench_record_insert(1000, 10),
|
||||
// Table
|
||||
bench_table_create(1),
|
||||
bench_table_create(10),
|
||||
@ -476,22 +440,6 @@ tango_benchmarks!(
|
||||
bench_table_select(10),
|
||||
bench_table_select(100),
|
||||
bench_table_select(1_000),
|
||||
bench_table_insert_row(1, 1),
|
||||
bench_table_insert_row(10, 1),
|
||||
bench_table_insert_row(100, 1),
|
||||
bench_table_insert_row(1000, 1),
|
||||
bench_table_insert_row(1, 10),
|
||||
bench_table_insert_row(10, 10),
|
||||
bench_table_insert_row(100, 10),
|
||||
bench_table_insert_row(1000, 10),
|
||||
bench_table_insert_col(1, 1),
|
||||
bench_table_insert_col(10, 1),
|
||||
bench_table_insert_col(100, 1),
|
||||
bench_table_insert_col(1000, 1),
|
||||
bench_table_insert_col(1, 10),
|
||||
bench_table_insert_col(10, 10),
|
||||
bench_table_insert_col(100, 10),
|
||||
bench_table_insert_col(1000, 10),
|
||||
// Eval
|
||||
// Interleave
|
||||
bench_eval_interleave(100),
|
||||
|
@ -5,29 +5,28 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.104.1"
|
||||
version = "0.102.0"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
|
||||
[dev-dependencies]
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.104.1" }
|
||||
nu-command = { path = "../nu-command", version = "0.104.1" }
|
||||
nu-std = { path = "../nu-std", version = "0.104.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.104.1" }
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.0" }
|
||||
nu-command = { path = "../nu-command", version = "0.102.0" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.102.0" }
|
||||
rstest = { workspace = true, default-features = false }
|
||||
tempfile = { workspace = true }
|
||||
|
||||
[dependencies]
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.104.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.104.1", features = ["os"] }
|
||||
nu-glob = { path = "../nu-glob", version = "0.104.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.104.1" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.104.1" }
|
||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.104.1", optional = true }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.104.1", features = ["os"] }
|
||||
nu-utils = { path = "../nu-utils", version = "0.104.1" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.104.1" }
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.102.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.102.0", features = ["os"] }
|
||||
nu-glob = { path = "../nu-glob", version = "0.102.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.102.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.102.0" }
|
||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.102.0", optional = true }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.102.0", features = ["os"] }
|
||||
nu-utils = { path = "../nu-utils", version = "0.102.0" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.102.0" }
|
||||
nu-ansi-term = { workspace = true }
|
||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||
|
||||
@ -41,7 +40,6 @@ miette = { workspace = true, features = ["fancy-no-backtrace"] }
|
||||
nucleo-matcher = { workspace = true }
|
||||
percent-encoding = { workspace = true }
|
||||
sysinfo = { workspace = true }
|
||||
strum = { workspace = true }
|
||||
unicode-segmentation = { workspace = true }
|
||||
uuid = { workspace = true, features = ["v4"] }
|
||||
which = { workspace = true }
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CommandlineEdit;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for CommandlineEdit {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"commandline edit"
|
||||
}
|
||||
@ -29,7 +29,7 @@ impl Command for CommandlineEdit {
|
||||
.required(
|
||||
"str",
|
||||
SyntaxShape::String,
|
||||
"The string to perform the operation with.",
|
||||
"the string to perform the operation with",
|
||||
)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
@ -2,9 +2,9 @@ use nu_engine::command_prelude::*;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CommandlineGetCursor;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for CommandlineGetCursor {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"commandline get-cursor"
|
||||
}
|
||||
|
@ -4,6 +4,6 @@ mod get_cursor;
|
||||
mod set_cursor;
|
||||
|
||||
pub use commandline_::Commandline;
|
||||
pub use edit::CommandlineEdit;
|
||||
pub use get_cursor::CommandlineGetCursor;
|
||||
pub use set_cursor::CommandlineSetCursor;
|
||||
pub use edit::SubCommand as CommandlineEdit;
|
||||
pub use get_cursor::SubCommand as CommandlineGetCursor;
|
||||
pub use set_cursor::SubCommand as CommandlineSetCursor;
|
||||
|
@ -3,9 +3,9 @@ use nu_engine::command_prelude::*;
|
||||
use unicode_segmentation::UnicodeSegmentation;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CommandlineSetCursor;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for CommandlineSetCursor {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"commandline set-cursor"
|
||||
}
|
||||
@ -18,7 +18,7 @@ impl Command for CommandlineSetCursor {
|
||||
"set the current cursor position to the end of the buffer",
|
||||
Some('e'),
|
||||
)
|
||||
.optional("pos", SyntaxShape::Int, "Cursor position to be set.")
|
||||
.optional("pos", SyntaxShape::Int, "Cursor position to be set")
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
|
@ -105,9 +105,10 @@ impl Command for History {
|
||||
.ok()
|
||||
})
|
||||
.map(move |entries| {
|
||||
entries.into_iter().enumerate().map(move |(idx, entry)| {
|
||||
create_sqlite_history_record(idx, entry, long, head)
|
||||
})
|
||||
entries
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(move |(idx, entry)| create_history_record(idx, entry, long, head))
|
||||
})
|
||||
.ok_or(IoError::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
@ -139,7 +140,7 @@ impl Command for History {
|
||||
}
|
||||
}
|
||||
|
||||
fn create_sqlite_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span) -> Value {
|
||||
fn create_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span) -> Value {
|
||||
//1. Format all the values
|
||||
//2. Create a record of either short or long columns and values
|
||||
|
||||
@ -150,8 +151,11 @@ fn create_sqlite_history_record(idx: usize, entry: HistoryItem, long: bool, head
|
||||
.unwrap_or_default(),
|
||||
head,
|
||||
);
|
||||
let start_timestamp_value = Value::date(
|
||||
entry.start_timestamp.unwrap_or_default().fixed_offset(),
|
||||
let start_timestamp_value = Value::string(
|
||||
entry
|
||||
.start_timestamp
|
||||
.map(|time| time.to_string())
|
||||
.unwrap_or_default(),
|
||||
head,
|
||||
);
|
||||
let command_value = Value::string(entry.command_line, head);
|
||||
|
@ -21,12 +21,12 @@ impl Command for HistoryImport {
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Import command line history."
|
||||
"Import command line history"
|
||||
}
|
||||
|
||||
fn extra_description(&self) -> &str {
|
||||
r#"Can import history from input, either successive command lines or more detailed records. If providing records, available fields are:
|
||||
command, start_timestamp, hostname, cwd, duration, exit_status.
|
||||
command_line, id, start_timestamp, hostname, cwd, duration, exit_status.
|
||||
|
||||
If no input is provided, will import all history items from existing history in the other format: if current history is stored in sqlite, it will store it in plain text and vice versa.
|
||||
|
||||
|
@ -1,87 +0,0 @@
|
||||
use super::{completion_options::NuMatcher, SemanticSuggestion};
|
||||
use crate::{
|
||||
completions::{Completer, CompletionOptions},
|
||||
SuggestionKind,
|
||||
};
|
||||
use nu_protocol::{
|
||||
engine::{Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
pub struct AttributeCompletion;
|
||||
pub struct AttributableCompletion;
|
||||
|
||||
impl Completer for AttributeCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
_stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
span: Span,
|
||||
offset: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let mut matcher = NuMatcher::new(prefix, options);
|
||||
|
||||
let attr_commands =
|
||||
working_set.find_commands_by_predicate(|s| s.starts_with(b"attr "), true);
|
||||
|
||||
for (decl_id, name, desc, ty) in attr_commands {
|
||||
let name = name.strip_prefix(b"attr ").unwrap_or(&name);
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: String::from_utf8_lossy(name).into_owned(),
|
||||
description: desc,
|
||||
style: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
append_whitespace: false,
|
||||
},
|
||||
kind: Some(SuggestionKind::Command(ty, Some(decl_id))),
|
||||
});
|
||||
}
|
||||
|
||||
matcher.results()
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for AttributableCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
_stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
span: Span,
|
||||
offset: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let mut matcher = NuMatcher::new(prefix, options);
|
||||
|
||||
for s in ["def", "extern", "export def", "export extern"] {
|
||||
let decl_id = working_set
|
||||
.find_decl(s.as_bytes())
|
||||
.expect("internal error, builtin declaration not found");
|
||||
let cmd = working_set.get_decl(decl_id);
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: cmd.name().into(),
|
||||
description: Some(cmd.description().into()),
|
||||
style: None,
|
||||
extra: None,
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
append_whitespace: false,
|
||||
},
|
||||
kind: Some(SuggestionKind::Command(cmd.command_type(), None)),
|
||||
});
|
||||
}
|
||||
|
||||
matcher.results()
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use crate::completions::CompletionOptions;
|
||||
use nu_protocol::{
|
||||
engine::{Stack, StateWorkingSet},
|
||||
DeclId, Span,
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
@ -12,9 +12,10 @@ pub trait Completer {
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion>;
|
||||
}
|
||||
@ -28,15 +29,9 @@ pub struct SemanticSuggestion {
|
||||
// TODO: think about name: maybe suggestion context?
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum SuggestionKind {
|
||||
Command(nu_protocol::engine::CommandType, Option<DeclId>),
|
||||
Value(nu_protocol::Type),
|
||||
CellPath,
|
||||
Directory,
|
||||
File,
|
||||
Flag,
|
||||
Command(nu_protocol::engine::CommandType),
|
||||
Type(nu_protocol::Type),
|
||||
Module,
|
||||
Operator,
|
||||
Variable,
|
||||
}
|
||||
|
||||
impl From<Suggestion> for SemanticSuggestion {
|
||||
|
@ -1,153 +0,0 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
|
||||
use nu_engine::{column::get_columns, eval_variable};
|
||||
use nu_protocol::{
|
||||
ast::{Expr, Expression, FullCellPath, PathMember},
|
||||
engine::{Stack, StateWorkingSet},
|
||||
eval_const::eval_constant,
|
||||
ShellError, Span, Value,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
use super::completion_options::NuMatcher;
|
||||
|
||||
pub struct CellPathCompletion<'a> {
|
||||
pub full_cell_path: &'a FullCellPath,
|
||||
pub position: usize,
|
||||
}
|
||||
|
||||
fn prefix_from_path_member(member: &PathMember, pos: usize) -> (String, Span) {
|
||||
let (prefix_str, start) = match member {
|
||||
PathMember::String { val, span, .. } => (val, span.start),
|
||||
PathMember::Int { val, span, .. } => (&val.to_string(), span.start),
|
||||
};
|
||||
let prefix_str = prefix_str.get(..pos + 1 - start).unwrap_or(prefix_str);
|
||||
// strip wrapping quotes
|
||||
let quotations = ['"', '\'', '`'];
|
||||
let prefix_str = prefix_str.strip_prefix(quotations).unwrap_or(prefix_str);
|
||||
(prefix_str.to_string(), Span::new(start, pos + 1))
|
||||
}
|
||||
|
||||
impl Completer for CellPathCompletion<'_> {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
stack: &Stack,
|
||||
_prefix: impl AsRef<str>,
|
||||
_span: Span,
|
||||
offset: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let mut prefix_str = String::new();
|
||||
// position at dots, e.g. `$env.config.<TAB>`
|
||||
let mut span = Span::new(self.position + 1, self.position + 1);
|
||||
let mut path_member_num_before_pos = 0;
|
||||
for member in self.full_cell_path.tail.iter() {
|
||||
if member.span().end <= self.position {
|
||||
path_member_num_before_pos += 1;
|
||||
} else if member.span().contains(self.position) {
|
||||
(prefix_str, span) = prefix_from_path_member(member, self.position);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let current_span = reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
};
|
||||
|
||||
let mut matcher = NuMatcher::new(prefix_str, options);
|
||||
let path_members = self
|
||||
.full_cell_path
|
||||
.tail
|
||||
.get(0..path_member_num_before_pos)
|
||||
.unwrap_or_default();
|
||||
let value = eval_cell_path(
|
||||
working_set,
|
||||
stack,
|
||||
&self.full_cell_path.head,
|
||||
path_members,
|
||||
span,
|
||||
)
|
||||
.unwrap_or_default();
|
||||
|
||||
for suggestion in get_suggestions_by_value(&value, current_span) {
|
||||
matcher.add_semantic_suggestion(suggestion);
|
||||
}
|
||||
matcher.results()
|
||||
}
|
||||
}
|
||||
|
||||
/// Follow cell path to get the value
|
||||
/// NOTE: This is a relatively lightweight implementation,
|
||||
/// so it may fail to get the exact value when the expression is complicated.
|
||||
/// One failing example would be `[$foo].0`
|
||||
pub(crate) fn eval_cell_path(
|
||||
working_set: &StateWorkingSet,
|
||||
stack: &Stack,
|
||||
head: &Expression,
|
||||
path_members: &[PathMember],
|
||||
span: Span,
|
||||
) -> Result<Value, ShellError> {
|
||||
// evaluate the head expression to get its value
|
||||
let head_value = if let Expr::Var(var_id) = head.expr {
|
||||
working_set
|
||||
.get_variable(var_id)
|
||||
.const_val
|
||||
.to_owned()
|
||||
.map_or_else(
|
||||
|| eval_variable(working_set.permanent_state, stack, var_id, span),
|
||||
Ok,
|
||||
)
|
||||
} else {
|
||||
eval_constant(working_set, head)
|
||||
}?;
|
||||
head_value
|
||||
.follow_cell_path(path_members, false)
|
||||
.map(Cow::into_owned)
|
||||
}
|
||||
|
||||
fn get_suggestions_by_value(
|
||||
value: &Value,
|
||||
current_span: reedline::Span,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let to_suggestion = |s: String, v: Option<&Value>| {
|
||||
// Check if the string needs quoting
|
||||
let value = if s.is_empty()
|
||||
|| s.chars()
|
||||
.any(|c: char| !(c.is_ascii_alphabetic() || ['_', '-'].contains(&c)))
|
||||
{
|
||||
format!("{:?}", s)
|
||||
} else {
|
||||
s
|
||||
};
|
||||
|
||||
SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value,
|
||||
span: current_span,
|
||||
description: v.map(|v| v.get_type().to_string()),
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::CellPath),
|
||||
}
|
||||
};
|
||||
match value {
|
||||
Value::Record { val, .. } => val
|
||||
.columns()
|
||||
.map(|s| to_suggestion(s.to_string(), val.get(s)))
|
||||
.collect(),
|
||||
Value::List { vals, .. } => get_columns(vals.as_slice())
|
||||
.into_iter()
|
||||
.map(|s| {
|
||||
let sub_val = vals
|
||||
.first()
|
||||
.and_then(|v| v.as_record().ok())
|
||||
.and_then(|rv| rv.get(&s));
|
||||
to_suggestion(s, sub_val)
|
||||
})
|
||||
.collect(),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
@ -4,8 +4,9 @@ use crate::{
|
||||
completions::{Completer, CompletionOptions},
|
||||
SuggestionKind,
|
||||
};
|
||||
use nu_parser::FlatShape;
|
||||
use nu_protocol::{
|
||||
engine::{CommandType, Stack, StateWorkingSet},
|
||||
engine::{CachedFile, Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
@ -13,13 +14,24 @@ use reedline::Suggestion;
|
||||
use super::{completion_options::NuMatcher, SemanticSuggestion};
|
||||
|
||||
pub struct CommandCompletion {
|
||||
/// Whether to include internal commands
|
||||
pub internals: bool,
|
||||
/// Whether to include external commands
|
||||
pub externals: bool,
|
||||
flattened: Vec<(Span, FlatShape)>,
|
||||
flat_shape: FlatShape,
|
||||
force_completion_after_space: bool,
|
||||
}
|
||||
|
||||
impl CommandCompletion {
|
||||
pub fn new(
|
||||
flattened: Vec<(Span, FlatShape)>,
|
||||
flat_shape: FlatShape,
|
||||
force_completion_after_space: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
flattened,
|
||||
flat_shape,
|
||||
force_completion_after_space,
|
||||
}
|
||||
}
|
||||
|
||||
fn external_command_completion(
|
||||
&self,
|
||||
working_set: &StateWorkingSet,
|
||||
@ -59,9 +71,6 @@ impl CommandCompletion {
|
||||
if suggs.contains_key(&value) {
|
||||
continue;
|
||||
}
|
||||
// TODO: check name matching before a relative heavy IO involved
|
||||
// `is_executable` for performance consideration, should avoid
|
||||
// duplicated `match_aux` call for matched items in the future
|
||||
if matcher.matches(&name) && is_executable::is_executable(item.path()) {
|
||||
// If there's an internal command with the same name, adds ^cmd to the
|
||||
// matcher so that both the internal and external command are included
|
||||
@ -75,10 +84,8 @@ impl CommandCompletion {
|
||||
append_whitespace: true,
|
||||
..Default::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Command(
|
||||
CommandType::External,
|
||||
None,
|
||||
)),
|
||||
// TODO: is there a way to create a test?
|
||||
kind: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -90,50 +97,46 @@ impl CommandCompletion {
|
||||
|
||||
suggs
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for CommandCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
fn complete_commands(
|
||||
&self,
|
||||
working_set: &StateWorkingSet,
|
||||
_stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
span: Span,
|
||||
offset: usize,
|
||||
find_externals: bool,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let mut matcher = NuMatcher::new(prefix, options);
|
||||
let partial = working_set.get_span_contents(span);
|
||||
let mut matcher = NuMatcher::new(String::from_utf8_lossy(partial), options.clone());
|
||||
|
||||
let sugg_span = reedline::Span::new(span.start - offset, span.end - offset);
|
||||
|
||||
let mut internal_suggs = HashMap::new();
|
||||
if self.internals {
|
||||
let filtered_commands = working_set.find_commands_by_predicate(
|
||||
|name| {
|
||||
let name = String::from_utf8_lossy(name);
|
||||
matcher.add(&name, name.to_string())
|
||||
},
|
||||
true,
|
||||
);
|
||||
for (decl_id, name, description, typ) in filtered_commands {
|
||||
let name = String::from_utf8_lossy(&name);
|
||||
internal_suggs.insert(
|
||||
name.to_string(),
|
||||
SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: name.to_string(),
|
||||
description,
|
||||
span: sugg_span,
|
||||
append_whitespace: true,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Command(typ, Some(decl_id))),
|
||||
let filtered_commands = working_set.find_commands_by_predicate(
|
||||
|name| {
|
||||
let name = String::from_utf8_lossy(name);
|
||||
matcher.add(&name, name.to_string())
|
||||
},
|
||||
true,
|
||||
);
|
||||
for (name, description, typ) in filtered_commands {
|
||||
let name = String::from_utf8_lossy(&name);
|
||||
internal_suggs.insert(
|
||||
name.to_string(),
|
||||
SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: name.to_string(),
|
||||
description,
|
||||
span: sugg_span,
|
||||
append_whitespace: true,
|
||||
..Suggestion::default()
|
||||
},
|
||||
);
|
||||
}
|
||||
kind: Some(SuggestionKind::Command(typ)),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let mut external_suggs = if self.externals {
|
||||
let mut external_suggs = if find_externals {
|
||||
self.external_command_completion(
|
||||
working_set,
|
||||
sugg_span,
|
||||
@ -156,3 +159,179 @@ impl Completer for CommandCompletion {
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for CommandCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
_stack: &Stack,
|
||||
_prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let last = self
|
||||
.flattened
|
||||
.iter()
|
||||
.rev()
|
||||
.skip_while(|x| x.0.end > pos)
|
||||
.take_while(|x| {
|
||||
matches!(
|
||||
x.1,
|
||||
FlatShape::InternalCall(_)
|
||||
| FlatShape::External
|
||||
| FlatShape::ExternalArg
|
||||
| FlatShape::Literal
|
||||
| FlatShape::String
|
||||
)
|
||||
})
|
||||
.last();
|
||||
|
||||
// The last item here would be the earliest shape that could possible by part of this subcommand
|
||||
let subcommands = if let Some(last) = last {
|
||||
self.complete_commands(
|
||||
working_set,
|
||||
Span::new(last.0.start, pos),
|
||||
offset,
|
||||
false,
|
||||
options,
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
|
||||
if !subcommands.is_empty() {
|
||||
return subcommands;
|
||||
}
|
||||
|
||||
let config = working_set.get_config();
|
||||
if matches!(self.flat_shape, nu_parser::FlatShape::External)
|
||||
|| matches!(self.flat_shape, nu_parser::FlatShape::InternalCall(_))
|
||||
|| ((span.end - span.start) == 0)
|
||||
|| is_passthrough_command(working_set.delta.get_file_contents())
|
||||
{
|
||||
// we're in a gap or at a command
|
||||
if working_set.get_span_contents(span).is_empty() && !self.force_completion_after_space
|
||||
{
|
||||
return vec![];
|
||||
}
|
||||
self.complete_commands(
|
||||
working_set,
|
||||
span,
|
||||
offset,
|
||||
config.completions.external.enable,
|
||||
options,
|
||||
)
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn find_non_whitespace_index(contents: &[u8], start: usize) -> usize {
|
||||
match contents.get(start..) {
|
||||
Some(contents) => {
|
||||
contents
|
||||
.iter()
|
||||
.take_while(|x| x.is_ascii_whitespace())
|
||||
.count()
|
||||
+ start
|
||||
}
|
||||
None => start,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_passthrough_command(working_set_file_contents: &[CachedFile]) -> bool {
|
||||
for cached_file in working_set_file_contents {
|
||||
let contents = &cached_file.content;
|
||||
let last_pipe_pos_rev = contents.iter().rev().position(|x| x == &b'|');
|
||||
let last_pipe_pos = last_pipe_pos_rev.map(|x| contents.len() - x).unwrap_or(0);
|
||||
|
||||
let cur_pos = find_non_whitespace_index(contents, last_pipe_pos);
|
||||
|
||||
let result = match contents.get(cur_pos..) {
|
||||
Some(contents) => contents.starts_with(b"sudo ") || contents.starts_with(b"doas "),
|
||||
None => false,
|
||||
};
|
||||
if result {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod command_completions_tests {
|
||||
use super::*;
|
||||
use nu_protocol::engine::EngineState;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
fn test_find_non_whitespace_index() {
|
||||
let commands = [
|
||||
(" hello", 4),
|
||||
("sudo ", 0),
|
||||
(" sudo ", 2),
|
||||
(" sudo ", 2),
|
||||
(" hello ", 1),
|
||||
(" hello ", 3),
|
||||
(" hello | sudo ", 4),
|
||||
(" sudo|sudo", 5),
|
||||
("sudo | sudo ", 0),
|
||||
(" hello sud", 1),
|
||||
];
|
||||
for (idx, ele) in commands.iter().enumerate() {
|
||||
let index = find_non_whitespace_index(ele.0.as_bytes(), 0);
|
||||
assert_eq!(index, ele.1, "Failed on index {}", idx);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_last_command_passthrough() {
|
||||
let commands = [
|
||||
(" hello", false),
|
||||
(" sudo ", true),
|
||||
("sudo ", true),
|
||||
(" hello", false),
|
||||
(" sudo", false),
|
||||
(" sudo ", true),
|
||||
(" sudo ", true),
|
||||
(" sudo ", true),
|
||||
(" hello ", false),
|
||||
(" hello | sudo ", true),
|
||||
(" sudo|sudo", false),
|
||||
("sudo | sudo ", true),
|
||||
(" hello sud", false),
|
||||
(" sudo | sud ", false),
|
||||
(" sudo|sudo ", true),
|
||||
(" sudo | sudo ls | sudo ", true),
|
||||
];
|
||||
for (idx, ele) in commands.iter().enumerate() {
|
||||
let input = ele.0.as_bytes();
|
||||
|
||||
let mut engine_state = EngineState::new();
|
||||
engine_state.add_file("test.nu".into(), Arc::new([]));
|
||||
|
||||
let delta = {
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
let _ = working_set.add_file("child.nu".into(), input);
|
||||
working_set.render()
|
||||
};
|
||||
|
||||
let result = engine_state.merge_delta(delta);
|
||||
assert!(
|
||||
result.is_ok(),
|
||||
"Merge delta has failed: {}",
|
||||
result.err().unwrap()
|
||||
);
|
||||
|
||||
let is_passthrough_command = is_passthrough_command(engine_state.get_file_contents());
|
||||
assert_eq!(
|
||||
is_passthrough_command, ele.1,
|
||||
"index for '{}': {}",
|
||||
ele.0, idx
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -22,22 +22,18 @@ pub struct PathBuiltFromString {
|
||||
/// Recursively goes through paths that match a given `partial`.
|
||||
/// built: State struct for a valid matching path built so far.
|
||||
///
|
||||
/// `want_directory`: Whether we want only directories as completion matches.
|
||||
/// Some commands like `cd` can only be run on directories whereas others
|
||||
/// like `ls` can be run on regular files as well.
|
||||
///
|
||||
/// `isdir`: whether the current partial path has a trailing slash.
|
||||
/// Parsing a path string into a pathbuf loses that bit of information.
|
||||
///
|
||||
/// `enable_exact_match`: Whether match algorithm is Prefix and all previous components
|
||||
/// of the path matched a directory exactly.
|
||||
/// want_directory: Whether we want only directories as completion matches.
|
||||
/// Some commands like `cd` can only be run on directories whereas others
|
||||
/// like `ls` can be run on regular files as well.
|
||||
fn complete_rec(
|
||||
partial: &[&str],
|
||||
built_paths: &[PathBuiltFromString],
|
||||
options: &CompletionOptions,
|
||||
want_directory: bool,
|
||||
isdir: bool,
|
||||
enable_exact_match: bool,
|
||||
) -> Vec<PathBuiltFromString> {
|
||||
if let Some((&base, rest)) = partial.split_first() {
|
||||
if base.chars().all(|c| c == '.') && (isdir || !rest.is_empty()) {
|
||||
@ -50,19 +46,12 @@ fn complete_rec(
|
||||
built
|
||||
})
|
||||
.collect();
|
||||
return complete_rec(
|
||||
rest,
|
||||
&built_paths,
|
||||
options,
|
||||
want_directory,
|
||||
isdir,
|
||||
enable_exact_match,
|
||||
);
|
||||
return complete_rec(rest, &built_paths, options, want_directory, isdir);
|
||||
}
|
||||
}
|
||||
|
||||
let prefix = partial.first().unwrap_or(&"");
|
||||
let mut matcher = NuMatcher::new(prefix, options);
|
||||
let mut matcher = NuMatcher::new(prefix, options.clone());
|
||||
|
||||
for built in built_paths {
|
||||
let mut path = built.cwd.clone();
|
||||
@ -76,11 +65,10 @@ fn complete_rec(
|
||||
|
||||
for entry in result.filter_map(|e| e.ok()) {
|
||||
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() && !entry.path().is_symlink();
|
||||
let mut built = built.clone();
|
||||
built.parts.push(entry_name.clone());
|
||||
// Symlinks to directories shouldn't have a trailing slash (#13275)
|
||||
built.isdir = entry_isdir && !entry.path().is_symlink();
|
||||
built.isdir = entry_isdir;
|
||||
|
||||
if !want_directory || entry_isdir {
|
||||
matcher.add(entry_name.clone(), (entry_name, built));
|
||||
@ -97,26 +85,27 @@ fn complete_rec(
|
||||
// Serves as confirmation to ignore longer completions for
|
||||
// components in between.
|
||||
if !rest.is_empty() || isdir {
|
||||
// Don't show longer completions if we have an exact match (#13204, #14794)
|
||||
let exact_match = enable_exact_match
|
||||
&& (if options.case_sensitive {
|
||||
entry_name.eq(base)
|
||||
} else {
|
||||
entry_name.eq_ignore_case(base)
|
||||
});
|
||||
completions.extend(complete_rec(
|
||||
rest,
|
||||
&[built],
|
||||
options,
|
||||
want_directory,
|
||||
isdir,
|
||||
exact_match,
|
||||
));
|
||||
} else {
|
||||
completions.push(built);
|
||||
}
|
||||
|
||||
// For https://github.com/nushell/nushell/issues/13204
|
||||
if isdir && options.match_algorithm == MatchAlgorithm::Prefix {
|
||||
let exact_match = if options.case_sensitive {
|
||||
entry_name.eq(base)
|
||||
} else {
|
||||
entry_name.to_folded_case().eq(&base.to_folded_case())
|
||||
};
|
||||
if exact_match {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
completions.push(built);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@ -150,7 +139,7 @@ impl OriginalCwd {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn surround_remove(partial: &str) -> String {
|
||||
fn surround_remove(partial: &str) -> String {
|
||||
for c in ['`', '"', '\''] {
|
||||
if partial.starts_with(c) {
|
||||
let ret = partial.strip_prefix(c).unwrap_or(partial);
|
||||
@ -168,7 +157,6 @@ pub struct FileSuggestion {
|
||||
pub span: nu_protocol::Span,
|
||||
pub path: String,
|
||||
pub style: Option<Style>,
|
||||
pub is_dir: bool,
|
||||
}
|
||||
|
||||
/// # Parameters
|
||||
@ -209,9 +197,10 @@ pub fn complete_item(
|
||||
let ls_colors = (engine_state.config.completions.use_ls_colors
|
||||
&& engine_state.config.use_ansi_coloring.get(engine_state))
|
||||
.then(|| {
|
||||
let ls_colors_env_str = stack
|
||||
.get_env_var(engine_state, "LS_COLORS")
|
||||
.and_then(|v| env_to_string("LS_COLORS", v, engine_state, stack).ok());
|
||||
let ls_colors_env_str = match stack.get_env_var(engine_state, "LS_COLORS") {
|
||||
Some(v) => env_to_string("LS_COLORS", v, engine_state, stack).ok(),
|
||||
None => None,
|
||||
};
|
||||
get_ls_colors(ls_colors_env_str)
|
||||
});
|
||||
|
||||
@ -265,57 +254,57 @@ pub fn complete_item(
|
||||
options,
|
||||
want_directory,
|
||||
isdir,
|
||||
options.match_algorithm == MatchAlgorithm::Prefix,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|mut p| {
|
||||
if should_collapse_dots {
|
||||
p = collapse_ndots(p);
|
||||
}
|
||||
let is_dir = p.isdir;
|
||||
let path = original_cwd.apply(p, path_separator);
|
||||
let real_path = expand_to_real_path(&path);
|
||||
let metadata = std::fs::symlink_metadata(&real_path).ok();
|
||||
let style = ls_colors.as_ref().map(|lsc| {
|
||||
lsc.style_for_path_with_metadata(&real_path, metadata.as_ref())
|
||||
.map(lscolors::Style::to_nu_ansi_term_style)
|
||||
.unwrap_or_default()
|
||||
lsc.style_for_path_with_metadata(
|
||||
&path,
|
||||
std::fs::symlink_metadata(expand_to_real_path(&path))
|
||||
.ok()
|
||||
.as_ref(),
|
||||
)
|
||||
.map(lscolors::Style::to_nu_ansi_term_style)
|
||||
.unwrap_or_default()
|
||||
});
|
||||
FileSuggestion {
|
||||
span,
|
||||
path: escape_path(path),
|
||||
path: escape_path(path, want_directory),
|
||||
style,
|
||||
is_dir,
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
// Fix files or folders with quotes or hashes
|
||||
pub fn escape_path(path: String) -> String {
|
||||
pub fn escape_path(path: String, dir: bool) -> String {
|
||||
// make glob pattern have the highest priority.
|
||||
if nu_glob::is_glob(path.as_str()) || path.contains('`') {
|
||||
// expand home `~` for https://github.com/nushell/nushell/issues/13905
|
||||
if nu_glob::is_glob(path.as_str()) {
|
||||
let pathbuf = nu_path::expand_tilde(path);
|
||||
let path = pathbuf.to_string_lossy();
|
||||
if path.contains('\'') {
|
||||
// decide to use double quotes
|
||||
// Path as Debug will do the escaping for `"`, `\`
|
||||
format!("{:?}", path)
|
||||
return if path.contains('\'') {
|
||||
// decide to use double quote, also need to escape `"` in path
|
||||
// or else users can't do anything with completed path either.
|
||||
format!("\"{}\"", path.replace('"', r#"\""#))
|
||||
} else {
|
||||
format!("'{path}'")
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let filename_contaminated = !dir && path.contains(['\'', '"', ' ', '#', '(', ')']);
|
||||
let dirname_contaminated = dir && path.contains(['\'', '"', ' ', '#']);
|
||||
let maybe_flag = path.starts_with('-');
|
||||
let maybe_variable = path.starts_with('$');
|
||||
let maybe_number = path.parse::<f64>().is_ok();
|
||||
if filename_contaminated || dirname_contaminated || maybe_flag || maybe_variable || maybe_number
|
||||
{
|
||||
format!("`{path}`")
|
||||
} else {
|
||||
let contaminated =
|
||||
path.contains(['\'', '"', ' ', '#', '(', ')', '{', '}', '[', ']', '|', ';']);
|
||||
let maybe_flag = path.starts_with('-');
|
||||
let maybe_variable = path.starts_with('$');
|
||||
let maybe_number = path.parse::<f64>().is_ok();
|
||||
if contaminated || maybe_flag || maybe_variable || maybe_number {
|
||||
format!("`{path}`")
|
||||
} else {
|
||||
path
|
||||
}
|
||||
path
|
||||
}
|
||||
}
|
||||
|
||||
@ -326,12 +315,12 @@ pub struct AdjustView {
|
||||
}
|
||||
|
||||
pub fn adjust_if_intermediate(
|
||||
prefix: &str,
|
||||
prefix: &[u8],
|
||||
working_set: &StateWorkingSet,
|
||||
mut span: nu_protocol::Span,
|
||||
) -> AdjustView {
|
||||
let span_contents = String::from_utf8_lossy(working_set.get_span_contents(span)).to_string();
|
||||
let mut prefix = prefix.to_string();
|
||||
let mut prefix = String::from_utf8_lossy(prefix).to_string();
|
||||
|
||||
// A difference of 1 because of the cursor's unicode code point in between.
|
||||
// Using .chars().count() because unicode and Windows.
|
||||
|
@ -18,12 +18,6 @@ pub enum MatchAlgorithm {
|
||||
/// "git switch" is matched by "git sw"
|
||||
Prefix,
|
||||
|
||||
/// Only show suggestions which have a substring matching with the given input
|
||||
///
|
||||
/// Example:
|
||||
/// "git checkout" is matched by "checkout"
|
||||
Substring,
|
||||
|
||||
/// Only show suggestions which contain the input chars at any place
|
||||
///
|
||||
/// Example:
|
||||
@ -31,8 +25,8 @@ pub enum MatchAlgorithm {
|
||||
Fuzzy,
|
||||
}
|
||||
|
||||
pub struct NuMatcher<'a, T> {
|
||||
options: &'a CompletionOptions,
|
||||
pub struct NuMatcher<T> {
|
||||
options: CompletionOptions,
|
||||
needle: String,
|
||||
state: State<T>,
|
||||
}
|
||||
@ -42,10 +36,6 @@ enum State<T> {
|
||||
/// Holds (haystack, item)
|
||||
items: Vec<(String, T)>,
|
||||
},
|
||||
Substring {
|
||||
/// Holds (haystack, item)
|
||||
items: Vec<(String, T)>,
|
||||
},
|
||||
Fuzzy {
|
||||
matcher: Matcher,
|
||||
atom: Atom,
|
||||
@ -55,11 +45,11 @@ enum State<T> {
|
||||
}
|
||||
|
||||
/// Filters and sorts suggestions
|
||||
impl<T> NuMatcher<'_, T> {
|
||||
impl<T> NuMatcher<T> {
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `needle` - The text to search for
|
||||
pub fn new(needle: impl AsRef<str>, options: &CompletionOptions) -> NuMatcher<T> {
|
||||
pub fn new(needle: impl AsRef<str>, options: CompletionOptions) -> NuMatcher<T> {
|
||||
let needle = trim_quotes_str(needle.as_ref());
|
||||
match options.match_algorithm {
|
||||
MatchAlgorithm::Prefix => {
|
||||
@ -74,18 +64,6 @@ impl<T> NuMatcher<'_, T> {
|
||||
state: State::Prefix { items: Vec::new() },
|
||||
}
|
||||
}
|
||||
MatchAlgorithm::Substring => {
|
||||
let lowercase_needle = if options.case_sensitive {
|
||||
needle.to_owned()
|
||||
} else {
|
||||
needle.to_folded_case()
|
||||
};
|
||||
NuMatcher {
|
||||
options,
|
||||
needle: lowercase_needle,
|
||||
state: State::Substring { items: Vec::new() },
|
||||
}
|
||||
}
|
||||
MatchAlgorithm::Fuzzy => {
|
||||
let atom = Atom::new(
|
||||
needle,
|
||||
@ -124,21 +102,11 @@ impl<T> NuMatcher<'_, T> {
|
||||
} else {
|
||||
Cow::Owned(haystack.to_folded_case())
|
||||
};
|
||||
let matches = haystack_folded.starts_with(self.needle.as_str());
|
||||
if matches {
|
||||
if let Some(item) = item {
|
||||
items.push((haystack.to_string(), item));
|
||||
}
|
||||
}
|
||||
matches
|
||||
}
|
||||
State::Substring { items } => {
|
||||
let haystack_folded = if self.options.case_sensitive {
|
||||
Cow::Borrowed(haystack)
|
||||
let matches = if self.options.positional {
|
||||
haystack_folded.starts_with(self.needle.as_str())
|
||||
} else {
|
||||
Cow::Owned(haystack.to_folded_case())
|
||||
haystack_folded.contains(self.needle.as_str())
|
||||
};
|
||||
let matches = haystack_folded.contains(self.needle.as_str());
|
||||
if matches {
|
||||
if let Some(item) = item {
|
||||
items.push((haystack.to_string(), item));
|
||||
@ -180,7 +148,7 @@ impl<T> NuMatcher<'_, T> {
|
||||
/// Get all the items that matched (sorted)
|
||||
pub fn results(self) -> Vec<T> {
|
||||
match self.state {
|
||||
State::Prefix { mut items, .. } | State::Substring { mut items, .. } => {
|
||||
State::Prefix { mut items, .. } => {
|
||||
items.sort_by(|(haystack1, _), (haystack2, _)| {
|
||||
let cmp_sensitive = haystack1.cmp(haystack2);
|
||||
if self.options.case_sensitive {
|
||||
@ -216,7 +184,7 @@ impl<T> NuMatcher<'_, T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl NuMatcher<'_, SemanticSuggestion> {
|
||||
impl NuMatcher<SemanticSuggestion> {
|
||||
pub fn add_semantic_suggestion(&mut self, sugg: SemanticSuggestion) -> bool {
|
||||
let value = sugg.suggestion.value.to_string();
|
||||
self.add(value, sugg)
|
||||
@ -227,7 +195,6 @@ impl From<CompletionAlgorithm> for MatchAlgorithm {
|
||||
fn from(value: CompletionAlgorithm) -> Self {
|
||||
match value {
|
||||
CompletionAlgorithm::Prefix => MatchAlgorithm::Prefix,
|
||||
CompletionAlgorithm::Substring => MatchAlgorithm::Substring,
|
||||
CompletionAlgorithm::Fuzzy => MatchAlgorithm::Fuzzy,
|
||||
}
|
||||
}
|
||||
@ -239,7 +206,6 @@ impl TryFrom<String> for MatchAlgorithm {
|
||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||
match value.as_str() {
|
||||
"prefix" => Ok(Self::Prefix),
|
||||
"substring" => Ok(Self::Substring),
|
||||
"fuzzy" => Ok(Self::Fuzzy),
|
||||
_ => Err(InvalidMatchAlgorithm::Unknown),
|
||||
}
|
||||
@ -264,6 +230,7 @@ impl std::error::Error for InvalidMatchAlgorithm {}
|
||||
#[derive(Clone)]
|
||||
pub struct CompletionOptions {
|
||||
pub case_sensitive: bool,
|
||||
pub positional: bool,
|
||||
pub match_algorithm: MatchAlgorithm,
|
||||
pub sort: CompletionSort,
|
||||
}
|
||||
@ -272,6 +239,7 @@ impl Default for CompletionOptions {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
case_sensitive: true,
|
||||
positional: true,
|
||||
match_algorithm: MatchAlgorithm::Prefix,
|
||||
sort: Default::default(),
|
||||
}
|
||||
@ -288,9 +256,6 @@ mod test {
|
||||
#[case(MatchAlgorithm::Prefix, "example text", "", true)]
|
||||
#[case(MatchAlgorithm::Prefix, "example text", "examp", true)]
|
||||
#[case(MatchAlgorithm::Prefix, "example text", "text", false)]
|
||||
#[case(MatchAlgorithm::Substring, "example text", "", true)]
|
||||
#[case(MatchAlgorithm::Substring, "example text", "text", true)]
|
||||
#[case(MatchAlgorithm::Substring, "example text", "mplxt", false)]
|
||||
#[case(MatchAlgorithm::Fuzzy, "example text", "", true)]
|
||||
#[case(MatchAlgorithm::Fuzzy, "example text", "examp", true)]
|
||||
#[case(MatchAlgorithm::Fuzzy, "example text", "ext", true)]
|
||||
@ -306,7 +271,7 @@ mod test {
|
||||
match_algorithm,
|
||||
..Default::default()
|
||||
};
|
||||
let mut matcher = NuMatcher::new(needle, &options);
|
||||
let mut matcher = NuMatcher::new(needle, options);
|
||||
matcher.add(haystack, haystack);
|
||||
if should_match {
|
||||
assert_eq!(vec![haystack], matcher.results());
|
||||
@ -321,7 +286,7 @@ mod test {
|
||||
match_algorithm: MatchAlgorithm::Fuzzy,
|
||||
..Default::default()
|
||||
};
|
||||
let mut matcher = NuMatcher::new("fob", &options);
|
||||
let mut matcher = NuMatcher::new("fob", options);
|
||||
for item in ["foo/bar", "fob", "foo bar"] {
|
||||
matcher.add(item, item);
|
||||
}
|
||||
@ -335,7 +300,7 @@ mod test {
|
||||
match_algorithm: MatchAlgorithm::Fuzzy,
|
||||
..Default::default()
|
||||
};
|
||||
let mut matcher = NuMatcher::new("'love spaces' ", &options);
|
||||
let mut matcher = NuMatcher::new("'love spaces' ", options);
|
||||
for item in [
|
||||
"'i love spaces'",
|
||||
"'i love spaces' so much",
|
||||
|
@ -1,12 +1,11 @@
|
||||
use crate::completions::{
|
||||
completer::map_value_completions, Completer, CompletionOptions, MatchAlgorithm,
|
||||
SemanticSuggestion,
|
||||
completer::map_value_completions, Completer, CompletionOptions, SemanticSuggestion,
|
||||
};
|
||||
use nu_engine::eval_call;
|
||||
use nu_protocol::{
|
||||
ast::{Argument, Call, Expr, Expression},
|
||||
debugger::WithoutDebug,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
engine::{Stack, StateWorkingSet},
|
||||
DeclId, PipelineData, Span, Type, Value,
|
||||
};
|
||||
use std::collections::HashMap;
|
||||
@ -14,18 +13,18 @@ use std::collections::HashMap;
|
||||
use super::completion_options::NuMatcher;
|
||||
|
||||
pub struct CustomCompletion<T: Completer> {
|
||||
stack: Stack,
|
||||
decl_id: DeclId,
|
||||
line: String,
|
||||
line_pos: usize,
|
||||
fallback: T,
|
||||
}
|
||||
|
||||
impl<T: Completer> CustomCompletion<T> {
|
||||
pub fn new(decl_id: DeclId, line: String, line_pos: usize, fallback: T) -> Self {
|
||||
pub fn new(stack: Stack, decl_id: DeclId, line: String, fallback: T) -> Self {
|
||||
Self {
|
||||
stack,
|
||||
decl_id,
|
||||
line,
|
||||
line_pos,
|
||||
fallback,
|
||||
}
|
||||
}
|
||||
@ -36,44 +35,38 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
pos: usize,
|
||||
orig_options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
// Line position
|
||||
let line_pos = pos - offset;
|
||||
|
||||
// Call custom declaration
|
||||
let mut stack_mut = stack.clone();
|
||||
let mut eval = |engine_state: &EngineState| {
|
||||
eval_call::<WithoutDebug>(
|
||||
engine_state,
|
||||
&mut stack_mut,
|
||||
&Call {
|
||||
decl_id: self.decl_id,
|
||||
head: span,
|
||||
arguments: vec![
|
||||
Argument::Positional(Expression::new_unknown(
|
||||
Expr::String(self.line.clone()),
|
||||
Span::unknown(),
|
||||
Type::String,
|
||||
)),
|
||||
Argument::Positional(Expression::new_unknown(
|
||||
Expr::Int(self.line_pos as i64),
|
||||
Span::unknown(),
|
||||
Type::Int,
|
||||
)),
|
||||
],
|
||||
parser_info: HashMap::new(),
|
||||
},
|
||||
PipelineData::empty(),
|
||||
)
|
||||
};
|
||||
let result = if self.decl_id.get() < working_set.permanent_state.num_decls() {
|
||||
eval(working_set.permanent_state)
|
||||
} else {
|
||||
let mut engine_state = working_set.permanent_state.clone();
|
||||
let _ = engine_state.merge_delta(working_set.delta.clone());
|
||||
eval(&engine_state)
|
||||
};
|
||||
let result = eval_call::<WithoutDebug>(
|
||||
working_set.permanent_state,
|
||||
&mut self.stack,
|
||||
&Call {
|
||||
decl_id: self.decl_id,
|
||||
head: span,
|
||||
arguments: vec![
|
||||
Argument::Positional(Expression::new_unknown(
|
||||
Expr::String(self.line.clone()),
|
||||
Span::unknown(),
|
||||
Type::String,
|
||||
)),
|
||||
Argument::Positional(Expression::new_unknown(
|
||||
Expr::Int(line_pos as i64),
|
||||
Span::unknown(),
|
||||
Type::Int,
|
||||
)),
|
||||
],
|
||||
parser_info: HashMap::new(),
|
||||
},
|
||||
PipelineData::empty(),
|
||||
);
|
||||
|
||||
let mut completion_options = orig_options.clone();
|
||||
let mut should_sort = true;
|
||||
@ -103,10 +96,10 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
||||
{
|
||||
completion_options.case_sensitive = case_sensitive;
|
||||
}
|
||||
let positional =
|
||||
options.get("positional").and_then(|val| val.as_bool().ok());
|
||||
if positional.is_some() {
|
||||
log::warn!("Use of the positional option is deprecated. Use the substring match algorithm instead.");
|
||||
if let Some(positional) =
|
||||
options.get("positional").and_then(|val| val.as_bool().ok())
|
||||
{
|
||||
completion_options.positional = positional;
|
||||
}
|
||||
if let Some(algorithm) = options
|
||||
.get("completion_algorithm")
|
||||
@ -114,11 +107,6 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
||||
.and_then(|option| option.try_into().ok())
|
||||
{
|
||||
completion_options.match_algorithm = algorithm;
|
||||
if let Some(false) = positional {
|
||||
if completion_options.match_algorithm == MatchAlgorithm::Prefix {
|
||||
completion_options.match_algorithm = MatchAlgorithm::Substring
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,6 +120,7 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
||||
prefix,
|
||||
span,
|
||||
offset,
|
||||
pos,
|
||||
orig_options,
|
||||
);
|
||||
}
|
||||
@ -149,7 +138,7 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut matcher = NuMatcher::new(prefix, &completion_options);
|
||||
let mut matcher = NuMatcher::new(String::from_utf8_lossy(prefix), completion_options);
|
||||
|
||||
if should_sort {
|
||||
for sugg in suggestions {
|
||||
|
@ -9,22 +9,29 @@ use nu_protocol::{
|
||||
use reedline::Suggestion;
|
||||
use std::path::Path;
|
||||
|
||||
use super::{completion_common::FileSuggestion, SemanticSuggestion, SuggestionKind};
|
||||
use super::{completion_common::FileSuggestion, SemanticSuggestion};
|
||||
|
||||
pub struct DirectoryCompletion;
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DirectoryCompletion {}
|
||||
|
||||
impl DirectoryCompletion {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for DirectoryCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
_pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let AdjustView { prefix, span, .. } =
|
||||
adjust_if_intermediate(prefix.as_ref(), working_set, span);
|
||||
let AdjustView { prefix, span, .. } = adjust_if_intermediate(prefix, working_set, span);
|
||||
|
||||
// Filter only the folders
|
||||
#[allow(deprecated)]
|
||||
@ -47,7 +54,8 @@ impl Completer for DirectoryCompletion {
|
||||
},
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Directory),
|
||||
// TODO????
|
||||
kind: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -1,22 +1,21 @@
|
||||
use crate::completions::{
|
||||
completion_common::{surround_remove, FileSuggestion},
|
||||
completion_options::NuMatcher,
|
||||
file_path_completion, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
};
|
||||
use crate::completions::{file_path_completion, Completer, CompletionOptions};
|
||||
use nu_path::expand_tilde;
|
||||
use nu_protocol::{
|
||||
engine::{Stack, StateWorkingSet, VirtualPath},
|
||||
engine::{Stack, StateWorkingSet},
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
path::{is_separator, PathBuf, MAIN_SEPARATOR_STR},
|
||||
};
|
||||
use std::path::{is_separator, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
|
||||
|
||||
pub struct DotNuCompletion {
|
||||
/// e.g. use std/a<tab>
|
||||
pub std_virtual_path: bool,
|
||||
use super::{SemanticSuggestion, SuggestionKind};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub struct DotNuCompletion {}
|
||||
|
||||
impl DotNuCompletion {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for DotNuCompletion {
|
||||
@ -24,90 +23,62 @@ impl Completer for DotNuCompletion {
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
_pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let prefix_str = prefix.as_ref();
|
||||
let prefix_str = String::from_utf8_lossy(prefix);
|
||||
let start_with_backquote = prefix_str.starts_with('`');
|
||||
let end_with_backquote = prefix_str.ends_with('`');
|
||||
let prefix_str = prefix_str.replace('`', "");
|
||||
// e.g. `./`, `..\`, `/`
|
||||
let not_lib_dirs = prefix_str
|
||||
.chars()
|
||||
.find(|c| *c != '.')
|
||||
.is_some_and(is_separator);
|
||||
let mut search_dirs: Vec<PathBuf> = vec![];
|
||||
|
||||
let (base, partial) = if let Some((parent, remain)) = prefix_str.rsplit_once(is_separator) {
|
||||
// If prefix_str is only a word we want to search in the current dir.
|
||||
// "/xx" should be split to "/" and "xx".
|
||||
if parent.is_empty() {
|
||||
(MAIN_SEPARATOR_STR, remain)
|
||||
} else {
|
||||
(parent, remain)
|
||||
}
|
||||
} else {
|
||||
(".", prefix_str.as_str())
|
||||
};
|
||||
// If prefix_str is only a word we want to search in the current dir
|
||||
let (base, partial) = prefix_str
|
||||
.rsplit_once(is_separator)
|
||||
.unwrap_or((".", &prefix_str));
|
||||
let base_dir = base.replace(is_separator, MAIN_SEPARATOR_STR);
|
||||
|
||||
// Fetch the lib dirs
|
||||
// NOTE: 2 ways to setup `NU_LIB_DIRS`
|
||||
// 1. `const NU_LIB_DIRS = [paths]`, equal to `nu -I paths`
|
||||
// 2. `$env.NU_LIB_DIRS = [paths]`
|
||||
let const_lib_dirs = working_set
|
||||
let lib_dirs: Vec<PathBuf> = working_set
|
||||
.find_variable(b"$NU_LIB_DIRS")
|
||||
.and_then(|vid| working_set.get_variable(vid).const_val.as_ref());
|
||||
let env_lib_dirs = working_set.get_env_var("NU_LIB_DIRS");
|
||||
let lib_dirs: HashSet<PathBuf> = [const_lib_dirs, env_lib_dirs]
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.flat_map(|lib_dirs| {
|
||||
.and_then(|vid| working_set.get_variable(vid).const_val.as_ref())
|
||||
.or(working_set.get_env_var("NU_LIB_DIRS"))
|
||||
.map(|lib_dirs| {
|
||||
lib_dirs
|
||||
.as_list()
|
||||
.into_iter()
|
||||
.flat_map(|it| it.iter().filter_map(|x| x.to_path().ok()))
|
||||
.map(expand_tilde)
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
.unwrap_or_default();
|
||||
|
||||
// Check if the base_dir is a folder
|
||||
// rsplit_once removes the separator
|
||||
let cwd = working_set.permanent_state.cwd(None);
|
||||
if base_dir != "." {
|
||||
let expanded_base_dir = expand_tilde(&base_dir);
|
||||
let is_base_dir_relative = expanded_base_dir.is_relative();
|
||||
// Search in base_dir as well as lib_dirs.
|
||||
// After expanded, base_dir can be a relative path or absolute path.
|
||||
// If relative, we join "current working dir" with it to get subdirectory and add to search_dirs.
|
||||
// If absolute, we add it to search_dirs.
|
||||
// Search in base_dir as well as lib_dirs
|
||||
if let Ok(mut cwd) = cwd {
|
||||
if is_base_dir_relative {
|
||||
cwd.push(&base_dir);
|
||||
search_dirs.push(cwd.into_std_path_buf());
|
||||
} else {
|
||||
search_dirs.push(expanded_base_dir);
|
||||
}
|
||||
}
|
||||
if !not_lib_dirs {
|
||||
search_dirs.extend(lib_dirs.into_iter().map(|mut dir| {
|
||||
dir.push(&base_dir);
|
||||
dir
|
||||
}));
|
||||
cwd.push(&base_dir);
|
||||
search_dirs.push(cwd.into_std_path_buf());
|
||||
}
|
||||
search_dirs.extend(lib_dirs.into_iter().map(|mut dir| {
|
||||
dir.push(&base_dir);
|
||||
dir
|
||||
}));
|
||||
} else {
|
||||
if let Ok(cwd) = cwd {
|
||||
search_dirs.push(cwd.into_std_path_buf());
|
||||
}
|
||||
if !not_lib_dirs {
|
||||
search_dirs.extend(lib_dirs);
|
||||
}
|
||||
search_dirs.extend(lib_dirs);
|
||||
}
|
||||
|
||||
// Fetch the files filtering the ones that ends with .nu
|
||||
// and transform them into suggestions
|
||||
let mut completions = file_path_completion(
|
||||
let completions = file_path_completion(
|
||||
span,
|
||||
partial,
|
||||
&search_dirs
|
||||
@ -118,67 +89,22 @@ impl Completer for DotNuCompletion {
|
||||
working_set.permanent_state,
|
||||
stack,
|
||||
);
|
||||
|
||||
if self.std_virtual_path {
|
||||
let mut matcher = NuMatcher::new(partial, options);
|
||||
let base_dir = surround_remove(&base_dir);
|
||||
if base_dir == "." {
|
||||
let surround_prefix = partial
|
||||
.chars()
|
||||
.take_while(|c| "`'\"".contains(*c))
|
||||
.collect::<String>();
|
||||
for path in ["std", "std-rfc"] {
|
||||
let path = format!("{}{}", surround_prefix, path);
|
||||
matcher.add(
|
||||
path.clone(),
|
||||
FileSuggestion {
|
||||
span,
|
||||
path,
|
||||
style: None,
|
||||
is_dir: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
} else if let Some(VirtualPath::Dir(sub_paths)) =
|
||||
working_set.find_virtual_path(&base_dir)
|
||||
{
|
||||
for sub_vp_id in sub_paths {
|
||||
let (path, sub_vp) = working_set.get_virtual_path(*sub_vp_id);
|
||||
let path = path
|
||||
.strip_prefix(&format!("{}/", base_dir))
|
||||
.unwrap_or(path)
|
||||
.to_string();
|
||||
matcher.add(
|
||||
path.clone(),
|
||||
FileSuggestion {
|
||||
path,
|
||||
span,
|
||||
style: None,
|
||||
is_dir: matches!(sub_vp, VirtualPath::Dir(_)),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
completions.extend(matcher.results());
|
||||
}
|
||||
|
||||
completions
|
||||
.into_iter()
|
||||
// Different base dir, so we list the .nu files or folders
|
||||
.filter(|it| {
|
||||
// for paths with spaces in them
|
||||
let path = it.path.trim_end_matches('`');
|
||||
path.ends_with(".nu") || it.is_dir
|
||||
path.ends_with(".nu") || path.ends_with(SEP)
|
||||
})
|
||||
.map(|x| {
|
||||
let append_whitespace = !x.is_dir && (!start_with_backquote || end_with_backquote);
|
||||
let append_whitespace =
|
||||
x.path.ends_with(".nu") && (!start_with_backquote || end_with_backquote);
|
||||
// Re-calculate the span to replace
|
||||
let mut span_offset = 0;
|
||||
let mut value = x.path.to_string();
|
||||
// Complete only the last path component
|
||||
if base_dir == MAIN_SEPARATOR_STR {
|
||||
span_offset = base_dir.len()
|
||||
} else if base_dir != "." {
|
||||
if base_dir != "." {
|
||||
span_offset = base_dir.len() + 1
|
||||
}
|
||||
// Retain only one '`'
|
||||
|
@ -1,112 +0,0 @@
|
||||
use crate::completions::{
|
||||
completion_common::surround_remove, completion_options::NuMatcher, Completer,
|
||||
CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
};
|
||||
use nu_protocol::{
|
||||
engine::{Stack, StateWorkingSet},
|
||||
ModuleId, Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
pub struct ExportableCompletion<'a> {
|
||||
pub module_id: ModuleId,
|
||||
pub temp_working_set: Option<StateWorkingSet<'a>>,
|
||||
}
|
||||
|
||||
/// If name contains space, wrap it in quotes
|
||||
fn wrapped_name(name: String) -> String {
|
||||
if !name.contains(' ') {
|
||||
return name;
|
||||
}
|
||||
if name.contains('\'') {
|
||||
format!("\"{}\"", name.replace('"', r#"\""#))
|
||||
} else {
|
||||
format!("'{name}'")
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for ExportableCompletion<'_> {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
_stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
span: Span,
|
||||
offset: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let mut matcher = NuMatcher::<()>::new(surround_remove(prefix.as_ref()), options);
|
||||
let mut results = Vec::new();
|
||||
let span = reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
};
|
||||
// TODO: use matcher.add_lazy to lazy evaluate an item if it matches the prefix
|
||||
let mut add_suggestion = |value: String,
|
||||
description: Option<String>,
|
||||
extra: Option<Vec<String>>,
|
||||
kind: SuggestionKind| {
|
||||
results.push(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value,
|
||||
span,
|
||||
description,
|
||||
extra,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(kind),
|
||||
});
|
||||
};
|
||||
|
||||
let working_set = self.temp_working_set.as_ref().unwrap_or(working_set);
|
||||
let module = working_set.get_module(self.module_id);
|
||||
|
||||
for (name, decl_id) in &module.decls {
|
||||
let name = String::from_utf8_lossy(name).to_string();
|
||||
if matcher.matches(&name) {
|
||||
let cmd = working_set.get_decl(*decl_id);
|
||||
add_suggestion(
|
||||
wrapped_name(name),
|
||||
Some(cmd.description().to_string()),
|
||||
None,
|
||||
// `None` here avoids arguments being expanded by snippet edit style for lsp
|
||||
SuggestionKind::Command(cmd.command_type(), None),
|
||||
);
|
||||
}
|
||||
}
|
||||
for (name, module_id) in &module.submodules {
|
||||
let name = String::from_utf8_lossy(name).to_string();
|
||||
if matcher.matches(&name) {
|
||||
let comments = working_set.get_module_comments(*module_id).map(|spans| {
|
||||
spans
|
||||
.iter()
|
||||
.map(|sp| {
|
||||
String::from_utf8_lossy(working_set.get_span_contents(*sp)).into()
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
});
|
||||
add_suggestion(
|
||||
wrapped_name(name),
|
||||
Some("Submodule".into()),
|
||||
comments,
|
||||
SuggestionKind::Module,
|
||||
);
|
||||
}
|
||||
}
|
||||
for (name, var_id) in &module.constants {
|
||||
let name = String::from_utf8_lossy(name).to_string();
|
||||
if matcher.matches(&name) {
|
||||
let var = working_set.get_variable(*var_id);
|
||||
add_suggestion(
|
||||
wrapped_name(name),
|
||||
var.const_val
|
||||
.as_ref()
|
||||
.and_then(|v| v.clone().coerce_into_string().ok()),
|
||||
None,
|
||||
SuggestionKind::Variable,
|
||||
);
|
||||
}
|
||||
}
|
||||
results
|
||||
}
|
||||
}
|
@ -9,25 +9,33 @@ use nu_protocol::{
|
||||
use reedline::Suggestion;
|
||||
use std::path::Path;
|
||||
|
||||
use super::{completion_common::FileSuggestion, SemanticSuggestion, SuggestionKind};
|
||||
use super::{completion_common::FileSuggestion, SemanticSuggestion};
|
||||
|
||||
pub struct FileCompletion;
|
||||
#[derive(Clone, Default)]
|
||||
pub struct FileCompletion {}
|
||||
|
||||
impl FileCompletion {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for FileCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
_pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let AdjustView {
|
||||
prefix,
|
||||
span,
|
||||
readjusted,
|
||||
} = adjust_if_intermediate(prefix.as_ref(), working_set, span);
|
||||
} = adjust_if_intermediate(prefix, working_set, span);
|
||||
|
||||
#[allow(deprecated)]
|
||||
let items: Vec<_> = complete_item(
|
||||
@ -50,11 +58,8 @@ impl Completer for FileCompletion {
|
||||
},
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(if x.is_dir {
|
||||
SuggestionKind::Directory
|
||||
} else {
|
||||
SuggestionKind::File
|
||||
}),
|
||||
// TODO????
|
||||
kind: None,
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -1,15 +1,22 @@
|
||||
use crate::completions::{
|
||||
completion_options::NuMatcher, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
};
|
||||
use crate::completions::{completion_options::NuMatcher, Completer, CompletionOptions};
|
||||
use nu_protocol::{
|
||||
ast::{Expr, Expression},
|
||||
engine::{Stack, StateWorkingSet},
|
||||
DeclId, Span,
|
||||
Span,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
|
||||
use super::SemanticSuggestion;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FlagCompletion {
|
||||
pub decl_id: DeclId,
|
||||
expression: Expression,
|
||||
}
|
||||
|
||||
impl FlagCompletion {
|
||||
pub fn new(expression: Expression) -> Self {
|
||||
Self { expression }
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for FlagCompletion {
|
||||
@ -17,42 +24,69 @@ impl Completer for FlagCompletion {
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
_stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
_pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let mut matcher = NuMatcher::new(prefix, options);
|
||||
let mut add_suggestion = |value: String, description: String| {
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value,
|
||||
description: Some(description),
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
// Check if it's a flag
|
||||
if let Expr::Call(call) = &self.expression.expr {
|
||||
let decl = working_set.get_decl(call.decl_id);
|
||||
let sig = decl.signature();
|
||||
|
||||
let mut matcher = NuMatcher::new(String::from_utf8_lossy(prefix), options.clone());
|
||||
|
||||
for named in &sig.named {
|
||||
let flag_desc = &named.desc;
|
||||
if let Some(short) = named.short {
|
||||
let mut named = vec![0; short.len_utf8()];
|
||||
short.encode_utf8(&mut named);
|
||||
named.insert(0, b'-');
|
||||
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: String::from_utf8_lossy(&named).to_string(),
|
||||
description: Some(flag_desc.to_string()),
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
append_whitespace: true,
|
||||
..Suggestion::default()
|
||||
},
|
||||
// TODO????
|
||||
kind: None,
|
||||
});
|
||||
}
|
||||
|
||||
if named.long.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut named = named.long.as_bytes().to_vec();
|
||||
named.insert(0, b'-');
|
||||
named.insert(0, b'-');
|
||||
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: String::from_utf8_lossy(&named).to_string(),
|
||||
description: Some(flag_desc.to_string()),
|
||||
span: reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
},
|
||||
append_whitespace: true,
|
||||
..Suggestion::default()
|
||||
},
|
||||
append_whitespace: true,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Flag),
|
||||
});
|
||||
};
|
||||
|
||||
let decl = working_set.get_decl(self.decl_id);
|
||||
let sig = decl.signature();
|
||||
for named in &sig.named {
|
||||
if let Some(short) = named.short {
|
||||
let mut name = String::from("-");
|
||||
name.push(short);
|
||||
add_suggestion(name, named.desc.clone());
|
||||
// TODO????
|
||||
kind: None,
|
||||
});
|
||||
}
|
||||
|
||||
if named.long.is_empty() {
|
||||
continue;
|
||||
}
|
||||
add_suggestion(format!("--{}", named.long), named.desc.clone());
|
||||
return matcher.results();
|
||||
}
|
||||
matcher.results()
|
||||
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
mod attribute_completions;
|
||||
mod base;
|
||||
mod cell_path_completions;
|
||||
mod command_completions;
|
||||
mod completer;
|
||||
mod completion_common;
|
||||
@ -8,22 +6,18 @@ mod completion_options;
|
||||
mod custom_completions;
|
||||
mod directory_completions;
|
||||
mod dotnu_completions;
|
||||
mod exportable_completions;
|
||||
mod file_completions;
|
||||
mod flag_completions;
|
||||
mod operator_completions;
|
||||
mod variable_completions;
|
||||
|
||||
pub use attribute_completions::{AttributableCompletion, AttributeCompletion};
|
||||
pub use base::{Completer, SemanticSuggestion, SuggestionKind};
|
||||
pub use cell_path_completions::CellPathCompletion;
|
||||
pub use command_completions::CommandCompletion;
|
||||
pub use completer::NuCompleter;
|
||||
pub use completion_options::{CompletionOptions, MatchAlgorithm};
|
||||
pub use custom_completions::CustomCompletion;
|
||||
pub use directory_completions::DirectoryCompletion;
|
||||
pub use dotnu_completions::DotNuCompletion;
|
||||
pub use exportable_completions::ExportableCompletion;
|
||||
pub use file_completions::{file_path_completion, FileCompletion};
|
||||
pub use flag_completions::FlagCompletion;
|
||||
pub use operator_completions::OperatorCompletion;
|
||||
|
@ -2,276 +2,169 @@ use crate::completions::{
|
||||
completion_options::NuMatcher, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||
};
|
||||
use nu_protocol::{
|
||||
ast::{self, Comparison, Expr, Expression},
|
||||
ast::{Expr, Expression},
|
||||
engine::{Stack, StateWorkingSet},
|
||||
Span, Type, Value, ENV_VARIABLE_ID,
|
||||
Span, Type,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use strum::{EnumMessage, IntoEnumIterator};
|
||||
|
||||
use super::cell_path_completions::eval_cell_path;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OperatorCompletion<'a> {
|
||||
pub left_hand_side: &'a Expression,
|
||||
pub struct OperatorCompletion {
|
||||
previous_expr: Expression,
|
||||
}
|
||||
|
||||
struct OperatorItem {
|
||||
pub symbols: String,
|
||||
pub description: String,
|
||||
}
|
||||
|
||||
fn operator_to_item<T: EnumMessage + AsRef<str>>(op: T) -> OperatorItem {
|
||||
OperatorItem {
|
||||
symbols: op.as_ref().into(),
|
||||
description: op.get_message().unwrap_or_default().into(),
|
||||
impl OperatorCompletion {
|
||||
pub fn new(previous_expr: Expression) -> Self {
|
||||
OperatorCompletion { previous_expr }
|
||||
}
|
||||
}
|
||||
|
||||
fn common_comparison_ops() -> Vec<OperatorItem> {
|
||||
vec![
|
||||
operator_to_item(Comparison::In),
|
||||
operator_to_item(Comparison::NotIn),
|
||||
operator_to_item(Comparison::Equal),
|
||||
operator_to_item(Comparison::NotEqual),
|
||||
]
|
||||
}
|
||||
|
||||
fn all_ops_for_immutable() -> Vec<OperatorItem> {
|
||||
ast::Comparison::iter()
|
||||
.map(operator_to_item)
|
||||
.chain(ast::Math::iter().map(operator_to_item))
|
||||
.chain(ast::Boolean::iter().map(operator_to_item))
|
||||
.chain(ast::Bits::iter().map(operator_to_item))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn collection_comparison_ops() -> Vec<OperatorItem> {
|
||||
let mut ops = common_comparison_ops();
|
||||
ops.push(operator_to_item(Comparison::Has));
|
||||
ops.push(operator_to_item(Comparison::NotHas));
|
||||
ops
|
||||
}
|
||||
|
||||
fn number_comparison_ops() -> Vec<OperatorItem> {
|
||||
Comparison::iter()
|
||||
.filter(|op| {
|
||||
!matches!(
|
||||
op,
|
||||
Comparison::RegexMatch
|
||||
| Comparison::NotRegexMatch
|
||||
| Comparison::StartsWith
|
||||
| Comparison::EndsWith
|
||||
| Comparison::Has
|
||||
| Comparison::NotHas
|
||||
)
|
||||
})
|
||||
.map(operator_to_item)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn math_ops() -> Vec<OperatorItem> {
|
||||
ast::Math::iter()
|
||||
.filter(|op| !matches!(op, ast::Math::Concatenate | ast::Math::Pow))
|
||||
.map(operator_to_item)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn bit_ops() -> Vec<OperatorItem> {
|
||||
ast::Bits::iter().map(operator_to_item).collect()
|
||||
}
|
||||
|
||||
fn all_assignment_ops() -> Vec<OperatorItem> {
|
||||
ast::Assignment::iter().map(operator_to_item).collect()
|
||||
}
|
||||
|
||||
fn numeric_assignment_ops() -> Vec<OperatorItem> {
|
||||
ast::Assignment::iter()
|
||||
.filter(|op| !matches!(op, ast::Assignment::ConcatenateAssign))
|
||||
.map(operator_to_item)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn concat_assignment_ops() -> Vec<OperatorItem> {
|
||||
vec![
|
||||
operator_to_item(ast::Assignment::Assign),
|
||||
operator_to_item(ast::Assignment::ConcatenateAssign),
|
||||
]
|
||||
}
|
||||
|
||||
fn valid_int_ops() -> Vec<OperatorItem> {
|
||||
let mut ops = valid_float_ops();
|
||||
ops.extend(bit_ops());
|
||||
ops
|
||||
}
|
||||
|
||||
fn valid_float_ops() -> Vec<OperatorItem> {
|
||||
let mut ops = valid_value_with_unit_ops();
|
||||
ops.push(operator_to_item(ast::Math::Pow));
|
||||
ops
|
||||
}
|
||||
|
||||
fn valid_string_ops() -> Vec<OperatorItem> {
|
||||
let mut ops: Vec<OperatorItem> = Comparison::iter().map(operator_to_item).collect();
|
||||
ops.push(operator_to_item(ast::Math::Concatenate));
|
||||
ops.push(OperatorItem {
|
||||
symbols: "like".into(),
|
||||
description: Comparison::RegexMatch
|
||||
.get_message()
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
});
|
||||
ops.push(OperatorItem {
|
||||
symbols: "not-like".into(),
|
||||
description: Comparison::NotRegexMatch
|
||||
.get_message()
|
||||
.unwrap_or_default()
|
||||
.into(),
|
||||
});
|
||||
ops
|
||||
}
|
||||
|
||||
fn valid_list_ops() -> Vec<OperatorItem> {
|
||||
let mut ops = collection_comparison_ops();
|
||||
ops.push(operator_to_item(ast::Math::Concatenate));
|
||||
ops
|
||||
}
|
||||
|
||||
fn valid_binary_ops() -> Vec<OperatorItem> {
|
||||
let mut ops = number_comparison_ops();
|
||||
ops.extend(bit_ops());
|
||||
ops.push(operator_to_item(ast::Math::Concatenate));
|
||||
ops
|
||||
}
|
||||
|
||||
fn valid_bool_ops() -> Vec<OperatorItem> {
|
||||
let mut ops: Vec<OperatorItem> = ast::Boolean::iter().map(operator_to_item).collect();
|
||||
ops.extend(common_comparison_ops());
|
||||
ops
|
||||
}
|
||||
|
||||
fn valid_value_with_unit_ops() -> Vec<OperatorItem> {
|
||||
let mut ops = number_comparison_ops();
|
||||
ops.extend(math_ops());
|
||||
ops
|
||||
}
|
||||
|
||||
fn ops_by_value(value: &Value, mutable: bool) -> Vec<OperatorItem> {
|
||||
let mut ops = match value {
|
||||
Value::Int { .. } => valid_int_ops(),
|
||||
Value::Float { .. } => valid_float_ops(),
|
||||
Value::String { .. } => valid_string_ops(),
|
||||
Value::Binary { .. } => valid_binary_ops(),
|
||||
Value::Bool { .. } => valid_bool_ops(),
|
||||
Value::Date { .. } => number_comparison_ops(),
|
||||
Value::Filesize { .. } | Value::Duration { .. } => valid_value_with_unit_ops(),
|
||||
Value::Range { .. } | Value::Record { .. } => collection_comparison_ops(),
|
||||
Value::List { .. } => valid_list_ops(),
|
||||
_ => all_ops_for_immutable(),
|
||||
};
|
||||
if mutable {
|
||||
ops.extend(match value {
|
||||
Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. } => numeric_assignment_ops(),
|
||||
Value::String { .. } | Value::Binary { .. } | Value::List { .. } => {
|
||||
concat_assignment_ops()
|
||||
}
|
||||
Value::Bool { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::Record { .. } => vec![operator_to_item(ast::Assignment::Assign)],
|
||||
_ => all_assignment_ops(),
|
||||
})
|
||||
}
|
||||
ops
|
||||
}
|
||||
|
||||
fn is_expression_mutable(expr: &Expr, working_set: &StateWorkingSet) -> bool {
|
||||
let Expr::FullCellPath(path) = expr else {
|
||||
return false;
|
||||
};
|
||||
let Expr::Var(id) = path.head.expr else {
|
||||
return false;
|
||||
};
|
||||
if id == ENV_VARIABLE_ID {
|
||||
return true;
|
||||
}
|
||||
let var = working_set.get_variable(id);
|
||||
var.mutable
|
||||
}
|
||||
|
||||
impl Completer for OperatorCompletion<'_> {
|
||||
impl Completer for OperatorCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
_stack: &Stack,
|
||||
_prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
_pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let mut needs_assignment_ops = true;
|
||||
// Complete according expression type
|
||||
// TODO: type inference on self.left_hand_side to get more accurate completions
|
||||
let mut possible_operations: Vec<OperatorItem> = match &self.left_hand_side.ty {
|
||||
Type::Int | Type::Number => valid_int_ops(),
|
||||
Type::Float => valid_float_ops(),
|
||||
Type::String => valid_string_ops(),
|
||||
Type::Binary => valid_binary_ops(),
|
||||
Type::Bool => valid_bool_ops(),
|
||||
Type::Date => number_comparison_ops(),
|
||||
Type::Filesize | Type::Duration => valid_value_with_unit_ops(),
|
||||
Type::Record(_) | Type::Range => collection_comparison_ops(),
|
||||
Type::List(_) | Type::Table(_) => valid_list_ops(),
|
||||
// Unknown type, resort to evaluated values
|
||||
Type::Any => match &self.left_hand_side.expr {
|
||||
Expr::FullCellPath(path) => {
|
||||
// for `$ <tab>`
|
||||
if matches!(path.head.expr, Expr::Garbage) {
|
||||
return vec![];
|
||||
}
|
||||
let value =
|
||||
eval_cell_path(working_set, stack, &path.head, &path.tail, path.head.span)
|
||||
.unwrap_or_default();
|
||||
let mutable = is_expression_mutable(&self.left_hand_side.expr, working_set);
|
||||
// to avoid duplication
|
||||
needs_assignment_ops = false;
|
||||
ops_by_value(&value, mutable)
|
||||
}
|
||||
_ => all_ops_for_immutable(),
|
||||
},
|
||||
_ => common_comparison_ops(),
|
||||
//Check if int, float, or string
|
||||
let partial = std::str::from_utf8(working_set.get_span_contents(span)).unwrap_or("");
|
||||
let op = match &self.previous_expr.expr {
|
||||
Expr::BinaryOp(x, _, _) => &x.expr,
|
||||
_ => {
|
||||
return vec![];
|
||||
}
|
||||
};
|
||||
let possible_operations = match op {
|
||||
Expr::Int(_) => vec![
|
||||
("+", "Add (Plus)"),
|
||||
("-", "Subtract (Minus)"),
|
||||
("*", "Multiply"),
|
||||
("/", "Divide"),
|
||||
("==", "Equal to"),
|
||||
("!=", "Not equal to"),
|
||||
("//", "Floor division"),
|
||||
("<", "Less than"),
|
||||
(">", "Greater than"),
|
||||
("<=", "Less than or equal to"),
|
||||
(">=", "Greater than or equal to"),
|
||||
("mod", "Floor division remainder (Modulo)"),
|
||||
("**", "Power of"),
|
||||
("bit-or", "Bitwise OR"),
|
||||
("bit-xor", "Bitwise exclusive OR"),
|
||||
("bit-and", "Bitwise AND"),
|
||||
("bit-shl", "Bitwise shift left"),
|
||||
("bit-shr", "Bitwise shift right"),
|
||||
("in", "Is a member of (doesn't use regex)"),
|
||||
("not-in", "Is not a member of (doesn't use regex)"),
|
||||
],
|
||||
Expr::String(_) => vec![
|
||||
("=~", "Contains regex match"),
|
||||
("like", "Contains regex match"),
|
||||
("!~", "Does not contain regex match"),
|
||||
("not-like", "Does not contain regex match"),
|
||||
(
|
||||
"++",
|
||||
"Concatenates two lists, two strings, or two binary values",
|
||||
),
|
||||
("in", "Is a member of (doesn't use regex)"),
|
||||
("not-in", "Is not a member of (doesn't use regex)"),
|
||||
("starts-with", "Starts with"),
|
||||
("ends-with", "Ends with"),
|
||||
],
|
||||
Expr::Float(_) => vec![
|
||||
("+", "Add (Plus)"),
|
||||
("-", "Subtract (Minus)"),
|
||||
("*", "Multiply"),
|
||||
("/", "Divide"),
|
||||
("==", "Equal to"),
|
||||
("!=", "Not equal to"),
|
||||
("//", "Floor division"),
|
||||
("<", "Less than"),
|
||||
(">", "Greater than"),
|
||||
("<=", "Less than or equal to"),
|
||||
(">=", "Greater than or equal to"),
|
||||
("mod", "Floor division remainder (Modulo)"),
|
||||
("**", "Power of"),
|
||||
("in", "Is a member of (doesn't use regex)"),
|
||||
("not-in", "Is not a member of (doesn't use regex)"),
|
||||
],
|
||||
Expr::Bool(_) => vec![
|
||||
(
|
||||
"and",
|
||||
"Both values are true (short-circuits when first value is false)",
|
||||
),
|
||||
(
|
||||
"or",
|
||||
"Either value is true (short-circuits when first value is true)",
|
||||
),
|
||||
("xor", "One value is true and the other is false"),
|
||||
("not", "Negates a value or expression"),
|
||||
("in", "Is a member of (doesn't use regex)"),
|
||||
("not-in", "Is not a member of (doesn't use regex)"),
|
||||
],
|
||||
Expr::FullCellPath(path) => match path.head.expr {
|
||||
Expr::List(_) => vec![
|
||||
(
|
||||
"++",
|
||||
"Concatenates two lists, two strings, or two binary values",
|
||||
),
|
||||
("has", "Contains a value of (doesn't use regex)"),
|
||||
("not-has", "Does not contain a value of (doesn't use regex)"),
|
||||
],
|
||||
Expr::Var(id) => get_variable_completions(id, working_set),
|
||||
_ => vec![],
|
||||
},
|
||||
_ => vec![],
|
||||
};
|
||||
// If the left hand side is a variable, add assignment operators if mutable
|
||||
if needs_assignment_ops && is_expression_mutable(&self.left_hand_side.expr, working_set) {
|
||||
possible_operations.extend(match &self.left_hand_side.ty {
|
||||
Type::Int | Type::Float | Type::Number => numeric_assignment_ops(),
|
||||
Type::Filesize | Type::Duration => numeric_assignment_ops(),
|
||||
Type::String | Type::Binary | Type::List(_) => concat_assignment_ops(),
|
||||
Type::Any => all_assignment_ops(),
|
||||
_ => vec![operator_to_item(ast::Assignment::Assign)],
|
||||
});
|
||||
}
|
||||
|
||||
let mut matcher = NuMatcher::new(prefix, options);
|
||||
for OperatorItem {
|
||||
symbols,
|
||||
description,
|
||||
} in possible_operations
|
||||
{
|
||||
let mut matcher = NuMatcher::new(partial, options.clone());
|
||||
for (symbol, desc) in possible_operations.into_iter() {
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: symbols.to_owned(),
|
||||
description: Some(description.to_owned()),
|
||||
value: symbol.to_string(),
|
||||
description: Some(desc.to_string()),
|
||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
||||
append_whitespace: true,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Operator),
|
||||
kind: Some(SuggestionKind::Command(
|
||||
nu_protocol::engine::CommandType::Builtin,
|
||||
)),
|
||||
});
|
||||
}
|
||||
matcher.results()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_variable_completions<'a>(
|
||||
id: nu_protocol::Id<nu_protocol::marker::Var>,
|
||||
working_set: &StateWorkingSet,
|
||||
) -> Vec<(&'a str, &'a str)> {
|
||||
let var = working_set.get_variable(id);
|
||||
if !var.mutable {
|
||||
return vec![];
|
||||
}
|
||||
|
||||
match var.ty {
|
||||
Type::List(_) | Type::String | Type::Binary => vec![
|
||||
(
|
||||
"++=",
|
||||
"Concatenates two lists, two strings, or two binary values",
|
||||
),
|
||||
("=", "Assigns a value to a variable."),
|
||||
],
|
||||
|
||||
Type::Int | Type::Float => vec![
|
||||
("=", "Assigns a value to a variable."),
|
||||
("+=", "Adds a value to a variable."),
|
||||
("-=", "Subtracts a value from a variable."),
|
||||
("*=", "Multiplies a variable by a value"),
|
||||
("/=", "Divides a variable by a value."),
|
||||
],
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
@ -1,67 +1,157 @@
|
||||
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
|
||||
use nu_engine::{column::get_columns, eval_variable};
|
||||
use nu_protocol::{
|
||||
engine::{Stack, StateWorkingSet},
|
||||
Span, VarId,
|
||||
Span, Value,
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use std::str;
|
||||
|
||||
use super::completion_options::NuMatcher;
|
||||
|
||||
pub struct VariableCompletion;
|
||||
#[derive(Clone)]
|
||||
pub struct VariableCompletion {
|
||||
var_context: (Vec<u8>, Vec<Vec<u8>>), // tuple with $var and the sublevels (.b.c.d)
|
||||
}
|
||||
|
||||
impl VariableCompletion {
|
||||
pub fn new(var_context: (Vec<u8>, Vec<Vec<u8>>)) -> Self {
|
||||
Self { var_context }
|
||||
}
|
||||
}
|
||||
|
||||
impl Completer for VariableCompletion {
|
||||
fn fetch(
|
||||
&mut self,
|
||||
working_set: &StateWorkingSet,
|
||||
_stack: &Stack,
|
||||
prefix: impl AsRef<str>,
|
||||
stack: &Stack,
|
||||
prefix: &[u8],
|
||||
span: Span,
|
||||
offset: usize,
|
||||
_pos: usize,
|
||||
options: &CompletionOptions,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let mut matcher = NuMatcher::new(prefix, options);
|
||||
let builtins = ["$nu", "$in", "$env"];
|
||||
let var_str = std::str::from_utf8(&self.var_context.0).unwrap_or("");
|
||||
let var_id = working_set.find_variable(&self.var_context.0);
|
||||
let current_span = reedline::Span {
|
||||
start: span.start - offset,
|
||||
end: span.end - offset,
|
||||
};
|
||||
let sublevels_count = self.var_context.1.len();
|
||||
let prefix_str = String::from_utf8_lossy(prefix);
|
||||
let mut matcher = NuMatcher::new(prefix_str, options.clone());
|
||||
|
||||
// Completions for the given variable
|
||||
if !var_str.is_empty() {
|
||||
// Completion for $env.<tab>
|
||||
if var_str == "$env" {
|
||||
let env_vars = stack.get_env_vars(working_set.permanent_state);
|
||||
|
||||
// Return nested values
|
||||
if sublevels_count > 0 {
|
||||
// Extract the target var ($env.<target-var>)
|
||||
let target_var = self.var_context.1[0].clone();
|
||||
let target_var_str =
|
||||
str::from_utf8(&target_var).unwrap_or_default().to_string();
|
||||
|
||||
// Everything after the target var is the nested level ($env.<target-var>.<nested_levels>...)
|
||||
let nested_levels: Vec<Vec<u8>> =
|
||||
self.var_context.1.clone().into_iter().skip(1).collect();
|
||||
|
||||
if let Some(val) = env_vars.get(&target_var_str) {
|
||||
for suggestion in nested_suggestions(val, &nested_levels, current_span) {
|
||||
matcher.add_semantic_suggestion(suggestion);
|
||||
}
|
||||
|
||||
return matcher.results();
|
||||
}
|
||||
} else {
|
||||
// No nesting provided, return all env vars
|
||||
for env_var in env_vars {
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: env_var.0,
|
||||
span: current_span,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Type(env_var.1.get_type())),
|
||||
});
|
||||
}
|
||||
|
||||
return matcher.results();
|
||||
}
|
||||
}
|
||||
|
||||
// Completions for $nu.<tab>
|
||||
if var_str == "$nu" {
|
||||
// Eval nu var
|
||||
if let Ok(nuval) = eval_variable(
|
||||
working_set.permanent_state,
|
||||
stack,
|
||||
nu_protocol::NU_VARIABLE_ID,
|
||||
nu_protocol::Span::new(current_span.start, current_span.end),
|
||||
) {
|
||||
for suggestion in nested_suggestions(&nuval, &self.var_context.1, current_span)
|
||||
{
|
||||
matcher.add_semantic_suggestion(suggestion);
|
||||
}
|
||||
|
||||
return matcher.results();
|
||||
}
|
||||
}
|
||||
|
||||
// Completion other variable types
|
||||
if let Some(var_id) = var_id {
|
||||
// Extract the variable value from the stack
|
||||
let var = stack.get_var(var_id, Span::new(span.start, span.end));
|
||||
|
||||
// If the value exists and it's of type Record
|
||||
if let Ok(value) = var {
|
||||
for suggestion in nested_suggestions(&value, &self.var_context.1, current_span)
|
||||
{
|
||||
matcher.add_semantic_suggestion(suggestion);
|
||||
}
|
||||
|
||||
return matcher.results();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Variable completion (e.g: $en<tab> to complete $env)
|
||||
let builtins = ["$nu", "$in", "$env"];
|
||||
for builtin in builtins {
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: builtin.to_string(),
|
||||
span: current_span,
|
||||
description: Some("reserved".into()),
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Variable),
|
||||
// TODO is there a way to get the VarId to get the type???
|
||||
kind: None,
|
||||
});
|
||||
}
|
||||
|
||||
let mut add_candidate = |name, var_id: &VarId| {
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: String::from_utf8_lossy(name).to_string(),
|
||||
span: current_span,
|
||||
description: Some(working_set.get_variable(*var_id).ty.to_string()),
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Variable),
|
||||
})
|
||||
};
|
||||
|
||||
// TODO: The following can be refactored (see find_commands_by_predicate() used in
|
||||
// command_completions).
|
||||
let mut removed_overlays = vec![];
|
||||
// Working set scope vars
|
||||
for scope_frame in working_set.delta.scope.iter().rev() {
|
||||
for overlay_frame in scope_frame.active_overlays(&mut removed_overlays).rev() {
|
||||
for (name, var_id) in &overlay_frame.vars {
|
||||
add_candidate(name, var_id);
|
||||
for v in &overlay_frame.vars {
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: String::from_utf8_lossy(v.0).to_string(),
|
||||
span: current_span,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Type(
|
||||
working_set.get_variable(*v.1).ty.clone(),
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Permanent state vars
|
||||
// for scope in &self.engine_state.scope {
|
||||
for overlay_frame in working_set
|
||||
@ -69,11 +159,98 @@ impl Completer for VariableCompletion {
|
||||
.active_overlays(&removed_overlays)
|
||||
.rev()
|
||||
{
|
||||
for (name, var_id) in &overlay_frame.vars {
|
||||
add_candidate(name, var_id);
|
||||
for v in &overlay_frame.vars {
|
||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: String::from_utf8_lossy(v.0).to_string(),
|
||||
span: current_span,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(SuggestionKind::Type(
|
||||
working_set.get_variable(*v.1).ty.clone(),
|
||||
)),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
matcher.results()
|
||||
}
|
||||
}
|
||||
|
||||
// Find recursively the values for sublevels
|
||||
// if no sublevels are set it returns the current value
|
||||
fn nested_suggestions(
|
||||
val: &Value,
|
||||
sublevels: &[Vec<u8>],
|
||||
current_span: reedline::Span,
|
||||
) -> Vec<SemanticSuggestion> {
|
||||
let mut output: Vec<SemanticSuggestion> = vec![];
|
||||
let value = recursive_value(val, sublevels).unwrap_or_else(Value::nothing);
|
||||
|
||||
let kind = SuggestionKind::Type(value.get_type());
|
||||
match value {
|
||||
Value::Record { val, .. } => {
|
||||
// Add all the columns as completion
|
||||
for col in val.columns() {
|
||||
output.push(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: col.clone(),
|
||||
span: current_span,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(kind.clone()),
|
||||
});
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
Value::List { vals, .. } => {
|
||||
for column_name in get_columns(vals.as_slice()) {
|
||||
output.push(SemanticSuggestion {
|
||||
suggestion: Suggestion {
|
||||
value: column_name,
|
||||
span: current_span,
|
||||
..Suggestion::default()
|
||||
},
|
||||
kind: Some(kind.clone()),
|
||||
});
|
||||
}
|
||||
|
||||
output
|
||||
}
|
||||
_ => output,
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts the recursive value (e.g: $var.a.b.c)
|
||||
fn recursive_value(val: &Value, sublevels: &[Vec<u8>]) -> Result<Value, Span> {
|
||||
// Go to next sublevel
|
||||
if let Some((sublevel, next_sublevels)) = sublevels.split_first() {
|
||||
let span = val.span();
|
||||
match val {
|
||||
Value::Record { val, .. } => {
|
||||
if let Some((_, value)) = val.iter().find(|(key, _)| key.as_bytes() == sublevel) {
|
||||
// If matches try to fetch recursively the next
|
||||
recursive_value(value, next_sublevels)
|
||||
} else {
|
||||
// Current sublevel value not found
|
||||
Err(span)
|
||||
}
|
||||
}
|
||||
Value::List { vals, .. } => {
|
||||
for col in get_columns(vals.as_slice()) {
|
||||
if col.as_bytes() == *sublevel {
|
||||
let val = val.get_data_by_key(&col).ok_or(span)?;
|
||||
return recursive_value(&val, next_sublevels);
|
||||
}
|
||||
}
|
||||
|
||||
// Current sublevel value not found
|
||||
Err(span)
|
||||
}
|
||||
_ => Ok(val.clone()),
|
||||
}
|
||||
} else {
|
||||
Ok(val.clone())
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
||||
debugger::WithoutDebug,
|
||||
engine::{EngineState, Stack, StateWorkingSet},
|
||||
report_parse_error, report_parse_warning,
|
||||
shell_error::io::*,
|
||||
shell_error::io::IoError,
|
||||
PipelineData, ShellError, Span, Value,
|
||||
};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
@ -27,11 +27,11 @@ pub fn evaluate_file(
|
||||
let cwd = engine_state.cwd_as_string(Some(stack))?;
|
||||
|
||||
let file_path = canonicalize_with(&path, cwd).map_err(|err| {
|
||||
IoError::new_internal_with_path(
|
||||
err.kind().not_found_as(NotFound::File),
|
||||
"Could not access file",
|
||||
nu_protocol::location!(),
|
||||
IoError::new_with_additional_context(
|
||||
err.kind(),
|
||||
Span::unknown(),
|
||||
PathBuf::from(&path),
|
||||
"Could not access file",
|
||||
)
|
||||
})?;
|
||||
|
||||
@ -46,21 +46,21 @@ pub fn evaluate_file(
|
||||
})?;
|
||||
|
||||
let file = std::fs::read(&file_path).map_err(|err| {
|
||||
IoError::new_internal_with_path(
|
||||
err.kind().not_found_as(NotFound::File),
|
||||
"Could not read file",
|
||||
nu_protocol::location!(),
|
||||
IoError::new_with_additional_context(
|
||||
err.kind(),
|
||||
Span::unknown(),
|
||||
file_path.clone(),
|
||||
"Could not read file",
|
||||
)
|
||||
})?;
|
||||
engine_state.file = Some(file_path.clone());
|
||||
|
||||
let parent = file_path.parent().ok_or_else(|| {
|
||||
IoError::new_internal_with_path(
|
||||
ErrorKind::DirectoryNotFound,
|
||||
"The file path does not have a parent",
|
||||
nu_protocol::location!(),
|
||||
IoError::new_with_additional_context(
|
||||
std::io::ErrorKind::NotFound,
|
||||
Span::unknown(),
|
||||
file_path.clone(),
|
||||
"The file path does not have a parent",
|
||||
)
|
||||
})?;
|
||||
|
||||
|
@ -740,15 +740,9 @@ fn add_keybinding(
|
||||
let span = mode.span();
|
||||
match &mode {
|
||||
Value::String { val, .. } => match val.as_str() {
|
||||
str if str.eq_ignore_ascii_case("emacs") => {
|
||||
add_parsed_keybinding(emacs_keybindings, keybinding, config)
|
||||
}
|
||||
str if str.eq_ignore_ascii_case("vi_insert") => {
|
||||
add_parsed_keybinding(insert_keybindings, keybinding, config)
|
||||
}
|
||||
str if str.eq_ignore_ascii_case("vi_normal") => {
|
||||
add_parsed_keybinding(normal_keybindings, keybinding, config)
|
||||
}
|
||||
"emacs" => add_parsed_keybinding(emacs_keybindings, keybinding, config),
|
||||
"vi_insert" => add_parsed_keybinding(insert_keybindings, keybinding, config),
|
||||
"vi_normal" => add_parsed_keybinding(normal_keybindings, keybinding, config),
|
||||
str => Err(ShellError::InvalidValue {
|
||||
valid: "'emacs', 'vi_insert', or 'vi_normal'".into(),
|
||||
actual: format!("'{str}'"),
|
||||
@ -998,54 +992,41 @@ fn event_from_record(
|
||||
) -> Result<ReedlineEvent, ShellError> {
|
||||
let event = match name {
|
||||
"none" => ReedlineEvent::None,
|
||||
"clearscreen" => ReedlineEvent::ClearScreen,
|
||||
"clearscrollback" => ReedlineEvent::ClearScrollback,
|
||||
"historyhintcomplete" => ReedlineEvent::HistoryHintComplete,
|
||||
"historyhintwordcomplete" => ReedlineEvent::HistoryHintWordComplete,
|
||||
"ctrld" => ReedlineEvent::CtrlD,
|
||||
"ctrlc" => ReedlineEvent::CtrlC,
|
||||
"clearscreen" => ReedlineEvent::ClearScreen,
|
||||
"clearscrollback" => ReedlineEvent::ClearScrollback,
|
||||
"enter" => ReedlineEvent::Enter,
|
||||
"submit" => ReedlineEvent::Submit,
|
||||
"submitornewline" => ReedlineEvent::SubmitOrNewline,
|
||||
"esc" | "escape" => ReedlineEvent::Esc,
|
||||
// Non-sensical for user configuration:
|
||||
//
|
||||
// `ReedlineEvent::Mouse` - itself a no-op
|
||||
// `ReedlineEvent::Resize` - requires size info specifically from the ANSI resize
|
||||
// event
|
||||
//
|
||||
// Handled above in `parse_event`:
|
||||
//
|
||||
// `ReedlineEvent::Edit`
|
||||
"repaint" => ReedlineEvent::Repaint,
|
||||
"previoushistory" => ReedlineEvent::PreviousHistory,
|
||||
"up" => ReedlineEvent::Up,
|
||||
"down" => ReedlineEvent::Down,
|
||||
"right" => ReedlineEvent::Right,
|
||||
"left" => ReedlineEvent::Left,
|
||||
"nexthistory" => ReedlineEvent::NextHistory,
|
||||
"searchhistory" => ReedlineEvent::SearchHistory,
|
||||
// Handled above in `parse_event`:
|
||||
//
|
||||
// `ReedlineEvent::Multiple`
|
||||
// `ReedlineEvent::UntilFound`
|
||||
"nexthistory" => ReedlineEvent::NextHistory,
|
||||
"previoushistory" => ReedlineEvent::PreviousHistory,
|
||||
"repaint" => ReedlineEvent::Repaint,
|
||||
"menudown" => ReedlineEvent::MenuDown,
|
||||
"menuup" => ReedlineEvent::MenuUp,
|
||||
"menuleft" => ReedlineEvent::MenuLeft,
|
||||
"menuright" => ReedlineEvent::MenuRight,
|
||||
"menunext" => ReedlineEvent::MenuNext,
|
||||
"menuprevious" => ReedlineEvent::MenuPrevious,
|
||||
"menupagenext" => ReedlineEvent::MenuPageNext,
|
||||
"menupageprevious" => ReedlineEvent::MenuPagePrevious,
|
||||
"openeditor" => ReedlineEvent::OpenEditor,
|
||||
"menu" => {
|
||||
let menu = extract_value("name", record, span)?;
|
||||
ReedlineEvent::Menu(menu.to_expanded_string("", config))
|
||||
}
|
||||
"menunext" => ReedlineEvent::MenuNext,
|
||||
"menuprevious" => ReedlineEvent::MenuPrevious,
|
||||
"menuup" => ReedlineEvent::MenuUp,
|
||||
"menudown" => ReedlineEvent::MenuDown,
|
||||
"menuleft" => ReedlineEvent::MenuLeft,
|
||||
"menuright" => ReedlineEvent::MenuRight,
|
||||
"menupagenext" => ReedlineEvent::MenuPageNext,
|
||||
"menupageprevious" => ReedlineEvent::MenuPagePrevious,
|
||||
"executehostcommand" => {
|
||||
let cmd = extract_value("cmd", record, span)?;
|
||||
ReedlineEvent::ExecuteHostCommand(cmd.to_expanded_string("", config))
|
||||
}
|
||||
"openeditor" => ReedlineEvent::OpenEditor,
|
||||
str => {
|
||||
return Err(ShellError::InvalidValue {
|
||||
valid: "a reedline event".into(),
|
||||
@ -1075,6 +1056,7 @@ fn edit_from_record(
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(false),
|
||||
},
|
||||
|
||||
"movetoend" => EditCommand::MoveToEnd {
|
||||
select: extract_value("select", record, span)
|
||||
.and_then(|value| value.as_bool())
|
||||
@ -1110,16 +1092,6 @@ fn edit_from_record(
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(false),
|
||||
},
|
||||
"movewordrightstart" => EditCommand::MoveWordRightStart {
|
||||
select: extract_value("select", record, span)
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(false),
|
||||
},
|
||||
"movebigwordrightstart" => EditCommand::MoveBigWordRightStart {
|
||||
select: extract_value("select", record, span)
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(false),
|
||||
},
|
||||
"movewordrightend" => EditCommand::MoveWordRightEnd {
|
||||
select: extract_value("select", record, span)
|
||||
.and_then(|value| value.as_bool())
|
||||
@ -1130,6 +1102,16 @@ fn edit_from_record(
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(false),
|
||||
},
|
||||
"movewordrightstart" => EditCommand::MoveWordRightStart {
|
||||
select: extract_value("select", record, span)
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(false),
|
||||
},
|
||||
"movebigwordrightstart" => EditCommand::MoveBigWordRightStart {
|
||||
select: extract_value("select", record, span)
|
||||
.and_then(|value| value.as_bool())
|
||||
.unwrap_or(false),
|
||||
},
|
||||
"movetoposition" => {
|
||||
let value = extract_value("value", record, span)?;
|
||||
let select = extract_value("select", record, span)
|
||||
@ -1151,13 +1133,6 @@ fn edit_from_record(
|
||||
EditCommand::InsertString(value.to_expanded_string("", config))
|
||||
}
|
||||
"insertnewline" => EditCommand::InsertNewline,
|
||||
"replacechar" => {
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value)?;
|
||||
EditCommand::ReplaceChar(char)
|
||||
}
|
||||
// `EditCommand::ReplaceChars` - Internal hack not sanely implementable as a
|
||||
// standalone binding
|
||||
"backspace" => EditCommand::Backspace,
|
||||
"delete" => EditCommand::Delete,
|
||||
"cutchar" => EditCommand::CutChar,
|
||||
@ -1165,7 +1140,6 @@ fn edit_from_record(
|
||||
"deleteword" => EditCommand::DeleteWord,
|
||||
"clear" => EditCommand::Clear,
|
||||
"cleartolineend" => EditCommand::ClearToLineEnd,
|
||||
"complete" => EditCommand::Complete,
|
||||
"cutcurrentline" => EditCommand::CutCurrentLine,
|
||||
"cutfromstart" => EditCommand::CutFromStart,
|
||||
"cutfromlinestart" => EditCommand::CutFromLineStart,
|
||||
@ -1182,7 +1156,6 @@ fn edit_from_record(
|
||||
"uppercaseword" => EditCommand::UppercaseWord,
|
||||
"lowercaseword" => EditCommand::LowercaseWord,
|
||||
"capitalizechar" => EditCommand::CapitalizeChar,
|
||||
"switchcasechar" => EditCommand::SwitchcaseChar,
|
||||
"swapwords" => EditCommand::SwapWords,
|
||||
"swapgraphemes" => EditCommand::SwapGraphemes,
|
||||
"undo" => EditCommand::Undo,
|
||||
@ -1239,64 +1212,17 @@ fn edit_from_record(
|
||||
.unwrap_or(false);
|
||||
EditCommand::MoveLeftBefore { c: char, select }
|
||||
}
|
||||
"selectall" => EditCommand::SelectAll,
|
||||
"complete" => EditCommand::Complete,
|
||||
"cutselection" => EditCommand::CutSelection,
|
||||
"copyselection" => EditCommand::CopySelection,
|
||||
"paste" => EditCommand::Paste,
|
||||
"copyfromstart" => EditCommand::CopyFromStart,
|
||||
"copyfromlinestart" => EditCommand::CopyFromLineStart,
|
||||
"copytoend" => EditCommand::CopyToEnd,
|
||||
"copytolineend" => EditCommand::CopyToLineEnd,
|
||||
"copycurrentline" => EditCommand::CopyCurrentLine,
|
||||
"copywordleft" => EditCommand::CopyWordLeft,
|
||||
"copybigwordleft" => EditCommand::CopyBigWordLeft,
|
||||
"copywordright" => EditCommand::CopyWordRight,
|
||||
"copybigwordright" => EditCommand::CopyBigWordRight,
|
||||
"copywordrighttonext" => EditCommand::CopyWordRightToNext,
|
||||
"copybigwordrighttonext" => EditCommand::CopyBigWordRightToNext,
|
||||
"copyleft" => EditCommand::CopyLeft,
|
||||
"copyright" => EditCommand::CopyRight,
|
||||
"copyrightuntil" => {
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value)?;
|
||||
EditCommand::CopyRightUntil(char)
|
||||
}
|
||||
"copyrightbefore" => {
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value)?;
|
||||
EditCommand::CopyRightBefore(char)
|
||||
}
|
||||
"copyleftuntil" => {
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value)?;
|
||||
EditCommand::CopyLeftUntil(char)
|
||||
}
|
||||
"copyleftbefore" => {
|
||||
let value = extract_value("value", record, span)?;
|
||||
let char = extract_char(value)?;
|
||||
EditCommand::CopyLeftBefore(char)
|
||||
}
|
||||
"swapcursorandanchor" => EditCommand::SwapCursorAndAnchor,
|
||||
#[cfg(feature = "system-clipboard")]
|
||||
"cutselectionsystem" => EditCommand::CutSelectionSystem,
|
||||
"copyselection" => EditCommand::CopySelection,
|
||||
#[cfg(feature = "system-clipboard")]
|
||||
"copyselectionsystem" => EditCommand::CopySelectionSystem,
|
||||
"paste" => EditCommand::Paste,
|
||||
#[cfg(feature = "system-clipboard")]
|
||||
"pastesystem" => EditCommand::PasteSystem,
|
||||
"cutinside" => {
|
||||
let value = extract_value("left", record, span)?;
|
||||
let left = extract_char(value)?;
|
||||
let value = extract_value("right", record, span)?;
|
||||
let right = extract_char(value)?;
|
||||
EditCommand::CutInside { left, right }
|
||||
}
|
||||
"yankinside" => {
|
||||
let value = extract_value("left", record, span)?;
|
||||
let left = extract_char(value)?;
|
||||
let value = extract_value("right", record, span)?;
|
||||
let right = extract_char(value)?;
|
||||
EditCommand::YankInside { left, right }
|
||||
}
|
||||
"selectall" => EditCommand::SelectAll,
|
||||
str => {
|
||||
return Err(ShellError::InvalidValue {
|
||||
valid: "a reedline EditCommand".into(),
|
||||
|
@ -20,7 +20,6 @@ use nu_cmd_base::util::get_editor;
|
||||
use nu_color_config::StyleComputer;
|
||||
#[allow(deprecated)]
|
||||
use nu_engine::env_to_strings;
|
||||
use nu_engine::exit::cleanup_exit;
|
||||
use nu_parser::{lex, parse, trim_quotes_str};
|
||||
use nu_protocol::shell_error::io::IoError;
|
||||
use nu_protocol::{
|
||||
@ -37,7 +36,6 @@ use reedline::{
|
||||
CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory,
|
||||
HistorySessionId, Reedline, SqliteBackedHistory, Vi,
|
||||
};
|
||||
use std::sync::atomic::Ordering;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
env::temp_dir,
|
||||
@ -694,11 +692,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||
);
|
||||
|
||||
println!();
|
||||
|
||||
cleanup_exit((), engine_state, 0);
|
||||
|
||||
// if cleanup_exit didn't exit, we should keep running
|
||||
return (true, stack, line_editor);
|
||||
return (false, stack, line_editor);
|
||||
}
|
||||
Err(err) => {
|
||||
let message = err.to_string();
|
||||
@ -864,7 +858,7 @@ fn do_auto_cd(
|
||||
path.to_string_lossy().to_string()
|
||||
};
|
||||
|
||||
if let PermissionResult::PermissionDenied = have_permission(path.clone()) {
|
||||
if let PermissionResult::PermissionDenied(_) = have_permission(path.clone()) {
|
||||
report_shell_error(
|
||||
engine_state,
|
||||
&ShellError::Io(IoError::new_with_additional_context(
|
||||
@ -936,9 +930,6 @@ fn do_run_cmd(
|
||||
trace!("eval source: {}", s);
|
||||
|
||||
let mut cmds = s.split_whitespace();
|
||||
|
||||
let had_warning_before = engine_state.exit_warning_given.load(Ordering::SeqCst);
|
||||
|
||||
if let Some("exit") = cmds.next() {
|
||||
let mut working_set = StateWorkingSet::new(engine_state);
|
||||
let _ = parse(&mut working_set, None, s.as_bytes(), false);
|
||||
@ -947,11 +938,13 @@ fn do_run_cmd(
|
||||
match cmds.next() {
|
||||
Some(s) => {
|
||||
if let Ok(n) = s.parse::<i32>() {
|
||||
return cleanup_exit(line_editor, engine_state, n);
|
||||
drop(line_editor);
|
||||
std::process::exit(n);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
return cleanup_exit(line_editor, engine_state, 0);
|
||||
drop(line_editor);
|
||||
std::process::exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -970,14 +963,6 @@ fn do_run_cmd(
|
||||
false,
|
||||
);
|
||||
|
||||
// if there was a warning before, and we got to this point, it means
|
||||
// the possible call to cleanup_exit did not occur.
|
||||
if had_warning_before && engine_state.is_interactive {
|
||||
engine_state
|
||||
.exit_warning_given
|
||||
.store(false, Ordering::SeqCst);
|
||||
}
|
||||
|
||||
line_editor
|
||||
}
|
||||
|
||||
|
@ -309,7 +309,6 @@ fn find_matching_block_end_in_expr(
|
||||
.unwrap_or(expression.span.start);
|
||||
|
||||
return match &expression.expr {
|
||||
// TODO: Can't these be handled with an `_ => None` branch? Refactor
|
||||
Expr::Bool(_) => None,
|
||||
Expr::Int(_) => None,
|
||||
Expr::Float(_) => None,
|
||||
@ -336,28 +335,6 @@ fn find_matching_block_end_in_expr(
|
||||
Expr::Nothing => None,
|
||||
Expr::Garbage => None,
|
||||
|
||||
Expr::AttributeBlock(ab) => ab
|
||||
.attributes
|
||||
.iter()
|
||||
.find_map(|attr| {
|
||||
find_matching_block_end_in_expr(
|
||||
line,
|
||||
working_set,
|
||||
&attr.expr,
|
||||
global_span_offset,
|
||||
global_cursor_offset,
|
||||
)
|
||||
})
|
||||
.or_else(|| {
|
||||
find_matching_block_end_in_expr(
|
||||
line,
|
||||
working_set,
|
||||
&ab.item,
|
||||
global_span_offset,
|
||||
global_cursor_offset,
|
||||
)
|
||||
}),
|
||||
|
||||
Expr::Table(table) => {
|
||||
if expr_last == global_cursor_offset {
|
||||
// cursor is at table end
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,7 @@ fn create_default_context() -> EngineState {
|
||||
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() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
// Target folder inside assets
|
||||
let dir = fs::fixtures().join("completions");
|
||||
@ -69,26 +69,7 @@ pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
(dir, dir_str, engine_state, stack)
|
||||
}
|
||||
|
||||
/// Adds pseudo PATH env for external completion tests
|
||||
pub fn new_external_engine() -> EngineState {
|
||||
let mut engine = create_default_context();
|
||||
let dir = fs::fixtures().join("external_completions").join("path");
|
||||
let dir_str = dir.to_string_lossy().to_string();
|
||||
let internal_span = nu_protocol::Span::new(0, dir_str.len());
|
||||
engine.add_env_var(
|
||||
"PATH".to_string(),
|
||||
Value::List {
|
||||
vals: vec![Value::String {
|
||||
val: dir_str,
|
||||
internal_span,
|
||||
}],
|
||||
internal_span,
|
||||
},
|
||||
);
|
||||
engine
|
||||
}
|
||||
|
||||
/// 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_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
// Target folder inside assets
|
||||
let dir = fs::fixtures().join("dotnu_completions");
|
||||
@ -105,23 +86,6 @@ pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
// Add $nu
|
||||
engine_state.generate_nu_constant();
|
||||
|
||||
// const $NU_LIB_DIRS
|
||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||
let var_id = working_set.add_variable(
|
||||
b"$NU_LIB_DIRS".into(),
|
||||
Span::unknown(),
|
||||
nu_protocol::Type::List(Box::new(nu_protocol::Type::String)),
|
||||
false,
|
||||
);
|
||||
working_set.set_variable_const_val(
|
||||
var_id,
|
||||
Value::test_list(vec![
|
||||
Value::string(file(dir.join("lib-dir1")), dir_span),
|
||||
Value::string(file(dir.join("lib-dir3")), dir_span),
|
||||
]),
|
||||
);
|
||||
let _ = engine_state.merge_delta(working_set.render());
|
||||
|
||||
// New stack
|
||||
let mut stack = Stack::new();
|
||||
|
||||
@ -131,12 +95,17 @@ pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
"TEST".to_string(),
|
||||
Value::string("NUSHELL".to_string(), dir_span),
|
||||
);
|
||||
|
||||
stack.add_env_var(
|
||||
"NU_LIB_DIRS".into(),
|
||||
Value::test_list(vec![
|
||||
Value::string(file(dir.join("lib-dir2")), dir_span),
|
||||
Value::string(file(dir.join("lib-dir3")), dir_span),
|
||||
]),
|
||||
"NU_LIB_DIRS".to_string(),
|
||||
Value::list(
|
||||
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),
|
||||
],
|
||||
dir_span,
|
||||
),
|
||||
);
|
||||
|
||||
// Merge environment into the permanent state
|
||||
@ -216,8 +185,8 @@ pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||
(dir, dir_str, engine_state, stack)
|
||||
}
|
||||
|
||||
/// match a list of suggestions with the expected values
|
||||
pub fn match_suggestions(expected: &Vec<&str>, suggestions: &Vec<Suggestion>) {
|
||||
// match a list of suggestions with the expected values
|
||||
pub fn match_suggestions(expected: &Vec<String>, suggestions: &Vec<Suggestion>) {
|
||||
let expected_len = expected.len();
|
||||
let suggestions_len = suggestions.len();
|
||||
if expected_len != suggestions_len {
|
||||
@ -228,34 +197,28 @@ pub fn match_suggestions(expected: &Vec<&str>, suggestions: &Vec<Suggestion>) {
|
||||
)
|
||||
}
|
||||
|
||||
let suggestions_str = suggestions
|
||||
let suggestoins_str = suggestions
|
||||
.iter()
|
||||
.map(|it| it.value.as_str())
|
||||
.map(|it| it.value.clone())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(expected, &suggestions_str);
|
||||
assert_eq!(expected, &suggestoins_str);
|
||||
}
|
||||
|
||||
/// match a list of suggestions with the expected values
|
||||
pub fn match_suggestions_by_string(expected: &[String], suggestions: &Vec<Suggestion>) {
|
||||
let expected = expected.iter().map(|it| it.as_str()).collect::<Vec<_>>();
|
||||
match_suggestions(&expected, suggestions);
|
||||
}
|
||||
|
||||
/// append the separator to the converted path
|
||||
// append the separator to the converted path
|
||||
pub fn folder(path: impl Into<PathBuf>) -> String {
|
||||
let mut converted_path = file(path);
|
||||
converted_path.push(MAIN_SEPARATOR);
|
||||
converted_path
|
||||
}
|
||||
|
||||
/// convert a given path to string
|
||||
// convert a given path to string
|
||||
pub fn file(path: impl Into<PathBuf>) -> String {
|
||||
path.into().into_os_string().into_string().unwrap()
|
||||
}
|
||||
|
||||
/// merge_input executes the given input into the engine
|
||||
/// and merges the state
|
||||
// merge_input executes the given input into the engine
|
||||
// and merges the state
|
||||
pub fn merge_input(
|
||||
input: &[u8],
|
||||
engine_state: &mut EngineState,
|
||||
|
@ -1,5 +1,3 @@
|
||||
pub mod completions_helpers;
|
||||
|
||||
pub use completions_helpers::{
|
||||
file, folder, match_suggestions, match_suggestions_by_string, merge_input, new_engine,
|
||||
};
|
||||
pub use completions_helpers::{file, folder, match_suggestions, merge_input, new_engine};
|
||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-base"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
||||
version = "0.104.1"
|
||||
version = "0.102.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -13,10 +13,10 @@ version = "0.104.1"
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.104.1", default-features = false }
|
||||
nu-parser = { path = "../nu-parser", version = "0.104.1" }
|
||||
nu-path = { path = "../nu-path", version = "0.104.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.104.1", default-features = false }
|
||||
nu-engine = { path = "../nu-engine", version = "0.102.0", default-features = false }
|
||||
nu-parser = { path = "../nu-parser", version = "0.102.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.102.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.102.0", default-features = false }
|
||||
|
||||
indexmap = { workspace = true }
|
||||
miette = { workspace = true }
|
||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-extra"
|
||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
||||
version = "0.104.1"
|
||||
version = "0.102.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -16,13 +16,13 @@ bench = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.104.1" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.104.1", default-features = false }
|
||||
nu-json = { version = "0.104.1", path = "../nu-json" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.104.1" }
|
||||
nu-pretty-hex = { version = "0.104.1", path = "../nu-pretty-hex" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.104.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.104.1", default-features = false }
|
||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.102.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.102.0", default-features = false }
|
||||
nu-json = { version = "0.102.0", path = "../nu-json" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.102.0" }
|
||||
nu-pretty-hex = { version = "0.102.0", path = "../nu-pretty-hex" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.102.0", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.102.0", default-features = false }
|
||||
|
||||
# Potential dependencies for extras
|
||||
heck = { workspace = true }
|
||||
@ -37,6 +37,6 @@ itertools = { workspace = true }
|
||||
mime = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.104.1" }
|
||||
nu-command = { path = "../nu-command", version = "0.104.1" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.104.1" }
|
||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.102.0" }
|
||||
nu-command = { path = "../nu-command", version = "0.102.0" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.102.0" }
|
||||
|
@ -26,7 +26,7 @@ impl Command for BitsAnd {
|
||||
.required(
|
||||
"target",
|
||||
SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]),
|
||||
"Right-hand side of the operation.",
|
||||
"right-hand side of the operation",
|
||||
)
|
||||
.named(
|
||||
"endian",
|
||||
|
120
crates/nu-cmd-extra/src/extra/bits/into.rs
Normal file
120
crates/nu-cmd-extra/src/extra/bits/into.rs
Normal file
@ -0,0 +1,120 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
use nu_protocol::{report_parse_warning, ParseWarning};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct BitsInto;
|
||||
|
||||
impl Command for BitsInto {
|
||||
fn name(&self) -> &str {
|
||||
"into bits"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("into bits")
|
||||
.input_output_types(vec![
|
||||
(Type::Binary, Type::String),
|
||||
(Type::Int, Type::String),
|
||||
(Type::Filesize, Type::String),
|
||||
(Type::Duration, Type::String),
|
||||
(Type::String, Type::String),
|
||||
(Type::Bool, Type::String),
|
||||
(Type::table(), Type::table()),
|
||||
(Type::record(), Type::record()),
|
||||
])
|
||||
.allow_variants_without_examples(true) // TODO: supply exhaustive examples
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"for a data structure input, convert data at the given cell paths",
|
||||
)
|
||||
.category(Category::Deprecated)
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Convert value to a binary string."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
report_parse_warning(
|
||||
&StateWorkingSet::new(engine_state),
|
||||
&ParseWarning::DeprecatedWarning {
|
||||
old_command: "into bits".into(),
|
||||
new_suggestion: "use `format bits`".into(),
|
||||
span: head,
|
||||
url: "`help format bits`".into(),
|
||||
},
|
||||
);
|
||||
crate::extra::strings::format::format_bits(engine_state, stack, call, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "convert a binary value into a string, padded to 8 places with 0s",
|
||||
example: "0x[1] | into bits",
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert an int into a string, padded to 8 places with 0s",
|
||||
example: "1 | into bits",
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a filesize value into a string, padded to 8 places with 0s",
|
||||
example: "1b | into bits",
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a duration value into a string, padded to 8 places with 0s",
|
||||
example: "1ns | into bits",
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a boolean value into a string, padded to 8 places with 0s",
|
||||
example: "true | into bits",
|
||||
result: Some(Value::string("00000001",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
description: "convert a string into a raw binary string, padded with 0s to 8 places",
|
||||
example: "'nushell.sh' | into bits",
|
||||
result: Some(Value::string("01101110 01110101 01110011 01101000 01100101 01101100 01101100 00101110 01110011 01101000",
|
||||
Span::test_data(),
|
||||
)),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(BitsInto {})
|
||||
}
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
mod and;
|
||||
mod bits_;
|
||||
mod into;
|
||||
mod not;
|
||||
mod or;
|
||||
mod rotate_left;
|
||||
@ -10,6 +11,7 @@ mod xor;
|
||||
|
||||
pub use and::BitsAnd;
|
||||
pub use bits_::Bits;
|
||||
pub use into::BitsInto;
|
||||
pub use not::BitsNot;
|
||||
pub use or::BitsOr;
|
||||
pub use rotate_left::BitsRol;
|
||||
@ -135,7 +137,7 @@ where
|
||||
(min, max) => (rhs, lhs, max, min),
|
||||
};
|
||||
|
||||
let pad = iter::repeat_n(0, max_len - min_len);
|
||||
let pad = iter::repeat(0).take(max_len - min_len);
|
||||
|
||||
let mut a;
|
||||
let mut b;
|
||||
@ -159,10 +161,9 @@ where
|
||||
}
|
||||
(Value::Binary { .. }, Value::Int { .. }) | (Value::Int { .. }, Value::Binary { .. }) => {
|
||||
Value::error(
|
||||
ShellError::OnlySupportsThisInputType {
|
||||
ShellError::PipelineMismatch {
|
||||
exp_input_type: "input, and argument, to be both int or both binary"
|
||||
.to_string(),
|
||||
wrong_type: "int and binary".to_string(),
|
||||
dst_span: rhs.span(),
|
||||
src_span: span,
|
||||
},
|
||||
|
@ -27,7 +27,7 @@ impl Command for BitsOr {
|
||||
.required(
|
||||
"target",
|
||||
SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]),
|
||||
"Right-hand side of the operation.",
|
||||
"right-hand side of the operation",
|
||||
)
|
||||
.named(
|
||||
"endian",
|
||||
|
@ -37,7 +37,7 @@ impl Command for BitsRol {
|
||||
),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
.required("bits", SyntaxShape::Int, "Number of bits to rotate left.")
|
||||
.required("bits", SyntaxShape::Int, "number of bits to rotate left")
|
||||
.switch(
|
||||
"signed",
|
||||
"always treat input number as a signed number",
|
||||
|
@ -37,7 +37,7 @@ impl Command for BitsRor {
|
||||
),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
.required("bits", SyntaxShape::Int, "Number of bits to rotate right.")
|
||||
.required("bits", SyntaxShape::Int, "number of bits to rotate right")
|
||||
.switch(
|
||||
"signed",
|
||||
"always treat input number as a signed number",
|
||||
|
@ -40,7 +40,7 @@ impl Command for BitsShl {
|
||||
),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
.required("bits", SyntaxShape::Int, "Number of bits to shift left.")
|
||||
.required("bits", SyntaxShape::Int, "number of bits to shift left")
|
||||
.switch(
|
||||
"signed",
|
||||
"always treat input number as a signed number",
|
||||
@ -249,7 +249,7 @@ fn shift_bytes_and_bits_left(data: &[u8], byte_shift: usize, bit_shift: usize) -
|
||||
Last | Only => lhs << bit_shift,
|
||||
_ => (lhs << bit_shift) | (rhs >> (8 - bit_shift)),
|
||||
})
|
||||
.chain(iter::repeat_n(0, byte_shift))
|
||||
.chain(iter::repeat(0).take(byte_shift))
|
||||
.collect::<Vec<u8>>()
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@ impl Command for BitsShr {
|
||||
),
|
||||
])
|
||||
.allow_variants_without_examples(true)
|
||||
.required("bits", SyntaxShape::Int, "Number of bits to shift right.")
|
||||
.required("bits", SyntaxShape::Int, "number of bits to shift right")
|
||||
.switch(
|
||||
"signed",
|
||||
"always treat input number as a signed number",
|
||||
|
@ -27,7 +27,7 @@ impl Command for BitsXor {
|
||||
.required(
|
||||
"target",
|
||||
SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]),
|
||||
"Right-hand side of the operation.",
|
||||
"right-hand side of the operation",
|
||||
)
|
||||
.named(
|
||||
"endian",
|
||||
|
74
crates/nu-cmd-extra/src/extra/conversions/fmt.rs
Normal file
74
crates/nu-cmd-extra/src/extra/conversions/fmt.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::{report_parse_warning, ParseWarning};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Fmt;
|
||||
|
||||
impl Command for Fmt {
|
||||
fn name(&self) -> &str {
|
||||
"fmt"
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Format a number."
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("fmt")
|
||||
.input_output_types(vec![(Type::Number, Type::record())])
|
||||
.category(Category::Deprecated)
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Get a record containing multiple formats for the number 42",
|
||||
example: "42 | fmt",
|
||||
result: Some(Value::test_record(record! {
|
||||
"binary" => Value::test_string("0b101010"),
|
||||
"debug" => Value::test_string("42"),
|
||||
"display" => Value::test_string("42"),
|
||||
"lowerexp" => Value::test_string("4.2e1"),
|
||||
"lowerhex" => Value::test_string("0x2a"),
|
||||
"octal" => Value::test_string("0o52"),
|
||||
"upperexp" => Value::test_string("4.2E1"),
|
||||
"upperhex" => Value::test_string("0x2A"),
|
||||
})),
|
||||
}]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
report_parse_warning(
|
||||
&StateWorkingSet::new(engine_state),
|
||||
&ParseWarning::DeprecatedWarning {
|
||||
old_command: "fmt".into(),
|
||||
new_suggestion: "use `format number`".into(),
|
||||
span: head,
|
||||
url: "`help format number`".into(),
|
||||
},
|
||||
);
|
||||
crate::extra::strings::format::format_number(engine_state, stack, call, input)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(Fmt {})
|
||||
}
|
||||
}
|
3
crates/nu-cmd-extra/src/extra/conversions/mod.rs
Normal file
3
crates/nu-cmd-extra/src/extra/conversions/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod fmt;
|
||||
|
||||
pub(crate) use fmt::Fmt;
|
@ -26,7 +26,7 @@ impl Command for EachWhile {
|
||||
.required(
|
||||
"closure",
|
||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||
"The closure to run.",
|
||||
"the closure to run",
|
||||
)
|
||||
.category(Category::Filters)
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ impl Command for Rotate {
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::String,
|
||||
"The names to give columns once rotated.",
|
||||
"the names to give columns once rotated",
|
||||
)
|
||||
.category(Category::Filters)
|
||||
.allow_variants_without_examples(true)
|
||||
|
@ -16,7 +16,7 @@ impl Command for UpdateCells {
|
||||
.required(
|
||||
"closure",
|
||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||
"The closure to run an update for each cell.",
|
||||
"the closure to run an update for each cell",
|
||||
)
|
||||
.named(
|
||||
"columns",
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathArcCos;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathArcCos {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math arccos"
|
||||
}
|
||||
@ -114,6 +114,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathArcCos {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathArcCosH;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathArcCosH {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math arccosh"
|
||||
}
|
||||
@ -100,6 +100,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathArcCosH {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathArcSin;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathArcSin {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math arcsin"
|
||||
}
|
||||
@ -115,6 +115,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathArcSin {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathArcSinH;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathArcSinH {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math arcsinh"
|
||||
}
|
||||
@ -88,6 +88,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathArcSinH {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathArcTan;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathArcTan {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math arctan"
|
||||
}
|
||||
@ -102,6 +102,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathArcTan {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathArcTanH;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathArcTanH {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math arctanh"
|
||||
}
|
||||
@ -101,6 +101,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathArcTanH {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathCos;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathCos {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math cos"
|
||||
}
|
||||
@ -108,6 +108,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathCos {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathCosH;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathCosH {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math cosh"
|
||||
}
|
||||
@ -88,6 +88,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathCosH {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathExp;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathExp {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math exp"
|
||||
}
|
||||
@ -93,6 +93,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathExp {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathLn;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathLn {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math ln"
|
||||
}
|
||||
@ -100,6 +100,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathLn {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -15,19 +15,19 @@ mod arcsinh;
|
||||
mod arctan;
|
||||
mod arctanh;
|
||||
|
||||
pub use cos::MathCos;
|
||||
pub use cosh::MathCosH;
|
||||
pub use sin::MathSin;
|
||||
pub use sinh::MathSinH;
|
||||
pub use tan::MathTan;
|
||||
pub use tanh::MathTanH;
|
||||
pub use cos::SubCommand as MathCos;
|
||||
pub use cosh::SubCommand as MathCosH;
|
||||
pub use sin::SubCommand as MathSin;
|
||||
pub use sinh::SubCommand as MathSinH;
|
||||
pub use tan::SubCommand as MathTan;
|
||||
pub use tanh::SubCommand as MathTanH;
|
||||
|
||||
pub use exp::MathExp;
|
||||
pub use ln::MathLn;
|
||||
pub use exp::SubCommand as MathExp;
|
||||
pub use ln::SubCommand as MathLn;
|
||||
|
||||
pub use arccos::MathArcCos;
|
||||
pub use arccosh::MathArcCosH;
|
||||
pub use arcsin::MathArcSin;
|
||||
pub use arcsinh::MathArcSinH;
|
||||
pub use arctan::MathArcTan;
|
||||
pub use arctanh::MathArcTanH;
|
||||
pub use arccos::SubCommand as MathArcCos;
|
||||
pub use arccosh::SubCommand as MathArcCosH;
|
||||
pub use arcsin::SubCommand as MathArcSin;
|
||||
pub use arcsinh::SubCommand as MathArcSinH;
|
||||
pub use arctan::SubCommand as MathArcTan;
|
||||
pub use arctanh::SubCommand as MathArcTanH;
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathSin;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathSin {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math sin"
|
||||
}
|
||||
@ -108,6 +108,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathSin {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathSinH;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathSinH {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math sinh"
|
||||
}
|
||||
@ -87,6 +87,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathSinH {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathTan;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathTan {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math tan"
|
||||
}
|
||||
@ -106,6 +106,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathTan {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MathTanH;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for MathTanH {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"math tanh"
|
||||
}
|
||||
@ -86,6 +86,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(MathTanH {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,14 @@
|
||||
mod bits;
|
||||
mod conversions;
|
||||
mod filters;
|
||||
mod formats;
|
||||
mod math;
|
||||
mod platform;
|
||||
mod strings;
|
||||
|
||||
pub use bits::{Bits, BitsAnd, BitsNot, BitsOr, BitsRol, BitsRor, BitsShl, BitsShr, BitsXor};
|
||||
pub use bits::{
|
||||
Bits, BitsAnd, BitsInto, BitsNot, BitsOr, BitsRol, BitsRor, BitsShl, BitsShr, BitsXor,
|
||||
};
|
||||
pub use formats::ToHtml;
|
||||
pub use math::{MathArcCos, MathArcCosH, MathArcSin, MathArcSinH, MathArcTan, MathArcTanH};
|
||||
pub use math::{MathCos, MathCosH, MathSin, MathSinH, MathTan, MathTanH};
|
||||
@ -26,6 +29,8 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
};
|
||||
}
|
||||
|
||||
bind_command!(conversions::Fmt);
|
||||
|
||||
bind_command!(
|
||||
filters::UpdateCells,
|
||||
filters::EachWhile,
|
||||
@ -58,6 +63,7 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
bind_command! {
|
||||
Bits,
|
||||
BitsAnd,
|
||||
BitsInto,
|
||||
BitsNot,
|
||||
BitsOr,
|
||||
BitsRol,
|
||||
|
@ -38,7 +38,7 @@ impl Command for SubCommand {
|
||||
.rest(
|
||||
"cell path",
|
||||
SyntaxShape::CellPath,
|
||||
"For a data structure input, add a gradient to strings at the given cell paths.",
|
||||
"for a data structure input, add a gradient to strings at the given cell paths",
|
||||
)
|
||||
.input_output_types(vec![
|
||||
(Type::String, Type::String),
|
||||
|
@ -40,7 +40,7 @@ impl Command for FormatBits {
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"For a data structure input, convert data at the given cell paths.",
|
||||
"for a data structure input, convert data at the given cell paths",
|
||||
)
|
||||
.category(Category::Conversions)
|
||||
}
|
||||
@ -111,7 +111,8 @@ impl Command for FormatBits {
|
||||
}
|
||||
}
|
||||
|
||||
fn format_bits(
|
||||
// TODO: crate public only during deprecation
|
||||
pub(crate) fn format_bits(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
|
@ -18,7 +18,7 @@ impl Command for FormatPattern {
|
||||
.required(
|
||||
"pattern",
|
||||
SyntaxShape::String,
|
||||
"The pattern to output. e.g.) \"{foo}: {bar}\".",
|
||||
"the pattern to output. e.g.) \"{foo}: {bar}\"",
|
||||
)
|
||||
.allow_variants_without_examples(true)
|
||||
.category(Category::Strings)
|
||||
@ -253,11 +253,12 @@ fn format_record(
|
||||
optional: false,
|
||||
})
|
||||
.collect();
|
||||
|
||||
let expanded_string = data_as_value
|
||||
.follow_cell_path(&path_members, false)?
|
||||
.to_expanded_string(", ", config);
|
||||
output.push_str(expanded_string.as_str())
|
||||
match data_as_value.clone().follow_cell_path(&path_members, false) {
|
||||
Ok(value_at_column) => {
|
||||
output.push_str(value_at_column.to_expanded_string(", ", config).as_str())
|
||||
}
|
||||
Err(se) => return Err(se),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@ mod bits;
|
||||
mod command;
|
||||
mod number;
|
||||
|
||||
pub(crate) use bits::FormatBits;
|
||||
pub(crate) use command::FormatPattern;
|
||||
pub(crate) use number::FormatNumber;
|
||||
// TODO remove `format_bits` visibility after removal of into bits
|
||||
pub(crate) use bits::{format_bits, FormatBits};
|
||||
// TODO remove `format_number` visibility after removal of into bits
|
||||
pub(crate) use number::{format_number, FormatNumber};
|
||||
|
@ -20,7 +20,7 @@ impl Command for FormatNumber {
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["display", "render", "fmt"]
|
||||
vec!["display", "render", "format"]
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -3,9 +3,9 @@ use heck::ToLowerCamelCase;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StrCamelCase;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for StrCamelCase {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str camel-case"
|
||||
}
|
||||
@ -25,7 +25,7 @@ impl Command for StrCamelCase {
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"For a data structure input, convert strings at the given cell paths.",
|
||||
"For a data structure input, convert strings at the given cell paths",
|
||||
)
|
||||
.category(Category::Strings)
|
||||
}
|
||||
@ -91,6 +91,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(StrCamelCase {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ use heck::ToKebabCase;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StrKebabCase;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for StrKebabCase {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str kebab-case"
|
||||
}
|
||||
@ -25,7 +25,7 @@ impl Command for StrKebabCase {
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"For a data structure input, convert strings at the given cell paths.",
|
||||
"For a data structure input, convert strings at the given cell paths",
|
||||
)
|
||||
.category(Category::Strings)
|
||||
}
|
||||
@ -90,6 +90,6 @@ mod tests {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(StrKebabCase {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -6,13 +6,13 @@ mod snake_case;
|
||||
mod str_;
|
||||
mod title_case;
|
||||
|
||||
pub use camel_case::StrCamelCase;
|
||||
pub use kebab_case::StrKebabCase;
|
||||
pub use pascal_case::StrPascalCase;
|
||||
pub use screaming_snake_case::StrScreamingSnakeCase;
|
||||
pub use snake_case::StrSnakeCase;
|
||||
pub use camel_case::SubCommand as StrCamelCase;
|
||||
pub use kebab_case::SubCommand as StrKebabCase;
|
||||
pub use pascal_case::SubCommand as StrPascalCase;
|
||||
pub use screaming_snake_case::SubCommand as StrScreamingSnakeCase;
|
||||
pub use snake_case::SubCommand as StrSnakeCase;
|
||||
pub use str_::Str;
|
||||
pub use title_case::StrTitleCase;
|
||||
pub use title_case::SubCommand as StrTitleCase;
|
||||
|
||||
use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument};
|
||||
use nu_engine::command_prelude::*;
|
||||
|
@ -3,9 +3,9 @@ use heck::ToUpperCamelCase;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StrPascalCase;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for StrPascalCase {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str pascal-case"
|
||||
}
|
||||
@ -25,7 +25,7 @@ impl Command for StrPascalCase {
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"For a data structure input, convert strings at the given cell paths.",
|
||||
"For a data structure input, convert strings at the given cell paths",
|
||||
)
|
||||
.category(Category::Strings)
|
||||
}
|
||||
@ -91,6 +91,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(StrPascalCase {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ use heck::ToShoutySnakeCase;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StrScreamingSnakeCase;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for StrScreamingSnakeCase {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str screaming-snake-case"
|
||||
}
|
||||
@ -25,7 +25,7 @@ impl Command for StrScreamingSnakeCase {
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"For a data structure input, convert strings at the given cell paths.",
|
||||
"For a data structure input, convert strings at the given cell paths",
|
||||
)
|
||||
.category(Category::Strings)
|
||||
}
|
||||
@ -91,6 +91,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(StrScreamingSnakeCase {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ use heck::ToSnakeCase;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StrSnakeCase;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for StrSnakeCase {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str snake-case"
|
||||
}
|
||||
@ -25,7 +25,7 @@ impl Command for StrSnakeCase {
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"For a data structure input, convert strings at the given cell paths.",
|
||||
"For a data structure input, convert strings at the given cell paths",
|
||||
)
|
||||
.category(Category::Strings)
|
||||
}
|
||||
@ -91,6 +91,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(StrSnakeCase {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ use heck::ToTitleCase;
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct StrTitleCase;
|
||||
pub struct SubCommand;
|
||||
|
||||
impl Command for StrTitleCase {
|
||||
impl Command for SubCommand {
|
||||
fn name(&self) -> &str {
|
||||
"str title-case"
|
||||
}
|
||||
@ -25,7 +25,7 @@ impl Command for StrTitleCase {
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::CellPath,
|
||||
"For a data structure input, convert strings at the given cell paths.",
|
||||
"For a data structure input, convert strings at the given cell paths",
|
||||
)
|
||||
.category(Category::Strings)
|
||||
}
|
||||
@ -86,6 +86,6 @@ mod test {
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(StrTitleCase {})
|
||||
test_examples(SubCommand {})
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cmd-lang"
|
||||
version = "0.104.1"
|
||||
version = "0.102.0"
|
||||
|
||||
[lib]
|
||||
bench = false
|
||||
@ -15,20 +15,16 @@ bench = false
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.104.1", default-features = false }
|
||||
nu-parser = { path = "../nu-parser", version = "0.104.1" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.104.1", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.104.1", default-features = false }
|
||||
nu-engine = { path = "../nu-engine", version = "0.102.0", default-features = false }
|
||||
nu-parser = { path = "../nu-parser", version = "0.102.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.102.0", default-features = false }
|
||||
nu-utils = { path = "../nu-utils", version = "0.102.0", default-features = false }
|
||||
|
||||
itertools = { workspace = true }
|
||||
shadow-rs = { version = "1.1", default-features = false }
|
||||
shadow-rs = { version = "0.38", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = { version = "1.1", default-features = false, features = ["build"] }
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = { workspace = true }
|
||||
quickcheck_macros = { workspace = true }
|
||||
shadow-rs = { version = "0.38", default-features = false }
|
||||
|
||||
[features]
|
||||
default = ["os"]
|
||||
@ -42,6 +38,7 @@ plugin = [
|
||||
"os",
|
||||
]
|
||||
|
||||
mimalloc = []
|
||||
trash-support = []
|
||||
sqlite = []
|
||||
static-link-openssl = []
|
||||
|
@ -18,4 +18,4 @@ A base crate is one with minimal dependencies in our system so that other develo
|
||||
|
||||
### Background on nu-cmd-lang
|
||||
|
||||
This crate was designed to be a small, concise set of tools or commands that serve as the *foundation layer* of both nu and nushell. These are the core commands needed to have a nice working version of the *nu language* without all of the support that the other commands provide inside nushell. Prior to the launch of this crate all of our commands were housed in the crate *nu-command*. Moving forward we would like to *slowly* break out the commands in nu-command into different crates; the naming and how this will work and where all the commands will be located is a "work in progress" especially now that the *standard library* is starting to become more popular as a location for commands. As time goes on some of our commands written in rust will be migrated to nu and when this happens they will be moved into the *standard library*.
|
||||
This crate was designed to be a small, concise set of tools or commands that serve as the *foundation layer* of both nu and nushell. These are the core commands needed to have a nice working version of the *nu language* without all of the support that the other commands provide inside nushell. Prior to the launch of this crate all of our commands were housed in the crate *nu-command*. Moving forward we would like to *slowly* break out the commands in nu-command into different crates; the naming and how this will work and where all the commands will be located is a "work in progress" especially now that the *standard library* is starting to become more popular as a location for commands. As time goes on some of our commands written in rust will be migrated to nu and when this happens they will be moved into the *standard library*.
|
||||
|
@ -1,61 +0,0 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AttrCategory;
|
||||
|
||||
impl Command for AttrCategory {
|
||||
fn name(&self) -> &str {
|
||||
"attr category"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("attr category")
|
||||
.input_output_type(Type::Nothing, Type::list(Type::String))
|
||||
.allow_variants_without_examples(true)
|
||||
.required(
|
||||
"category",
|
||||
SyntaxShape::String,
|
||||
"Category of the custom command.",
|
||||
)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Attribute for adding a category to custom commands."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let arg: String = call.req(engine_state, stack, 0)?;
|
||||
Ok(Value::string(arg, call.head).into_pipeline_data())
|
||||
}
|
||||
|
||||
fn run_const(
|
||||
&self,
|
||||
working_set: &StateWorkingSet,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let arg: String = call.req_const(working_set, 0)?;
|
||||
Ok(Value::string(arg, call.head).into_pipeline_data())
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Add a category to a custom command",
|
||||
example: r###"# Double numbers
|
||||
@category math
|
||||
def double []: [number -> number] { $in * 2 }"###,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
@ -1,159 +0,0 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AttrExample;
|
||||
|
||||
impl Command for AttrExample {
|
||||
fn name(&self) -> &str {
|
||||
"attr example"
|
||||
}
|
||||
|
||||
// TODO: When const closure are available, switch to using them for the `example` argument
|
||||
// rather than a block. That should remove the need for `requires_ast_for_arguments` to be true
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("attr example")
|
||||
.input_output_types(vec![(
|
||||
Type::Nothing,
|
||||
Type::Record(
|
||||
[
|
||||
("description".into(), Type::String),
|
||||
("example".into(), Type::String),
|
||||
]
|
||||
.into(),
|
||||
),
|
||||
)])
|
||||
.allow_variants_without_examples(true)
|
||||
.required(
|
||||
"description",
|
||||
SyntaxShape::String,
|
||||
"Description of the example.",
|
||||
)
|
||||
.required(
|
||||
"example",
|
||||
SyntaxShape::OneOf(vec![SyntaxShape::Block, SyntaxShape::String]),
|
||||
"Example code snippet.",
|
||||
)
|
||||
.named(
|
||||
"result",
|
||||
SyntaxShape::Any,
|
||||
"Expected output of example.",
|
||||
None,
|
||||
)
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Attribute for adding examples to custom commands."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let description: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let result: Option<Value> = call.get_flag(engine_state, stack, "result")?;
|
||||
|
||||
let example_string: Result<String, _> = call.req(engine_state, stack, 1);
|
||||
let example_expr = call
|
||||
.positional_nth(stack, 1)
|
||||
.ok_or(ShellError::MissingParameter {
|
||||
param_name: "example".into(),
|
||||
span: call.head,
|
||||
})?;
|
||||
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
|
||||
attr_example_impl(
|
||||
example_expr,
|
||||
example_string,
|
||||
&working_set,
|
||||
call,
|
||||
description,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
fn run_const(
|
||||
&self,
|
||||
working_set: &StateWorkingSet,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let description: Spanned<String> = call.req_const(working_set, 0)?;
|
||||
let result: Option<Value> = call.get_flag_const(working_set, "result")?;
|
||||
|
||||
let example_string: Result<String, _> = call.req_const(working_set, 1);
|
||||
let example_expr =
|
||||
call.assert_ast_call()?
|
||||
.positional_nth(1)
|
||||
.ok_or(ShellError::MissingParameter {
|
||||
param_name: "example".into(),
|
||||
span: call.head,
|
||||
})?;
|
||||
|
||||
attr_example_impl(
|
||||
example_expr,
|
||||
example_string,
|
||||
working_set,
|
||||
call,
|
||||
description,
|
||||
result,
|
||||
)
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn requires_ast_for_arguments(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Add examples to custom command",
|
||||
example: r###"# Double numbers
|
||||
@example "double an int" { 2 | double } --result 4
|
||||
@example "double a float" { 0.25 | double } --result 0.5
|
||||
def double []: [number -> number] { $in * 2 }"###,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
fn attr_example_impl(
|
||||
example_expr: &nu_protocol::ast::Expression,
|
||||
example_string: Result<String, ShellError>,
|
||||
working_set: &StateWorkingSet<'_>,
|
||||
call: &Call<'_>,
|
||||
description: Spanned<String>,
|
||||
result: Option<Value>,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let example_content = match example_expr.as_block() {
|
||||
Some(block_id) => {
|
||||
let block = working_set.get_block(block_id);
|
||||
let contents =
|
||||
working_set.get_span_contents(block.span.expect("a block must have a span"));
|
||||
let contents = contents
|
||||
.strip_prefix(b"{")
|
||||
.and_then(|x| x.strip_suffix(b"}"))
|
||||
.unwrap_or(contents)
|
||||
.trim_ascii();
|
||||
String::from_utf8_lossy(contents).into_owned()
|
||||
}
|
||||
None => example_string?,
|
||||
};
|
||||
|
||||
let mut rec = record! {
|
||||
"description" => Value::string(description.item, description.span),
|
||||
"example" => Value::string(example_content, example_expr.span),
|
||||
};
|
||||
if let Some(result) = result {
|
||||
rec.push("result", result);
|
||||
}
|
||||
|
||||
Ok(Value::record(rec, call.head).into_pipeline_data())
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
mod category;
|
||||
mod example;
|
||||
mod search_terms;
|
||||
|
||||
pub use category::AttrCategory;
|
||||
pub use example::AttrExample;
|
||||
pub use search_terms::AttrSearchTerms;
|
@ -1,57 +0,0 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AttrSearchTerms;
|
||||
|
||||
impl Command for AttrSearchTerms {
|
||||
fn name(&self) -> &str {
|
||||
"attr search-terms"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("attr search-terms")
|
||||
.input_output_type(Type::Nothing, Type::list(Type::String))
|
||||
.allow_variants_without_examples(true)
|
||||
.rest("terms", SyntaxShape::String, "Search terms.")
|
||||
.category(Category::Core)
|
||||
}
|
||||
|
||||
fn description(&self) -> &str {
|
||||
"Attribute for adding search terms to custom commands."
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let args = call.rest(engine_state, stack, 0)?;
|
||||
Ok(Value::list(args, call.head).into_pipeline_data())
|
||||
}
|
||||
|
||||
fn run_const(
|
||||
&self,
|
||||
working_set: &StateWorkingSet,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let args = call.rest_const(working_set, 0)?;
|
||||
Ok(Value::list(args, call.head).into_pipeline_data())
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Add search terms to a custom command",
|
||||
example: r###"# Double numbers
|
||||
@search-terms multiply times
|
||||
def double []: [number -> number] { $in * 2 }"###,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
}
|
@ -72,19 +72,6 @@ impl Command for Const {
|
||||
}
|
||||
}
|
||||
|
||||
fn run_const(
|
||||
&self,
|
||||
_working_set: &StateWorkingSet,
|
||||
_call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
|
@ -1,9 +1,6 @@
|
||||
use nu_engine::command_prelude::*;
|
||||
use nu_protocol::{
|
||||
engine::{Closure, StateWorkingSet},
|
||||
BlockId, ByteStreamSource, Category, PipelineMetadata, Signature,
|
||||
};
|
||||
use std::any::type_name;
|
||||
use nu_protocol::{engine::StateWorkingSet, ByteStreamSource, PipelineMetadata};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Describe;
|
||||
|
||||
@ -76,116 +73,39 @@ impl Command for Describe {
|
||||
"{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
|
||||
result: Some(Value::test_record(record!(
|
||||
"type" => Value::test_string("record"),
|
||||
"detailed_type" => Value::test_string("record<shell: string, uwu: bool, features: record<bugs: bool, multiplatform: bool, speed: int>, fib: list<int>, on_save: closure, first_commit: datetime, my_duration: duration>"),
|
||||
"columns" => Value::test_record(record!(
|
||||
"shell" => Value::test_record(record!(
|
||||
"type" => Value::test_string("string"),
|
||||
"detailed_type" => Value::test_string("string"),
|
||||
"rust_type" => Value::test_string("&alloc::string::String"),
|
||||
"value" => Value::test_string("true"),
|
||||
)),
|
||||
"uwu" => Value::test_record(record!(
|
||||
"type" => Value::test_string("bool"),
|
||||
"detailed_type" => Value::test_string("bool"),
|
||||
"rust_type" => Value::test_string("bool"),
|
||||
"value" => Value::test_bool(true),
|
||||
)),
|
||||
"shell" => Value::test_string("string"),
|
||||
"uwu" => Value::test_string("bool"),
|
||||
"features" => Value::test_record(record!(
|
||||
"type" => Value::test_string("record"),
|
||||
"detailed_type" => Value::test_string("record<bugs: bool, multiplatform: bool, speed: int>"),
|
||||
"columns" => Value::test_record(record!(
|
||||
"bugs" => Value::test_record(record!(
|
||||
"type" => Value::test_string("bool"),
|
||||
"detailed_type" => Value::test_string("bool"),
|
||||
"rust_type" => Value::test_string("bool"),
|
||||
"value" => Value::test_bool(false),
|
||||
)),
|
||||
"multiplatform" => Value::test_record(record!(
|
||||
"type" => Value::test_string("bool"),
|
||||
"detailed_type" => Value::test_string("bool"),
|
||||
"rust_type" => Value::test_string("bool"),
|
||||
"value" => Value::test_bool(true),
|
||||
)),
|
||||
"speed" => Value::test_record(record!(
|
||||
"type" => Value::test_string("int"),
|
||||
"detailed_type" => Value::test_string("int"),
|
||||
"rust_type" => Value::test_string("i64"),
|
||||
"value" => Value::test_int(10),
|
||||
)),
|
||||
"bugs" => Value::test_string("bool"),
|
||||
"multiplatform" => Value::test_string("bool"),
|
||||
"speed" => Value::test_string("int"),
|
||||
)),
|
||||
"rust_type" => Value::test_string("&nu_utils::shared_cow::SharedCow<nu_protocol::value::record::Record>"),
|
||||
)),
|
||||
"fib" => Value::test_record(record!(
|
||||
"type" => Value::test_string("list"),
|
||||
"detailed_type" => Value::test_string("list<int>"),
|
||||
"length" => Value::test_int(6),
|
||||
"rust_type" => Value::test_string("&mut alloc::vec::Vec<nu_protocol::value::Value>"),
|
||||
"value" => Value::test_list(vec![
|
||||
Value::test_record(record!(
|
||||
"type" => Value::test_string("int"),
|
||||
"detailed_type" => Value::test_string("int"),
|
||||
"rust_type" => Value::test_string("i64"),
|
||||
"value" => Value::test_int(1),
|
||||
)),
|
||||
Value::test_record(record!(
|
||||
"type" => Value::test_string("int"),
|
||||
"detailed_type" => Value::test_string("int"),
|
||||
"rust_type" => Value::test_string("i64"),
|
||||
"value" => Value::test_int(1),
|
||||
)),
|
||||
Value::test_record(record!(
|
||||
"type" => Value::test_string("int"),
|
||||
"detailed_type" => Value::test_string("int"),
|
||||
"rust_type" => Value::test_string("i64"),
|
||||
"value" => Value::test_int(2),
|
||||
)),
|
||||
Value::test_record(record!(
|
||||
"type" => Value::test_string("int"),
|
||||
"detailed_type" => Value::test_string("int"),
|
||||
"rust_type" => Value::test_string("i64"),
|
||||
"value" => Value::test_int(3),
|
||||
)),
|
||||
Value::test_record(record!(
|
||||
"type" => Value::test_string("int"),
|
||||
"detailed_type" => Value::test_string("int"),
|
||||
"rust_type" => Value::test_string("i64"),
|
||||
"value" => Value::test_int(5),
|
||||
)),
|
||||
Value::test_record(record!(
|
||||
"type" => Value::test_string("int"),
|
||||
"detailed_type" => Value::test_string("int"),
|
||||
"rust_type" => Value::test_string("i64"),
|
||||
"value" => Value::test_int(8),
|
||||
))]
|
||||
),
|
||||
"values" => Value::test_list(vec![
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
Value::test_string("int"),
|
||||
]),
|
||||
)),
|
||||
"on_save" => Value::test_record(record!(
|
||||
"type" => Value::test_string("closure"),
|
||||
"detailed_type" => Value::test_string("closure"),
|
||||
"rust_type" => Value::test_string("&alloc::boxed::Box<nu_protocol::engine::closure::Closure>"),
|
||||
"value" => Value::test_closure(Closure {
|
||||
block_id: BlockId::new(1),
|
||||
captures: vec![],
|
||||
}),
|
||||
"signature" => Value::test_record(record!(
|
||||
"name" => Value::test_string(""),
|
||||
"category" => Value::test_string("default"),
|
||||
)),
|
||||
)),
|
||||
"first_commit" => Value::test_record(record!(
|
||||
"type" => Value::test_string("datetime"),
|
||||
"detailed_type" => Value::test_string("datetime"),
|
||||
"rust_type" => Value::test_string("chrono::datetime::DateTime<chrono::offset::fixed::FixedOffset>"),
|
||||
"value" => Value::test_date("2019-05-10 00:00:00Z".parse().unwrap_or_default()),
|
||||
)),
|
||||
"my_duration" => Value::test_record(record!(
|
||||
"type" => Value::test_string("duration"),
|
||||
"detailed_type" => Value::test_string("duration"),
|
||||
"rust_type" => Value::test_string("i64"),
|
||||
"value" => Value::test_duration(260_000_000_000),
|
||||
))
|
||||
"first_commit" => Value::test_string("date"),
|
||||
"my_duration" => Value::test_string("duration"),
|
||||
)),
|
||||
"rust_type" => Value::test_string("&nu_utils::shared_cow::SharedCow<nu_protocol::value::record::Record>"),
|
||||
))),
|
||||
},
|
||||
Example {
|
||||
@ -255,9 +175,7 @@ fn run(
|
||||
|
||||
Value::record(
|
||||
record! {
|
||||
"type" => Value::string("bytestream", head),
|
||||
"detailed_type" => Value::string(type_, head),
|
||||
"rust_type" => Value::string(type_of(&stream), head),
|
||||
"type" => Value::string(type_, head),
|
||||
"origin" => Value::string(origin, head),
|
||||
"metadata" => metadata_to_value(metadata, head),
|
||||
},
|
||||
@ -274,7 +192,6 @@ fn run(
|
||||
description
|
||||
}
|
||||
PipelineData::ListStream(stream, ..) => {
|
||||
let type_ = type_of(&stream);
|
||||
if options.detailed {
|
||||
let subtype = if options.no_collect {
|
||||
Value::string("any", head)
|
||||
@ -284,8 +201,6 @@ fn run(
|
||||
Value::record(
|
||||
record! {
|
||||
"type" => Value::string("stream", head),
|
||||
"detailed_type" => Value::string("list stream", head),
|
||||
"rust_type" => Value::string(type_, head),
|
||||
"origin" => Value::string("nushell", head),
|
||||
"subtype" => subtype,
|
||||
"metadata" => metadata_to_value(metadata, head),
|
||||
@ -314,95 +229,45 @@ fn run(
|
||||
}
|
||||
|
||||
enum Description {
|
||||
String(String),
|
||||
Record(Record),
|
||||
}
|
||||
|
||||
impl Description {
|
||||
fn into_value(self, span: Span) -> Value {
|
||||
match self {
|
||||
Description::String(ty) => Value::string(ty, span),
|
||||
Description::Record(record) => Value::record(record, span),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn describe_value(value: Value, head: Span, engine_state: Option<&EngineState>) -> Value {
|
||||
let Description::Record(record) = describe_value_inner(value, head, engine_state);
|
||||
let record = match describe_value_inner(value, head, engine_state) {
|
||||
Description::String(ty) => record! { "type" => Value::string(ty, head) },
|
||||
Description::Record(record) => record,
|
||||
};
|
||||
Value::record(record, head)
|
||||
}
|
||||
|
||||
fn type_of<T>(_: &T) -> String {
|
||||
type_name::<T>().to_string()
|
||||
}
|
||||
|
||||
fn describe_value_inner(
|
||||
mut value: Value,
|
||||
value: Value,
|
||||
head: Span,
|
||||
engine_state: Option<&EngineState>,
|
||||
) -> Description {
|
||||
let value_type = value.get_type().to_string();
|
||||
match value {
|
||||
Value::Bool { val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("bool", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Int { val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("int", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Float { val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("float", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Filesize { val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("filesize", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Duration { val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("duration", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Date { val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("datetime", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Range { ref val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("range", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::String { ref val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("string", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Glob { ref val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("glob", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Nothing { .. } => Description::Record(record! {
|
||||
"type" => Value::string("nothing", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string("", head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Record { ref val, .. } => {
|
||||
let mut columns = val.clone().into_owned();
|
||||
Value::Bool { .. }
|
||||
| Value::Int { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::String { .. }
|
||||
| Value::Glob { .. }
|
||||
| Value::Nothing { .. } => Description::String(value.get_type().to_string()),
|
||||
Value::Record { val, .. } => {
|
||||
let mut columns = val.into_owned();
|
||||
for (_, val) in &mut columns {
|
||||
*val =
|
||||
describe_value_inner(std::mem::take(val), head, engine_state).into_value(head);
|
||||
@ -410,34 +275,25 @@ fn describe_value_inner(
|
||||
|
||||
Description::Record(record! {
|
||||
"type" => Value::string("record", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"columns" => Value::record(columns.clone(), head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"columns" => Value::record(columns, head),
|
||||
})
|
||||
}
|
||||
Value::List { ref mut vals, .. } => {
|
||||
for val in &mut *vals {
|
||||
Value::List { mut vals, .. } => {
|
||||
for val in &mut vals {
|
||||
*val =
|
||||
describe_value_inner(std::mem::take(val), head, engine_state).into_value(head);
|
||||
}
|
||||
|
||||
Description::Record(record! {
|
||||
"type" => Value::string("list", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"length" => Value::int(vals.len() as i64, head),
|
||||
"rust_type" => Value::string(type_of(&vals), head),
|
||||
"value" => value,
|
||||
"values" => Value::list(vals, head),
|
||||
})
|
||||
}
|
||||
Value::Closure { ref val, .. } => {
|
||||
Value::Closure { val, .. } => {
|
||||
let block = engine_state.map(|engine_state| engine_state.get_block(val.block_id));
|
||||
|
||||
let mut record = record! {
|
||||
"type" => Value::string("closure", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
};
|
||||
let mut record = record! { "type" => Value::string("closure", head) };
|
||||
if let Some(block) = block {
|
||||
record.push(
|
||||
"signature",
|
||||
@ -452,37 +308,21 @@ fn describe_value_inner(
|
||||
}
|
||||
Description::Record(record)
|
||||
}
|
||||
Value::Error { ref error, .. } => Description::Record(record! {
|
||||
Value::Error { error, .. } => Description::Record(record! {
|
||||
"type" => Value::string("error", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"subtype" => Value::string(error.to_string(), head),
|
||||
"rust_type" => Value::string(type_of(&error), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::Binary { ref val, .. } => Description::Record(record! {
|
||||
Value::Binary { val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("binary", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"length" => Value::int(val.len() as i64, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value,
|
||||
}),
|
||||
Value::CellPath { ref val, .. } => Description::Record(record! {
|
||||
Value::CellPath { val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("cell-path", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"length" => Value::int(val.members.len() as i64, head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" => value
|
||||
}),
|
||||
Value::Custom { ref val, .. } => Description::Record(record! {
|
||||
Value::Custom { val, .. } => Description::Record(record! {
|
||||
"type" => Value::string("custom", head),
|
||||
"detailed_type" => Value::string(value_type, head),
|
||||
"subtype" => Value::string(val.type_name(), head),
|
||||
"rust_type" => Value::string(type_of(&val), head),
|
||||
"value" =>
|
||||
match val.to_base_value(head) {
|
||||
Ok(base_value) => base_value,
|
||||
Err(err) => Value::error(err, head),
|
||||
}
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,16 @@ impl Command for Do {
|
||||
"ignore errors as the closure runs",
|
||||
Some('i'),
|
||||
)
|
||||
.switch(
|
||||
"ignore-shell-errors",
|
||||
"ignore shell errors as the closure runs",
|
||||
Some('s'),
|
||||
)
|
||||
.switch(
|
||||
"ignore-program-errors",
|
||||
"ignore external program errors as the closure runs",
|
||||
Some('p'),
|
||||
)
|
||||
.switch(
|
||||
"capture-errors",
|
||||
"catch errors as the closure runs, and return them",
|
||||
@ -61,6 +71,36 @@ impl Command for Do {
|
||||
let rest: Vec<Value> = call.rest(engine_state, caller_stack, 1)?;
|
||||
let ignore_all_errors = call.has_flag(engine_state, caller_stack, "ignore-errors")?;
|
||||
|
||||
if call.has_flag(engine_state, caller_stack, "ignore-shell-errors")? {
|
||||
nu_protocol::report_shell_warning(
|
||||
engine_state,
|
||||
&ShellError::GenericError {
|
||||
error: "Deprecated option".into(),
|
||||
msg: "`--ignore-shell-errors` is deprecated and will be removed in 0.102.0."
|
||||
.into(),
|
||||
span: Some(call.head),
|
||||
help: Some("Please use the `--ignore-errors(-i)`".into()),
|
||||
inner: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
if call.has_flag(engine_state, caller_stack, "ignore-program-errors")? {
|
||||
nu_protocol::report_shell_warning(
|
||||
engine_state,
|
||||
&ShellError::GenericError {
|
||||
error: "Deprecated option".into(),
|
||||
msg: "`--ignore-program-errors` is deprecated and will be removed in 0.102.0."
|
||||
.into(),
|
||||
span: Some(call.head),
|
||||
help: Some("Please use the `--ignore-errors(-i)`".into()),
|
||||
inner: vec![],
|
||||
},
|
||||
);
|
||||
}
|
||||
let ignore_shell_errors = ignore_all_errors
|
||||
|| call.has_flag(engine_state, caller_stack, "ignore-shell-errors")?;
|
||||
let ignore_program_errors = ignore_all_errors
|
||||
|| call.has_flag(engine_state, caller_stack, "ignore-program-errors")?;
|
||||
let capture_errors = call.has_flag(engine_state, caller_stack, "capture-errors")?;
|
||||
let has_env = call.has_flag(engine_state, caller_stack, "env")?;
|
||||
|
||||
@ -166,7 +206,7 @@ impl Command for Do {
|
||||
}
|
||||
}
|
||||
Ok(PipelineData::ByteStream(mut stream, metadata))
|
||||
if ignore_all_errors
|
||||
if ignore_program_errors
|
||||
&& !matches!(
|
||||
caller_stack.stdout(),
|
||||
OutDest::Pipe | OutDest::PipeSeparate | OutDest::Value
|
||||
@ -178,10 +218,10 @@ impl Command for Do {
|
||||
}
|
||||
Ok(PipelineData::ByteStream(stream, metadata))
|
||||
}
|
||||
Ok(PipelineData::Value(Value::Error { .. }, ..)) | Err(_) if ignore_all_errors => {
|
||||
Ok(PipelineData::Value(Value::Error { .. }, ..)) | Err(_) if ignore_shell_errors => {
|
||||
Ok(PipelineData::empty())
|
||||
}
|
||||
Ok(PipelineData::ListStream(stream, metadata)) if ignore_all_errors => {
|
||||
Ok(PipelineData::ListStream(stream, metadata)) if ignore_shell_errors => {
|
||||
let stream = stream.map(move |value| {
|
||||
if let Value::Error { .. } = value {
|
||||
Value::nothing(head)
|
||||
|
@ -34,22 +34,13 @@ little reason to use this over just writing the values as-is."#
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let args = call.rest(engine_state, stack, 0)?;
|
||||
echo_impl(args, call.head)
|
||||
}
|
||||
|
||||
fn run_const(
|
||||
&self,
|
||||
working_set: &StateWorkingSet,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let args = call.rest_const(working_set, 0)?;
|
||||
echo_impl(args, call.head)
|
||||
}
|
||||
|
||||
fn is_const(&self) -> bool {
|
||||
true
|
||||
let mut args = call.rest(engine_state, stack, 0)?;
|
||||
let value = match args.len() {
|
||||
0 => Value::string("", call.head),
|
||||
1 => args.pop().expect("one element"),
|
||||
_ => Value::list(args, call.head),
|
||||
};
|
||||
Ok(value.into_pipeline_data())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -72,15 +63,6 @@ little reason to use this over just writing the values as-is."#
|
||||
}
|
||||
}
|
||||
|
||||
fn echo_impl(mut args: Vec<Value>, head: Span) -> Result<PipelineData, ShellError> {
|
||||
let value = match args.len() {
|
||||
0 => Value::string("", head),
|
||||
1 => args.pop().expect("one element"),
|
||||
_ => Value::list(args, head),
|
||||
};
|
||||
Ok(value.into_pipeline_data())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
|
@ -32,10 +32,6 @@ This command is a parser keyword. For details, check:
|
||||
https://www.nushell.sh/book/thinking_in_nu.html"#
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["unset"]
|
||||
}
|
||||
|
||||
fn command_type(&self) -> CommandType {
|
||||
CommandType::Keyword
|
||||
}
|
||||
|
@ -29,10 +29,6 @@ impl Command for HideEnv {
|
||||
"Hide environment variables in the current scope."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["unset", "drop"]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
|
@ -1,5 +1,4 @@
|
||||
mod alias;
|
||||
mod attr;
|
||||
mod break_;
|
||||
mod collect;
|
||||
mod const_;
|
||||
@ -36,7 +35,6 @@ mod version;
|
||||
mod while_;
|
||||
|
||||
pub use alias::Alias;
|
||||
pub use attr::*;
|
||||
pub use break_::Break;
|
||||
pub use collect::Collect;
|
||||
pub use const_::Const;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user