Compare commits
219 Commits
revert-131
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
4e83ccdf86 | ||
|
6d36941e55 | ||
|
2f44801414 | ||
|
9172b22985 | ||
|
1c37f4b958 | ||
|
56ed532038 | ||
|
b974f8f7e3 | ||
|
802bfed173 | ||
|
07e7c8c81f | ||
|
20b53067cd | ||
|
f4c0d9d45b | ||
|
85b06b22d9 | ||
|
63f00e78d1 | ||
|
ff1ad77130 | ||
|
af34d5c062 | ||
|
ed82f9ee18 | ||
|
d081e3386f | ||
|
ca8eb856e8 | ||
|
168835ecd2 | ||
|
4157ca711d | ||
|
d34a24db33 | ||
|
2c6b1471e1 | ||
|
ae5fed41ed | ||
|
ca73d85c09 | ||
|
f82c43f850 | ||
|
3dc9691aaa | ||
|
42531e017c | ||
|
928c57db41 | ||
|
d880241102 | ||
|
813aac89bd | ||
|
d2bf82d22b | ||
|
3f12b14053 | ||
|
8e2917b9ae | ||
|
3c3ec7891c | ||
|
6b839c3c32 | ||
|
ea22c319b6 | ||
|
7432e67da1 | ||
|
0576794e74 | ||
|
12f57dbc62 | ||
|
fe57c5c22e | ||
|
18161e5707 | ||
|
466b3899e0 | ||
|
7b82c6b482 | ||
|
e3f78b8793 | ||
|
c31291753c | ||
|
f7d6c28a00 | ||
|
d618fd0527 | ||
|
d80de68665 | ||
|
5f7afafe51 | ||
|
53fbf62493 | ||
|
e68f744dda | ||
|
e2d0514bb5 | ||
|
4a7d4401b8 | ||
|
6446f26283 | ||
|
9f90d611e1 | ||
|
a88c3f48e2 | ||
|
5c2439abc0 | ||
|
a80dfe8e80 | ||
|
6a62ced645 | ||
|
366e52b76d | ||
|
6fcd09682c | ||
|
9ab706db62 | ||
|
01891d637d | ||
|
5db57abc7d | ||
|
dbd60ed4f4 | ||
|
aa9a42776b | ||
|
4665323bb4 | ||
|
e281c03403 | ||
|
e8764de3c6 | ||
|
f3843a6176 | ||
|
c19944f291 | ||
|
22379c9846 | ||
|
aa7d7d0cc3 | ||
|
f976c31887 | ||
|
ac18e43603 | ||
|
63cea44130 | ||
|
5417c89387 | ||
|
b66671d339 | ||
|
1981c50c8f | ||
|
fcb8e36caa | ||
|
0918050ac8 | ||
|
3d1145e759 | ||
|
cf4864a9cd | ||
|
ae40d56fc5 | ||
|
c5aa15c7f6 | ||
|
f5bff8c9c8 | ||
|
b0bf54614f | ||
|
a2758e6c40 | ||
|
d42cf55431 | ||
|
46b5e510ac | ||
|
02659b1c8a | ||
|
8f981c1eb4 | ||
|
0b8d0bcd7a | ||
|
ee875bb8a3 | ||
|
d56457d63e | ||
|
4bd87d0496 | ||
|
ccd0160c32 | ||
|
acd4cb83e8 | ||
|
a7a5315638 | ||
|
9fec5883c0 | ||
|
f65bc97a54 | ||
|
deaa711ca6 | ||
|
076a29ae19 | ||
|
9de7f931c0 | ||
|
d97512df8e | ||
|
801cfae279 | ||
|
f87cf895c2 | ||
|
ac561b1b0e | ||
|
1a5bf2447a | ||
|
d7392f1f3b | ||
|
ea8c4e3af2 | ||
|
616e9faaf1 | ||
|
b68c7cf3fa | ||
|
0178295363 | ||
|
ad8054ebed | ||
|
ff27d6a18e | ||
|
4cdceca1f7 | ||
|
1964dacaef | ||
|
e98b2ceb8c | ||
|
399a7c8836 | ||
|
c6b6b1b7a8 | ||
|
152fb5be39 | ||
|
83081f9852 | ||
|
6ce5530fc2 | ||
|
5af8d62666 | ||
|
32db5d3aa3 | ||
|
fa183b6669 | ||
|
d2a1f96dbd | ||
|
de2b752771 | ||
|
948b90299d | ||
|
34da26d039 | ||
|
8707d14f95 | ||
|
1514b9fbef | ||
|
b27cd70fd1 | ||
|
afaa019fae | ||
|
8833d3f89f | ||
|
d5e00c0d5d | ||
|
3fae77209a | ||
|
ca7a2ae1d6 | ||
|
ba1d900020 | ||
|
f59dfac130 | ||
|
9e738193f3 | ||
|
8316a1597e | ||
|
0cfd5fbece | ||
|
9b63e17072 | ||
|
122ff1f19c | ||
|
0d060aeae8 | ||
|
e5cf4863e9 | ||
|
69e4790b00 | ||
|
a2873336bb | ||
|
4fe0f860a8 | ||
|
33d0537cae | ||
|
40e629beb1 | ||
|
1b1928c103 | ||
|
153b45bc63 | ||
|
4f8d82bb88 | ||
|
720b4cbd01 | ||
|
a71732ba12 | ||
|
57452337ff | ||
|
1f1f581357 | ||
|
0d79b63711 | ||
|
46ed69ab12 | ||
|
ee74ec7423 | ||
|
198aedb6c2 | ||
|
58e8ea6084 | ||
|
020f4436d9 | ||
|
8a7a407627 | ||
|
b679c2bfa2 | ||
|
0fd0e36be8 | ||
|
38ecb6d380 | ||
|
c5a00ca3f1 | ||
|
5a486029db | ||
|
def36865ef | ||
|
55ee476306 | ||
|
f241110005 | ||
|
0dd35cddcd | ||
|
f93c6680bd | ||
|
43e349a274 | ||
|
4509944988 | ||
|
9b7f899410 | ||
|
b6bdadbc6f | ||
|
dcb6ab6370 | ||
|
db86dd9f26 | ||
|
10e84038af | ||
|
91d44f15c1 | ||
|
dd8f8861ed | ||
|
9845d13347 | ||
|
4c82a748c1 | ||
|
20834c9d47 | ||
|
7d2d573eb8 | ||
|
c09a8a5ec9 | ||
|
bb6cb94e55 | ||
|
26bdba2068 | ||
|
bdc32345bd | ||
|
44aa0a2de4 | ||
|
12991cd36f | ||
|
103f59be52 | ||
|
532c0023c2 | ||
|
a894e9c246 | ||
|
c171a2b8b7 | ||
|
28ed0fe700 | ||
|
ae6489f04b | ||
|
97876fb0b4 | ||
|
b79a2255d2 | ||
|
3a6d8aac0b | ||
|
b1cf0e258d | ||
|
af6e1cb5a6 | ||
|
323d5457f9 | ||
|
634361b2d1 | ||
|
bdbb096526 | ||
|
63c863c81b | ||
|
17daf783b2 | ||
|
1e430d155e | ||
|
0372e8c53c | ||
|
b0d1b4b182 | ||
|
a8376fad40 | ||
|
c09488f515 | ||
|
944d941dec | ||
|
a55a48529d |
7
.github/dependabot.yml
vendored
7
.github/dependabot.yml
vendored
|
@ -26,6 +26,13 @@ updates:
|
||||||
patterns:
|
patterns:
|
||||||
- "polars"
|
- "polars"
|
||||||
- "polars-*"
|
- "polars-*"
|
||||||
|
# uutils/coreutils also versions all their workspace crates the same at the moment
|
||||||
|
# Most of them have bleeding edge version requirements (some not)
|
||||||
|
# see: https://github.com/uutils/coreutils/blob/main/Cargo.toml
|
||||||
|
uutils:
|
||||||
|
patterns:
|
||||||
|
- "uucore"
|
||||||
|
- "uu_*"
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
|
|
2
.github/workflows/audit.yml
vendored
2
.github/workflows/audit.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
||||||
# Prevent sudden announcement of a new advisory from failing ci:
|
# Prevent sudden announcement of a new advisory from failing ci:
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.7
|
||||||
- uses: rustsec/audit-check@v1.4.1
|
- uses: rustsec/audit-check@v1.4.1
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
|
@ -33,10 +33,10 @@ jobs:
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.7
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
|
||||||
|
|
||||||
- name: cargo fmt
|
- name: cargo fmt
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
|
@ -66,10 +66,10 @@ jobs:
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.7
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.default-flags }}
|
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.default-flags }}
|
||||||
|
@ -95,10 +95,10 @@ jobs:
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.7
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
|
||||||
|
|
||||||
- name: Install Nushell
|
- name: Install Nushell
|
||||||
run: cargo install --path . --locked --no-default-features
|
run: cargo install --path . --locked --no-default-features
|
||||||
|
@ -146,10 +146,10 @@ jobs:
|
||||||
runs-on: ${{ matrix.platform }}
|
runs-on: ${{ matrix.platform }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.7
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS
|
run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS
|
||||||
|
|
30
.github/workflows/nightly-build.yml
vendored
30
.github/workflows/nightly-build.yml
vendored
|
@ -27,7 +27,7 @@ jobs:
|
||||||
# if: github.repository == 'nushell/nightly'
|
# if: github.repository == 'nushell/nightly'
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4.1.6
|
uses: actions/checkout@v4.1.7
|
||||||
if: github.repository == 'nushell/nightly'
|
if: github.repository == 'nushell/nightly'
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
@ -36,10 +36,10 @@ jobs:
|
||||||
token: ${{ secrets.WORKFLOW_TOKEN }}
|
token: ${{ secrets.WORKFLOW_TOKEN }}
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.11
|
uses: hustcer/setup-nu@v3.12
|
||||||
if: github.repository == 'nushell/nightly'
|
if: github.repository == 'nushell/nightly'
|
||||||
with:
|
with:
|
||||||
version: 0.93.0
|
version: 0.95.0
|
||||||
|
|
||||||
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
|
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
|
||||||
- name: Prepare for Nightly Release
|
- name: Prepare for Nightly Release
|
||||||
|
@ -99,20 +99,20 @@ jobs:
|
||||||
extra: msi
|
extra: msi
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
- target: x86_64-unknown-linux-gnu
|
- target: x86_64-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
- target: x86_64-unknown-linux-musl
|
- target: x86_64-unknown-linux-musl
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
- target: aarch64-unknown-linux-gnu
|
- target: aarch64-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
- target: armv7-unknown-linux-gnueabihf
|
- target: armv7-unknown-linux-gnueabihf
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
- target: riscv64gc-unknown-linux-gnu
|
- target: riscv64gc-unknown-linux-gnu
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.7
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
@ -122,15 +122,15 @@ jobs:
|
||||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||||
|
|
||||||
- name: Setup Rust toolchain and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
|
||||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||||
with:
|
with:
|
||||||
rustflags: ''
|
rustflags: ''
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.11
|
uses: hustcer/setup-nu@v3.12
|
||||||
with:
|
with:
|
||||||
version: 0.93.0
|
version: 0.95.0
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
|
@ -161,7 +161,7 @@ jobs:
|
||||||
# REF: https://github.com/marketplace/actions/gh-release
|
# REF: https://github.com/marketplace/actions/gh-release
|
||||||
# Create a release only in nushell/nightly repo
|
# Create a release only in nushell/nightly repo
|
||||||
- name: Publish Archive
|
- name: Publish Archive
|
||||||
uses: softprops/action-gh-release@v2.0.5
|
uses: softprops/action-gh-release@v2.0.8
|
||||||
if: ${{ startsWith(github.repository, 'nushell/nightly') }}
|
if: ${{ startsWith(github.repository, 'nushell/nightly') }}
|
||||||
with:
|
with:
|
||||||
prerelease: true
|
prerelease: true
|
||||||
|
@ -181,14 +181,14 @@ jobs:
|
||||||
- name: Waiting for Release
|
- name: Waiting for Release
|
||||||
run: sleep 1800
|
run: sleep 1800
|
||||||
|
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.7
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.11
|
uses: hustcer/setup-nu@v3.12
|
||||||
with:
|
with:
|
||||||
version: 0.93.0
|
version: 0.95.0
|
||||||
|
|
||||||
# Keep the last a few releases
|
# Keep the last a few releases
|
||||||
- name: Delete Older Releases
|
- name: Delete Older Releases
|
||||||
|
|
6
.github/workflows/release-pkg.nu
vendored
6
.github/workflows/release-pkg.nu
vendored
|
@ -161,8 +161,12 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
||||||
let releaseStem = $'($bin)-($version)-($target)'
|
let releaseStem = $'($bin)-($version)-($target)'
|
||||||
|
|
||||||
print $'(char nl)Download less related stuffs...'; hr-line
|
print $'(char nl)Download less related stuffs...'; hr-line
|
||||||
|
# todo: less-v661 is out but is released as a zip file. maybe we should switch to that and extract it?
|
||||||
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
||||||
aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
|
# the below was renamed because it was failing to download for darren. it should work but it wasn't
|
||||||
|
# todo: maybe we should get rid of this aria2c dependency and just use http get?
|
||||||
|
#aria2c https://raw.githubusercontent.com/jftuga/less-Windows/master/LICENSE -o LICENSE-for-less.txt
|
||||||
|
aria2c https://github.com/jftuga/less-Windows/blob/master/LICENSE -o LICENSE-for-less.txt
|
||||||
|
|
||||||
# Create Windows msi release package
|
# Create Windows msi release package
|
||||||
if (get-env _EXTRA_) == 'msi' {
|
if (get-env _EXTRA_) == 'msi' {
|
||||||
|
|
18
.github/workflows/release.yml
vendored
18
.github/workflows/release.yml
vendored
|
@ -49,36 +49,36 @@ jobs:
|
||||||
extra: msi
|
extra: msi
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
- target: x86_64-unknown-linux-gnu
|
- target: x86_64-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
- target: x86_64-unknown-linux-musl
|
- target: x86_64-unknown-linux-musl
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
- target: aarch64-unknown-linux-gnu
|
- target: aarch64-unknown-linux-gnu
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
- target: armv7-unknown-linux-gnueabihf
|
- target: armv7-unknown-linux-gnueabihf
|
||||||
os: ubuntu-20.04
|
os: ubuntu-22.04
|
||||||
- target: riscv64gc-unknown-linux-gnu
|
- target: riscv64gc-unknown-linux-gnu
|
||||||
os: ubuntu-latest
|
os: ubuntu-latest
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4.1.6
|
- uses: actions/checkout@v4.1.7
|
||||||
|
|
||||||
- name: Update Rust Toolchain Target
|
- name: Update Rust Toolchain Target
|
||||||
run: |
|
run: |
|
||||||
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
echo "targets = ['${{matrix.target}}']" >> rust-toolchain.toml
|
||||||
|
|
||||||
- name: Setup Rust toolchain
|
- name: Setup Rust toolchain
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.8.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.9.0
|
||||||
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
# WARN: Keep the rustflags to prevent from the winget submission error: `CAQuietExec: Error 0xc0000135`
|
||||||
with:
|
with:
|
||||||
cache: false
|
cache: false
|
||||||
rustflags: ''
|
rustflags: ''
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.11
|
uses: hustcer/setup-nu@v3.12
|
||||||
with:
|
with:
|
||||||
version: 0.93.0
|
version: 0.95.0
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
|
@ -91,7 +91,7 @@ jobs:
|
||||||
|
|
||||||
# REF: https://github.com/marketplace/actions/gh-release
|
# REF: https://github.com/marketplace/actions/gh-release
|
||||||
- name: Publish Archive
|
- name: Publish Archive
|
||||||
uses: softprops/action-gh-release@v2.0.5
|
uses: softprops/action-gh-release@v2.0.8
|
||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
|
|
4
.github/workflows/typos.yml
vendored
4
.github/workflows/typos.yml
vendored
|
@ -7,7 +7,7 @@ jobs:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Actions Repository
|
- name: Checkout Actions Repository
|
||||||
uses: actions/checkout@v4.1.6
|
uses: actions/checkout@v4.1.7
|
||||||
|
|
||||||
- name: Check spelling
|
- name: Check spelling
|
||||||
uses: crate-ci/typos@v1.22.1
|
uses: crate-ci/typos@v1.23.5
|
||||||
|
|
571
Cargo.lock
generated
571
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
90
Cargo.toml
90
Cargo.toml
|
@ -10,8 +10,8 @@ homepage = "https://www.nushell.sh"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu"
|
name = "nu"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.77.2"
|
rust-version = "1.78.0"
|
||||||
version = "0.94.3"
|
version = "0.96.2"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -39,6 +39,7 @@ members = [
|
||||||
"crates/nu-lsp",
|
"crates/nu-lsp",
|
||||||
"crates/nu-pretty-hex",
|
"crates/nu-pretty-hex",
|
||||||
"crates/nu-protocol",
|
"crates/nu-protocol",
|
||||||
|
"crates/nu-derive-value",
|
||||||
"crates/nu-plugin",
|
"crates/nu-plugin",
|
||||||
"crates/nu-plugin-core",
|
"crates/nu-plugin-core",
|
||||||
"crates/nu-plugin-engine",
|
"crates/nu-plugin-engine",
|
||||||
|
@ -74,13 +75,16 @@ chardetng = "0.1.17"
|
||||||
chrono = { default-features = false, version = "0.4.34" }
|
chrono = { default-features = false, version = "0.4.34" }
|
||||||
chrono-humanize = "0.2.3"
|
chrono-humanize = "0.2.3"
|
||||||
chrono-tz = "0.8"
|
chrono-tz = "0.8"
|
||||||
|
convert_case = "0.6"
|
||||||
crossbeam-channel = "0.5.8"
|
crossbeam-channel = "0.5.8"
|
||||||
crossterm = "0.27"
|
crossterm = "0.27"
|
||||||
csv = "1.3"
|
csv = "1.3"
|
||||||
ctrlc = "3.4"
|
ctrlc = "3.4"
|
||||||
|
deunicode = "1.6.0"
|
||||||
dialoguer = { default-features = false, version = "0.11" }
|
dialoguer = { default-features = false, version = "0.11" }
|
||||||
digest = { default-features = false, version = "0.10" }
|
digest = { default-features = false, version = "0.10" }
|
||||||
dirs-next = "2.0"
|
dirs = "5.0"
|
||||||
|
dirs-sys = "0.4"
|
||||||
dtparse = "2.0"
|
dtparse = "2.0"
|
||||||
encoding_rs = "0.8"
|
encoding_rs = "0.8"
|
||||||
fancy-regex = "0.13"
|
fancy-regex = "0.13"
|
||||||
|
@ -93,7 +97,7 @@ heck = "0.5.0"
|
||||||
human-date-parser = "0.1.1"
|
human-date-parser = "0.1.1"
|
||||||
indexmap = "2.2"
|
indexmap = "2.2"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
interprocess = "2.1.0"
|
interprocess = "2.2.0"
|
||||||
is_executable = "1.0"
|
is_executable = "1.0"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
@ -112,26 +116,29 @@ mockito = { version = "1.4", default-features = false }
|
||||||
native-tls = "0.2"
|
native-tls = "0.2"
|
||||||
nix = { version = "0.28", default-features = false }
|
nix = { version = "0.28", default-features = false }
|
||||||
notify-debouncer-full = { version = "0.3", default-features = false }
|
notify-debouncer-full = { version = "0.3", default-features = false }
|
||||||
nu-ansi-term = "0.50.0"
|
nu-ansi-term = "0.50.1"
|
||||||
num-format = "0.4"
|
num-format = "0.4"
|
||||||
num-traits = "0.2"
|
num-traits = "0.2"
|
||||||
omnipath = "0.1"
|
omnipath = "0.1"
|
||||||
once_cell = "1.18"
|
once_cell = "1.18"
|
||||||
open = "5.1"
|
open = "5.3"
|
||||||
os_pipe = { version = "1.2", features = ["io_safety"] }
|
os_pipe = { version = "1.2", features = ["io_safety"] }
|
||||||
pathdiff = "0.2"
|
pathdiff = "0.2"
|
||||||
percent-encoding = "2"
|
percent-encoding = "2"
|
||||||
pretty_assertions = "1.4"
|
pretty_assertions = "1.4"
|
||||||
print-positions = "0.6"
|
print-positions = "0.6"
|
||||||
|
proc-macro-error = { version = "1.0", default-features = false }
|
||||||
|
proc-macro2 = "1.0"
|
||||||
procfs = "0.16.0"
|
procfs = "0.16.0"
|
||||||
pwd = "1.3"
|
pwd = "1.3"
|
||||||
quick-xml = "0.31.0"
|
quick-xml = "0.31.0"
|
||||||
quickcheck = "1.0"
|
quickcheck = "1.0"
|
||||||
quickcheck_macros = "1.0"
|
quickcheck_macros = "1.0"
|
||||||
|
quote = "1.0"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
ratatui = "0.26"
|
ratatui = "0.26"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
reedline = "0.32.0"
|
reedline = "0.33.0"
|
||||||
regex = "1.9.5"
|
regex = "1.9.5"
|
||||||
rmp = "0.8"
|
rmp = "0.8"
|
||||||
rmp-serde = "1.3"
|
rmp-serde = "1.3"
|
||||||
|
@ -139,7 +146,7 @@ ropey = "1.6.1"
|
||||||
roxmltree = "0.19"
|
roxmltree = "0.19"
|
||||||
rstest = { version = "0.18", default-features = false }
|
rstest = { version = "0.18", default-features = false }
|
||||||
rusqlite = "0.31"
|
rusqlite = "0.31"
|
||||||
rust-embed = "8.4.0"
|
rust-embed = "8.5.0"
|
||||||
same-file = "1.0"
|
same-file = "1.0"
|
||||||
serde = { version = "1.0", default-features = false }
|
serde = { version = "1.0", default-features = false }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
|
@ -147,6 +154,7 @@ serde_urlencoded = "0.7.1"
|
||||||
serde_yaml = "0.9"
|
serde_yaml = "0.9"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
strip-ansi-escapes = "0.2.0"
|
strip-ansi-escapes = "0.2.0"
|
||||||
|
syn = "2.0"
|
||||||
sysinfo = "0.30"
|
sysinfo = "0.30"
|
||||||
tabled = { version = "0.14.0", default-features = false }
|
tabled = { version = "0.14.0", default-features = false }
|
||||||
tempfile = "3.10"
|
tempfile = "3.10"
|
||||||
|
@ -157,44 +165,45 @@ trash = "3.3"
|
||||||
umask = "2.1"
|
umask = "2.1"
|
||||||
unicode-segmentation = "1.11"
|
unicode-segmentation = "1.11"
|
||||||
unicode-width = "0.1"
|
unicode-width = "0.1"
|
||||||
ureq = { version = "2.9", default-features = false }
|
ureq = { version = "2.10", default-features = false }
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
uu_cp = "0.0.25"
|
uu_cp = "0.0.27"
|
||||||
uu_mkdir = "0.0.25"
|
uu_mkdir = "0.0.27"
|
||||||
uu_mktemp = "0.0.25"
|
uu_mktemp = "0.0.27"
|
||||||
uu_mv = "0.0.25"
|
uu_mv = "0.0.27"
|
||||||
uu_whoami = "0.0.25"
|
uu_whoami = "0.0.27"
|
||||||
uu_uname = "0.0.25"
|
uu_uname = "0.0.27"
|
||||||
uucore = "0.0.25"
|
uucore = "0.0.27"
|
||||||
uuid = "1.8.0"
|
uuid = "1.10.0"
|
||||||
v_htmlescape = "0.15.0"
|
v_htmlescape = "0.15.0"
|
||||||
wax = "0.6"
|
wax = "0.6"
|
||||||
which = "6.0.0"
|
which = "6.0.0"
|
||||||
windows = "0.54"
|
windows = "0.54"
|
||||||
|
windows-sys = "0.48"
|
||||||
winreg = "0.52"
|
winreg = "0.52"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.94.3" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.96.2" }
|
||||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.94.3" }
|
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.96.2" }
|
||||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.94.3" }
|
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.96.2" }
|
||||||
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.94.3", optional = true }
|
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.96.2", optional = true }
|
||||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.94.3" }
|
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.96.2" }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.94.3" }
|
nu-command = { path = "./crates/nu-command", version = "0.96.2" }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.94.3" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.96.2" }
|
||||||
nu-explore = { path = "./crates/nu-explore", version = "0.94.3" }
|
nu-explore = { path = "./crates/nu-explore", version = "0.96.2" }
|
||||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.94.3" }
|
nu-lsp = { path = "./crates/nu-lsp/", version = "0.96.2" }
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.94.3" }
|
nu-parser = { path = "./crates/nu-parser", version = "0.96.2" }
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.94.3" }
|
nu-path = { path = "./crates/nu-path", version = "0.96.2" }
|
||||||
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.94.3" }
|
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.96.2" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.94.3" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.96.2" }
|
||||||
nu-std = { path = "./crates/nu-std", version = "0.94.3" }
|
nu-std = { path = "./crates/nu-std", version = "0.96.2" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.94.3" }
|
nu-system = { path = "./crates/nu-system", version = "0.96.2" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.94.3" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.96.2" }
|
||||||
|
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
crossterm = { workspace = true }
|
crossterm = { workspace = true }
|
||||||
ctrlc = { workspace = true }
|
ctrlc = { workspace = true }
|
||||||
|
dirs = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
|
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
|
||||||
mimalloc = { version = "0.1.42", default-features = false, optional = true }
|
mimalloc = { version = "0.1.42", default-features = false, optional = true }
|
||||||
|
@ -218,13 +227,14 @@ nix = { workspace = true, default-features = false, features = [
|
||||||
] }
|
] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.94.3" }
|
nu-test-support = { path = "./crates/nu-test-support", version = "0.96.2" }
|
||||||
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.94.3" }
|
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.96.2" }
|
||||||
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.94.3" }
|
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.96.2" }
|
||||||
assert_cmd = "2.0"
|
assert_cmd = "2.0"
|
||||||
dirs-next = { workspace = true }
|
dirs = { workspace = true }
|
||||||
tango-bench = "0.5"
|
tango-bench = "0.5"
|
||||||
pretty_assertions = { workspace = true }
|
pretty_assertions = { workspace = true }
|
||||||
|
regex = { workspace = true }
|
||||||
rstest = { workspace = true, default-features = false }
|
rstest = { workspace = true, default-features = false }
|
||||||
serial_test = "3.1"
|
serial_test = "3.1"
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
@ -244,7 +254,6 @@ default = ["default-no-clipboard", "system-clipboard"]
|
||||||
# See https://github.com/nushell/nushell/pull/11535
|
# See https://github.com/nushell/nushell/pull/11535
|
||||||
default-no-clipboard = [
|
default-no-clipboard = [
|
||||||
"plugin",
|
"plugin",
|
||||||
"which-support",
|
|
||||||
"trash-support",
|
"trash-support",
|
||||||
"sqlite",
|
"sqlite",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
|
@ -264,7 +273,6 @@ system-clipboard = [
|
||||||
]
|
]
|
||||||
|
|
||||||
# Stable (Default)
|
# Stable (Default)
|
||||||
which-support = ["nu-command/which-support", "nu-cmd-lang/which-support"]
|
|
||||||
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
|
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
|
||||||
|
|
||||||
# SQLite commands for nushell
|
# SQLite commands for nushell
|
||||||
|
@ -298,7 +306,7 @@ bench = false
|
||||||
# To use a development version of a dependency please use a global override here
|
# To use a development version of a dependency please use a global override here
|
||||||
# changing versions in each sub-crate of the workspace is tedious
|
# changing versions in each sub-crate of the workspace is tedious
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||||
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
||||||
|
|
||||||
# Run all benchmarks with `cargo bench`
|
# Run all benchmarks with `cargo bench`
|
||||||
|
|
|
@ -52,7 +52,7 @@ To use `Nu` in GitHub Action, check [setup-nu](https://github.com/marketplace/ac
|
||||||
|
|
||||||
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
|
Detailed installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). Nu is available via many package managers:
|
||||||
|
|
||||||
[](https://repology.org/project/nushell/versions)
|
[](https://repology.org/project/nushell/versions)
|
||||||
|
|
||||||
For details about which platforms the Nushell team actively supports, see [our platform support policy](devdocs/PLATFORM_SUPPORT.md).
|
For details about which platforms the Nushell team actively supports, see [our platform support policy](devdocs/PLATFORM_SUPPORT.md).
|
||||||
|
|
||||||
|
@ -222,6 +222,7 @@ Please submit an issue or PR to be added to this list.
|
||||||
- [clap](https://github.com/clap-rs/clap/tree/master/clap_complete_nushell)
|
- [clap](https://github.com/clap-rs/clap/tree/master/clap_complete_nushell)
|
||||||
- [Dorothy](http://github.com/bevry/dorothy)
|
- [Dorothy](http://github.com/bevry/dorothy)
|
||||||
- [Direnv](https://github.com/direnv/direnv/blob/master/docs/hook.md#nushell)
|
- [Direnv](https://github.com/direnv/direnv/blob/master/docs/hook.md#nushell)
|
||||||
|
- [x-cmd](https://x-cmd.com/mod/nu)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|
29
SECURITY.md
Normal file
29
SECURITY.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# Security Policy
|
||||||
|
|
||||||
|
As a shell and programming language Nushell provides you with great powers and the potential to do dangerous things to your computer and data. Whenever there is a risk that a malicious actor can abuse a bug or a violation of documented behavior/assumptions in Nushell to harm you this is a *security* risk.
|
||||||
|
We want to fix those issues without exposing our users to unnecessary risk. Thus we want to explain our security policy.
|
||||||
|
Additional issues may be part of *safety* where the behavior of Nushell as designed and implemented can cause unintended harm or a bug causes damage without the involvement of a third party.
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
As Nushell is still under very active pre-stable development, the only version the core team prioritizes for security and safety fixes is the [most recent version as published on GitHub](https://github.com/nushell/nushell/releases/latest).
|
||||||
|
Only if you provide a strong reasoning and the necessary resources, will we consider blessing a backported fix with an official patch release for a previous version.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you suspect that a bug or behavior of Nushell can affect security or may be potentially exploitable, please report the issue to us in private.
|
||||||
|
Either reach out to the core team on [our Discord server](https://discord.gg/NtAbbGn) to arrange a private channel or use the [GitHub vulnerability reporting form](https://github.com/nushell/nushell/security/advisories/new).
|
||||||
|
Please try to answer the following questions:
|
||||||
|
- How can we reach you for further questions?
|
||||||
|
- What is the bug? Which system of Nushell may be affected?
|
||||||
|
- Do you have proof-of-concept for a potential exploit or have you observed an exploit in the wild?
|
||||||
|
- What is your assessment of the severity based on what could be impacted should the bug be exploited?
|
||||||
|
- Are additional people aware of the issue or deserve credit for identifying the issue?
|
||||||
|
|
||||||
|
We will try to get back to you within a week with:
|
||||||
|
- acknowledging the receipt of the report
|
||||||
|
- an initial plan of how we want to address this including the primary points of contact for further communication
|
||||||
|
- our preliminary assessment of how severe we judge the issue
|
||||||
|
- a proposal for how we can coordinate responsible disclosure (e.g. how we ship the bugfix, if we need to coordinate with distribution maintainers, when you can release a blog post if you want to etc.)
|
||||||
|
|
||||||
|
For purely *safety* related issues where the impact is severe by direct user action instead of malicious input or third parties, feel free to open a regular issue. If we deem that there may be an additional *security* risk on a *safety* issue we may continue discussions in a restricted forum.
|
|
@ -4,11 +4,14 @@ use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
|
||||||
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
PipelineData, Span, Spanned, Value,
|
PipelineData, Signals, Span, Spanned, Value,
|
||||||
};
|
};
|
||||||
use nu_std::load_standard_library;
|
use nu_std::load_standard_library;
|
||||||
use nu_utils::{get_default_config, get_default_env};
|
use nu_utils::{get_default_config, get_default_env};
|
||||||
use std::rc::Rc;
|
use std::{
|
||||||
|
rc::Rc,
|
||||||
|
sync::{atomic::AtomicBool, Arc},
|
||||||
|
};
|
||||||
|
|
||||||
use std::hint::black_box;
|
use std::hint::black_box;
|
||||||
|
|
||||||
|
@ -42,13 +45,16 @@ fn setup_stack_and_engine_from_command(command: &str) -> (Stack, EngineState) {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
// Support running benchmarks with IR mode
|
||||||
|
stack.use_ir = std::env::var_os("NU_USE_IR").is_some();
|
||||||
|
|
||||||
evaluate_commands(
|
evaluate_commands(
|
||||||
&commands,
|
&commands,
|
||||||
&mut engine,
|
&mut engine,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
None,
|
Default::default(),
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
@ -90,8 +96,7 @@ fn bench_command(
|
||||||
&mut engine,
|
&mut engine,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
PipelineData::empty(),
|
PipelineData::empty(),
|
||||||
None,
|
Default::default(),
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
|
@ -250,14 +255,12 @@ fn bench_eval_interleave(n: i32) -> impl IntoBenchmarks {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_eval_interleave_with_ctrlc(n: i32) -> impl IntoBenchmarks {
|
fn bench_eval_interleave_with_interrupt(n: i32) -> impl IntoBenchmarks {
|
||||||
let mut engine = setup_engine();
|
let mut engine = setup_engine();
|
||||||
engine.ctrlc = Some(std::sync::Arc::new(std::sync::atomic::AtomicBool::new(
|
engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
|
||||||
false,
|
|
||||||
)));
|
|
||||||
let stack = Stack::new();
|
let stack = Stack::new();
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("eval_interleave_with_ctrlc_{n}"),
|
&format!("eval_interleave_with_interrupt_{n}"),
|
||||||
&format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
|
&format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
|
@ -445,9 +448,9 @@ tango_benchmarks!(
|
||||||
bench_eval_interleave(100),
|
bench_eval_interleave(100),
|
||||||
bench_eval_interleave(1_000),
|
bench_eval_interleave(1_000),
|
||||||
bench_eval_interleave(10_000),
|
bench_eval_interleave(10_000),
|
||||||
bench_eval_interleave_with_ctrlc(100),
|
bench_eval_interleave_with_interrupt(100),
|
||||||
bench_eval_interleave_with_ctrlc(1_000),
|
bench_eval_interleave_with_interrupt(1_000),
|
||||||
bench_eval_interleave_with_ctrlc(10_000),
|
bench_eval_interleave_with_interrupt(10_000),
|
||||||
// For
|
// For
|
||||||
bench_eval_for(1),
|
bench_eval_for(1),
|
||||||
bench_eval_for(10),
|
bench_eval_for(10),
|
||||||
|
|
|
@ -5,27 +5,27 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.94.3"
|
version = "0.96.2"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.3" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.96.2" }
|
||||||
nu-command = { path = "../nu-command", version = "0.94.3" }
|
nu-command = { path = "../nu-command", version = "0.96.2" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.94.3" }
|
nu-test-support = { path = "../nu-test-support", version = "0.96.2" }
|
||||||
rstest = { workspace = true, default-features = false }
|
rstest = { workspace = true, default-features = false }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.94.3" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.96.2" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.94.3" }
|
nu-engine = { path = "../nu-engine", version = "0.96.2" }
|
||||||
nu-path = { path = "../nu-path", version = "0.94.3" }
|
nu-path = { path = "../nu-path", version = "0.96.2" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.94.3" }
|
nu-parser = { path = "../nu-parser", version = "0.96.2" }
|
||||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.94.3", optional = true }
|
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.96.2", optional = true }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
|
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.94.3" }
|
nu-utils = { path = "../nu-utils", version = "0.96.2" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.94.3" }
|
nu-color-config = { path = "../nu-color-config", version = "0.96.2" }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
|
@ -46,4 +46,4 @@ which = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
plugin = ["nu-plugin-engine"]
|
plugin = ["nu-plugin-engine"]
|
||||||
system-clipboard = ["reedline/system_clipboard"]
|
system-clipboard = ["reedline/system_clipboard"]
|
7
crates/nu-cli/README.md
Normal file
7
crates/nu-cli/README.md
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
This crate implements the core functionality of the interactive Nushell REPL and interfaces with `reedline`.
|
||||||
|
Currently implements the syntax highlighting and completions logic.
|
||||||
|
Furthermore includes a few commands that are specific to `reedline`
|
||||||
|
|
||||||
|
## Internal Nushell crate
|
||||||
|
|
||||||
|
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.
|
|
@ -47,7 +47,7 @@ impl Command for History {
|
||||||
if let Some(config_path) = nu_path::config_dir() {
|
if let Some(config_path) = nu_path::config_dir() {
|
||||||
let clear = call.has_flag(engine_state, stack, "clear")?;
|
let clear = call.has_flag(engine_state, stack, "clear")?;
|
||||||
let long = call.has_flag(engine_state, stack, "long")?;
|
let long = call.has_flag(engine_state, stack, "long")?;
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let signals = engine_state.signals().clone();
|
||||||
|
|
||||||
let mut history_path = config_path;
|
let mut history_path = config_path;
|
||||||
history_path.push("nushell");
|
history_path.push("nushell");
|
||||||
|
@ -67,7 +67,7 @@ impl Command for History {
|
||||||
} else {
|
} else {
|
||||||
let history_reader: Option<Box<dyn ReedlineHistory>> = match history.file_format {
|
let history_reader: Option<Box<dyn ReedlineHistory>> = match history.file_format {
|
||||||
HistoryFileFormat::Sqlite => {
|
HistoryFileFormat::Sqlite => {
|
||||||
SqliteBackedHistory::with_file(history_path.clone(), None, None)
|
SqliteBackedHistory::with_file(history_path.clone().into(), None, None)
|
||||||
.map(|inner| {
|
.map(|inner| {
|
||||||
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
||||||
boxed
|
boxed
|
||||||
|
@ -77,7 +77,7 @@ impl Command for History {
|
||||||
|
|
||||||
HistoryFileFormat::PlainText => FileBackedHistory::with_file(
|
HistoryFileFormat::PlainText => FileBackedHistory::with_file(
|
||||||
history.max_size as usize,
|
history.max_size as usize,
|
||||||
history_path.clone(),
|
history_path.clone().into(),
|
||||||
)
|
)
|
||||||
.map(|inner| {
|
.map(|inner| {
|
||||||
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
||||||
|
@ -107,7 +107,7 @@ impl Command for History {
|
||||||
file: history_path.display().to_string(),
|
file: history_path.display().to_string(),
|
||||||
span: head,
|
span: head,
|
||||||
})?
|
})?
|
||||||
.into_pipeline_data(head, ctrlc)),
|
.into_pipeline_data(head, signals)),
|
||||||
HistoryFileFormat::Sqlite => Ok(history_reader
|
HistoryFileFormat::Sqlite => Ok(history_reader
|
||||||
.and_then(|h| {
|
.and_then(|h| {
|
||||||
h.search(SearchQuery::everything(SearchDirection::Forward, None))
|
h.search(SearchQuery::everything(SearchDirection::Forward, None))
|
||||||
|
@ -122,7 +122,7 @@ impl Command for History {
|
||||||
file: history_path.display().to_string(),
|
file: history_path.display().to_string(),
|
||||||
span: head,
|
span: head,
|
||||||
})?
|
})?
|
||||||
.into_pipeline_data(head, ctrlc)),
|
.into_pipeline_data(head, signals)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -156,58 +156,34 @@ fn create_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span)
|
||||||
//2. Create a record of either short or long columns and values
|
//2. Create a record of either short or long columns and values
|
||||||
|
|
||||||
let item_id_value = Value::int(
|
let item_id_value = Value::int(
|
||||||
match entry.id {
|
entry
|
||||||
Some(id) => {
|
.id
|
||||||
let ids = id.to_string();
|
.and_then(|id| id.to_string().parse::<i64>().ok())
|
||||||
match ids.parse::<i64>() {
|
.unwrap_or_default(),
|
||||||
Ok(i) => i,
|
|
||||||
_ => 0i64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => 0i64,
|
|
||||||
},
|
|
||||||
head,
|
head,
|
||||||
);
|
);
|
||||||
let start_timestamp_value = Value::string(
|
let start_timestamp_value = Value::string(
|
||||||
match entry.start_timestamp {
|
entry
|
||||||
Some(time) => time.to_string(),
|
.start_timestamp
|
||||||
None => "".into(),
|
.map(|time| time.to_string())
|
||||||
},
|
.unwrap_or_default(),
|
||||||
head,
|
head,
|
||||||
);
|
);
|
||||||
let command_value = Value::string(entry.command_line, head);
|
let command_value = Value::string(entry.command_line, head);
|
||||||
let session_id_value = Value::int(
|
let session_id_value = Value::int(
|
||||||
match entry.session_id {
|
entry
|
||||||
Some(sid) => {
|
.session_id
|
||||||
let sids = sid.to_string();
|
.and_then(|id| id.to_string().parse::<i64>().ok())
|
||||||
match sids.parse::<i64>() {
|
.unwrap_or_default(),
|
||||||
Ok(i) => i,
|
|
||||||
_ => 0i64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => 0i64,
|
|
||||||
},
|
|
||||||
head,
|
|
||||||
);
|
|
||||||
let hostname_value = Value::string(
|
|
||||||
match entry.hostname {
|
|
||||||
Some(host) => host,
|
|
||||||
None => "".into(),
|
|
||||||
},
|
|
||||||
head,
|
|
||||||
);
|
|
||||||
let cwd_value = Value::string(
|
|
||||||
match entry.cwd {
|
|
||||||
Some(cwd) => cwd,
|
|
||||||
None => "".into(),
|
|
||||||
},
|
|
||||||
head,
|
head,
|
||||||
);
|
);
|
||||||
|
let hostname_value = Value::string(entry.hostname.unwrap_or_default(), head);
|
||||||
|
let cwd_value = Value::string(entry.cwd.unwrap_or_default(), head);
|
||||||
let duration_value = Value::duration(
|
let duration_value = Value::duration(
|
||||||
match entry.duration {
|
entry
|
||||||
Some(d) => d.as_nanos().try_into().unwrap_or(0),
|
.duration
|
||||||
None => 0,
|
.and_then(|d| d.as_nanos().try_into().ok())
|
||||||
},
|
.unwrap_or(0),
|
||||||
head,
|
head,
|
||||||
);
|
);
|
||||||
let exit_status_value = Value::int(entry.exit_status.unwrap_or(0), head);
|
let exit_status_value = Value::int(entry.exit_status.unwrap_or(0), head);
|
||||||
|
|
|
@ -49,22 +49,26 @@ impl Command for KeybindingsList {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let records = if call.named_len() == 0 {
|
let all_options = ["modifiers", "keycodes", "edits", "modes", "events"];
|
||||||
let all_options = ["modifiers", "keycodes", "edits", "modes", "events"];
|
|
||||||
all_options
|
let presence = all_options
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|argument| get_records(argument, call.head))
|
.map(|option| call.has_flag(engine_state, stack, option))
|
||||||
.collect()
|
.collect::<Result<Vec<_>, ShellError>>()?;
|
||||||
} else {
|
|
||||||
call.named_iter()
|
let no_option_specified = presence.iter().all(|present| !*present);
|
||||||
.flat_map(|(argument, _, _)| get_records(argument.item.as_str(), call.head))
|
|
||||||
.collect()
|
let records = all_options
|
||||||
};
|
.iter()
|
||||||
|
.zip(presence)
|
||||||
|
.filter(|(_, present)| no_option_specified || *present)
|
||||||
|
.flat_map(|(option, _)| get_records(option, call.head))
|
||||||
|
.collect();
|
||||||
|
|
||||||
Ok(Value::list(records, call.head).into_pipeline_data())
|
Ok(Value::list(records, call.head).into_pipeline_data())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
use crate::completions::{CompletionOptions, SortBy};
|
use crate::completions::CompletionOptions;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
levenshtein_distance, Span,
|
Span,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
// Completer trait represents the three stages of the completion
|
|
||||||
// fetch, filter and sort
|
|
||||||
pub trait Completer {
|
pub trait Completer {
|
||||||
|
/// Fetch, filter, and sort completions
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn fetch(
|
fn fetch(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -19,32 +18,6 @@ pub trait Completer {
|
||||||
pos: usize,
|
pos: usize,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
) -> Vec<SemanticSuggestion>;
|
) -> Vec<SemanticSuggestion>;
|
||||||
|
|
||||||
fn get_sort_by(&self) -> SortBy {
|
|
||||||
SortBy::Ascending
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
|
|
||||||
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
|
||||||
let mut filtered_items = items;
|
|
||||||
|
|
||||||
// Sort items
|
|
||||||
match self.get_sort_by() {
|
|
||||||
SortBy::LevenshteinDistance => {
|
|
||||||
filtered_items.sort_by(|a, b| {
|
|
||||||
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
|
|
||||||
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
|
|
||||||
a_distance.cmp(&b_distance)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
SortBy::Ascending => {
|
|
||||||
filtered_items.sort_by(|a, b| a.suggestion.value.cmp(&b.suggestion.value));
|
|
||||||
}
|
|
||||||
SortBy::None => {}
|
|
||||||
};
|
|
||||||
|
|
||||||
filtered_items
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, PartialEq)]
|
#[derive(Debug, Default, PartialEq)]
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::{Completer, CompletionOptions, MatchAlgorithm, SortBy},
|
completions::{Completer, CompletionOptions, MatchAlgorithm},
|
||||||
SuggestionKind,
|
SuggestionKind,
|
||||||
};
|
};
|
||||||
use nu_parser::FlatShape;
|
use nu_parser::FlatShape;
|
||||||
|
@ -9,7 +9,7 @@ use nu_protocol::{
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
use super::SemanticSuggestion;
|
use super::{completion_common::sort_suggestions, SemanticSuggestion};
|
||||||
|
|
||||||
pub struct CommandCompletion {
|
pub struct CommandCompletion {
|
||||||
flattened: Vec<(Span, FlatShape)>,
|
flattened: Vec<(Span, FlatShape)>,
|
||||||
|
@ -99,10 +99,9 @@ impl CommandCompletion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(&x.0).to_string(),
|
value: String::from_utf8_lossy(&x.0).to_string(),
|
||||||
description: x.1,
|
description: x.1,
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span::new(span.start - offset, span.end - offset),
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Command(x.2)),
|
kind: Some(SuggestionKind::Command(x.2)),
|
||||||
})
|
})
|
||||||
|
@ -118,11 +117,9 @@ impl CommandCompletion {
|
||||||
.map(move |x| SemanticSuggestion {
|
.map(move |x| SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: x,
|
value: x,
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span::new(span.start - offset, span.end - offset),
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO: is there a way to create a test?
|
// TODO: is there a way to create a test?
|
||||||
kind: None,
|
kind: None,
|
||||||
|
@ -136,11 +133,9 @@ impl CommandCompletion {
|
||||||
results.push(SemanticSuggestion {
|
results.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: format!("^{}", external.suggestion.value),
|
value: format!("^{}", external.suggestion.value),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: external.suggestion.span,
|
span: external.suggestion.span,
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: external.kind,
|
kind: external.kind,
|
||||||
})
|
})
|
||||||
|
@ -161,7 +156,7 @@ impl Completer for CommandCompletion {
|
||||||
&mut self,
|
&mut self,
|
||||||
working_set: &StateWorkingSet,
|
working_set: &StateWorkingSet,
|
||||||
_stack: &Stack,
|
_stack: &Stack,
|
||||||
_prefix: Vec<u8>,
|
prefix: Vec<u8>,
|
||||||
span: Span,
|
span: Span,
|
||||||
offset: usize,
|
offset: usize,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
|
@ -198,7 +193,7 @@ impl Completer for CommandCompletion {
|
||||||
};
|
};
|
||||||
|
|
||||||
if !subcommands.is_empty() {
|
if !subcommands.is_empty() {
|
||||||
return subcommands;
|
return sort_suggestions(&String::from_utf8_lossy(&prefix), subcommands, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = working_set.get_config();
|
let config = working_set.get_config();
|
||||||
|
@ -223,11 +218,7 @@ impl Completer for CommandCompletion {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
subcommands.into_iter().chain(commands).collect::<Vec<_>>()
|
sort_suggestions(&String::from_utf8_lossy(&prefix), commands, options)
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sort_by(&self) -> SortBy {
|
|
||||||
SortBy::LevenshteinDistance
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,11 +48,11 @@ impl NuCompleter {
|
||||||
let options = CompletionOptions {
|
let options = CompletionOptions {
|
||||||
case_sensitive: config.case_sensitive_completions,
|
case_sensitive: config.case_sensitive_completions,
|
||||||
match_algorithm: config.completion_algorithm.into(),
|
match_algorithm: config.completion_algorithm.into(),
|
||||||
|
sort: config.completion_sort,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch
|
completer.fetch(
|
||||||
let mut suggestions = completer.fetch(
|
|
||||||
working_set,
|
working_set,
|
||||||
&self.stack,
|
&self.stack,
|
||||||
prefix.clone(),
|
prefix.clone(),
|
||||||
|
@ -60,12 +60,7 @@ impl NuCompleter {
|
||||||
offset,
|
offset,
|
||||||
pos,
|
pos,
|
||||||
&options,
|
&options,
|
||||||
);
|
)
|
||||||
|
|
||||||
// Sort
|
|
||||||
suggestions = completer.sort(suggestions, prefix);
|
|
||||||
|
|
||||||
suggestions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn external_completion(
|
fn external_completion(
|
||||||
|
@ -449,14 +444,11 @@ pub fn map_value_completions<'a>(
|
||||||
return Some(SemanticSuggestion {
|
return Some(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: s,
|
value: s,
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: span.start - offset,
|
start: span.start - offset,
|
||||||
end: span.end - offset,
|
end: span.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Type(x.get_type())),
|
kind: Some(SuggestionKind::Type(x.get_type())),
|
||||||
});
|
});
|
||||||
|
@ -466,14 +458,11 @@ pub fn map_value_completions<'a>(
|
||||||
if let Ok(record) = x.as_record() {
|
if let Ok(record) = x.as_record() {
|
||||||
let mut suggestion = Suggestion {
|
let mut suggestion = Suggestion {
|
||||||
value: String::from(""), // Initialize with empty string
|
value: String::from(""), // Initialize with empty string
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: span.start - offset,
|
start: span.start - offset,
|
||||||
end: span.end - offset,
|
end: span.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Iterate the cols looking for `value` and `description`
|
// Iterate the cols looking for `value` and `description`
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
use crate::completions::{matches, CompletionOptions};
|
use crate::{
|
||||||
|
completions::{matches, CompletionOptions},
|
||||||
|
SemanticSuggestion,
|
||||||
|
};
|
||||||
|
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use nu_engine::env_to_string;
|
use nu_engine::env_to_string;
|
||||||
use nu_path::{expand_to_real_path, home_dir};
|
use nu_path::{expand_to_real_path, home_dir};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Span,
|
CompletionSort, Span,
|
||||||
};
|
};
|
||||||
use nu_utils::get_ls_colors;
|
use nu_utils::get_ls_colors;
|
||||||
use std::path::{
|
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
|
||||||
is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR,
|
|
||||||
};
|
use super::MatchAlgorithm;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct PathBuiltFromString {
|
pub struct PathBuiltFromString {
|
||||||
|
@ -17,12 +21,21 @@ pub struct PathBuiltFromString {
|
||||||
isdir: bool,
|
isdir: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete_rec(
|
/// Recursively goes through paths that match a given `partial`.
|
||||||
|
/// built: State struct for a valid matching path built so far.
|
||||||
|
///
|
||||||
|
/// `isdir`: whether the current partial path has a trailing slash.
|
||||||
|
/// Parsing a path string into a pathbuf loses that bit of information.
|
||||||
|
///
|
||||||
|
/// 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.
|
||||||
|
pub fn complete_rec(
|
||||||
partial: &[&str],
|
partial: &[&str],
|
||||||
built: &PathBuiltFromString,
|
built: &PathBuiltFromString,
|
||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
dir: bool,
|
want_directory: bool,
|
||||||
isdir: bool,
|
isdir: bool,
|
||||||
) -> Vec<PathBuiltFromString> {
|
) -> Vec<PathBuiltFromString> {
|
||||||
let mut completions = vec![];
|
let mut completions = vec![];
|
||||||
|
@ -32,7 +45,7 @@ fn complete_rec(
|
||||||
let mut built = built.clone();
|
let mut built = built.clone();
|
||||||
built.parts.push(base.to_string());
|
built.parts.push(base.to_string());
|
||||||
built.isdir = true;
|
built.isdir = true;
|
||||||
return complete_rec(rest, &built, cwd, options, dir, isdir);
|
return complete_rec(rest, &built, cwd, options, want_directory, isdir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +58,7 @@ fn complete_rec(
|
||||||
return completions;
|
return completions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let mut entries = Vec::new();
|
||||||
for entry in result.filter_map(|e| e.ok()) {
|
for entry in result.filter_map(|e| e.ok()) {
|
||||||
let entry_name = entry.file_name().to_string_lossy().into_owned();
|
let entry_name = entry.file_name().to_string_lossy().into_owned();
|
||||||
let entry_isdir = entry.path().is_dir();
|
let entry_isdir = entry.path().is_dir();
|
||||||
|
@ -52,22 +66,45 @@ fn complete_rec(
|
||||||
built.parts.push(entry_name.clone());
|
built.parts.push(entry_name.clone());
|
||||||
built.isdir = entry_isdir;
|
built.isdir = entry_isdir;
|
||||||
|
|
||||||
if !dir || entry_isdir {
|
if !want_directory || entry_isdir {
|
||||||
match partial.split_first() {
|
entries.push((entry_name, built));
|
||||||
Some((base, rest)) => {
|
}
|
||||||
if matches(base, &entry_name, options) {
|
}
|
||||||
if !rest.is_empty() || isdir {
|
|
||||||
completions
|
let prefix = partial.first().unwrap_or(&"");
|
||||||
.extend(complete_rec(rest, &built, cwd, options, dir, isdir));
|
let sorted_entries = sort_completions(prefix, entries, options, |(entry, _)| entry);
|
||||||
} else {
|
|
||||||
completions.push(built);
|
for (entry_name, built) in sorted_entries {
|
||||||
}
|
match partial.split_first() {
|
||||||
|
Some((base, rest)) => {
|
||||||
|
if matches(base, &entry_name, options) {
|
||||||
|
// We use `isdir` to confirm that the current component has
|
||||||
|
// at least one next component or a slash.
|
||||||
|
// Serves as confirmation to ignore longer completions for
|
||||||
|
// components in between.
|
||||||
|
if !rest.is_empty() || isdir {
|
||||||
|
completions.extend(complete_rec(
|
||||||
|
rest,
|
||||||
|
&built,
|
||||||
|
cwd,
|
||||||
|
options,
|
||||||
|
want_directory,
|
||||||
|
isdir,
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
completions.push(built);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
if entry_name.eq(base)
|
||||||
completions.push(built);
|
&& matches!(options.match_algorithm, MatchAlgorithm::Prefix)
|
||||||
|
&& isdir
|
||||||
|
{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
None => {
|
||||||
|
completions.push(built);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
completions
|
completions
|
||||||
|
@ -81,16 +118,16 @@ enum OriginalCwd {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OriginalCwd {
|
impl OriginalCwd {
|
||||||
fn apply(&self, mut p: PathBuiltFromString) -> String {
|
fn apply(&self, mut p: PathBuiltFromString, path_separator: char) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::None => {}
|
Self::None => {}
|
||||||
Self::Home => p.parts.insert(0, "~".to_string()),
|
Self::Home => p.parts.insert(0, "~".to_string()),
|
||||||
Self::Prefix(s) => p.parts.insert(0, s.clone()),
|
Self::Prefix(s) => p.parts.insert(0, s.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ret = p.parts.join(MAIN_SEPARATOR_STR);
|
let mut ret = p.parts.join(&path_separator.to_string());
|
||||||
if p.isdir {
|
if p.isdir {
|
||||||
ret.push(SEP);
|
ret.push(path_separator);
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
@ -121,6 +158,14 @@ pub fn complete_item(
|
||||||
) -> Vec<(nu_protocol::Span, String, Option<Style>)> {
|
) -> Vec<(nu_protocol::Span, String, Option<Style>)> {
|
||||||
let partial = surround_remove(partial);
|
let partial = surround_remove(partial);
|
||||||
let isdir = partial.ends_with(is_separator);
|
let isdir = partial.ends_with(is_separator);
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let path_separator = SEP;
|
||||||
|
#[cfg(windows)]
|
||||||
|
let path_separator = partial
|
||||||
|
.chars()
|
||||||
|
.rfind(|c: &char| is_separator(*c))
|
||||||
|
.unwrap_or(SEP);
|
||||||
let cwd_pathbuf = Path::new(cwd).to_path_buf();
|
let cwd_pathbuf = Path::new(cwd).to_path_buf();
|
||||||
let ls_colors = (engine_state.config.use_ls_colors_completions
|
let ls_colors = (engine_state.config.use_ls_colors_completions
|
||||||
&& engine_state.config.use_ansi_coloring)
|
&& engine_state.config.use_ansi_coloring)
|
||||||
|
@ -158,7 +203,7 @@ pub fn complete_item(
|
||||||
}
|
}
|
||||||
Some(Component::Normal(home)) if home.to_string_lossy() == "~" => {
|
Some(Component::Normal(home)) if home.to_string_lossy() == "~" => {
|
||||||
components.next();
|
components.next();
|
||||||
cwd = home_dir().unwrap_or(cwd_pathbuf);
|
cwd = home_dir().map(Into::into).unwrap_or(cwd_pathbuf);
|
||||||
prefix_len = 1;
|
prefix_len = 1;
|
||||||
original_cwd = OriginalCwd::Home;
|
original_cwd = OriginalCwd::Home;
|
||||||
}
|
}
|
||||||
|
@ -183,7 +228,7 @@ pub fn complete_item(
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
let path = original_cwd.apply(p);
|
let path = original_cwd.apply(p, path_separator);
|
||||||
let style = ls_colors.as_ref().map(|lsc| {
|
let style = ls_colors.as_ref().map(|lsc| {
|
||||||
lsc.style_for_path_with_metadata(
|
lsc.style_for_path_with_metadata(
|
||||||
&path,
|
&path,
|
||||||
|
@ -256,3 +301,42 @@ pub fn adjust_if_intermediate(
|
||||||
readjusted,
|
readjusted,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Convenience function to sort suggestions using [`sort_completions`]
|
||||||
|
pub fn sort_suggestions(
|
||||||
|
prefix: &str,
|
||||||
|
items: Vec<SemanticSuggestion>,
|
||||||
|
options: &CompletionOptions,
|
||||||
|
) -> Vec<SemanticSuggestion> {
|
||||||
|
sort_completions(prefix, items, options, |it| &it.suggestion.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Arguments
|
||||||
|
/// * `prefix` - What the user's typed, for sorting by fuzzy matcher score
|
||||||
|
pub fn sort_completions<T>(
|
||||||
|
prefix: &str,
|
||||||
|
mut items: Vec<T>,
|
||||||
|
options: &CompletionOptions,
|
||||||
|
get_value: fn(&T) -> &str,
|
||||||
|
) -> Vec<T> {
|
||||||
|
// Sort items
|
||||||
|
if options.sort == CompletionSort::Smart && options.match_algorithm == MatchAlgorithm::Fuzzy {
|
||||||
|
let mut matcher = SkimMatcherV2::default();
|
||||||
|
if options.case_sensitive {
|
||||||
|
matcher = matcher.respect_case();
|
||||||
|
} else {
|
||||||
|
matcher = matcher.ignore_case();
|
||||||
|
};
|
||||||
|
items.sort_by(|a, b| {
|
||||||
|
let a_str = get_value(a);
|
||||||
|
let b_str = get_value(b);
|
||||||
|
let a_score = matcher.fuzzy_match(a_str, prefix).unwrap_or_default();
|
||||||
|
let b_score = matcher.fuzzy_match(b_str, prefix).unwrap_or_default();
|
||||||
|
b_score.cmp(&a_score).then(a_str.cmp(b_str))
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
items
|
||||||
|
}
|
||||||
|
|
|
@ -1,17 +1,10 @@
|
||||||
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
||||||
use nu_parser::trim_quotes_str;
|
use nu_parser::trim_quotes_str;
|
||||||
use nu_protocol::CompletionAlgorithm;
|
use nu_protocol::{CompletionAlgorithm, CompletionSort};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum SortBy {
|
|
||||||
LevenshteinDistance,
|
|
||||||
Ascending,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes how suggestions should be matched.
|
/// Describes how suggestions should be matched.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum MatchAlgorithm {
|
pub enum MatchAlgorithm {
|
||||||
/// Only show suggestions which begin with the given input
|
/// Only show suggestions which begin with the given input
|
||||||
///
|
///
|
||||||
|
@ -96,6 +89,7 @@ pub struct CompletionOptions {
|
||||||
pub case_sensitive: bool,
|
pub case_sensitive: bool,
|
||||||
pub positional: bool,
|
pub positional: bool,
|
||||||
pub match_algorithm: MatchAlgorithm,
|
pub match_algorithm: MatchAlgorithm,
|
||||||
|
pub sort: CompletionSort,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CompletionOptions {
|
impl Default for CompletionOptions {
|
||||||
|
@ -104,6 +98,7 @@ impl Default for CompletionOptions {
|
||||||
case_sensitive: true,
|
case_sensitive: true,
|
||||||
positional: true,
|
positional: true,
|
||||||
match_algorithm: MatchAlgorithm::Prefix,
|
match_algorithm: MatchAlgorithm::Prefix,
|
||||||
|
sort: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,23 @@
|
||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
completer::map_value_completions, Completer, CompletionOptions, MatchAlgorithm,
|
completer::map_value_completions, Completer, CompletionOptions, MatchAlgorithm,
|
||||||
SemanticSuggestion, SortBy,
|
SemanticSuggestion,
|
||||||
};
|
};
|
||||||
use nu_engine::eval_call;
|
use nu_engine::eval_call;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Argument, Call, Expr, Expression},
|
ast::{Argument, Call, Expr, Expression},
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
PipelineData, Span, Type, Value,
|
CompletionSort, PipelineData, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use nu_utils::IgnoreCaseExt;
|
use nu_utils::IgnoreCaseExt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use super::completion_common::sort_suggestions;
|
||||||
|
|
||||||
pub struct CustomCompletion {
|
pub struct CustomCompletion {
|
||||||
stack: Stack,
|
stack: Stack,
|
||||||
decl_id: usize,
|
decl_id: usize,
|
||||||
line: String,
|
line: String,
|
||||||
sort_by: SortBy,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomCompletion {
|
impl CustomCompletion {
|
||||||
|
@ -25,7 +26,6 @@ impl CustomCompletion {
|
||||||
stack,
|
stack,
|
||||||
decl_id,
|
decl_id,
|
||||||
line,
|
line,
|
||||||
sort_by: SortBy::None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,10 +91,6 @@ impl Completer for CustomCompletion {
|
||||||
.and_then(|val| val.as_bool().ok())
|
.and_then(|val| val.as_bool().ok())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if should_sort {
|
|
||||||
self.sort_by = SortBy::Ascending;
|
|
||||||
}
|
|
||||||
|
|
||||||
custom_completion_options = Some(CompletionOptions {
|
custom_completion_options = Some(CompletionOptions {
|
||||||
case_sensitive: options
|
case_sensitive: options
|
||||||
.get("case_sensitive")
|
.get("case_sensitive")
|
||||||
|
@ -112,6 +108,11 @@ impl Completer for CustomCompletion {
|
||||||
.unwrap_or(MatchAlgorithm::Prefix),
|
.unwrap_or(MatchAlgorithm::Prefix),
|
||||||
None => completion_options.match_algorithm,
|
None => completion_options.match_algorithm,
|
||||||
},
|
},
|
||||||
|
sort: if should_sort {
|
||||||
|
CompletionSort::Alphabetical
|
||||||
|
} else {
|
||||||
|
CompletionSort::Smart
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,15 +123,11 @@ impl Completer for CustomCompletion {
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
if let Some(custom_completion_options) = custom_completion_options {
|
let options = custom_completion_options
|
||||||
filter(&prefix, suggestions, &custom_completion_options)
|
.as_ref()
|
||||||
} else {
|
.unwrap_or(completion_options);
|
||||||
filter(&prefix, suggestions, completion_options)
|
let suggestions = filter(&prefix, suggestions, completion_options);
|
||||||
}
|
sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, options)
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sort_by(&self) -> SortBy {
|
|
||||||
self.sort_by
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
||||||
Completer, CompletionOptions, SortBy,
|
Completer, CompletionOptions,
|
||||||
};
|
};
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
levenshtein_distance, Span,
|
Span,
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::path::{Path, MAIN_SEPARATOR as SEP};
|
use std::path::Path;
|
||||||
|
|
||||||
use super::SemanticSuggestion;
|
use super::SemanticSuggestion;
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ impl Completer for DirectoryCompletion {
|
||||||
|
|
||||||
// Filter only the folders
|
// Filter only the folders
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let output: Vec<_> = directory_completion(
|
let items: Vec<_> = directory_completion(
|
||||||
span,
|
span,
|
||||||
&prefix,
|
&prefix,
|
||||||
&working_set.permanent_state.current_work_dir(),
|
&working_set.permanent_state.current_work_dir(),
|
||||||
|
@ -48,55 +48,23 @@ impl Completer for DirectoryCompletion {
|
||||||
.map(move |x| SemanticSuggestion {
|
.map(move |x| SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: x.1,
|
value: x.1,
|
||||||
description: None,
|
|
||||||
style: x.2,
|
style: x.2,
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: x.0.start - offset,
|
start: x.0.start - offset,
|
||||||
end: x.0.end - offset,
|
end: x.0.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort results prioritizing the non hidden folders
|
|
||||||
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
|
|
||||||
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
|
||||||
|
|
||||||
// Sort items
|
|
||||||
let mut sorted_items = items;
|
|
||||||
|
|
||||||
match self.get_sort_by() {
|
|
||||||
SortBy::Ascending => {
|
|
||||||
sorted_items.sort_by(|a, b| {
|
|
||||||
// Ignore trailing slashes in folder names when sorting
|
|
||||||
a.suggestion
|
|
||||||
.value
|
|
||||||
.trim_end_matches(SEP)
|
|
||||||
.cmp(b.suggestion.value.trim_end_matches(SEP))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
SortBy::LevenshteinDistance => {
|
|
||||||
sorted_items.sort_by(|a, b| {
|
|
||||||
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
|
|
||||||
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
|
|
||||||
a_distance.cmp(&b_distance)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separate the results between hidden and non hidden
|
// Separate the results between hidden and non hidden
|
||||||
let mut hidden: Vec<SemanticSuggestion> = vec![];
|
let mut hidden: Vec<SemanticSuggestion> = vec![];
|
||||||
let mut non_hidden: Vec<SemanticSuggestion> = vec![];
|
let mut non_hidden: Vec<SemanticSuggestion> = vec![];
|
||||||
|
|
||||||
for item in sorted_items.into_iter() {
|
for item in items.into_iter() {
|
||||||
let item_path = Path::new(&item.suggestion.value);
|
let item_path = Path::new(&item.suggestion.value);
|
||||||
|
|
||||||
if let Some(value) = item_path.file_name() {
|
if let Some(value) = item_path.file_name() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::completions::{file_path_completion, Completer, CompletionOptions, SortBy};
|
use crate::completions::{file_path_completion, Completer, CompletionOptions};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
Span,
|
Span,
|
||||||
|
@ -6,7 +6,7 @@ use nu_protocol::{
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
|
use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
|
||||||
|
|
||||||
use super::SemanticSuggestion;
|
use super::{completion_common::sort_suggestions, SemanticSuggestion};
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct DotNuCompletion {}
|
pub struct DotNuCompletion {}
|
||||||
|
@ -116,14 +116,13 @@ impl Completer for DotNuCompletion {
|
||||||
.map(move |x| SemanticSuggestion {
|
.map(move |x| SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: x.1,
|
value: x.1,
|
||||||
description: None,
|
|
||||||
style: x.2,
|
style: x.2,
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: x.0.start - offset,
|
start: x.0.start - offset,
|
||||||
end: x.0.end - offset,
|
end: x.0.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
|
@ -131,10 +130,6 @@ impl Completer for DotNuCompletion {
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
output
|
sort_suggestions(&prefix_str, output, options)
|
||||||
}
|
|
||||||
|
|
||||||
fn get_sort_by(&self) -> SortBy {
|
|
||||||
SortBy::LevenshteinDistance
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
||||||
Completer, CompletionOptions, SortBy,
|
Completer, CompletionOptions,
|
||||||
};
|
};
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
levenshtein_distance, Span,
|
Span,
|
||||||
};
|
};
|
||||||
use nu_utils::IgnoreCaseExt;
|
use nu_utils::IgnoreCaseExt;
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::path::{Path, MAIN_SEPARATOR as SEP};
|
use std::path::Path;
|
||||||
|
|
||||||
use super::SemanticSuggestion;
|
use super::SemanticSuggestion;
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ impl Completer for FileCompletion {
|
||||||
} = adjust_if_intermediate(&prefix, working_set, span);
|
} = adjust_if_intermediate(&prefix, working_set, span);
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let output: Vec<_> = complete_item(
|
let items: Vec<_> = complete_item(
|
||||||
readjusted,
|
readjusted,
|
||||||
span,
|
span,
|
||||||
&prefix,
|
&prefix,
|
||||||
|
@ -53,55 +53,25 @@ impl Completer for FileCompletion {
|
||||||
.map(move |x| SemanticSuggestion {
|
.map(move |x| SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: x.1,
|
value: x.1,
|
||||||
description: None,
|
|
||||||
style: x.2,
|
style: x.2,
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: x.0.start - offset,
|
start: x.0.start - offset,
|
||||||
end: x.0.end - offset,
|
end: x.0.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
output
|
// Sort results prioritizing the non hidden folders
|
||||||
}
|
|
||||||
|
|
||||||
// Sort results prioritizing the non hidden folders
|
|
||||||
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
|
|
||||||
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
|
|
||||||
|
|
||||||
// Sort items
|
|
||||||
let mut sorted_items = items;
|
|
||||||
|
|
||||||
match self.get_sort_by() {
|
|
||||||
SortBy::Ascending => {
|
|
||||||
sorted_items.sort_by(|a, b| {
|
|
||||||
// Ignore trailing slashes in folder names when sorting
|
|
||||||
a.suggestion
|
|
||||||
.value
|
|
||||||
.trim_end_matches(SEP)
|
|
||||||
.cmp(b.suggestion.value.trim_end_matches(SEP))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
SortBy::LevenshteinDistance => {
|
|
||||||
sorted_items.sort_by(|a, b| {
|
|
||||||
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
|
|
||||||
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
|
|
||||||
a_distance.cmp(&b_distance)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
// Separate the results between hidden and non hidden
|
// Separate the results between hidden and non hidden
|
||||||
let mut hidden: Vec<SemanticSuggestion> = vec![];
|
let mut hidden: Vec<SemanticSuggestion> = vec![];
|
||||||
let mut non_hidden: Vec<SemanticSuggestion> = vec![];
|
let mut non_hidden: Vec<SemanticSuggestion> = vec![];
|
||||||
|
|
||||||
for item in sorted_items.into_iter() {
|
for item in items.into_iter() {
|
||||||
let item_path = Path::new(&item.suggestion.value);
|
let item_path = Path::new(&item.suggestion.value);
|
||||||
|
|
||||||
if let Some(value) = item_path.file_name() {
|
if let Some(value) = item_path.file_name() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::completions::{Completer, CompletionOptions};
|
use crate::completions::{completion_common::sort_suggestions, Completer, CompletionOptions};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Expr, Expression},
|
ast::{Expr, Expression},
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
|
@ -49,13 +49,12 @@ impl Completer for FlagCompletion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(&named).to_string(),
|
value: String::from_utf8_lossy(&named).to_string(),
|
||||||
description: Some(flag_desc.to_string()),
|
description: Some(flag_desc.to_string()),
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: span.start - offset,
|
start: span.start - offset,
|
||||||
end: span.end - offset,
|
end: span.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
|
@ -76,13 +75,12 @@ impl Completer for FlagCompletion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(&named).to_string(),
|
value: String::from_utf8_lossy(&named).to_string(),
|
||||||
description: Some(flag_desc.to_string()),
|
description: Some(flag_desc.to_string()),
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: span.start - offset,
|
start: span.start - offset,
|
||||||
end: span.end - offset,
|
end: span.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
|
@ -90,7 +88,7 @@ impl Completer for FlagCompletion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return sort_suggestions(&String::from_utf8_lossy(&prefix), output, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec![]
|
vec![]
|
||||||
|
|
|
@ -13,7 +13,7 @@ mod variable_completions;
|
||||||
pub use base::{Completer, SemanticSuggestion, SuggestionKind};
|
pub use base::{Completer, SemanticSuggestion, SuggestionKind};
|
||||||
pub use command_completions::CommandCompletion;
|
pub use command_completions::CommandCompletion;
|
||||||
pub use completer::NuCompleter;
|
pub use completer::NuCompleter;
|
||||||
pub use completion_options::{CompletionOptions, MatchAlgorithm, SortBy};
|
pub use completion_options::{CompletionOptions, MatchAlgorithm};
|
||||||
pub use custom_completions::CustomCompletion;
|
pub use custom_completions::CustomCompletion;
|
||||||
pub use directory_completions::DirectoryCompletion;
|
pub use directory_completions::DirectoryCompletion;
|
||||||
pub use dotnu_completions::DotNuCompletion;
|
pub use dotnu_completions::DotNuCompletion;
|
||||||
|
|
|
@ -9,6 +9,8 @@ use nu_protocol::{
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
|
use super::completion_common::sort_suggestions;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VariableCompletion {
|
pub struct VariableCompletion {
|
||||||
var_context: (Vec<u8>, Vec<Vec<u8>>), // tuple with $var and the sublevels (.b.c.d)
|
var_context: (Vec<u8>, Vec<Vec<u8>>), // tuple with $var and the sublevels (.b.c.d)
|
||||||
|
@ -40,6 +42,7 @@ impl Completer for VariableCompletion {
|
||||||
end: span.end - offset,
|
end: span.end - offset,
|
||||||
};
|
};
|
||||||
let sublevels_count = self.var_context.1.len();
|
let sublevels_count = self.var_context.1.len();
|
||||||
|
let prefix_str = String::from_utf8_lossy(&prefix);
|
||||||
|
|
||||||
// Completions for the given variable
|
// Completions for the given variable
|
||||||
if !var_str.is_empty() {
|
if !var_str.is_empty() {
|
||||||
|
@ -69,7 +72,7 @@ impl Completer for VariableCompletion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return sort_suggestions(&prefix_str, output, options);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No nesting provided, return all env vars
|
// No nesting provided, return all env vars
|
||||||
|
@ -82,18 +85,15 @@ impl Completer for VariableCompletion {
|
||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: env_var.0,
|
value: env_var.0,
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Type(env_var.1.get_type())),
|
kind: Some(SuggestionKind::Type(env_var.1.get_type())),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return sort_suggestions(&prefix_str, output, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +117,7 @@ impl Completer for VariableCompletion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return sort_suggestions(&prefix_str, output, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,7 +139,7 @@ impl Completer for VariableCompletion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return output;
|
return sort_suggestions(&prefix_str, output, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,11 +154,8 @@ impl Completer for VariableCompletion {
|
||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: builtin.to_string(),
|
value: builtin.to_string(),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO is there a way to get the VarId to get the type???
|
// TODO is there a way to get the VarId to get the type???
|
||||||
kind: None,
|
kind: None,
|
||||||
|
@ -181,11 +178,8 @@ impl Completer for VariableCompletion {
|
||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(v.0).to_string(),
|
value: String::from_utf8_lossy(v.0).to_string(),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Type(
|
kind: Some(SuggestionKind::Type(
|
||||||
working_set.get_variable(*v.1).ty.clone(),
|
working_set.get_variable(*v.1).ty.clone(),
|
||||||
|
@ -212,11 +206,8 @@ impl Completer for VariableCompletion {
|
||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(v.0).to_string(),
|
value: String::from_utf8_lossy(v.0).to_string(),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Type(
|
kind: Some(SuggestionKind::Type(
|
||||||
working_set.get_variable(*v.1).ty.clone(),
|
working_set.get_variable(*v.1).ty.clone(),
|
||||||
|
@ -226,6 +217,8 @@ impl Completer for VariableCompletion {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
output = sort_suggestions(&prefix_str, output, options);
|
||||||
|
|
||||||
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
|
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
|
||||||
|
|
||||||
output
|
output
|
||||||
|
@ -250,11 +243,8 @@ fn nested_suggestions(
|
||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: col.clone(),
|
value: col.clone(),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(kind.clone()),
|
kind: Some(kind.clone()),
|
||||||
});
|
});
|
||||||
|
@ -267,11 +257,8 @@ fn nested_suggestions(
|
||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: column_name,
|
value: column_name,
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(kind.clone()),
|
kind: Some(kind.clone()),
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
||||||
report_error_new, HistoryFileFormat, PipelineData,
|
report_error_new, HistoryFileFormat, PipelineData,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::perf;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
|
@ -53,13 +53,10 @@ pub fn read_plugin_file(
|
||||||
// Reading signatures from plugin registry file
|
// Reading signatures from plugin registry file
|
||||||
// The plugin.msgpackz file stores the parsed signature collected from each registered plugin
|
// The plugin.msgpackz file stores the parsed signature collected from each registered plugin
|
||||||
add_plugin_file(engine_state, plugin_file.clone(), storage_path);
|
add_plugin_file(engine_state, plugin_file.clone(), storage_path);
|
||||||
perf(
|
perf!(
|
||||||
"add plugin file to engine_state",
|
"add plugin file to engine_state",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
engine_state.get_config().use_ansi_coloring
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
engine_state.get_config().use_ansi_coloring,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
|
@ -137,13 +134,10 @@ pub fn read_plugin_file(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
&format!("read plugin file {}", plugin_path.display()),
|
&format!("read plugin file {}", plugin_path.display()),
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
engine_state.get_config().use_ansi_coloring
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
engine_state.get_config().use_ansi_coloring,
|
|
||||||
);
|
);
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
|
|
||||||
|
@ -156,13 +150,10 @@ pub fn read_plugin_file(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
&format!("load plugin file {}", plugin_path.display()),
|
&format!("load plugin file {}", plugin_path.display()),
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
engine_state.get_config().use_ansi_coloring
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
engine_state.get_config().use_ansi_coloring,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -201,7 +192,8 @@ pub fn add_plugin_file(
|
||||||
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
||||||
// Path to store plugins signatures
|
// Path to store plugins signatures
|
||||||
plugin_path.push(storage_path);
|
plugin_path.push(storage_path);
|
||||||
let mut plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
let mut plugin_path =
|
||||||
|
canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path.into());
|
||||||
plugin_path.push(PLUGIN_FILE);
|
plugin_path.push(PLUGIN_FILE);
|
||||||
let plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
let plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
||||||
engine_state.plugin_path = Some(plugin_path);
|
engine_state.plugin_path = Some(plugin_path);
|
||||||
|
@ -256,7 +248,7 @@ pub(crate) fn get_history_path(storage_path: &str, mode: HistoryFileFormat) -> O
|
||||||
HistoryFileFormat::PlainText => HISTORY_FILE_TXT,
|
HistoryFileFormat::PlainText => HISTORY_FILE_TXT,
|
||||||
HistoryFileFormat::Sqlite => HISTORY_FILE_SQLITE,
|
HistoryFileFormat::Sqlite => HISTORY_FILE_SQLITE,
|
||||||
});
|
});
|
||||||
history_path
|
history_path.into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,7 +336,10 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState, storage_path: &str) -
|
||||||
name: identity.name().to_owned(),
|
name: identity.name().to_owned(),
|
||||||
filename: identity.filename().to_owned(),
|
filename: identity.filename().to_owned(),
|
||||||
shell: identity.shell().map(|p| p.to_owned()),
|
shell: identity.shell().map(|p| p.to_owned()),
|
||||||
data: PluginRegistryItemData::Valid { commands },
|
data: PluginRegistryItemData::Valid {
|
||||||
|
metadata: Default::default(),
|
||||||
|
commands,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -378,13 +373,10 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState, storage_path: &str) -
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"migrate old plugin file",
|
"migrate old plugin file",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
engine_state.get_config().use_ansi_coloring
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
engine_state.get_config().use_ansi_coloring,
|
|
||||||
);
|
);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,24 +8,53 @@ use nu_protocol::{
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct EvaluateCommandsOpts {
|
||||||
|
pub table_mode: Option<Value>,
|
||||||
|
pub error_style: Option<Value>,
|
||||||
|
pub no_newline: bool,
|
||||||
|
}
|
||||||
|
|
||||||
/// Run a command (or commands) given to us by the user
|
/// Run a command (or commands) given to us by the user
|
||||||
pub fn evaluate_commands(
|
pub fn evaluate_commands(
|
||||||
commands: &Spanned<String>,
|
commands: &Spanned<String>,
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
table_mode: Option<Value>,
|
opts: EvaluateCommandsOpts,
|
||||||
no_newline: bool,
|
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
|
let EvaluateCommandsOpts {
|
||||||
|
table_mode,
|
||||||
|
error_style,
|
||||||
|
no_newline,
|
||||||
|
} = opts;
|
||||||
|
|
||||||
|
// Handle the configured error style early
|
||||||
|
if let Some(e_style) = error_style {
|
||||||
|
match e_style.coerce_str()?.parse() {
|
||||||
|
Ok(e_style) => {
|
||||||
|
Arc::make_mut(&mut engine_state.config).error_style = e_style;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
return Err(ShellError::GenericError {
|
||||||
|
error: "Invalid value for `--error-style`".into(),
|
||||||
|
msg: err.into(),
|
||||||
|
span: Some(e_style.span()),
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
convert_env_values(engine_state, stack)?;
|
convert_env_values(engine_state, stack)?;
|
||||||
|
|
||||||
// Parse the source code
|
// Parse the source code
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
if let Some(ref t_mode) = table_mode {
|
if let Some(ref t_mode) = table_mode {
|
||||||
let mut config = engine_state.get_config().clone();
|
Arc::make_mut(&mut engine_state.config).table_mode =
|
||||||
config.table_mode = t_mode.coerce_str()?.parse().unwrap_or_default();
|
t_mode.coerce_str()?.parse().unwrap_or_default();
|
||||||
engine_state.set_config(config);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
@ -40,6 +69,11 @@ pub fn evaluate_commands(
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(err) = working_set.compile_errors.first() {
|
||||||
|
report_error(&working_set, err);
|
||||||
|
// Not a fatal error, for now
|
||||||
|
}
|
||||||
|
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -76,12 +76,21 @@ pub fn evaluate_file(
|
||||||
trace!("parsing file: {}", file_path_str);
|
trace!("parsing file: {}", file_path_str);
|
||||||
let block = parse(&mut working_set, Some(file_path_str), &file, false);
|
let block = parse(&mut working_set, Some(file_path_str), &file, false);
|
||||||
|
|
||||||
|
if let Some(warning) = working_set.parse_warnings.first() {
|
||||||
|
report_error(&working_set, warning);
|
||||||
|
}
|
||||||
|
|
||||||
// If any parse errors were found, report the first error and exit.
|
// If any parse errors were found, report the first error and exit.
|
||||||
if let Some(err) = working_set.parse_errors.first() {
|
if let Some(err) = working_set.parse_errors.first() {
|
||||||
report_error(&working_set, err);
|
report_error(&working_set, err);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(err) = working_set.compile_errors.first() {
|
||||||
|
report_error(&working_set, err);
|
||||||
|
// Not a fatal error, for now
|
||||||
|
}
|
||||||
|
|
||||||
// Look for blocks whose name starts with "main" and replace it with the filename.
|
// Look for blocks whose name starts with "main" and replace it with the filename.
|
||||||
for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) {
|
for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) {
|
||||||
if block.signature.name == "main" {
|
if block.signature.name == "main" {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
mod commands;
|
mod commands;
|
||||||
mod completions;
|
mod completions;
|
||||||
mod config_files;
|
mod config_files;
|
||||||
|
@ -17,7 +18,7 @@ mod validation;
|
||||||
pub use commands::add_cli_context;
|
pub use commands::add_cli_context;
|
||||||
pub use completions::{FileCompletion, NuCompleter, SemanticSuggestion, SuggestionKind};
|
pub use completions::{FileCompletion, NuCompleter, SemanticSuggestion, SuggestionKind};
|
||||||
pub use config_files::eval_config_contents;
|
pub use config_files::eval_config_contents;
|
||||||
pub use eval_cmds::evaluate_commands;
|
pub use eval_cmds::{evaluate_commands, EvaluateCommandsOpts};
|
||||||
pub use eval_file::evaluate_file;
|
pub use eval_file::evaluate_file;
|
||||||
pub use menus::NuHelpCompleter;
|
pub use menus::NuHelpCompleter;
|
||||||
pub use nu_cmd_base::util::get_init_cwd;
|
pub use nu_cmd_base::util::get_init_cwd;
|
||||||
|
|
|
@ -1,25 +1,34 @@
|
||||||
use nu_engine::documentation::get_flags_section;
|
use nu_engine::documentation::{get_flags_section, HelpStyle};
|
||||||
use nu_protocol::{engine::EngineState, levenshtein_distance};
|
use nu_protocol::{engine::EngineState, levenshtein_distance, Config};
|
||||||
use nu_utils::IgnoreCaseExt;
|
use nu_utils::IgnoreCaseExt;
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
use std::{fmt::Write, sync::Arc};
|
use std::{fmt::Write, sync::Arc};
|
||||||
|
|
||||||
pub struct NuHelpCompleter(Arc<EngineState>);
|
pub struct NuHelpCompleter {
|
||||||
|
engine_state: Arc<EngineState>,
|
||||||
|
config: Arc<Config>,
|
||||||
|
}
|
||||||
|
|
||||||
impl NuHelpCompleter {
|
impl NuHelpCompleter {
|
||||||
pub fn new(engine_state: Arc<EngineState>) -> Self {
|
pub fn new(engine_state: Arc<EngineState>, config: Arc<Config>) -> Self {
|
||||||
Self(engine_state)
|
Self {
|
||||||
|
engine_state,
|
||||||
|
config,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
|
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||||
let folded_line = line.to_folded_case();
|
let folded_line = line.to_folded_case();
|
||||||
|
|
||||||
|
let mut help_style = HelpStyle::default();
|
||||||
|
help_style.update_from_config(&self.engine_state, &self.config);
|
||||||
|
|
||||||
let mut commands = self
|
let mut commands = self
|
||||||
.0
|
.engine_state
|
||||||
.get_decls_sorted(false)
|
.get_decls_sorted(false)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|(_, decl_id)| {
|
.filter_map(|(_, decl_id)| {
|
||||||
let decl = self.0.get_decl(decl_id);
|
let decl = self.engine_state.get_decl(decl_id);
|
||||||
(decl.name().to_folded_case().contains(&folded_line)
|
(decl.name().to_folded_case().contains(&folded_line)
|
||||||
|| decl.usage().to_folded_case().contains(&folded_line)
|
|| decl.usage().to_folded_case().contains(&folded_line)
|
||||||
|| decl
|
|| decl
|
||||||
|
@ -54,8 +63,8 @@ impl NuHelpCompleter {
|
||||||
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
|
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
|
||||||
|
|
||||||
if !sig.named.is_empty() {
|
if !sig.named.is_empty() {
|
||||||
long_desc.push_str(&get_flags_section(Some(&*self.0.clone()), &sig, |v| {
|
long_desc.push_str(&get_flags_section(&sig, &help_style, |v| {
|
||||||
v.to_parsable_string(", ", &self.0.config)
|
v.to_parsable_string(", ", &self.config)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +80,7 @@ impl NuHelpCompleter {
|
||||||
let opt_suffix = if let Some(value) = &positional.default_value {
|
let opt_suffix = if let Some(value) = &positional.default_value {
|
||||||
format!(
|
format!(
|
||||||
" (optional, default: {})",
|
" (optional, default: {})",
|
||||||
&value.to_parsable_string(", ", &self.0.config),
|
&value.to_parsable_string(", ", &self.config),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(" (optional)").to_string()
|
(" (optional)").to_string()
|
||||||
|
@ -101,13 +110,12 @@ impl NuHelpCompleter {
|
||||||
Suggestion {
|
Suggestion {
|
||||||
value: decl.name().into(),
|
value: decl.name().into(),
|
||||||
description: Some(long_desc),
|
description: Some(long_desc),
|
||||||
style: None,
|
|
||||||
extra: Some(extra),
|
extra: Some(extra),
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: pos - line.len(),
|
start: pos - line.len(),
|
||||||
end: pos,
|
end: pos,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -138,7 +146,8 @@ mod test {
|
||||||
) {
|
) {
|
||||||
let engine_state =
|
let engine_state =
|
||||||
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context());
|
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context());
|
||||||
let mut completer = NuHelpCompleter::new(engine_state.into());
|
let config = engine_state.get_config().clone();
|
||||||
|
let mut completer = NuHelpCompleter::new(engine_state.into(), config);
|
||||||
let suggestions = completer.complete(line, end);
|
let suggestions = completer.complete(line, end);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
|
|
@ -142,10 +142,9 @@ fn convert_to_suggestions(
|
||||||
vec![Suggestion {
|
vec![Suggestion {
|
||||||
value: text,
|
value: text,
|
||||||
description,
|
description,
|
||||||
style: None,
|
|
||||||
extra,
|
extra,
|
||||||
span,
|
span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
Value::List { vals, .. } => vals
|
Value::List { vals, .. } => vals
|
||||||
|
@ -154,9 +153,6 @@ fn convert_to_suggestions(
|
||||||
.collect(),
|
.collect(),
|
||||||
_ => vec![Suggestion {
|
_ => vec![Suggestion {
|
||||||
value: format!("Not a record: {value:?}"),
|
value: format!("Not a record: {value:?}"),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: if only_buffer_difference {
|
start: if only_buffer_difference {
|
||||||
pos - line.len()
|
pos - line.len()
|
||||||
|
@ -169,7 +165,7 @@ fn convert_to_suggestions(
|
||||||
line.len()
|
line.len()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
|
|
||||||
|
@ -32,14 +34,11 @@ impl Command for NuHighlight {
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let signals = engine_state.signals();
|
||||||
let engine_state = std::sync::Arc::new(engine_state.clone());
|
|
||||||
let config = engine_state.get_config().clone();
|
|
||||||
|
|
||||||
let highlighter = crate::NuHighlighter {
|
let highlighter = crate::NuHighlighter {
|
||||||
engine_state,
|
engine_state: Arc::new(engine_state.clone()),
|
||||||
stack: std::sync::Arc::new(stack.clone()),
|
stack: Arc::new(stack.clone()),
|
||||||
config,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
input.map(
|
input.map(
|
||||||
|
@ -50,7 +49,7 @@ impl Command for NuHighlight {
|
||||||
}
|
}
|
||||||
Err(err) => Value::error(err, head),
|
Err(err) => Value::error(err, head),
|
||||||
},
|
},
|
||||||
ctrlc,
|
signals,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,15 +75,21 @@ const DEFAULT_HELP_MENU: &str = r#"
|
||||||
// Adds all menus to line editor
|
// Adds all menus to line editor
|
||||||
pub(crate) fn add_menus(
|
pub(crate) fn add_menus(
|
||||||
mut line_editor: Reedline,
|
mut line_editor: Reedline,
|
||||||
engine_state: Arc<EngineState>,
|
engine_state_ref: Arc<EngineState>,
|
||||||
stack: &Stack,
|
stack: &Stack,
|
||||||
config: &Config,
|
config: Arc<Config>,
|
||||||
) -> Result<Reedline, ShellError> {
|
) -> Result<Reedline, ShellError> {
|
||||||
//log::trace!("add_menus: config: {:#?}", &config);
|
//log::trace!("add_menus: config: {:#?}", &config);
|
||||||
line_editor = line_editor.clear_menus();
|
line_editor = line_editor.clear_menus();
|
||||||
|
|
||||||
for menu in &config.menus {
|
for menu in &config.menus {
|
||||||
line_editor = add_menu(line_editor, menu, engine_state.clone(), stack, config)?
|
line_editor = add_menu(
|
||||||
|
line_editor,
|
||||||
|
menu,
|
||||||
|
engine_state_ref.clone(),
|
||||||
|
stack,
|
||||||
|
config.clone(),
|
||||||
|
)?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checking if the default menus have been added from the config file
|
// Checking if the default menus have been added from the config file
|
||||||
|
@ -93,13 +99,16 @@ pub(crate) fn add_menus(
|
||||||
("help_menu", DEFAULT_HELP_MENU),
|
("help_menu", DEFAULT_HELP_MENU),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let mut engine_state = (*engine_state_ref).clone();
|
||||||
|
let mut menu_eval_results = vec![];
|
||||||
|
|
||||||
for (name, definition) in default_menus {
|
for (name, definition) in default_menus {
|
||||||
if !config
|
if !config
|
||||||
.menus
|
.menus
|
||||||
.iter()
|
.iter()
|
||||||
.any(|menu| menu.name.to_expanded_string("", config) == name)
|
.any(|menu| menu.name.to_expanded_string("", &config) == name)
|
||||||
{
|
{
|
||||||
let (block, _) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let output = parse(
|
let output = parse(
|
||||||
&mut working_set,
|
&mut working_set,
|
||||||
|
@ -111,15 +120,31 @@ pub(crate) fn add_menus(
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
engine_state.merge_delta(delta)?;
|
||||||
|
|
||||||
let mut temp_stack = Stack::new().capture();
|
let mut temp_stack = Stack::new().capture();
|
||||||
let input = PipelineData::Empty;
|
let input = PipelineData::Empty;
|
||||||
let res = eval_block::<WithoutDebug>(&engine_state, &mut temp_stack, &block, input)?;
|
menu_eval_results.push(eval_block::<WithoutDebug>(
|
||||||
|
&engine_state,
|
||||||
|
&mut temp_stack,
|
||||||
|
&block,
|
||||||
|
input,
|
||||||
|
)?);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let PipelineData::Value(value, None) = res {
|
let new_engine_state_ref = Arc::new(engine_state);
|
||||||
for menu in create_menus(&value)? {
|
|
||||||
line_editor =
|
for res in menu_eval_results.into_iter() {
|
||||||
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?;
|
if let PipelineData::Value(value, None) = res {
|
||||||
}
|
for menu in create_menus(&value)? {
|
||||||
|
line_editor = add_menu(
|
||||||
|
line_editor,
|
||||||
|
&menu,
|
||||||
|
new_engine_state_ref.clone(),
|
||||||
|
stack,
|
||||||
|
config.clone(),
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,27 +157,27 @@ fn add_menu(
|
||||||
menu: &ParsedMenu,
|
menu: &ParsedMenu,
|
||||||
engine_state: Arc<EngineState>,
|
engine_state: Arc<EngineState>,
|
||||||
stack: &Stack,
|
stack: &Stack,
|
||||||
config: &Config,
|
config: Arc<Config>,
|
||||||
) -> Result<Reedline, ShellError> {
|
) -> Result<Reedline, ShellError> {
|
||||||
let span = menu.menu_type.span();
|
let span = menu.menu_type.span();
|
||||||
if let Value::Record { val, .. } = &menu.menu_type {
|
if let Value::Record { val, .. } = &menu.menu_type {
|
||||||
let layout = extract_value("layout", val, span)?.to_expanded_string("", config);
|
let layout = extract_value("layout", val, span)?.to_expanded_string("", &config);
|
||||||
|
|
||||||
match layout.as_str() {
|
match layout.as_str() {
|
||||||
"columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, config),
|
"columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, &config),
|
||||||
"list" => add_list_menu(line_editor, menu, engine_state, stack, config),
|
"list" => add_list_menu(line_editor, menu, engine_state, stack, config),
|
||||||
"ide" => add_ide_menu(line_editor, menu, engine_state, stack, config),
|
"ide" => add_ide_menu(line_editor, menu, engine_state, stack, config),
|
||||||
"description" => add_description_menu(line_editor, menu, engine_state, stack, config),
|
"description" => add_description_menu(line_editor, menu, engine_state, stack, config),
|
||||||
_ => Err(ShellError::UnsupportedConfigValue {
|
_ => Err(ShellError::UnsupportedConfigValue {
|
||||||
expected: "columnar, list, ide or description".to_string(),
|
expected: "columnar, list, ide or description".to_string(),
|
||||||
value: menu.menu_type.to_abbreviated_string(config),
|
value: menu.menu_type.to_abbreviated_string(&config),
|
||||||
span: menu.menu_type.span(),
|
span: menu.menu_type.span(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(ShellError::UnsupportedConfigValue {
|
Err(ShellError::UnsupportedConfigValue {
|
||||||
expected: "only record type".to_string(),
|
expected: "only record type".to_string(),
|
||||||
value: menu.menu_type.to_abbreviated_string(config),
|
value: menu.menu_type.to_abbreviated_string(&config),
|
||||||
span: menu.menu_type.span(),
|
span: menu.menu_type.span(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -168,6 +193,29 @@ fn get_style(record: &Record, name: &str, span: Span) -> Option<Style> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_menu_style<M: MenuBuilder>(mut menu: M, style: &Value) -> M {
|
||||||
|
let span = style.span();
|
||||||
|
let Value::Record { val, .. } = &style else {
|
||||||
|
return menu;
|
||||||
|
};
|
||||||
|
if let Some(style) = get_style(val, "text", span) {
|
||||||
|
menu = menu.with_text_style(style);
|
||||||
|
}
|
||||||
|
if let Some(style) = get_style(val, "selected_text", span) {
|
||||||
|
menu = menu.with_selected_text_style(style);
|
||||||
|
}
|
||||||
|
if let Some(style) = get_style(val, "description_text", span) {
|
||||||
|
menu = menu.with_description_text_style(style);
|
||||||
|
}
|
||||||
|
if let Some(style) = get_style(val, "match_text", span) {
|
||||||
|
menu = menu.with_match_text_style(style);
|
||||||
|
}
|
||||||
|
if let Some(style) = get_style(val, "selected_match_text", span) {
|
||||||
|
menu = menu.with_selected_match_text_style(style);
|
||||||
|
}
|
||||||
|
menu
|
||||||
|
}
|
||||||
|
|
||||||
// Adds a columnar menu to the editor engine
|
// Adds a columnar menu to the editor engine
|
||||||
pub(crate) fn add_columnar_menu(
|
pub(crate) fn add_columnar_menu(
|
||||||
line_editor: Reedline,
|
line_editor: Reedline,
|
||||||
|
@ -206,24 +254,7 @@ pub(crate) fn add_columnar_menu(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = menu.style.span();
|
columnar_menu = set_menu_style(columnar_menu, &menu.style);
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
|
||||||
if let Some(style) = get_style(val, "text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_selected_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "description_text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_description_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "match_text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_match_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_match_text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_selected_match_text_style(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", config);
|
let marker = menu.marker.to_expanded_string("", config);
|
||||||
columnar_menu = columnar_menu.with_marker(&marker);
|
columnar_menu = columnar_menu.with_marker(&marker);
|
||||||
|
@ -263,9 +294,9 @@ pub(crate) fn add_list_menu(
|
||||||
menu: &ParsedMenu,
|
menu: &ParsedMenu,
|
||||||
engine_state: Arc<EngineState>,
|
engine_state: Arc<EngineState>,
|
||||||
stack: &Stack,
|
stack: &Stack,
|
||||||
config: &Config,
|
config: Arc<Config>,
|
||||||
) -> Result<Reedline, ShellError> {
|
) -> Result<Reedline, ShellError> {
|
||||||
let name = menu.name.to_expanded_string("", config);
|
let name = menu.name.to_expanded_string("", &config);
|
||||||
let mut list_menu = ListMenu::default().with_name(&name);
|
let mut list_menu = ListMenu::default().with_name(&name);
|
||||||
|
|
||||||
let span = menu.menu_type.span();
|
let span = menu.menu_type.span();
|
||||||
|
@ -279,20 +310,9 @@ pub(crate) fn add_list_menu(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = menu.style.span();
|
list_menu = set_menu_style(list_menu, &menu.style);
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
|
||||||
if let Some(style) = get_style(val, "text", span) {
|
|
||||||
list_menu = list_menu.with_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_text", span) {
|
|
||||||
list_menu = list_menu.with_selected_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "description_text", span) {
|
|
||||||
list_menu = list_menu.with_description_text_style(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", config);
|
let marker = menu.marker.to_expanded_string("", &config);
|
||||||
list_menu = list_menu.with_marker(&marker);
|
list_menu = list_menu.with_marker(&marker);
|
||||||
|
|
||||||
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
|
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
|
||||||
|
@ -318,7 +338,7 @@ pub(crate) fn add_list_menu(
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::UnsupportedConfigValue {
|
_ => Err(ShellError::UnsupportedConfigValue {
|
||||||
expected: "block or omitted value".to_string(),
|
expected: "block or omitted value".to_string(),
|
||||||
value: menu.source.to_abbreviated_string(config),
|
value: menu.source.to_abbreviated_string(&config),
|
||||||
span: menu.source.span(),
|
span: menu.source.span(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -330,10 +350,10 @@ pub(crate) fn add_ide_menu(
|
||||||
menu: &ParsedMenu,
|
menu: &ParsedMenu,
|
||||||
engine_state: Arc<EngineState>,
|
engine_state: Arc<EngineState>,
|
||||||
stack: &Stack,
|
stack: &Stack,
|
||||||
config: &Config,
|
config: Arc<Config>,
|
||||||
) -> Result<Reedline, ShellError> {
|
) -> Result<Reedline, ShellError> {
|
||||||
let span = menu.menu_type.span();
|
let span = menu.menu_type.span();
|
||||||
let name = menu.name.to_expanded_string("", config);
|
let name = menu.name.to_expanded_string("", &config);
|
||||||
let mut ide_menu = IdeMenu::default().with_name(&name);
|
let mut ide_menu = IdeMenu::default().with_name(&name);
|
||||||
|
|
||||||
if let Value::Record { val, .. } = &menu.menu_type {
|
if let Value::Record { val, .. } = &menu.menu_type {
|
||||||
|
@ -398,7 +418,7 @@ pub(crate) fn add_ide_menu(
|
||||||
} else {
|
} else {
|
||||||
return Err(ShellError::UnsupportedConfigValue {
|
return Err(ShellError::UnsupportedConfigValue {
|
||||||
expected: "bool or record".to_string(),
|
expected: "bool or record".to_string(),
|
||||||
value: border.to_abbreviated_string(config),
|
value: border.to_abbreviated_string(&config),
|
||||||
span: border.span(),
|
span: border.span(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -422,7 +442,7 @@ pub(crate) fn add_ide_menu(
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::UnsupportedConfigValue {
|
return Err(ShellError::UnsupportedConfigValue {
|
||||||
expected: "\"left\", \"right\" or \"prefer_right\"".to_string(),
|
expected: "\"left\", \"right\" or \"prefer_right\"".to_string(),
|
||||||
value: description_mode.to_abbreviated_string(config),
|
value: description_mode.to_abbreviated_string(&config),
|
||||||
span: description_mode.span(),
|
span: description_mode.span(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -471,26 +491,9 @@ pub(crate) fn add_ide_menu(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = menu.style.span();
|
ide_menu = set_menu_style(ide_menu, &menu.style);
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
|
||||||
if let Some(style) = get_style(val, "text", span) {
|
|
||||||
ide_menu = ide_menu.with_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_text", span) {
|
|
||||||
ide_menu = ide_menu.with_selected_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "description_text", span) {
|
|
||||||
ide_menu = ide_menu.with_description_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "match_text", span) {
|
|
||||||
ide_menu = ide_menu.with_match_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_match_text", span) {
|
|
||||||
ide_menu = ide_menu.with_selected_match_text_style(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", config);
|
let marker = menu.marker.to_expanded_string("", &config);
|
||||||
ide_menu = ide_menu.with_marker(&marker);
|
ide_menu = ide_menu.with_marker(&marker);
|
||||||
|
|
||||||
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
|
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
|
||||||
|
@ -516,7 +519,7 @@ pub(crate) fn add_ide_menu(
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::UnsupportedConfigValue {
|
_ => Err(ShellError::UnsupportedConfigValue {
|
||||||
expected: "block or omitted value".to_string(),
|
expected: "block or omitted value".to_string(),
|
||||||
value: menu.source.to_abbreviated_string(config),
|
value: menu.source.to_abbreviated_string(&config),
|
||||||
span,
|
span,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
@ -528,9 +531,9 @@ pub(crate) fn add_description_menu(
|
||||||
menu: &ParsedMenu,
|
menu: &ParsedMenu,
|
||||||
engine_state: Arc<EngineState>,
|
engine_state: Arc<EngineState>,
|
||||||
stack: &Stack,
|
stack: &Stack,
|
||||||
config: &Config,
|
config: Arc<Config>,
|
||||||
) -> Result<Reedline, ShellError> {
|
) -> Result<Reedline, ShellError> {
|
||||||
let name = menu.name.to_expanded_string("", config);
|
let name = menu.name.to_expanded_string("", &config);
|
||||||
let mut description_menu = DescriptionMenu::default().with_name(&name);
|
let mut description_menu = DescriptionMenu::default().with_name(&name);
|
||||||
|
|
||||||
let span = menu.menu_type.span();
|
let span = menu.menu_type.span();
|
||||||
|
@ -576,20 +579,9 @@ pub(crate) fn add_description_menu(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = menu.style.span();
|
description_menu = set_menu_style(description_menu, &menu.style);
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
|
||||||
if let Some(style) = get_style(val, "text", span) {
|
|
||||||
description_menu = description_menu.with_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_text", span) {
|
|
||||||
description_menu = description_menu.with_selected_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "description_text", span) {
|
|
||||||
description_menu = description_menu.with_description_text_style(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", config);
|
let marker = menu.marker.to_expanded_string("", &config);
|
||||||
description_menu = description_menu.with_marker(&marker);
|
description_menu = description_menu.with_marker(&marker);
|
||||||
|
|
||||||
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
|
let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
|
||||||
|
@ -598,7 +590,7 @@ pub(crate) fn add_description_menu(
|
||||||
let span = menu.source.span();
|
let span = menu.source.span();
|
||||||
match &menu.source {
|
match &menu.source {
|
||||||
Value::Nothing { .. } => {
|
Value::Nothing { .. } => {
|
||||||
let completer = Box::new(NuHelpCompleter::new(engine_state));
|
let completer = Box::new(NuHelpCompleter::new(engine_state, config));
|
||||||
Ok(line_editor.with_menu(ReedlineMenu::WithCompleter {
|
Ok(line_editor.with_menu(ReedlineMenu::WithCompleter {
|
||||||
menu: Box::new(description_menu),
|
menu: Box::new(description_menu),
|
||||||
completer,
|
completer,
|
||||||
|
@ -619,7 +611,7 @@ pub(crate) fn add_description_menu(
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::UnsupportedConfigValue {
|
_ => Err(ShellError::UnsupportedConfigValue {
|
||||||
expected: "closure or omitted value".to_string(),
|
expected: "closure or omitted value".to_string(),
|
||||||
value: menu.source.to_abbreviated_string(config),
|
value: menu.source.to_abbreviated_string(&config),
|
||||||
span: menu.source.span(),
|
span: menu.source.span(),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ use nu_protocol::{
|
||||||
};
|
};
|
||||||
use nu_utils::{
|
use nu_utils::{
|
||||||
filesystem::{have_permission, PermissionResult},
|
filesystem::{have_permission, PermissionResult},
|
||||||
utils::perf,
|
perf,
|
||||||
};
|
};
|
||||||
use reedline::{
|
use reedline::{
|
||||||
CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory,
|
CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory,
|
||||||
|
@ -43,7 +43,7 @@ use std::{
|
||||||
io::{self, IsTerminal, Write},
|
io::{self, IsTerminal, Write},
|
||||||
panic::{catch_unwind, AssertUnwindSafe},
|
panic::{catch_unwind, AssertUnwindSafe},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::{atomic::Ordering, Arc},
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
|
@ -89,14 +89,7 @@ pub fn evaluate_repl(
|
||||||
if let Err(e) = convert_env_values(engine_state, &unique_stack) {
|
if let Err(e) = convert_env_values(engine_state, &unique_stack) {
|
||||||
report_error_new(engine_state, &e);
|
report_error_new(engine_state, &e);
|
||||||
}
|
}
|
||||||
perf(
|
perf!("translate env vars", start_time, use_color);
|
||||||
"translate env vars",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
// seed env vars
|
// seed env vars
|
||||||
unique_stack.add_env_var(
|
unique_stack.add_env_var(
|
||||||
|
@ -225,28 +218,14 @@ fn get_line_editor(
|
||||||
|
|
||||||
// Now that reedline is created, get the history session id and store it in engine_state
|
// Now that reedline is created, get the history session id and store it in engine_state
|
||||||
store_history_id_in_engine(engine_state, &line_editor);
|
store_history_id_in_engine(engine_state, &line_editor);
|
||||||
perf(
|
perf!("setup reedline", start_time, use_color);
|
||||||
"setup reedline",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(history) = engine_state.history_config() {
|
if let Some(history) = engine_state.history_config() {
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
|
|
||||||
line_editor = setup_history(nushell_path, engine_state, line_editor, history)?;
|
line_editor = setup_history(nushell_path, engine_state, line_editor, history)?;
|
||||||
|
|
||||||
perf(
|
perf!("setup history", start_time, use_color);
|
||||||
"setup history",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
Ok(line_editor)
|
Ok(line_editor)
|
||||||
}
|
}
|
||||||
|
@ -289,28 +268,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
if let Err(err) = engine_state.merge_env(&mut stack, cwd) {
|
if let Err(err) = engine_state.merge_env(&mut stack, cwd) {
|
||||||
report_error_new(engine_state, &err);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
perf(
|
// Check whether $env.NU_USE_IR is set, so that the user can change it in the REPL
|
||||||
"merge env",
|
// Temporary while IR eval is optional
|
||||||
start_time,
|
stack.use_ir = stack.has_env_var(engine_state, "NU_USE_IR");
|
||||||
file!(),
|
perf!("merge env", start_time, use_color);
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
// Reset the ctrl-c handler
|
engine_state.reset_signals();
|
||||||
if let Some(ctrlc) = &mut engine_state.ctrlc {
|
perf!("reset signals", start_time, use_color);
|
||||||
ctrlc.store(false, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
perf(
|
|
||||||
"reset ctrlc",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
// Right before we start our prompt and take input from the user,
|
// Right before we start our prompt and take input from the user,
|
||||||
|
@ -320,14 +285,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
report_error_new(engine_state, &err);
|
report_error_new(engine_state, &err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
perf(
|
perf!("pre-prompt hook", start_time, use_color);
|
||||||
"pre-prompt hook",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
// Next, check all the environment variables they ask for
|
// Next, check all the environment variables they ask for
|
||||||
|
@ -336,17 +294,10 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
if let Err(error) = hook::eval_env_change_hook(env_change, engine_state, &mut stack) {
|
if let Err(error) = hook::eval_env_change_hook(env_change, engine_state, &mut stack) {
|
||||||
report_error_new(engine_state, &error)
|
report_error_new(engine_state, &error)
|
||||||
}
|
}
|
||||||
perf(
|
perf!("env-change hook", start_time, use_color);
|
||||||
"env-change hook",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
let engine_reference = Arc::new(engine_state.clone());
|
let engine_reference = Arc::new(engine_state.clone());
|
||||||
let config = engine_state.get_config();
|
let config = stack.get_config(engine_state);
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
// Find the configured cursor shapes for each mode
|
// Find the configured cursor shapes for each mode
|
||||||
|
@ -355,14 +306,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal),
|
vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal),
|
||||||
emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs),
|
emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs),
|
||||||
};
|
};
|
||||||
perf(
|
perf!("get config/cursor config", start_time, use_color);
|
||||||
"get config/cursor config",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
// at this line we have cloned the state for the completer and the transient prompt
|
// at this line we have cloned the state for the completer and the transient prompt
|
||||||
|
@ -379,7 +323,6 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
engine_state: engine_reference.clone(),
|
engine_state: engine_reference.clone(),
|
||||||
// STACK-REFERENCE 1
|
// STACK-REFERENCE 1
|
||||||
stack: stack_arc.clone(),
|
stack: stack_arc.clone(),
|
||||||
config: config.clone(),
|
|
||||||
}))
|
}))
|
||||||
.with_validator(Box::new(NuValidator {
|
.with_validator(Box::new(NuValidator {
|
||||||
engine_state: engine_reference.clone(),
|
engine_state: engine_reference.clone(),
|
||||||
|
@ -392,16 +335,17 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
.with_quick_completions(config.quick_completions)
|
.with_quick_completions(config.quick_completions)
|
||||||
.with_partial_completions(config.partial_completions)
|
.with_partial_completions(config.partial_completions)
|
||||||
.with_ansi_colors(config.use_ansi_coloring)
|
.with_ansi_colors(config.use_ansi_coloring)
|
||||||
|
.with_cwd(Some(
|
||||||
|
engine_state
|
||||||
|
.cwd(None)
|
||||||
|
.map(|cwd| cwd.into_std_path_buf())
|
||||||
|
.unwrap_or_default()
|
||||||
|
.to_string_lossy()
|
||||||
|
.to_string(),
|
||||||
|
))
|
||||||
.with_cursor_config(cursor_config);
|
.with_cursor_config(cursor_config);
|
||||||
|
|
||||||
perf(
|
perf!("reedline builder", start_time, use_color);
|
||||||
"reedline builder",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
let style_computer = StyleComputer::from_config(engine_state, &stack_arc);
|
let style_computer = StyleComputer::from_config(engine_state, &stack_arc);
|
||||||
|
|
||||||
|
@ -416,14 +360,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
line_editor.disable_hints()
|
line_editor.disable_hints()
|
||||||
};
|
};
|
||||||
|
|
||||||
perf(
|
perf!("reedline coloring/style_computer", start_time, use_color);
|
||||||
"reedline coloring/style_computer",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
trace!("adding menus");
|
trace!("adding menus");
|
||||||
|
@ -433,14 +370,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
Reedline::create()
|
Reedline::create()
|
||||||
});
|
});
|
||||||
|
|
||||||
perf(
|
perf!("reedline adding menus", start_time, use_color);
|
||||||
"reedline adding menus",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
let buffer_editor = get_editor(engine_state, &stack_arc, Span::unknown());
|
let buffer_editor = get_editor(engine_state, &stack_arc, Span::unknown());
|
||||||
|
@ -457,14 +387,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
line_editor
|
line_editor
|
||||||
};
|
};
|
||||||
|
|
||||||
perf(
|
perf!("reedline buffer_editor", start_time, use_color);
|
||||||
"reedline buffer_editor",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(history) = engine_state.history_config() {
|
if let Some(history) = engine_state.history_config() {
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
|
@ -474,28 +397,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
perf(
|
perf!("sync_history", start_time, use_color);
|
||||||
"sync_history",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
// Changing the line editor based on the found keybindings
|
// Changing the line editor based on the found keybindings
|
||||||
line_editor = setup_keybindings(engine_state, line_editor);
|
line_editor = setup_keybindings(engine_state, line_editor);
|
||||||
|
|
||||||
perf(
|
perf!("keybindings", start_time, use_color);
|
||||||
"keybindings",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
start_time = std::time::Instant::now();
|
start_time = std::time::Instant::now();
|
||||||
let config = &engine_state.get_config().clone();
|
let config = &engine_state.get_config().clone();
|
||||||
|
@ -512,14 +421,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
nu_prompt,
|
nu_prompt,
|
||||||
);
|
);
|
||||||
|
|
||||||
perf(
|
perf!("update_prompt", start_time, use_color);
|
||||||
"update_prompt",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
*entry_num += 1;
|
*entry_num += 1;
|
||||||
|
|
||||||
|
@ -546,14 +448,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
// so we should avoid it or making stack cheaper to clone.
|
// so we should avoid it or making stack cheaper to clone.
|
||||||
let mut stack = Arc::unwrap_or_clone(stack_arc);
|
let mut stack = Arc::unwrap_or_clone(stack_arc);
|
||||||
|
|
||||||
perf(
|
perf!("line_editor setup", start_time, use_color);
|
||||||
"line_editor setup",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
let line_editor_input_time = std::time::Instant::now();
|
let line_editor_input_time = std::time::Instant::now();
|
||||||
match input {
|
match input {
|
||||||
|
@ -590,14 +485,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
perf(
|
perf!("pre_execution_hook", start_time, use_color);
|
||||||
"pre_execution_hook",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
||||||
repl.cursor_pos = line_editor.current_insertion_point();
|
repl.cursor_pos = line_editor.current_insertion_point();
|
||||||
|
@ -612,26 +500,20 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
|
|
||||||
run_ansi_sequence(VSCODE_PRE_EXECUTION_MARKER);
|
run_ansi_sequence(VSCODE_PRE_EXECUTION_MARKER);
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"pre_execute_marker (633;C) ansi escape sequence",
|
"pre_execute_marker (633;C) ansi escape sequence",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
} else if shell_integration_osc133 {
|
} else if shell_integration_osc133 {
|
||||||
start_time = Instant::now();
|
start_time = Instant::now();
|
||||||
|
|
||||||
run_ansi_sequence(PRE_EXECUTION_MARKER);
|
run_ansi_sequence(PRE_EXECUTION_MARKER);
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"pre_execute_marker (133;C) ansi escape sequence",
|
"pre_execute_marker (133;C) ansi escape sequence",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if shell_integration_osc133 {
|
} else if shell_integration_osc133 {
|
||||||
|
@ -639,13 +521,10 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
|
|
||||||
run_ansi_sequence(PRE_EXECUTION_MARKER);
|
run_ansi_sequence(PRE_EXECUTION_MARKER);
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"pre_execute_marker (133;C) ansi escape sequence",
|
"pre_execute_marker (133;C) ansi escape sequence",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -769,22 +648,16 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
perf(
|
perf!(
|
||||||
"processing line editor input",
|
"processing line editor input",
|
||||||
line_editor_input_time,
|
line_editor_input_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"time between prompts in line editor loop",
|
"time between prompts in line editor loop",
|
||||||
loop_start_time,
|
loop_start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
(true, stack, line_editor)
|
(true, stack, line_editor)
|
||||||
|
@ -800,13 +673,14 @@ fn prepare_history_metadata(
|
||||||
line_editor: &mut Reedline,
|
line_editor: &mut Reedline,
|
||||||
) {
|
) {
|
||||||
if !s.is_empty() && line_editor.has_last_command_context() {
|
if !s.is_empty() && line_editor.has_last_command_context() {
|
||||||
#[allow(deprecated)]
|
|
||||||
let result = line_editor
|
let result = line_editor
|
||||||
.update_last_command_context(&|mut c| {
|
.update_last_command_context(&|mut c| {
|
||||||
c.start_timestamp = Some(chrono::Utc::now());
|
c.start_timestamp = Some(chrono::Utc::now());
|
||||||
c.hostname = hostname.map(str::to_string);
|
c.hostname = hostname.map(str::to_string);
|
||||||
|
c.cwd = engine_state
|
||||||
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
|
.cwd(None)
|
||||||
|
.ok()
|
||||||
|
.map(|path| path.to_string_lossy().to_string());
|
||||||
c
|
c
|
||||||
})
|
})
|
||||||
.into_diagnostic();
|
.into_diagnostic();
|
||||||
|
@ -1061,14 +935,7 @@ fn run_shell_integration_osc2(
|
||||||
// ESC]2;stringBEL -- Set window title to string
|
// ESC]2;stringBEL -- Set window title to string
|
||||||
run_ansi_sequence(&format!("\x1b]2;{title}\x07"));
|
run_ansi_sequence(&format!("\x1b]2;{title}\x07"));
|
||||||
|
|
||||||
perf(
|
perf!("set title with command osc2", start_time, use_color);
|
||||||
"set title with command osc2",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1093,13 +960,10 @@ fn run_shell_integration_osc7(
|
||||||
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
|
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
|
||||||
));
|
));
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"communicate path to terminal with osc7",
|
"communicate path to terminal with osc7",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1116,13 +980,10 @@ fn run_shell_integration_osc9_9(engine_state: &EngineState, stack: &mut Stack, u
|
||||||
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
|
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
|
||||||
));
|
));
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"communicate path to terminal with osc9;9",
|
"communicate path to terminal with osc9;9",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1142,13 +1003,10 @@ fn run_shell_integration_osc633(engine_state: &EngineState, stack: &mut Stack, u
|
||||||
VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX
|
VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX
|
||||||
));
|
));
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"communicate path to terminal with osc633;P",
|
"communicate path to terminal with osc633;P",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1371,13 +1229,10 @@ fn run_finaliziation_ansi_sequence(
|
||||||
shell_integration_osc133,
|
shell_integration_osc133,
|
||||||
));
|
));
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"post_execute_marker (633;D) ansi escape sequences",
|
"post_execute_marker (633;D) ansi escape sequences",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
} else if shell_integration_osc133 {
|
} else if shell_integration_osc133 {
|
||||||
let start_time = Instant::now();
|
let start_time = Instant::now();
|
||||||
|
@ -1389,13 +1244,10 @@ fn run_finaliziation_ansi_sequence(
|
||||||
shell_integration_osc133,
|
shell_integration_osc133,
|
||||||
));
|
));
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"post_execute_marker (133;D) ansi escape sequences",
|
"post_execute_marker (133;D) ansi escape sequences",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if shell_integration_osc133 {
|
} else if shell_integration_osc133 {
|
||||||
|
@ -1408,13 +1260,10 @@ fn run_finaliziation_ansi_sequence(
|
||||||
shell_integration_osc133,
|
shell_integration_osc133,
|
||||||
));
|
));
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
"post_execute_marker (133;D) ansi escape sequences",
|
"post_execute_marker (133;D) ansi escape sequences",
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
use_color
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1488,20 +1337,26 @@ fn are_session_ids_in_sync() {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_auto_cd {
|
mod test_auto_cd {
|
||||||
use super::{do_auto_cd, parse_operation, ReplOperation};
|
use super::{do_auto_cd, parse_operation, ReplOperation};
|
||||||
|
use nu_path::AbsolutePath;
|
||||||
use nu_protocol::engine::{EngineState, Stack};
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
use std::path::Path;
|
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
/// Create a symlink. Works on both Unix and Windows.
|
/// Create a symlink. Works on both Unix and Windows.
|
||||||
#[cfg(any(unix, windows))]
|
#[cfg(any(unix, windows))]
|
||||||
fn symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> std::io::Result<()> {
|
fn symlink(
|
||||||
|
original: impl AsRef<AbsolutePath>,
|
||||||
|
link: impl AsRef<AbsolutePath>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
let original = original.as_ref();
|
||||||
|
let link = link.as_ref();
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
std::os::unix::fs::symlink(original, link)
|
std::os::unix::fs::symlink(original, link)
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
if original.as_ref().is_dir() {
|
if original.is_dir() {
|
||||||
std::os::windows::fs::symlink_dir(original, link)
|
std::os::windows::fs::symlink_dir(original, link)
|
||||||
} else {
|
} else {
|
||||||
std::os::windows::fs::symlink_file(original, link)
|
std::os::windows::fs::symlink_file(original, link)
|
||||||
|
@ -1513,11 +1368,11 @@ mod test_auto_cd {
|
||||||
/// `before`, and after `input` is parsed and evaluated, PWD should be
|
/// `before`, and after `input` is parsed and evaluated, PWD should be
|
||||||
/// changed to `after`.
|
/// changed to `after`.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check(before: impl AsRef<Path>, input: &str, after: impl AsRef<Path>) {
|
fn check(before: impl AsRef<AbsolutePath>, input: &str, after: impl AsRef<AbsolutePath>) {
|
||||||
// Setup EngineState and Stack.
|
// Setup EngineState and Stack.
|
||||||
let mut engine_state = EngineState::new();
|
let mut engine_state = EngineState::new();
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
stack.set_cwd(before).unwrap();
|
stack.set_cwd(before.as_ref()).unwrap();
|
||||||
|
|
||||||
// Parse the input. It must be an auto-cd operation.
|
// Parse the input. It must be an auto-cd operation.
|
||||||
let op = parse_operation(input.to_string(), &engine_state, &stack).unwrap();
|
let op = parse_operation(input.to_string(), &engine_state, &stack).unwrap();
|
||||||
|
@ -1533,54 +1388,66 @@ mod test_auto_cd {
|
||||||
// don't have to be byte-wise equal (on Windows, the 8.3 filename
|
// don't have to be byte-wise equal (on Windows, the 8.3 filename
|
||||||
// conversion messes things up),
|
// conversion messes things up),
|
||||||
let updated_cwd = std::fs::canonicalize(updated_cwd).unwrap();
|
let updated_cwd = std::fs::canonicalize(updated_cwd).unwrap();
|
||||||
let after = std::fs::canonicalize(after).unwrap();
|
let after = std::fs::canonicalize(after.as_ref()).unwrap();
|
||||||
assert_eq!(updated_cwd, after);
|
assert_eq!(updated_cwd, after);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_root() {
|
fn auto_cd_root() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let root = if cfg!(windows) { r"C:\" } else { "/" };
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
check(&tempdir, root, root);
|
|
||||||
|
let input = if cfg!(windows) { r"C:\" } else { "/" };
|
||||||
|
let root = AbsolutePath::try_new(input).unwrap();
|
||||||
|
check(tempdir, input, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_tilde() {
|
fn auto_cd_tilde() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
let home = nu_path::home_dir().unwrap();
|
let home = nu_path::home_dir().unwrap();
|
||||||
check(&tempdir, "~", home);
|
check(tempdir, "~", home);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_dot() {
|
fn auto_cd_dot() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
check(&tempdir, ".", &tempdir);
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
|
check(tempdir, ".", tempdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_double_dot() {
|
fn auto_cd_double_dot() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo");
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
check(dir, "..", &tempdir);
|
check(dir, "..", tempdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_triple_dot() {
|
fn auto_cd_triple_dot() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo").join("bar");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo").join("bar");
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
check(dir, "...", &tempdir);
|
check(dir, "...", tempdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_relative() {
|
fn auto_cd_relative() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let foo = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
let bar = tempdir.path().join("bar");
|
|
||||||
|
let foo = tempdir.join("foo");
|
||||||
|
let bar = tempdir.join("bar");
|
||||||
std::fs::create_dir_all(&foo).unwrap();
|
std::fs::create_dir_all(&foo).unwrap();
|
||||||
std::fs::create_dir_all(&bar).unwrap();
|
std::fs::create_dir_all(&bar).unwrap();
|
||||||
|
|
||||||
let input = if cfg!(windows) { r"..\bar" } else { "../bar" };
|
let input = if cfg!(windows) { r"..\bar" } else { "../bar" };
|
||||||
check(foo, input, bar);
|
check(foo, input, bar);
|
||||||
}
|
}
|
||||||
|
@ -1588,32 +1455,35 @@ mod test_auto_cd {
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_trailing_slash() {
|
fn auto_cd_trailing_slash() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo");
|
||||||
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
let input = if cfg!(windows) { r"foo\" } else { "foo/" };
|
let input = if cfg!(windows) { r"foo\" } else { "foo/" };
|
||||||
check(&tempdir, input, dir);
|
check(tempdir, input, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_symlink() {
|
fn auto_cd_symlink() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
|
||||||
let link = tempdir.path().join("link");
|
|
||||||
symlink(&dir, &link).unwrap();
|
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo");
|
||||||
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
|
let link = tempdir.join("link");
|
||||||
|
symlink(&dir, &link).unwrap();
|
||||||
let input = if cfg!(windows) { r".\link" } else { "./link" };
|
let input = if cfg!(windows) { r".\link" } else { "./link" };
|
||||||
check(&tempdir, input, link);
|
check(tempdir, input, link);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "was not parsed into an auto-cd operation")]
|
#[should_panic(expected = "was not parsed into an auto-cd operation")]
|
||||||
fn auto_cd_nonexistent_directory() {
|
fn auto_cd_nonexistent_directory() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo");
|
||||||
let input = if cfg!(windows) { r"foo\" } else { "foo/" };
|
let input = if cfg!(windows) { r"foo\" } else { "foo/" };
|
||||||
check(&tempdir, input, dir);
|
check(tempdir, input, dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use nu_parser::{flatten_block, parse, FlatShape};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
|
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Config, Span,
|
Span,
|
||||||
};
|
};
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -14,15 +14,14 @@ use std::sync::Arc;
|
||||||
pub struct NuHighlighter {
|
pub struct NuHighlighter {
|
||||||
pub engine_state: Arc<EngineState>,
|
pub engine_state: Arc<EngineState>,
|
||||||
pub stack: Arc<Stack>,
|
pub stack: Arc<Stack>,
|
||||||
pub config: Config,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Highlighter for NuHighlighter {
|
impl Highlighter for NuHighlighter {
|
||||||
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
||||||
trace!("highlighting: {}", line);
|
trace!("highlighting: {}", line);
|
||||||
|
|
||||||
let highlight_resolved_externals =
|
let config = self.stack.get_config(&self.engine_state);
|
||||||
self.engine_state.get_config().highlight_resolved_externals;
|
let highlight_resolved_externals = config.highlight_resolved_externals;
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||||
let block = parse(&mut working_set, None, line.as_bytes(), false);
|
let block = parse(&mut working_set, None, line.as_bytes(), false);
|
||||||
let (shapes, global_span_offset) = {
|
let (shapes, global_span_offset) = {
|
||||||
|
@ -88,7 +87,7 @@ impl Highlighter for NuHighlighter {
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
||||||
output.push((get_shape_color(shape.as_str(), &self.config), text));
|
output.push((get_shape_color(shape.as_str(), &config), text));
|
||||||
};
|
};
|
||||||
|
|
||||||
match shape.1 {
|
match shape.1 {
|
||||||
|
@ -128,9 +127,9 @@ impl Highlighter for NuHighlighter {
|
||||||
let start = part.start - span.start;
|
let start = part.start - span.start;
|
||||||
let end = part.end - span.start;
|
let end = part.end - span.start;
|
||||||
let text = next_token[start..end].to_string();
|
let text = next_token[start..end].to_string();
|
||||||
let mut style = get_shape_color(shape.as_str(), &self.config);
|
let mut style = get_shape_color(shape.as_str(), &config);
|
||||||
if highlight {
|
if highlight {
|
||||||
style = get_matching_brackets_style(style, &self.config);
|
style = get_matching_brackets_style(style, &config);
|
||||||
}
|
}
|
||||||
output.push((style, text));
|
output.push((style, text));
|
||||||
}
|
}
|
||||||
|
@ -138,6 +137,7 @@ impl Highlighter for NuHighlighter {
|
||||||
|
|
||||||
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::GlobInterpolation => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
||||||
add_colored_token(&shape.1, next_token)
|
add_colored_token(&shape.1, next_token)
|
||||||
|
@ -429,6 +429,14 @@ fn find_matching_block_end_in_expr(
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
Expr::Collect(_, expr) => find_matching_block_end_in_expr(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
expr,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
),
|
||||||
|
|
||||||
Expr::Block(block_id)
|
Expr::Block(block_id)
|
||||||
| Expr::Closure(block_id)
|
| Expr::Closure(block_id)
|
||||||
| Expr::RowCondition(block_id)
|
| Expr::RowCondition(block_id)
|
||||||
|
@ -452,15 +460,17 @@ fn find_matching_block_end_in_expr(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::StringInterpolation(exprs) => exprs.iter().find_map(|expr| {
|
Expr::StringInterpolation(exprs) | Expr::GlobInterpolation(exprs, _) => {
|
||||||
find_matching_block_end_in_expr(
|
exprs.iter().find_map(|expr| {
|
||||||
line,
|
find_matching_block_end_in_expr(
|
||||||
working_set,
|
line,
|
||||||
expr,
|
working_set,
|
||||||
global_span_offset,
|
expr,
|
||||||
global_cursor_offset,
|
global_span_offset,
|
||||||
)
|
global_cursor_offset,
|
||||||
}),
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
Expr::List(list) => {
|
Expr::List(list) => {
|
||||||
if expr_last == global_cursor_offset {
|
if expr_last == global_cursor_offset {
|
||||||
|
|
|
@ -8,7 +8,7 @@ use nu_protocol::{
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
use nu_utils::utils::perf;
|
use nu_utils::perf;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
// This will collect environment variables from std::env and adds them to a stack.
|
// This will collect environment variables from std::env and adds them to a stack.
|
||||||
|
@ -228,13 +228,10 @@ pub fn eval_source(
|
||||||
let _ = enable_vt_processing();
|
let _ = enable_vt_processing();
|
||||||
}
|
}
|
||||||
|
|
||||||
perf(
|
perf!(
|
||||||
&format!("eval_source {}", &fname),
|
&format!("eval_source {}", &fname),
|
||||||
start_time,
|
start_time,
|
||||||
file!(),
|
engine_state.get_config().use_ansi_coloring
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
engine_state.get_config().use_ansi_coloring,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
exit_code
|
exit_code
|
||||||
|
@ -265,6 +262,11 @@ fn evaluate_source(
|
||||||
return Ok(Some(1));
|
return Ok(Some(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(err) = working_set.compile_errors.first() {
|
||||||
|
report_error(&working_set, err);
|
||||||
|
// Not a fatal error, for now
|
||||||
|
}
|
||||||
|
|
||||||
(output, working_set.render())
|
(output, working_set.render())
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -319,16 +321,10 @@ mod test {
|
||||||
|
|
||||||
let env = engine_state.render_env_vars();
|
let env = engine_state.render_env_vars();
|
||||||
|
|
||||||
assert!(
|
assert!(matches!(env.get("FOO"), Some(&Value::String { val, .. }) if val == "foo"));
|
||||||
matches!(env.get(&"FOO".to_string()), Some(&Value::String { val, .. }) if val == "foo")
|
assert!(matches!(env.get("SYMBOLS"), Some(&Value::String { val, .. }) if val == symbols));
|
||||||
);
|
assert!(matches!(env.get(symbols), Some(&Value::String { val, .. }) if val == "symbols"));
|
||||||
assert!(
|
assert!(env.contains_key("PWD"));
|
||||||
matches!(env.get(&"SYMBOLS".to_string()), Some(&Value::String { val, .. }) if val == symbols)
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
matches!(env.get(&symbols.to_string()), Some(&Value::String { val, .. }) if val == "symbols")
|
|
||||||
);
|
|
||||||
assert!(env.get(&"PWD".to_string()).is_some());
|
|
||||||
assert_eq!(env.len(), 4);
|
assert_eq!(env.len(), 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
7
crates/nu-cli/tests/commands/keybindings_list.rs
Normal file
7
crates/nu-cli/tests/commands/keybindings_list.rs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn not_empty() {
|
||||||
|
let result = nu!("keybindings list | is-not-empty");
|
||||||
|
assert_eq!(result.out, "true");
|
||||||
|
}
|
|
@ -1 +1,2 @@
|
||||||
|
mod keybindings_list;
|
||||||
mod nu_highlight;
|
mod nu_highlight;
|
||||||
|
|
|
@ -11,7 +11,7 @@ use std::{
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
use support::{
|
use support::{
|
||||||
completions_helpers::{new_partial_engine, new_quote_engine},
|
completions_helpers::{new_dotnu_engine, new_partial_engine, new_quote_engine},
|
||||||
file, folder, match_suggestions, new_engine,
|
file, folder, match_suggestions, new_engine,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,7 +32,6 @@ fn completer() -> NuCompleter {
|
||||||
fn completer_strings() -> NuCompleter {
|
fn completer_strings() -> NuCompleter {
|
||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
// Add record value as example
|
// Add record value as example
|
||||||
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
|
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
|
||||||
def my-command [animal: string@animals] { print $animal }"#;
|
def my-command [animal: string@animals] { print $animal }"#;
|
||||||
|
@ -85,8 +84,43 @@ fn custom_completer() -> NuCompleter {
|
||||||
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[fixture]
|
||||||
|
fn subcommand_completer() -> NuCompleter {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
let commands = r#"
|
||||||
|
$env.config.completions.algorithm = "fuzzy"
|
||||||
|
def foo [] {}
|
||||||
|
def "foo bar" [] {}
|
||||||
|
def "foo abaz" [] {}
|
||||||
|
def "foo aabcrr" [] {}
|
||||||
|
def food [] {}
|
||||||
|
"#;
|
||||||
|
assert!(support::merge_input(commands.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
// Instantiate a new completer
|
||||||
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use fuzzy completions but sort in alphabetical order
|
||||||
|
#[fixture]
|
||||||
|
fn fuzzy_alpha_sort_completer() -> NuCompleter {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
let config = r#"
|
||||||
|
$env.config.completions.algorithm = "fuzzy"
|
||||||
|
$env.config.completions.sort = "alphabetical"
|
||||||
|
"#;
|
||||||
|
assert!(support::merge_input(config.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
// Instantiate a new completer
|
||||||
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn variables_dollar_sign_with_varialblecompletion() {
|
fn variables_dollar_sign_with_variablecompletion() {
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
@ -102,28 +136,28 @@ fn variables_double_dash_argument_with_flagcompletion(mut completer: NuCompleter
|
||||||
let suggestions = completer.complete("tst --", 6);
|
let suggestions = completer.complete("tst --", 6);
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
|
||||||
// dbg!(&expected, &suggestions);
|
// dbg!(&expected, &suggestions);
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
|
fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
|
||||||
let suggestions = completer.complete("tst -", 5);
|
let suggestions = completer.complete("tst -", 5);
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) {
|
fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) {
|
||||||
let suggestions = completer_strings.complete("my-c ", 4);
|
let suggestions = completer_strings.complete("my-c ", 4);
|
||||||
let expected: Vec<String> = vec!["my-command".into()];
|
let expected: Vec<String> = vec!["my-command".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn variables_subcommands_with_customcompletion(mut completer_strings: NuCompleter) {
|
fn variables_subcommands_with_customcompletion(mut completer_strings: NuCompleter) {
|
||||||
let suggestions = completer_strings.complete("my-command ", 11);
|
let suggestions = completer_strings.complete("my-command ", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
|
@ -132,49 +166,48 @@ fn variables_customcompletion_subcommands_with_customcompletion_2(
|
||||||
) {
|
) {
|
||||||
let suggestions = completer_strings.complete("my-command ", 11);
|
let suggestions = completer_strings.complete("my-command ", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dotnu_completions() {
|
fn dotnu_completions() {
|
||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_dotnu_engine();
|
||||||
|
|
||||||
// Instantiate a new completer
|
// Instantiate a new completer
|
||||||
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
|
||||||
|
let expected = vec![
|
||||||
|
"asdf.nu".into(),
|
||||||
|
"bar.nu".into(),
|
||||||
|
"bat.nu".into(),
|
||||||
|
"baz.nu".into(),
|
||||||
|
#[cfg(windows)]
|
||||||
|
"dir_module\\".into(),
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
"dir_module/".into(),
|
||||||
|
"foo.nu".into(),
|
||||||
|
"spam.nu".into(),
|
||||||
|
"xyzzy.nu".into(),
|
||||||
|
];
|
||||||
|
|
||||||
// Test source completion
|
// Test source completion
|
||||||
let completion_str = "source-env ".to_string();
|
let completion_str = "source-env ".to_string();
|
||||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||||
|
|
||||||
assert_eq!(2, suggestions.len());
|
match_suggestions(&expected, &suggestions);
|
||||||
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
|
|
||||||
#[cfg(windows)]
|
|
||||||
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
|
|
||||||
|
|
||||||
// Test use completion
|
// Test use completion
|
||||||
let completion_str = "use ".to_string();
|
let completion_str = "use ".to_string();
|
||||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||||
|
|
||||||
assert_eq!(2, suggestions.len());
|
match_suggestions(&expected, &suggestions);
|
||||||
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
|
|
||||||
#[cfg(windows)]
|
|
||||||
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
|
|
||||||
|
|
||||||
// Test overlay use completion
|
// Test overlay use completion
|
||||||
let completion_str = "overlay use ".to_string();
|
let completion_str = "overlay use ".to_string();
|
||||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||||
|
|
||||||
assert_eq!(2, suggestions.len());
|
match_suggestions(&expected, &suggestions);
|
||||||
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
|
|
||||||
#[cfg(windows)]
|
|
||||||
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
|
|
||||||
#[cfg(not(windows))]
|
|
||||||
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -238,8 +271,22 @@ fn file_completions() {
|
||||||
folder(dir.join(".hidden_folder")),
|
folder(dir.join(".hidden_folder")),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let separator = '/';
|
||||||
|
let target_dir = format!("cp {dir_str}{separator}");
|
||||||
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
let expected_slash_paths: Vec<String> = expected_paths
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.replace('\\', "/"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
match_suggestions(&expected_slash_paths, &slash_suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completions for a file
|
// Test completions for a file
|
||||||
let target_dir = format!("cp {}", folder(dir.join("another")));
|
let target_dir = format!("cp {}", folder(dir.join("another")));
|
||||||
|
@ -249,17 +296,91 @@ fn file_completions() {
|
||||||
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
|
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completions for hidden files
|
// Test completions for hidden files
|
||||||
let target_dir = format!("ls {}/.", folder(dir.join(".hidden_folder")));
|
let target_dir = format!("ls {}{MAIN_SEPARATOR}.", folder(dir.join(".hidden_folder")));
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
let expected_paths: Vec<String> =
|
let expected_paths: Vec<String> =
|
||||||
vec![file(dir.join(".hidden_folder").join(".hidden_subfile"))];
|
vec![file(dir.join(".hidden_folder").join(".hidden_subfile"))];
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let target_dir = format!("ls {}/.", folder(dir.join(".hidden_folder")));
|
||||||
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
let expected_slash: Vec<String> = expected_paths
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.replace('\\', "/"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
match_suggestions(&expected_slash, &slash_suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn file_completions_with_mixed_separators() {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, dir_str, engine, stack) = new_dotnu_engine();
|
||||||
|
|
||||||
|
// Instantiate a new completer
|
||||||
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
|
||||||
|
// Create Expected values
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
file(dir.join("lib-dir1").join("bar.nu")),
|
||||||
|
file(dir.join("lib-dir1").join("baz.nu")),
|
||||||
|
file(dir.join("lib-dir1").join("xyzzy.nu")),
|
||||||
|
];
|
||||||
|
let expecetd_slash_paths: Vec<String> = expected_paths
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.replace(MAIN_SEPARATOR, "/"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}/lib-dir1/");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expecetd_slash_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("cp {dir_str}\\lib-dir1/");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expecetd_slash_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}/lib-dir1\\/");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expecetd_slash_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}\\lib-dir1\\/");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expecetd_slash_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}\\lib-dir1\\");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}/lib-dir1\\");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}/lib-dir1/\\");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}\\lib-dir1/\\");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -276,13 +397,14 @@ fn partial_completions() {
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
folder(dir.join("partial_a")),
|
folder(dir.join("partial")),
|
||||||
folder(dir.join("partial_b")),
|
folder(dir.join("partial-a")),
|
||||||
folder(dir.join("partial_c")),
|
folder(dir.join("partial-b")),
|
||||||
|
folder(dir.join("partial-c")),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completions for the files whose name begin with "h"
|
// Test completions for the files whose name begin with "h"
|
||||||
// and are present under directories whose names begin with "pa"
|
// and are present under directories whose names begin with "pa"
|
||||||
|
@ -292,17 +414,18 @@ fn partial_completions() {
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
file(dir.join("partial_a").join("have_ext.exe")),
|
file(dir.join("partial").join("hello.txt")),
|
||||||
file(dir.join("partial_a").join("have_ext.txt")),
|
file(dir.join("partial-a").join("have_ext.exe")),
|
||||||
file(dir.join("partial_a").join("hello")),
|
file(dir.join("partial-a").join("have_ext.txt")),
|
||||||
file(dir.join("partial_a").join("hola")),
|
file(dir.join("partial-a").join("hello")),
|
||||||
file(dir.join("partial_b").join("hello_b")),
|
file(dir.join("partial-a").join("hola")),
|
||||||
file(dir.join("partial_b").join("hi_b")),
|
file(dir.join("partial-b").join("hello_b")),
|
||||||
file(dir.join("partial_c").join("hello_c")),
|
file(dir.join("partial-b").join("hi_b")),
|
||||||
|
file(dir.join("partial-c").join("hello_c")),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion for all files under directories whose names begin with "pa"
|
// Test completion for all files under directories whose names begin with "pa"
|
||||||
let dir_str = folder(dir.join("pa"));
|
let dir_str = folder(dir.join("pa"));
|
||||||
|
@ -311,18 +434,19 @@ fn partial_completions() {
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
file(dir.join("partial_a").join("anotherfile")),
|
file(dir.join("partial").join("hello.txt")),
|
||||||
file(dir.join("partial_a").join("have_ext.exe")),
|
file(dir.join("partial-a").join("anotherfile")),
|
||||||
file(dir.join("partial_a").join("have_ext.txt")),
|
file(dir.join("partial-a").join("have_ext.exe")),
|
||||||
file(dir.join("partial_a").join("hello")),
|
file(dir.join("partial-a").join("have_ext.txt")),
|
||||||
file(dir.join("partial_a").join("hola")),
|
file(dir.join("partial-a").join("hello")),
|
||||||
file(dir.join("partial_b").join("hello_b")),
|
file(dir.join("partial-a").join("hola")),
|
||||||
file(dir.join("partial_b").join("hi_b")),
|
file(dir.join("partial-b").join("hello_b")),
|
||||||
file(dir.join("partial_c").join("hello_c")),
|
file(dir.join("partial-b").join("hi_b")),
|
||||||
|
file(dir.join("partial-c").join("hello_c")),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion for a single file
|
// Test completion for a single file
|
||||||
let dir_str = file(dir.join("fi").join("so"));
|
let dir_str = file(dir.join("fi").join("so"));
|
||||||
|
@ -333,7 +457,7 @@ fn partial_completions() {
|
||||||
let expected_paths: Vec<String> = vec![file(dir.join("final_partial").join("somefile"))];
|
let expected_paths: Vec<String> = vec![file(dir.join("final_partial").join("somefile"))];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion where there is a sneaky `..` in the path
|
// Test completion where there is a sneaky `..` in the path
|
||||||
let dir_str = file(dir.join("par").join("..").join("fi").join("so"));
|
let dir_str = file(dir.join("par").join("..").join("fi").join("so"));
|
||||||
|
@ -343,19 +467,25 @@ fn partial_completions() {
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
file(
|
file(
|
||||||
dir.join("partial_a")
|
dir.join("partial")
|
||||||
.join("..")
|
.join("..")
|
||||||
.join("final_partial")
|
.join("final_partial")
|
||||||
.join("somefile"),
|
.join("somefile"),
|
||||||
),
|
),
|
||||||
file(
|
file(
|
||||||
dir.join("partial_b")
|
dir.join("partial-a")
|
||||||
.join("..")
|
.join("..")
|
||||||
.join("final_partial")
|
.join("final_partial")
|
||||||
.join("somefile"),
|
.join("somefile"),
|
||||||
),
|
),
|
||||||
file(
|
file(
|
||||||
dir.join("partial_c")
|
dir.join("partial-b")
|
||||||
|
.join("..")
|
||||||
|
.join("final_partial")
|
||||||
|
.join("somefile"),
|
||||||
|
),
|
||||||
|
file(
|
||||||
|
dir.join("partial-c")
|
||||||
.join("..")
|
.join("..")
|
||||||
.join("final_partial")
|
.join("final_partial")
|
||||||
.join("somefile"),
|
.join("somefile"),
|
||||||
|
@ -363,35 +493,35 @@ fn partial_completions() {
|
||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion for all files under directories whose names begin with "pa"
|
// Test completion for all files under directories whose names begin with "pa"
|
||||||
let file_str = file(dir.join("partial_a").join("have"));
|
let file_str = file(dir.join("partial-a").join("have"));
|
||||||
let target_file = format!("rm {file_str}");
|
let target_file = format!("rm {file_str}");
|
||||||
let suggestions = completer.complete(&target_file, target_file.len());
|
let suggestions = completer.complete(&target_file, target_file.len());
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
file(dir.join("partial_a").join("have_ext.exe")),
|
file(dir.join("partial-a").join("have_ext.exe")),
|
||||||
file(dir.join("partial_a").join("have_ext.txt")),
|
file(dir.join("partial-a").join("have_ext.txt")),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion for all files under directories whose names begin with "pa"
|
// Test completion for all files under directories whose names begin with "pa"
|
||||||
let file_str = file(dir.join("partial_a").join("have_ext."));
|
let file_str = file(dir.join("partial-a").join("have_ext."));
|
||||||
let file_dir = format!("rm {file_str}");
|
let file_dir = format!("rm {file_str}");
|
||||||
let suggestions = completer.complete(&file_dir, file_dir.len());
|
let suggestions = completer.complete(&file_dir, file_dir.len());
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
file(dir.join("partial_a").join("have_ext.exe")),
|
file(dir.join("partial-a").join("have_ext.exe")),
|
||||||
file(dir.join("partial_a").join("have_ext.txt")),
|
file(dir.join("partial-a").join("have_ext.txt")),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -426,15 +556,16 @@ fn command_ls_with_filecompletion() {
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
let target_dir = "ls custom_completion.";
|
let target_dir = "ls custom_completion.";
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
|
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn command_open_with_filecompletion() {
|
fn command_open_with_filecompletion() {
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
@ -467,14 +598,14 @@ fn command_open_with_filecompletion() {
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
let target_dir = "open custom_completion.";
|
let target_dir = "open custom_completion.";
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
|
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -509,7 +640,7 @@ fn command_rm_with_globcompletion() {
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -544,7 +675,7 @@ fn command_cp_with_globcompletion() {
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -579,7 +710,7 @@ fn command_save_with_filecompletion() {
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -614,7 +745,7 @@ fn command_touch_with_filecompletion() {
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -649,7 +780,28 @@ fn command_watch_with_filecompletion() {
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn subcommand_completions(mut subcommand_completer: NuCompleter) {
|
||||||
|
let prefix = "foo br";
|
||||||
|
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
||||||
|
match_suggestions(
|
||||||
|
&vec!["foo bar".to_string(), "foo aabcrr".to_string()],
|
||||||
|
&suggestions,
|
||||||
|
);
|
||||||
|
|
||||||
|
let prefix = "foo b";
|
||||||
|
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
||||||
|
match_suggestions(
|
||||||
|
&vec![
|
||||||
|
"foo bar".to_string(),
|
||||||
|
"foo aabcrr".to_string(),
|
||||||
|
"foo abaz".to_string(),
|
||||||
|
],
|
||||||
|
&suggestions,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -662,19 +814,19 @@ fn file_completion_quoted() {
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
let expected_paths: Vec<String> = vec![
|
let expected_paths: Vec<String> = vec![
|
||||||
"\'[a] bc.txt\'".to_string(),
|
|
||||||
"`--help`".to_string(),
|
"`--help`".to_string(),
|
||||||
"`-42`".to_string(),
|
"`-42`".to_string(),
|
||||||
"`-inf`".to_string(),
|
"`-inf`".to_string(),
|
||||||
"`4.2`".to_string(),
|
"`4.2`".to_string(),
|
||||||
|
"\'[a] bc.txt\'".to_string(),
|
||||||
"`te st.txt`".to_string(),
|
"`te st.txt`".to_string(),
|
||||||
"`te#st.txt`".to_string(),
|
"`te#st.txt`".to_string(),
|
||||||
"`te'st.txt`".to_string(),
|
"`te'st.txt`".to_string(),
|
||||||
"`te(st).txt`".to_string(),
|
"`te(st).txt`".to_string(),
|
||||||
format!("`{}`", folder("test dir".into())),
|
format!("`{}`", folder("test dir")),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
let dir: PathBuf = "test dir".into();
|
let dir: PathBuf = "test dir".into();
|
||||||
let target_dir = format!("open '{}'", folder(dir.clone()));
|
let target_dir = format!("open '{}'", folder(dir.clone()));
|
||||||
|
@ -685,7 +837,7 @@ fn file_completion_quoted() {
|
||||||
format!("`{}`", file(dir.join("single quote"))),
|
format!("`{}`", file(dir.join("single quote"))),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -720,7 +872,7 @@ fn flag_completions() {
|
||||||
];
|
];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -744,8 +896,21 @@ fn folder_with_directorycompletions() {
|
||||||
folder(dir.join(".hidden_folder")),
|
folder(dir.join(".hidden_folder")),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let target_dir = format!("cd {dir_str}/");
|
||||||
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
let expected_slash_paths: Vec<String> = expected_paths
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.replace('\\', "/"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
match_suggestions(&expected_slash_paths, &slash_suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -763,11 +928,13 @@ fn variables_completions() {
|
||||||
// Test completions for $nu
|
// Test completions for $nu
|
||||||
let suggestions = completer.complete("$nu.", 4);
|
let suggestions = completer.complete("$nu.", 4);
|
||||||
|
|
||||||
assert_eq!(15, suggestions.len());
|
assert_eq!(18, suggestions.len());
|
||||||
|
|
||||||
let expected: Vec<String> = vec![
|
let expected: Vec<String> = vec![
|
||||||
|
"cache-dir".into(),
|
||||||
"config-path".into(),
|
"config-path".into(),
|
||||||
"current-exe".into(),
|
"current-exe".into(),
|
||||||
|
"data-dir".into(),
|
||||||
"default-config-dir".into(),
|
"default-config-dir".into(),
|
||||||
"env-path".into(),
|
"env-path".into(),
|
||||||
"history-enabled".into(),
|
"history-enabled".into(),
|
||||||
|
@ -781,10 +948,11 @@ fn variables_completions() {
|
||||||
"plugin-path".into(),
|
"plugin-path".into(),
|
||||||
"startup-time".into(),
|
"startup-time".into(),
|
||||||
"temp-path".into(),
|
"temp-path".into(),
|
||||||
|
"vendor-autoload-dirs".into(),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for $nu.h (filter)
|
// Test completions for $nu.h (filter)
|
||||||
let suggestions = completer.complete("$nu.h", 5);
|
let suggestions = completer.complete("$nu.h", 5);
|
||||||
|
@ -798,7 +966,7 @@ fn variables_completions() {
|
||||||
];
|
];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for $nu.os-info
|
// Test completions for $nu.os-info
|
||||||
let suggestions = completer.complete("$nu.os-info.", 12);
|
let suggestions = completer.complete("$nu.os-info.", 12);
|
||||||
|
@ -810,7 +978,7 @@ fn variables_completions() {
|
||||||
"name".into(),
|
"name".into(),
|
||||||
];
|
];
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for custom var
|
// Test completions for custom var
|
||||||
let suggestions = completer.complete("$actor.", 7);
|
let suggestions = completer.complete("$actor.", 7);
|
||||||
|
@ -820,7 +988,7 @@ fn variables_completions() {
|
||||||
let expected: Vec<String> = vec!["age".into(), "name".into()];
|
let expected: Vec<String> = vec!["age".into(), "name".into()];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for custom var (filtering)
|
// Test completions for custom var (filtering)
|
||||||
let suggestions = completer.complete("$actor.n", 8);
|
let suggestions = completer.complete("$actor.n", 8);
|
||||||
|
@ -830,7 +998,7 @@ fn variables_completions() {
|
||||||
let expected: Vec<String> = vec!["name".into()];
|
let expected: Vec<String> = vec!["name".into()];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for $env
|
// Test completions for $env
|
||||||
let suggestions = completer.complete("$env.", 5);
|
let suggestions = completer.complete("$env.", 5);
|
||||||
|
@ -843,7 +1011,7 @@ fn variables_completions() {
|
||||||
let expected: Vec<String> = vec!["PATH".into(), "PWD".into(), "TEST".into()];
|
let expected: Vec<String> = vec!["PATH".into(), "PWD".into(), "TEST".into()];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for $env
|
// Test completions for $env
|
||||||
let suggestions = completer.complete("$env.T", 6);
|
let suggestions = completer.complete("$env.T", 6);
|
||||||
|
@ -853,7 +1021,12 @@ fn variables_completions() {
|
||||||
let expected: Vec<String> = vec!["TEST".into()];
|
let expected: Vec<String> = vec!["TEST".into()];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
|
let suggestions = completer.complete("$", 1);
|
||||||
|
let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()];
|
||||||
|
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -872,7 +1045,7 @@ fn alias_of_command_and_flags() {
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -891,7 +1064,7 @@ fn alias_of_basic_command() {
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -913,7 +1086,7 @@ fn alias_of_another_alias() {
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
||||||
|
@ -976,35 +1149,35 @@ fn unknown_command_completion() {
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
|
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
|
||||||
let suggestions = completer.complete("tst -h", 5);
|
let suggestions = completer.complete("tst -h", 5);
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
|
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
|
||||||
let suggestions = completer_strings.complete("my-command c", 11);
|
let suggestions = completer_strings.complete("my-command c", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
|
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
|
||||||
let suggestions = completer_strings.complete("my-command c | ls", 11);
|
let suggestions = completer_strings.complete("my-command c | ls", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
|
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
|
||||||
let suggestions = completer.complete("tst -h | ls", 5);
|
let suggestions = completer.complete("tst -h | ls", 5);
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1038,77 +1211,88 @@ fn filecompletions_triggers_after_cursor() {
|
||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam ", 5);
|
let suggestions = extern_completer.complete("spam ", 5);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam --foo=", 11);
|
let suggestions = extern_completer.complete("spam --foo=", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam --foo ", 11);
|
let suggestions = extern_completer.complete("spam --foo ", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam -f ", 8);
|
let suggestions = extern_completer.complete("spam -f ", 8);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam -b ", 8);
|
let suggestions = extern_completer.complete("spam -b ", 8);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_complete_flags(mut extern_completer: NuCompleter) {
|
fn extern_complete_flags(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam -", 6);
|
let suggestions = extern_completer.complete("spam -", 6);
|
||||||
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
|
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn custom_completer_triggers_cursor_before_word(mut custom_completer: NuCompleter) {
|
fn custom_completer_triggers_cursor_before_word(mut custom_completer: NuCompleter) {
|
||||||
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
||||||
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
|
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn custom_completer_triggers_cursor_on_word_left_boundary(mut custom_completer: NuCompleter) {
|
fn custom_completer_triggers_cursor_on_word_left_boundary(mut custom_completer: NuCompleter) {
|
||||||
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
||||||
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
|
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn custom_completer_triggers_cursor_next_to_word(mut custom_completer: NuCompleter) {
|
fn custom_completer_triggers_cursor_next_to_word(mut custom_completer: NuCompleter) {
|
||||||
let suggestions = custom_completer.complete("cmd foo bar", 11);
|
let suggestions = custom_completer.complete("cmd foo bar", 11);
|
||||||
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into()];
|
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn custom_completer_triggers_cursor_after_word(mut custom_completer: NuCompleter) {
|
fn custom_completer_triggers_cursor_after_word(mut custom_completer: NuCompleter) {
|
||||||
let suggestions = custom_completer.complete("cmd foo bar ", 12);
|
let suggestions = custom_completer.complete("cmd foo bar ", 12);
|
||||||
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into(), "".into()];
|
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into(), "".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn sort_fuzzy_completions_in_alphabetical_order(mut fuzzy_alpha_sort_completer: NuCompleter) {
|
||||||
|
let suggestions = fuzzy_alpha_sort_completer.complete("ls nu", 5);
|
||||||
|
// Even though "nushell" is a better match, it should come second because
|
||||||
|
// the completions should be sorted in alphabetical order
|
||||||
|
match_suggestions(
|
||||||
|
&vec!["custom_completion.nu".into(), "nushell".into()],
|
||||||
|
&suggestions,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ignore = "was reverted, still needs fixing"]
|
#[ignore = "was reverted, still needs fixing"]
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
|
use nu_path::{AbsolutePathBuf, PathBuf};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
|
@ -7,14 +8,14 @@ use nu_protocol::{
|
||||||
};
|
};
|
||||||
use nu_test_support::fs;
|
use nu_test_support::fs;
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::path::{PathBuf, MAIN_SEPARATOR};
|
use std::path::MAIN_SEPARATOR;
|
||||||
|
|
||||||
fn create_default_context() -> EngineState {
|
fn create_default_context() -> EngineState {
|
||||||
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
|
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
|
||||||
}
|
}
|
||||||
|
|
||||||
// creates a new engine with the current path into the completions fixtures folder
|
// creates a new engine with the current path into the completions fixtures folder
|
||||||
pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
// Target folder inside assets
|
||||||
let dir = fs::fixtures().join("completions");
|
let dir = fs::fixtures().join("completions");
|
||||||
let dir_str = dir
|
let dir_str = dir
|
||||||
|
@ -68,7 +69,53 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
||||||
(dir, dir_str, engine_state, stack)
|
(dir, dir_str, engine_state, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
|
// creates a new engine with the current path into the completions fixtures folder
|
||||||
|
pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||||
|
// Target folder inside assets
|
||||||
|
let dir = fs::fixtures().join("dotnu_completions");
|
||||||
|
let dir_str = dir
|
||||||
|
.clone()
|
||||||
|
.into_os_string()
|
||||||
|
.into_string()
|
||||||
|
.unwrap_or_default();
|
||||||
|
let dir_span = nu_protocol::Span::new(0, dir_str.len());
|
||||||
|
|
||||||
|
// Create a new engine with default context
|
||||||
|
let mut engine_state = create_default_context();
|
||||||
|
|
||||||
|
// Add $nu
|
||||||
|
engine_state.generate_nu_constant();
|
||||||
|
|
||||||
|
// New stack
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
|
||||||
|
// Add pwd as env var
|
||||||
|
stack.add_env_var("PWD".to_string(), Value::string(dir_str.clone(), dir_span));
|
||||||
|
stack.add_env_var(
|
||||||
|
"TEST".to_string(),
|
||||||
|
Value::string("NUSHELL".to_string(), dir_span),
|
||||||
|
);
|
||||||
|
|
||||||
|
stack.add_env_var(
|
||||||
|
"NU_LIB_DIRS".to_string(),
|
||||||
|
Value::List {
|
||||||
|
vals: vec![
|
||||||
|
Value::string(file(dir.join("lib-dir1")), dir_span),
|
||||||
|
Value::string(file(dir.join("lib-dir2")), dir_span),
|
||||||
|
Value::string(file(dir.join("lib-dir3")), dir_span),
|
||||||
|
],
|
||||||
|
internal_span: dir_span,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Merge environment into the permanent state
|
||||||
|
let merge_result = engine_state.merge_env(&mut stack, &dir);
|
||||||
|
assert!(merge_result.is_ok());
|
||||||
|
|
||||||
|
(dir, dir_str, engine_state, stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_quote_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
// Target folder inside assets
|
||||||
let dir = fs::fixtures().join("quoted_completions");
|
let dir = fs::fixtures().join("quoted_completions");
|
||||||
let dir_str = dir
|
let dir_str = dir
|
||||||
|
@ -103,7 +150,7 @@ pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
|
||||||
(dir, dir_str, engine_state, stack)
|
(dir, dir_str, engine_state, stack)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) {
|
pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
// Target folder inside assets
|
||||||
let dir = fs::fixtures().join("partial_completions");
|
let dir = fs::fixtures().join("partial_completions");
|
||||||
let dir_str = dir
|
let dir_str = dir
|
||||||
|
@ -139,7 +186,7 @@ pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// match a list of suggestions with the expected values
|
// match a list of suggestions with the expected values
|
||||||
pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
|
pub fn match_suggestions(expected: &Vec<String>, suggestions: &Vec<Suggestion>) {
|
||||||
let expected_len = expected.len();
|
let expected_len = expected.len();
|
||||||
let suggestions_len = suggestions.len();
|
let suggestions_len = suggestions.len();
|
||||||
if expected_len != suggestions_len {
|
if expected_len != suggestions_len {
|
||||||
|
@ -149,22 +196,25 @@ pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
|
||||||
Expected: {expected:#?}\n"
|
Expected: {expected:#?}\n"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
expected.iter().zip(suggestions).for_each(|it| {
|
|
||||||
assert_eq!(it.0, &it.1.value);
|
let suggestoins_str = suggestions
|
||||||
});
|
.iter()
|
||||||
|
.map(|it| it.value.clone())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
assert_eq!(expected, &suggestoins_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// append the separator to the converted path
|
// append the separator to the converted path
|
||||||
pub fn folder(path: PathBuf) -> String {
|
pub fn folder(path: impl Into<PathBuf>) -> String {
|
||||||
let mut converted_path = file(path);
|
let mut converted_path = file(path);
|
||||||
converted_path.push(MAIN_SEPARATOR);
|
converted_path.push(MAIN_SEPARATOR);
|
||||||
|
|
||||||
converted_path
|
converted_path
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert a given path to string
|
// convert a given path to string
|
||||||
pub fn file(path: PathBuf) -> String {
|
pub fn file(path: impl Into<PathBuf>) -> String {
|
||||||
path.into_os_string().into_string().unwrap_or_default()
|
path.into().into_os_string().into_string().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// merge_input executes the given input into the engine
|
// merge_input executes the given input into the engine
|
||||||
|
@ -173,7 +223,7 @@ pub fn merge_input(
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
dir: PathBuf,
|
dir: AbsolutePathBuf,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(engine_state);
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
|
|
|
@ -5,17 +5,17 @@ edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-base"
|
name = "nu-cmd-base"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
||||||
version = "0.94.3"
|
version = "0.96.2"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.94.3" }
|
nu-engine = { path = "../nu-engine", version = "0.96.2" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.94.3" }
|
nu-parser = { path = "../nu-parser", version = "0.96.2" }
|
||||||
nu-path = { path = "../nu-path", version = "0.94.3" }
|
nu-path = { path = "../nu-path", version = "0.96.2" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
|
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
|
||||||
|
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
miette = { workspace = true }
|
miette = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
5
crates/nu-cmd-base/README.md
Normal file
5
crates/nu-cmd-base/README.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
Utilities used by the different `nu-command`/`nu-cmd-*` crates, should not contain any full `Command` implementations.
|
||||||
|
|
||||||
|
## Internal Nushell crate
|
||||||
|
|
||||||
|
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.
|
|
@ -194,7 +194,7 @@ pub fn eval_hook(
|
||||||
let Some(follow) = val.get("code") else {
|
let Some(follow) = val.get("code") else {
|
||||||
return Err(ShellError::CantFindColumn {
|
return Err(ShellError::CantFindColumn {
|
||||||
col_name: "code".into(),
|
col_name: "code".into(),
|
||||||
span,
|
span: Some(span),
|
||||||
src_span: span,
|
src_span: span,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use nu_protocol::{ast::CellPath, PipelineData, ShellError, Span, Value};
|
use nu_protocol::{ast::CellPath, PipelineData, ShellError, Signals, Span, Value};
|
||||||
use std::sync::{atomic::AtomicBool, Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub trait CmdArgument {
|
pub trait CmdArgument {
|
||||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>>;
|
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>>;
|
||||||
|
@ -40,7 +40,7 @@ pub fn operate<C, A>(
|
||||||
mut arg: A,
|
mut arg: A,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
span: Span,
|
span: Span,
|
||||||
ctrlc: Option<Arc<AtomicBool>>,
|
signals: &Signals,
|
||||||
) -> Result<PipelineData, ShellError>
|
) -> Result<PipelineData, ShellError>
|
||||||
where
|
where
|
||||||
A: CmdArgument + Send + Sync + 'static,
|
A: CmdArgument + Send + Sync + 'static,
|
||||||
|
@ -55,7 +55,7 @@ where
|
||||||
_ => cmd(&v, &arg, span),
|
_ => cmd(&v, &arg, span),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ctrlc,
|
signals,
|
||||||
),
|
),
|
||||||
Some(column_paths) => {
|
Some(column_paths) => {
|
||||||
let arg = Arc::new(arg);
|
let arg = Arc::new(arg);
|
||||||
|
@ -79,7 +79,7 @@ where
|
||||||
}
|
}
|
||||||
v
|
v
|
||||||
},
|
},
|
||||||
ctrlc,
|
signals,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
pub mod formats;
|
pub mod formats;
|
||||||
pub mod hook;
|
pub mod hook;
|
||||||
pub mod input_handler;
|
pub mod input_handler;
|
||||||
|
|
|
@ -1,32 +1,40 @@
|
||||||
|
use nu_path::AbsolutePathBuf;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
Range, ShellError, Span, Value,
|
Range, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
use std::{ops::Bound, path::PathBuf};
|
use std::ops::Bound;
|
||||||
|
|
||||||
pub fn get_init_cwd() -> PathBuf {
|
pub fn get_init_cwd() -> AbsolutePathBuf {
|
||||||
std::env::current_dir().unwrap_or_else(|_| {
|
std::env::current_dir()
|
||||||
std::env::var("PWD")
|
.ok()
|
||||||
.map(Into::into)
|
.and_then(|path| AbsolutePathBuf::try_from(path).ok())
|
||||||
.unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
|
.or_else(|| {
|
||||||
})
|
std::env::var("PWD")
|
||||||
|
.ok()
|
||||||
|
.and_then(|path| AbsolutePathBuf::try_from(path).ok())
|
||||||
|
})
|
||||||
|
.or_else(nu_path::home_dir)
|
||||||
|
.expect("Failed to get current working directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> AbsolutePathBuf {
|
||||||
engine_state
|
engine_state
|
||||||
.cwd(Some(stack))
|
.cwd(Some(stack))
|
||||||
.unwrap_or(crate::util::get_init_cwd())
|
.ok()
|
||||||
|
.unwrap_or_else(get_init_cwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
type MakeRangeError = fn(&str, Span) -> ShellError;
|
type MakeRangeError = fn(&str, Span) -> ShellError;
|
||||||
|
|
||||||
|
/// Returns a inclusive pair of boundary in given `range`.
|
||||||
pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> {
|
pub fn process_range(range: &Range) -> Result<(isize, isize), MakeRangeError> {
|
||||||
match range {
|
match range {
|
||||||
Range::IntRange(range) => {
|
Range::IntRange(range) => {
|
||||||
let start = range.start().try_into().unwrap_or(0);
|
let start = range.start().try_into().unwrap_or(0);
|
||||||
let end = match range.end() {
|
let end = match range.end() {
|
||||||
Bound::Included(v) => (v + 1) as isize,
|
Bound::Included(v) => v as isize,
|
||||||
Bound::Excluded(v) => v as isize,
|
Bound::Excluded(v) => (v - 1) as isize,
|
||||||
Bound::Unbounded => isize::MAX,
|
Bound::Unbounded => isize::MAX,
|
||||||
};
|
};
|
||||||
Ok((start, end))
|
Ok((start, end))
|
||||||
|
|
|
@ -5,7 +5,7 @@ edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-extra"
|
name = "nu-cmd-extra"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
||||||
version = "0.94.3"
|
version = "0.96.2"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -13,13 +13,13 @@ version = "0.94.3"
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.94.3" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.96.2" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.94.3" }
|
nu-engine = { path = "../nu-engine", version = "0.96.2" }
|
||||||
nu-json = { version = "0.94.3", path = "../nu-json" }
|
nu-json = { version = "0.96.2", path = "../nu-json" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.94.3" }
|
nu-parser = { path = "../nu-parser", version = "0.96.2" }
|
||||||
nu-pretty-hex = { version = "0.94.3", path = "../nu-pretty-hex" }
|
nu-pretty-hex = { version = "0.96.2", path = "../nu-pretty-hex" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
|
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.94.3" }
|
nu-utils = { path = "../nu-utils", version = "0.96.2" }
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
heck = { workspace = true }
|
heck = { workspace = true }
|
||||||
|
@ -32,11 +32,7 @@ serde_urlencoded = { workspace = true }
|
||||||
v_htmlescape = { workspace = true }
|
v_htmlescape = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
|
|
||||||
[features]
|
|
||||||
extra = ["default"]
|
|
||||||
default = []
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.3" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.96.2" }
|
||||||
nu-command = { path = "../nu-command", version = "0.94.3" }
|
nu-command = { path = "../nu-command", version = "0.96.2" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.94.3" }
|
nu-test-support = { path = "../nu-test-support", version = "0.96.2" }
|
|
@ -79,7 +79,7 @@ impl Command for BitsAnd {
|
||||||
|
|
||||||
input.map(
|
input.map(
|
||||||
move |value| binary_op(&value, &target, little_endian, |(l, r)| l & r, head),
|
move |value| binary_op(&value, &target, little_endian, |(l, r)| l & r, head),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
use nu_protocol::Signals;
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
|
|
||||||
pub struct Arguments {
|
pub struct Arguments {
|
||||||
|
@ -118,12 +121,43 @@ fn into_bits(
|
||||||
let cell_paths = call.rest(engine_state, stack, 0)?;
|
let cell_paths = call.rest(engine_state, stack, 0)?;
|
||||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||||
|
|
||||||
if let PipelineData::ByteStream(stream, ..) = input {
|
if let PipelineData::ByteStream(stream, metadata) = input {
|
||||||
// TODO: in the future, we may want this to stream out, converting each to bytes
|
Ok(PipelineData::ByteStream(
|
||||||
Ok(Value::binary(stream.into_bytes()?, head).into_pipeline_data())
|
byte_stream_to_bits(stream, head),
|
||||||
|
metadata,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
let args = Arguments { cell_paths };
|
let args = Arguments { cell_paths };
|
||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.signals())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn byte_stream_to_bits(stream: ByteStream, head: Span) -> ByteStream {
|
||||||
|
if let Some(mut reader) = stream.reader() {
|
||||||
|
let mut is_first = true;
|
||||||
|
ByteStream::from_fn(
|
||||||
|
head,
|
||||||
|
Signals::empty(),
|
||||||
|
ByteStreamType::String,
|
||||||
|
move |buffer| {
|
||||||
|
let mut byte = [0];
|
||||||
|
if reader.read(&mut byte[..]).err_span(head)? > 0 {
|
||||||
|
// Format the byte as bits
|
||||||
|
if is_first {
|
||||||
|
is_first = false;
|
||||||
|
} else {
|
||||||
|
buffer.push(b' ');
|
||||||
|
}
|
||||||
|
write!(buffer, "{:08b}", byte[0]).expect("format failed");
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
// EOF
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ByteStream::read(io::empty(), head, Signals::empty(), ByteStreamType::String)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,7 +82,7 @@ impl Command for BitsNot {
|
||||||
number_size,
|
number_size,
|
||||||
};
|
};
|
||||||
|
|
||||||
operate(action, args, input, head, engine_state.ctrlc.clone())
|
operate(action, args, input, head, engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -80,7 +80,7 @@ impl Command for BitsOr {
|
||||||
|
|
||||||
input.map(
|
input.map(
|
||||||
move |value| binary_op(&value, &target, little_endian, |(l, r)| l | r, head),
|
move |value| binary_op(&value, &target, little_endian, |(l, r)| l | r, head),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl Command for BitsRol {
|
||||||
bits,
|
bits,
|
||||||
};
|
};
|
||||||
|
|
||||||
operate(action, args, input, head, engine_state.ctrlc.clone())
|
operate(action, args, input, head, engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -86,7 +86,7 @@ impl Command for BitsRor {
|
||||||
bits,
|
bits,
|
||||||
};
|
};
|
||||||
|
|
||||||
operate(action, args, input, head, engine_state.ctrlc.clone())
|
operate(action, args, input, head, engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -88,7 +88,7 @@ impl Command for BitsShl {
|
||||||
bits,
|
bits,
|
||||||
};
|
};
|
||||||
|
|
||||||
operate(action, args, input, head, engine_state.ctrlc.clone())
|
operate(action, args, input, head, engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -88,7 +88,7 @@ impl Command for BitsShr {
|
||||||
bits,
|
bits,
|
||||||
};
|
};
|
||||||
|
|
||||||
operate(action, args, input, head, engine_state.ctrlc.clone())
|
operate(action, args, input, head, engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -80,7 +80,7 @@ impl Command for BitsXor {
|
||||||
|
|
||||||
input.map(
|
input.map(
|
||||||
move |value| binary_op(&value, &target, little_endian, |(l, r)| l ^ r, head),
|
move |value| binary_op(&value, &target, little_endian, |(l, r)| l ^ r, head),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ fn fmt(
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||||
let args = CellPathOnlyArgs::from(cell_paths);
|
let args = CellPathOnlyArgs::from(cell_paths);
|
||||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
operate(action, args, input, call.head, engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
|
|
|
@ -89,7 +89,7 @@ impl Command for EachWhile {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fuse()
|
.fuse()
|
||||||
.into_pipeline_data(head, engine_state.ctrlc.clone()))
|
.into_pipeline_data(head, engine_state.signals().clone()))
|
||||||
}
|
}
|
||||||
PipelineData::ByteStream(stream, ..) => {
|
PipelineData::ByteStream(stream, ..) => {
|
||||||
let span = stream.span();
|
let span = stream.span();
|
||||||
|
@ -107,7 +107,7 @@ impl Command for EachWhile {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fuse()
|
.fuse()
|
||||||
.into_pipeline_data(head, engine_state.ctrlc.clone()))
|
.into_pipeline_data(head, engine_state.signals().clone()))
|
||||||
} else {
|
} else {
|
||||||
Ok(PipelineData::Empty)
|
Ok(PipelineData::Empty)
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ impl Command for UpdateCells {
|
||||||
columns,
|
columns,
|
||||||
span: head,
|
span: head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data(head, engine_state.ctrlc.clone())
|
.into_pipeline_data(head, engine_state.signals().clone())
|
||||||
.set_metadata(metadata))
|
.set_metadata(metadata))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -239,7 +239,7 @@ fn to_html(
|
||||||
let partial = call.has_flag(engine_state, stack, "partial")?;
|
let partial = call.has_flag(engine_state, stack, "partial")?;
|
||||||
let list = call.has_flag(engine_state, stack, "list")?;
|
let list = call.has_flag(engine_state, stack, "list")?;
|
||||||
let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
|
let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
|
||||||
let config = engine_state.get_config();
|
let config = &stack.get_config(engine_state);
|
||||||
|
|
||||||
let vec_of_values = input.into_iter().collect::<Vec<Value>>();
|
let vec_of_values = input.into_iter().collect::<Vec<Value>>();
|
||||||
let headers = merge_descriptors(&vec_of_values);
|
let headers = merge_descriptors(&vec_of_values);
|
||||||
|
@ -368,6 +368,7 @@ fn theme_demo(span: Span) -> PipelineData {
|
||||||
.collect();
|
.collect();
|
||||||
Value::list(result, span).into_pipeline_data_with_metadata(PipelineMetadata {
|
Value::list(result, span).into_pipeline_data_with_metadata(PipelineMetadata {
|
||||||
data_source: DataSource::HtmlThemes,
|
data_source: DataSource::HtmlThemes,
|
||||||
|
content_type: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl Command for SubCommand {
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(
|
||||||
move |value| operate(value, head, use_degrees),
|
move |value| operate(value, head, use_degrees),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,7 @@ impl Command for SubCommand {
|
||||||
if matches!(input, PipelineData::Empty) {
|
if matches!(input, PipelineData::Empty) {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
move |value| operate(value, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl Command for SubCommand {
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(
|
||||||
move |value| operate(value, head, use_degrees),
|
move |value| operate(value, head, use_degrees),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,7 @@ impl Command for SubCommand {
|
||||||
if matches!(input, PipelineData::Empty) {
|
if matches!(input, PipelineData::Empty) {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
move |value| operate(value, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl Command for SubCommand {
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(
|
||||||
move |value| operate(value, head, use_degrees),
|
move |value| operate(value, head, use_degrees),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,7 @@ impl Command for SubCommand {
|
||||||
if matches!(input, PipelineData::Empty) {
|
if matches!(input, PipelineData::Empty) {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
move |value| operate(value, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl Command for SubCommand {
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(
|
||||||
move |value| operate(value, head, use_degrees),
|
move |value| operate(value, head, use_degrees),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,7 @@ impl Command for SubCommand {
|
||||||
if matches!(input, PipelineData::Empty) {
|
if matches!(input, PipelineData::Empty) {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
move |value| operate(value, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -41,10 +41,7 @@ impl Command for SubCommand {
|
||||||
if matches!(input, PipelineData::Empty) {
|
if matches!(input, PipelineData::Empty) {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
move |value| operate(value, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -41,10 +41,7 @@ impl Command for SubCommand {
|
||||||
if matches!(input, PipelineData::Empty) {
|
if matches!(input, PipelineData::Empty) {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
move |value| operate(value, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl Command for SubCommand {
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(
|
||||||
move |value| operate(value, head, use_degrees),
|
move |value| operate(value, head, use_degrees),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,7 @@ impl Command for SubCommand {
|
||||||
if matches!(input, PipelineData::Empty) {
|
if matches!(input, PipelineData::Empty) {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
move |value| operate(value, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl Command for SubCommand {
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(
|
||||||
move |value| operate(value, head, use_degrees),
|
move |value| operate(value, head, use_degrees),
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,10 +41,7 @@ impl Command for SubCommand {
|
||||||
if matches!(input, PipelineData::Empty) {
|
if matches!(input, PipelineData::Empty) {
|
||||||
return Err(ShellError::PipelineEmpty { dst_span: head });
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
}
|
}
|
||||||
input.map(
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
move |value| operate(value, head),
|
|
||||||
engine_state.ctrlc.clone(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
|
|
@ -140,7 +140,7 @@ fn operate(
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
engine_state.ctrlc.clone(),
|
engine_state.signals(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -88,7 +88,7 @@ pub fn operate(
|
||||||
cell_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
|
|
||||||
general_operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
general_operate(action, args, input, call.head, engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(
|
fn action(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use nu_engine::{command_prelude::*, get_eval_expression};
|
use nu_engine::command_prelude::*;
|
||||||
use nu_parser::parse_expression;
|
use nu_protocol::{ast::PathMember, engine::StateWorkingSet, Config, ListStream};
|
||||||
use nu_protocol::{ast::PathMember, engine::StateWorkingSet, ListStream};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FormatPattern;
|
pub struct FormatPattern;
|
||||||
|
@ -44,6 +43,8 @@ impl Command for FormatPattern {
|
||||||
let it_id = working_set.add_variable(b"$it".to_vec(), call.head, Type::Any, false);
|
let it_id = working_set.add_variable(b"$it".to_vec(), call.head, Type::Any, false);
|
||||||
stack.add_var(it_id, input_val.clone());
|
stack.add_var(it_id, input_val.clone());
|
||||||
|
|
||||||
|
let config = stack.get_config(engine_state);
|
||||||
|
|
||||||
match specified_pattern {
|
match specified_pattern {
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
Ok(pattern) => {
|
Ok(pattern) => {
|
||||||
|
@ -57,14 +58,7 @@ impl Command for FormatPattern {
|
||||||
string_span.start + 1,
|
string_span.start + 1,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
format(
|
format(input_val, &ops, engine_state, &config, call.head)
|
||||||
input_val,
|
|
||||||
&ops,
|
|
||||||
engine_state,
|
|
||||||
&mut working_set,
|
|
||||||
stack,
|
|
||||||
call.head,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -100,8 +94,6 @@ enum FormatOperation {
|
||||||
FixedText(String),
|
FixedText(String),
|
||||||
// raw input is something like {column1.column2}
|
// raw input is something like {column1.column2}
|
||||||
ValueFromColumn(String, Span),
|
ValueFromColumn(String, Span),
|
||||||
// raw input is something like {$it.column1.column2} or {$var}.
|
|
||||||
ValueNeedEval(String, Span),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a pattern that is fed into the Format command, we can process it and subdivide it
|
/// Given a pattern that is fed into the Format command, we can process it and subdivide it
|
||||||
|
@ -110,7 +102,6 @@ enum FormatOperation {
|
||||||
/// there without any further processing.
|
/// there without any further processing.
|
||||||
/// FormatOperation::ValueFromColumn contains the name of a column whose values will be
|
/// FormatOperation::ValueFromColumn contains the name of a column whose values will be
|
||||||
/// formatted according to the input pattern.
|
/// formatted according to the input pattern.
|
||||||
/// FormatOperation::ValueNeedEval contains expression which need to eval, it has the following form:
|
|
||||||
/// "$it.column1.column2" or "$variable"
|
/// "$it.column1.column2" or "$variable"
|
||||||
fn extract_formatting_operations(
|
fn extract_formatting_operations(
|
||||||
input: String,
|
input: String,
|
||||||
|
@ -161,10 +152,17 @@ fn extract_formatting_operations(
|
||||||
|
|
||||||
if !column_name.is_empty() {
|
if !column_name.is_empty() {
|
||||||
if column_need_eval {
|
if column_need_eval {
|
||||||
output.push(FormatOperation::ValueNeedEval(
|
return Err(ShellError::GenericError {
|
||||||
column_name.clone(),
|
error: "Removed functionality".into(),
|
||||||
Span::new(span_start + column_span_start, span_start + column_span_end),
|
msg: "The ability to use variables ($it) in `format pattern` has been removed."
|
||||||
));
|
.into(),
|
||||||
|
span: Some(error_span),
|
||||||
|
help: Some(
|
||||||
|
"You can use other formatting options, such as string interpolation."
|
||||||
|
.into(),
|
||||||
|
),
|
||||||
|
inner: vec![],
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
output.push(FormatOperation::ValueFromColumn(
|
output.push(FormatOperation::ValueFromColumn(
|
||||||
column_name.clone(),
|
column_name.clone(),
|
||||||
|
@ -185,47 +183,30 @@ fn format(
|
||||||
input_data: Value,
|
input_data: Value,
|
||||||
format_operations: &[FormatOperation],
|
format_operations: &[FormatOperation],
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
working_set: &mut StateWorkingSet,
|
config: &Config,
|
||||||
stack: &mut Stack,
|
|
||||||
head_span: Span,
|
head_span: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let data_as_value = input_data;
|
let data_as_value = input_data;
|
||||||
|
|
||||||
// We can only handle a Record or a List of Records
|
// We can only handle a Record or a List of Records
|
||||||
match data_as_value {
|
match data_as_value {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => match format_record(format_operations, &data_as_value, config) {
|
||||||
match format_record(
|
Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)),
|
||||||
format_operations,
|
Err(value) => Err(value),
|
||||||
&data_as_value,
|
},
|
||||||
engine_state,
|
|
||||||
working_set,
|
|
||||||
stack,
|
|
||||||
) {
|
|
||||||
Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)),
|
|
||||||
Err(value) => Err(value),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
let mut list = vec![];
|
let mut list = vec![];
|
||||||
for val in vals.iter() {
|
for val in vals.iter() {
|
||||||
match val {
|
match val {
|
||||||
Value::Record { .. } => {
|
Value::Record { .. } => match format_record(format_operations, val, config) {
|
||||||
match format_record(
|
Ok(value) => {
|
||||||
format_operations,
|
list.push(Value::string(value, head_span));
|
||||||
val,
|
|
||||||
engine_state,
|
|
||||||
working_set,
|
|
||||||
stack,
|
|
||||||
) {
|
|
||||||
Ok(value) => {
|
|
||||||
list.push(Value::string(value, head_span));
|
|
||||||
}
|
|
||||||
Err(value) => {
|
|
||||||
return Err(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
Err(value) => {
|
||||||
|
return Err(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
Value::Error { error, .. } => return Err(*error.clone()),
|
Value::Error { error, .. } => return Err(*error.clone()),
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ShellError::OnlySupportsThisInputType {
|
return Err(ShellError::OnlySupportsThisInputType {
|
||||||
|
@ -238,7 +219,7 @@ fn format(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(ListStream::new(list.into_iter(), head_span, engine_state.ctrlc.clone()).into())
|
Ok(ListStream::new(list.into_iter(), head_span, engine_state.signals().clone()).into())
|
||||||
}
|
}
|
||||||
// Unwrapping this ShellError is a bit unfortunate.
|
// Unwrapping this ShellError is a bit unfortunate.
|
||||||
// Ideally, its Span would be preserved.
|
// Ideally, its Span would be preserved.
|
||||||
|
@ -255,13 +236,9 @@ fn format(
|
||||||
fn format_record(
|
fn format_record(
|
||||||
format_operations: &[FormatOperation],
|
format_operations: &[FormatOperation],
|
||||||
data_as_value: &Value,
|
data_as_value: &Value,
|
||||||
engine_state: &EngineState,
|
config: &Config,
|
||||||
working_set: &mut StateWorkingSet,
|
|
||||||
stack: &mut Stack,
|
|
||||||
) -> Result<String, ShellError> {
|
) -> Result<String, ShellError> {
|
||||||
let config = engine_state.get_config();
|
|
||||||
let mut output = String::new();
|
let mut output = String::new();
|
||||||
let eval_expression = get_eval_expression(engine_state);
|
|
||||||
|
|
||||||
for op in format_operations {
|
for op in format_operations {
|
||||||
match op {
|
match op {
|
||||||
|
@ -283,23 +260,6 @@ fn format_record(
|
||||||
Err(se) => return Err(se),
|
Err(se) => return Err(se),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
FormatOperation::ValueNeedEval(_col_name, span) => {
|
|
||||||
let exp = parse_expression(working_set, &[*span]);
|
|
||||||
match working_set.parse_errors.first() {
|
|
||||||
None => {
|
|
||||||
let parsed_result = eval_expression(engine_state, stack, &exp);
|
|
||||||
if let Ok(val) = parsed_result {
|
|
||||||
output.push_str(&val.to_abbreviated_string(config))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(err) => {
|
|
||||||
return Err(ShellError::TypeMismatch {
|
|
||||||
err_message: format!("expression is invalid, detail message: {err:?}"),
|
|
||||||
span: *span,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(output)
|
Ok(output)
|
||||||
|
|
|
@ -44,7 +44,7 @@ where
|
||||||
case_operation,
|
case_operation,
|
||||||
cell_paths,
|
cell_paths,
|
||||||
};
|
};
|
||||||
general_operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
general_operate(action, args, input, call.head, engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action<F>(input: &Value, args: &Arguments<F>, head: Span) -> Value
|
fn action<F>(input: &Value, args: &Arguments<F>, head: Span) -> Value
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
#![doc = include_str!("../README.md")]
|
||||||
mod example_test;
|
mod example_test;
|
||||||
pub mod extra;
|
pub mod extra;
|
||||||
pub use extra::*;
|
pub use extra::*;
|
||||||
|
|
13
crates/nu-cmd-extra/tests/commands/bits/into.rs
Normal file
13
crates/nu-cmd-extra/tests/commands/bits/into.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn byte_stream_into_bits() {
|
||||||
|
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits");
|
||||||
|
assert_eq!("00000001 00000010 00000011", result.out);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn byte_stream_into_bits_is_stream() {
|
||||||
|
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits | describe");
|
||||||
|
assert_eq!("string (stream)", result.out);
|
||||||
|
}
|
1
crates/nu-cmd-extra/tests/commands/bits/mod.rs
Normal file
1
crates/nu-cmd-extra/tests/commands/bits/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
mod into;
|
|
@ -1 +1,2 @@
|
||||||
|
mod bits;
|
||||||
mod bytes;
|
mod bytes;
|
||||||
|
|
|
@ -6,27 +6,26 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.94.3"
|
version = "0.96.2"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.94.3" }
|
nu-engine = { path = "../nu-engine", version = "0.96.2" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.94.3" }
|
nu-parser = { path = "../nu-parser", version = "0.96.2" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
|
nu-protocol = { path = "../nu-protocol", version = "0.96.2" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.94.3" }
|
nu-utils = { path = "../nu-utils", version = "0.96.2" }
|
||||||
|
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
shadow-rs = { version = "0.28", default-features = false }
|
shadow-rs = { version = "0.30", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.28", default-features = false }
|
shadow-rs = { version = "0.30", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mimalloc = []
|
mimalloc = []
|
||||||
which-support = []
|
|
||||||
trash-support = []
|
trash-support = []
|
||||||
sqlite = []
|
sqlite = []
|
||||||
static-link-openssl = []
|
static-link-openssl = []
|
||||||
system-clipboard = []
|
system-clipboard = []
|
|
@ -1,4 +1,5 @@
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
use nu_protocol::engine::CommandType;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Break;
|
pub struct Break;
|
||||||
|
@ -18,6 +19,17 @@ impl Command for Break {
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html
|
||||||
|
|
||||||
|
break can only be used in while, loop, and for loops. It can not be used with each or other filter commands"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_type(&self) -> CommandType {
|
||||||
|
CommandType::Keyword
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
|
|
|
@ -50,6 +50,7 @@ is particularly large, this can cause high memory usage."#
|
||||||
// check where some input came from.
|
// check where some input came from.
|
||||||
Some(PipelineMetadata {
|
Some(PipelineMetadata {
|
||||||
data_source: DataSource::FilePath(_),
|
data_source: DataSource::FilePath(_),
|
||||||
|
content_type: None,
|
||||||
}) => None,
|
}) => None,
|
||||||
other => other,
|
other => other,
|
||||||
};
|
};
|
||||||
|
|
|
@ -46,6 +46,9 @@ impl Command for Const {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
let call = call.assert_ast_call()?;
|
||||||
let var_id = if let Some(id) = call.positional_nth(0).and_then(|pos| pos.as_var()) {
|
let var_id = if let Some(id) = call.positional_nth(0).and_then(|pos| pos.as_var()) {
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
use nu_protocol::engine::CommandType;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Continue;
|
pub struct Continue;
|
||||||
|
@ -18,6 +19,16 @@ impl Command for Continue {
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html
|
||||||
|
|
||||||
|
continue can only be used in while, loop, and for loops. It can not be used with each or other filter commands"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_type(&self) -> CommandType {
|
||||||
|
CommandType::Keyword
|
||||||
|
}
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
|
|
|
@ -67,8 +67,8 @@ impl Command for Def {
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Define a custom wrapper for an external command",
|
description: "Define a custom wrapper for an external command",
|
||||||
example: r#"def --wrapped my-echo [...rest] { echo $rest }; my-echo spam"#,
|
example: r#"def --wrapped my-echo [...rest] { ^echo ...$rest }; my-echo -e 'spam\tspam'"#,
|
||||||
result: Some(Value::test_list(vec![Value::test_string("spam")])),
|
result: Some(Value::test_string("spam\tspam")),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,7 @@ impl Command for Do {
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("do")
|
Signature::build("do")
|
||||||
.required(
|
.required("closure", SyntaxShape::Closure(None), "The closure to run.")
|
||||||
"closure",
|
|
||||||
SyntaxShape::OneOf(vec![SyntaxShape::Closure(None), SyntaxShape::Any]),
|
|
||||||
"The closure to run.",
|
|
||||||
)
|
|
||||||
.input_output_types(vec![(Type::Any, Type::Any)])
|
.input_output_types(vec![(Type::Any, Type::Any)])
|
||||||
.switch(
|
.switch(
|
||||||
"ignore-errors",
|
"ignore-errors",
|
||||||
|
@ -85,6 +81,10 @@ impl Command for Do {
|
||||||
|
|
||||||
bind_args_to(&mut callee_stack, &block.signature, rest, head)?;
|
bind_args_to(&mut callee_stack, &block.signature, rest, head)?;
|
||||||
let eval_block_with_early_return = get_eval_block_with_early_return(engine_state);
|
let eval_block_with_early_return = get_eval_block_with_early_return(engine_state);
|
||||||
|
|
||||||
|
// Applies to all block evaluation once set true
|
||||||
|
callee_stack.use_ir = caller_stack.has_env_var(engine_state, "NU_USE_IR");
|
||||||
|
|
||||||
let result = eval_block_with_early_return(engine_state, &mut callee_stack, block, input);
|
let result = eval_block_with_early_return(engine_state, &mut callee_stack, block, input);
|
||||||
|
|
||||||
if has_env {
|
if has_env {
|
||||||
|
@ -229,14 +229,24 @@ impl Command for Do {
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Run the closure, with a positional parameter",
|
description: "Run the closure with a positional, type-checked parameter",
|
||||||
example: r#"do {|x| 100 + $x } 77"#,
|
example: r#"do {|x:int| 100 + $x } 77"#,
|
||||||
result: Some(Value::test_int(177)),
|
result: Some(Value::test_int(177)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Run the closure, with input",
|
description: "Run the closure with pipeline input",
|
||||||
example: r#"77 | do {|x| 100 + $in }"#,
|
example: r#"77 | do { 100 + $in }"#,
|
||||||
result: None, // TODO: returns 177
|
result: Some(Value::test_int(177)),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Run the closure with a default parameter value",
|
||||||
|
example: r#"77 | do {|x=100| $x + $in }"#,
|
||||||
|
result: Some(Value::test_int(177)),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Run the closure with two positional parameters",
|
||||||
|
example: r#"do {|x,y| $x + $y } 77 100"#,
|
||||||
|
result: Some(Value::test_int(177)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Run the closure and keep changes to the environment",
|
description: "Run the closure and keep changes to the environment",
|
||||||
|
|
|
@ -56,16 +56,7 @@ impl Command for ErrorMake {
|
||||||
Example {
|
Example {
|
||||||
description: "Create a simple custom error",
|
description: "Create a simple custom error",
|
||||||
example: r#"error make {msg: "my custom error message"}"#,
|
example: r#"error make {msg: "my custom error message"}"#,
|
||||||
result: Some(Value::error(
|
result: None,
|
||||||
ShellError::GenericError {
|
|
||||||
error: "my custom error message".into(),
|
|
||||||
msg: "".into(),
|
|
||||||
span: None,
|
|
||||||
help: None,
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
Span::unknown(),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Create a more complex custom error",
|
description: "Create a more complex custom error",
|
||||||
|
@ -82,16 +73,7 @@ impl Command for ErrorMake {
|
||||||
}
|
}
|
||||||
help: "A help string, suggesting a fix to the user" # optional
|
help: "A help string, suggesting a fix to the user" # optional
|
||||||
}"#,
|
}"#,
|
||||||
result: Some(Value::error(
|
result: None,
|
||||||
ShellError::GenericError {
|
|
||||||
error: "my custom error message".into(),
|
|
||||||
msg: "my custom label text".into(),
|
|
||||||
span: Some(Span::new(123, 456)),
|
|
||||||
help: Some("A help string, suggesting a fix to the user".into()),
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
Span::unknown(),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
|
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
|
||||||
use nu_protocol::engine::CommandType;
|
use nu_protocol::{engine::CommandType, Signals};
|
||||||
use nu_protocol::ParseWarning;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct For;
|
pub struct For;
|
||||||
|
@ -29,11 +28,6 @@ impl Command for For {
|
||||||
"Range of the loop.",
|
"Range of the loop.",
|
||||||
)
|
)
|
||||||
.required("block", SyntaxShape::Block, "The block to run.")
|
.required("block", SyntaxShape::Block, "The block to run.")
|
||||||
.switch(
|
|
||||||
"numbered",
|
|
||||||
"DEPRECATED: return a numbered item ($it.index and $it.item)",
|
|
||||||
Some('n'),
|
|
||||||
)
|
|
||||||
.creates_scope()
|
.creates_scope()
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
@ -54,6 +48,9 @@ impl Command for For {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
let call = call.assert_ast_call()?;
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let var_id = call
|
let var_id = call
|
||||||
.positional_nth(0)
|
.positional_nth(0)
|
||||||
|
@ -78,23 +75,6 @@ impl Command for For {
|
||||||
|
|
||||||
let value = eval_expression(engine_state, stack, keyword_expr)?;
|
let value = eval_expression(engine_state, stack, keyword_expr)?;
|
||||||
|
|
||||||
let numbered = call.has_flag(engine_state, stack, "numbered")?;
|
|
||||||
if numbered {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ParseWarning::DeprecatedWarning {
|
|
||||||
old_command: "--numbered/-n".into(),
|
|
||||||
new_suggestion: "use `enumerate`".into(),
|
|
||||||
span: call
|
|
||||||
.get_named_arg("numbered")
|
|
||||||
.expect("`get_named_arg` found `--numbered` but still failed")
|
|
||||||
.span,
|
|
||||||
url: "See `help for` examples".into(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
|
||||||
let engine_state = engine_state.clone();
|
let engine_state = engine_state.clone();
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
||||||
|
@ -103,29 +83,14 @@ impl Command for For {
|
||||||
let span = value.span();
|
let span = value.span();
|
||||||
match value {
|
match value {
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
for (idx, x) in vals.into_iter().enumerate() {
|
for x in vals.into_iter() {
|
||||||
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
|
engine_state.signals().check(head)?;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// with_env() is used here to ensure that each iteration uses
|
// with_env() is used here to ensure that each iteration uses
|
||||||
// a different set of environment variables.
|
// a different set of environment variables.
|
||||||
// Hence, a 'cd' in the first loop won't affect the next loop.
|
// Hence, a 'cd' in the first loop won't affect the next loop.
|
||||||
|
|
||||||
stack.add_var(
|
stack.add_var(var_id, x);
|
||||||
var_id,
|
|
||||||
if numbered {
|
|
||||||
Value::record(
|
|
||||||
record! {
|
|
||||||
"index" => Value::int(idx as i64, head),
|
|
||||||
"item" => x,
|
|
||||||
},
|
|
||||||
head,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
x
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
||||||
Err(ShellError::Break { .. }) => {
|
Err(ShellError::Break { .. }) => {
|
||||||
|
@ -151,21 +116,9 @@ impl Command for For {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::Range { val, .. } => {
|
Value::Range { val, .. } => {
|
||||||
for (idx, x) in val.into_range_iter(span, ctrlc).enumerate() {
|
for x in val.into_range_iter(span, Signals::empty()) {
|
||||||
stack.add_var(
|
engine_state.signals().check(head)?;
|
||||||
var_id,
|
stack.add_var(var_id, x);
|
||||||
if numbered {
|
|
||||||
Value::record(
|
|
||||||
record! {
|
|
||||||
"index" => Value::int(idx as i64, head),
|
|
||||||
"item" => x,
|
|
||||||
},
|
|
||||||
head,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
x
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
||||||
Err(ShellError::Break { .. }) => {
|
Err(ShellError::Break { .. }) => {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use nu_engine::{
|
||||||
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
||||||
};
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::StateWorkingSet,
|
engine::{CommandType, StateWorkingSet},
|
||||||
eval_const::{eval_const_subexpression, eval_constant, eval_constant_with_input},
|
eval_const::{eval_const_subexpression, eval_constant, eval_constant_with_input},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -41,6 +41,15 @@ impl Command for If {
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_type(&self) -> CommandType {
|
||||||
|
CommandType::Keyword
|
||||||
|
}
|
||||||
|
|
||||||
fn is_const(&self) -> bool {
|
fn is_const(&self) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -51,6 +60,9 @@ impl Command for If {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
let call = call.assert_ast_call()?;
|
||||||
let cond = call.positional_nth(0).expect("checked through parser");
|
let cond = call.positional_nth(0).expect("checked through parser");
|
||||||
let then_block = call
|
let then_block = call
|
||||||
.positional_nth(1)
|
.positional_nth(1)
|
||||||
|
@ -90,6 +102,9 @@ impl Command for If {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
let call = call.assert_ast_call()?;
|
||||||
let cond = call.positional_nth(0).expect("checked through parser");
|
let cond = call.positional_nth(0).expect("checked through parser");
|
||||||
let then_block = call
|
let then_block = call
|
||||||
.positional_nth(1)
|
.positional_nth(1)
|
||||||
|
@ -122,6 +137,10 @@ impl Command for If {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["else", "conditional"]
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
|
|
@ -46,6 +46,9 @@ impl Command for Let {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
let call = call.assert_ast_call()?;
|
||||||
let var_id = call
|
let var_id = call
|
||||||
.positional_nth(0)
|
.positional_nth(0)
|
||||||
.expect("checked through parser")
|
.expect("checked through parser")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use nu_engine::{command_prelude::*, get_eval_block};
|
use nu_engine::{command_prelude::*, get_eval_block};
|
||||||
|
use nu_protocol::engine::CommandType;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Loop;
|
pub struct Loop;
|
||||||
|
@ -20,6 +21,15 @@ impl Command for Loop {
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_type(&self) -> CommandType {
|
||||||
|
CommandType::Keyword
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -27,6 +37,10 @@ impl Command for Loop {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
let call = call.assert_ast_call()?;
|
||||||
|
let head = call.head;
|
||||||
let block_id = call
|
let block_id = call
|
||||||
.positional_nth(0)
|
.positional_nth(0)
|
||||||
.expect("checked through parser")
|
.expect("checked through parser")
|
||||||
|
@ -39,9 +53,7 @@ impl Command for Loop {
|
||||||
let stack = &mut stack.push_redirection(None, None);
|
let stack = &mut stack.push_redirection(None, None);
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
|
engine_state.signals().check(head)?;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
match eval_block(engine_state, stack, block, PipelineData::empty()) {
|
match eval_block(engine_state, stack, block, PipelineData::empty()) {
|
||||||
Err(ShellError::Break { .. }) => {
|
Err(ShellError::Break { .. }) => {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use nu_engine::{
|
use nu_engine::{
|
||||||
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
||||||
};
|
};
|
||||||
use nu_protocol::engine::Matcher;
|
use nu_protocol::engine::{CommandType, Matcher};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Match;
|
pub struct Match;
|
||||||
|
@ -27,6 +27,15 @@ impl Command for Match {
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn extra_usage(&self) -> &str {
|
||||||
|
r#"This command is a parser keyword. For details, check:
|
||||||
|
https://www.nushell.sh/book/thinking_in_nu.html"#
|
||||||
|
}
|
||||||
|
|
||||||
|
fn command_type(&self) -> CommandType {
|
||||||
|
CommandType::Keyword
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
|
@ -34,6 +43,9 @@ impl Command for Match {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
let call = call.assert_ast_call()?;
|
||||||
let value: Value = call.req(engine_state, stack, 0)?;
|
let value: Value = call.req(engine_state, stack, 0)?;
|
||||||
let matches = call
|
let matches = call
|
||||||
.positional_nth(1)
|
.positional_nth(1)
|
||||||
|
|
|
@ -46,6 +46,9 @@ impl Command for Mut {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
let call = call.assert_ast_call()?;
|
||||||
let var_id = call
|
let var_id = call
|
||||||
.positional_nth(0)
|
.positional_nth(0)
|
||||||
.expect("checked through parser")
|
.expect("checked through parser")
|
||||||
|
|
|
@ -65,9 +65,9 @@ impl Command for OverlayUse {
|
||||||
name_arg.item = trim_quotes_str(&name_arg.item).to_string();
|
name_arg.item = trim_quotes_str(&name_arg.item).to_string();
|
||||||
|
|
||||||
let maybe_origin_module_id =
|
let maybe_origin_module_id =
|
||||||
if let Some(overlay_expr) = call.get_parser_info("overlay_expr") {
|
if let Some(overlay_expr) = call.get_parser_info(caller_stack, "overlay_expr") {
|
||||||
if let Expr::Overlay(module_id) = &overlay_expr.expr {
|
if let Expr::Overlay(module_id) = &overlay_expr.expr {
|
||||||
module_id
|
*module_id
|
||||||
} else {
|
} else {
|
||||||
return Err(ShellError::NushellFailedSpanned {
|
return Err(ShellError::NushellFailedSpanned {
|
||||||
msg: "Not an overlay".to_string(),
|
msg: "Not an overlay".to_string(),
|
||||||
|
@ -110,7 +110,7 @@ impl Command for OverlayUse {
|
||||||
// a) adding a new overlay
|
// a) adding a new overlay
|
||||||
// b) refreshing an active overlay (the origin module changed)
|
// b) refreshing an active overlay (the origin module changed)
|
||||||
|
|
||||||
let module = engine_state.get_module(*module_id);
|
let module = engine_state.get_module(module_id);
|
||||||
|
|
||||||
// Evaluate the export-env block (if any) and keep its environment
|
// Evaluate the export-env block (if any) and keep its environment
|
||||||
if let Some(block_id) = module.env_block {
|
if let Some(block_id) = module.env_block {
|
||||||
|
@ -118,7 +118,7 @@ impl Command for OverlayUse {
|
||||||
&name_arg.item,
|
&name_arg.item,
|
||||||
engine_state,
|
engine_state,
|
||||||
caller_stack,
|
caller_stack,
|
||||||
get_dirs_var_from_call(call),
|
get_dirs_var_from_call(caller_stack, call),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let block = engine_state.get_block(block_id);
|
let block = engine_state.get_block(block_id);
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user