mirror of
https://github.com/nushell/nushell.git
synced 2025-07-03 16:13:20 +02:00
Compare commits
357 Commits
Author | SHA1 | Date | |
---|---|---|---|
118857aedc | |||
a340e965e8 | |||
25a5e8d8e8 | |||
2fe84bd197 | |||
c95c1e845c | |||
105ec0c89f | |||
2c1b787db5 | |||
fdb677e932 | |||
a18ff1d3a2 | |||
a86a0dd16e | |||
f4136aa3f4 | |||
082e8d0de8 | |||
9da0f41ebb | |||
372d576846 | |||
c795f16143 | |||
a4a3c514ba | |||
5478ec44bb | |||
6902bbe547 | |||
4e5da8cd91 | |||
d248451428 | |||
3e758e899f | |||
f69a812055 | |||
6fba4b409e | |||
cb7ac9199d | |||
a6b8e2f95c | |||
0b202d55f0 | |||
e88a6bff60 | |||
a234e6ff51 | |||
ae0cf8780d | |||
680a2fa2aa | |||
70277cc2ba | |||
574106bc03 | |||
2a8364d259 | |||
760c9ef2e9 | |||
c3079a14d9 | |||
4f7e9aac62 | |||
7ee8aa78cc | |||
d9d022733f | |||
1d032ce80c | |||
975a89269e | |||
db5b6c790f | |||
2bed202b82 | |||
8a0f2ca9f9 | |||
24ab294cda | |||
bfa95bbd24 | |||
3f700f03ad | |||
f0e90a3733 | |||
cde8a629c5 | |||
70aa7ad993 | |||
29b3512494 | |||
d961ea19cc | |||
3db9c81958 | |||
55240d98a5 | |||
fda181d566 | |||
2e484156e0 | |||
52604f8b00 | |||
2fed1f5967 | |||
5be8717fe8 | |||
091d14f085 | |||
4c19242c0d | |||
3df0177ba5 | |||
f7888fce83 | |||
cf1a53143c | |||
28a94048c5 | |||
fb691c0da5 | |||
7972aea530 | |||
aa710eeb9a | |||
91e843a6d4 | |||
ebcb26f9d5 | |||
f8b0af70ff | |||
12465193a4 | |||
bd3930d00d | |||
81e86c40e1 | |||
2fe25d6299 | |||
4aeede2dd5 | |||
0e46ef9769 | |||
962467fdfd | |||
d27232df6e | |||
d7cec2088a | |||
22d1fdcdf6 | |||
ba59f71f20 | |||
2352548467 | |||
3efbda63b8 | |||
1fe62ee613 | |||
126d11fcb7 | |||
ea4f8ff400 | |||
ebcdf5a8b1 | |||
440b9c8e1f | |||
96a886eb84 | |||
61d59f13fa | |||
5da7dcdbdb | |||
f92f11c0cf | |||
3bf96523a4 | |||
8d46398e13 | |||
461d558983 | |||
65c9160170 | |||
e3124d3561 | |||
b886fd364c | |||
21d949207f | |||
4a9e2ac37b | |||
9cc74e7a9f | |||
4adcf079e2 | |||
81cec2e50f | |||
ed7b2615c1 | |||
74e0e4f092 | |||
42fc9f52a1 | |||
c563e0cfb0 | |||
8671a3dbbd | |||
fc813af4c8 | |||
b83aa17c96 | |||
c7e10c3c57 | |||
e7d2717424 | |||
222c307648 | |||
eb9eb09ac5 | |||
6eacbabe17 | |||
33303f083c | |||
483974311d | |||
179ea5ae87 | |||
bdc7cdbcc4 | |||
2b524cd861 | |||
ad9f051d61 | |||
cfbe835910 | |||
8896ba80a4 | |||
803c24f9ce | |||
2f74574e35 | |||
8b9f02246f | |||
d9ecb7da93 | |||
18ce5de500 | |||
fbde02370a | |||
13452a7aa2 | |||
a8c49857d9 | |||
90afb65329 | |||
ff4907ed3b | |||
cbd7608898 | |||
adc9bbdc18 | |||
37bc922a67 | |||
ae51f6d722 | |||
1b2079ffdb | |||
9a968c4bdd | |||
89df01f829 | |||
dbb30cc9e0 | |||
02d63705cc | |||
ea97229688 | |||
6bf955a5a5 | |||
f90035e084 | |||
cc8b623ff8 | |||
60cb13c493 | |||
c10e483683 | |||
2d0c7b2214 | |||
88d421dcb6 | |||
7c50f7c714 | |||
bc043dcaeb | |||
10be753ab7 | |||
6906a0ca50 | |||
833471241a | |||
c4dcfdb77b | |||
1e8876b076 | |||
5483519c7d | |||
457f162fd9 | |||
58a8f30a25 | |||
70ba5d9d68 | |||
7b88bda9a1 | |||
bb37306d07 | |||
505cc014ac | |||
ff79959fdf | |||
8c2b1a22d4 | |||
3d62753e80 | |||
36c30ade3a | |||
e0eb29f161 | |||
c2ac8f730e | |||
1a0986903f | |||
0f25641722 | |||
7d6d48f3f7 | |||
6a8c183c1a | |||
0beb28e827 | |||
8352a09117 | |||
a9252c5075 | |||
73fbe26ef9 | |||
52fa9a978b | |||
d4357ad981 | |||
a0d7c1a4fd | |||
a340511e95 | |||
426e64501d | |||
b0d68c31e8 | |||
583cb96cff | |||
ff8831318d | |||
ce308ee461 | |||
21388175b8 | |||
520f11fb8f | |||
39b95fc59e | |||
63e68934f6 | |||
acc152564c | |||
8f63db4c95 | |||
cb133ed387 | |||
a7547a54bc | |||
d1969a3c9a | |||
ce582cdafb | |||
55de232a1c | |||
deca337a56 | |||
60e9f469af | |||
b500ac57c2 | |||
eadb8da9f7 | |||
cda15d91dd | |||
651a8716fb | |||
a1b7574306 | |||
09f12b9c4a | |||
9ae74e3941 | |||
d8bec8668f | |||
12ccaf5e33 | |||
5fecf59f54 | |||
a3aae2d26c | |||
d1d6518ece | |||
2d868323b6 | |||
0389815137 | |||
11cdb94699 | |||
0ca5c2f135 | |||
715b0d90a9 | |||
05c36d1bc7 | |||
208ebeefab | |||
b33f4b7f55 | |||
f41b1460aa | |||
220858d641 | |||
db261e3ed9 | |||
82eb1c5584 | |||
6be291b00a | |||
7add38fe32 | |||
78903724f5 | |||
cb57f0a539 | |||
717081bd2f | |||
e1ffaf2548 | |||
1db4be12d1 | |||
6193679dfc | |||
a9657e17ad | |||
03d455a688 | |||
bae04352ca | |||
a1497716f1 | |||
b5b63d2bf9 | |||
5c59611083 | |||
1503ee09ba | |||
24dba9dc53 | |||
a2dc3e3b33 | |||
95998bdd53 | |||
bd5de023a1 | |||
38e761493d | |||
7fcebf37ec | |||
0e9927ea4d | |||
d273ce89df | |||
2dc5c19b71 | |||
669b44ad7d | |||
eff063822a | |||
9a5c4d36be | |||
cd4560e97a | |||
24cc2f9d87 | |||
8f81812ef9 | |||
2229370b13 | |||
a33650a69e | |||
56d7e4bb89 | |||
e5f589ccdd | |||
8c4d3eaa7e | |||
89322f59f2 | |||
4e307480e4 | |||
d601abaee0 | |||
ceaa0f9375 | |||
d31b7024d8 | |||
9dd30d7756 | |||
eff9305eb3 | |||
885b87a842 | |||
017daeed18 | |||
c8c018452f | |||
1a0778d77e | |||
d75aa7ed1b | |||
39edd7e080 | |||
61dbcf3de6 | |||
f8ed4b45fd | |||
7b57f132bb | |||
dfca117551 | |||
29eb109b1e | |||
70d8163181 | |||
e4cef8a154 | |||
15146e68ad | |||
b0f9cda9b5 | |||
173162df2e | |||
c0b944edb6 | |||
26699d96eb | |||
08940ba4f8 | |||
ecb9799b6a | |||
a886e30e04 | |||
147009a161 | |||
12a1eefe73 | |||
0f8f3bcf9a | |||
639f4bd499 | |||
e82df7c1c9 | |||
41f4d0dcbc | |||
eb2a91ea7c | |||
b81d46574c | |||
1c6c85d35d | |||
67ea25afca | |||
f25525be6c | |||
a72f94f452 | |||
210c6f1c43 | |||
0cd90e2388 | |||
7ca2a6f8ac | |||
237a685605 | |||
2bf0397d80 | |||
5ec823996a | |||
67b6188b19 | |||
df74a0c961 | |||
af6c4bdc9c | |||
d7f26b177a | |||
470d130289 | |||
a23e96c945 | |||
9ba16dbdaf | |||
43f9ec295f | |||
f39e5b3f37 | |||
6c0b65b570 | |||
1dcaffb792 | |||
ca4222277e | |||
5c2bcd068b | |||
9aba96604b | |||
7be90c2644 | |||
7e9e93cf82 | |||
6d1f7cb3e3 | |||
334cf1862a | |||
49d86855ce | |||
5fe97b8d59 | |||
2bad1371f0 | |||
3030608de0 | |||
5d32cd2c40 | |||
07be33c119 | |||
eaf522b41f | |||
e76586ede4 | |||
1979b61a92 | |||
02fcc485fb | |||
55e05be0d8 | |||
e10ac2ede6 | |||
bf1f2d5ebd | |||
6aed1b42ae | |||
f33a26123c | |||
7c160725ed | |||
5832823dff | |||
3fe355c4a6 | |||
dd56c813f9 | |||
7a6cfa24fc | |||
2ea2a904e8 | |||
dfba62da00 | |||
b241e9edd5 | |||
946cef77f1 | |||
c99c8119fe | |||
2b4914608e | |||
8b80ceac32 | |||
e89bb2ee96 | |||
862d53bb6e | |||
820d0c0959 | |||
968eb45fb2 | |||
2c1d261cca | |||
69d1c8e948 | |||
2c7ab6e898 |
40
.github/labeler.yml
vendored
Normal file
40
.github/labeler.yml
vendored
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# A bot for automatically labelling pull requests
|
||||||
|
# See https://github.com/actions/labeler
|
||||||
|
|
||||||
|
dataframe:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- crates/nu_plugin_polars/**
|
||||||
|
|
||||||
|
std-library:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- crates/nu-std/**
|
||||||
|
|
||||||
|
ci:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- .github/workflows/**
|
||||||
|
|
||||||
|
|
||||||
|
LSP:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- crates/nu-lsp/**
|
||||||
|
|
||||||
|
parser:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
- crates/nu-parser/**
|
||||||
|
|
||||||
|
pr:plugins:
|
||||||
|
- changed-files:
|
||||||
|
- any-glob-to-any-file:
|
||||||
|
# plugins API
|
||||||
|
- crates/nu-plugin/**
|
||||||
|
- crates/nu-plugin-core/**
|
||||||
|
- crates/nu-plugin-engine/**
|
||||||
|
- crates/nu-plugin-protocol/**
|
||||||
|
- crates/nu-plugin-test-support/**
|
||||||
|
# specific plugins (like polars)
|
||||||
|
- crates/nu_plugin_*/**
|
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@ -37,7 +37,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.1.7
|
- 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.11.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||||
|
|
||||||
- name: cargo fmt
|
- name: cargo fmt
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
@ -65,7 +65,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.1.7
|
- 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.11.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||||
|
|
||||||
- name: Tests
|
- name: Tests
|
||||||
run: cargo test --workspace --profile ci --exclude nu_plugin_*
|
run: cargo test --workspace --profile ci --exclude nu_plugin_*
|
||||||
@ -94,7 +94,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.1.7
|
- 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.11.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||||
|
|
||||||
- name: Install Nushell
|
- name: Install Nushell
|
||||||
run: cargo install --path . --locked --force
|
run: cargo install --path . --locked --force
|
||||||
@ -145,7 +145,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.1.7
|
- 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.11.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||||
|
|
||||||
- name: Clippy
|
- name: Clippy
|
||||||
run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS
|
run: cargo clippy --package nu_plugin_* -- $CLIPPY_OPTIONS
|
||||||
@ -186,7 +186,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4.1.7
|
- 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.11.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.12.0
|
||||||
|
|
||||||
- name: Add wasm32-unknown-unknown target
|
- name: Add wasm32-unknown-unknown target
|
||||||
run: rustup target add wasm32-unknown-unknown
|
run: rustup target add wasm32-unknown-unknown
|
||||||
|
25
.github/workflows/friendly-config-reminder.yml
vendored
Normal file
25
.github/workflows/friendly-config-reminder.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: Comment on changes to the config
|
||||||
|
on:
|
||||||
|
pull_request_target:
|
||||||
|
paths:
|
||||||
|
- 'crates/nu-protocol/src/config/**'
|
||||||
|
jobs:
|
||||||
|
comment:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check if there is already a bot comment
|
||||||
|
uses: peter-evans/find-comment@v3
|
||||||
|
id: fc
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
comment-author: 'github-actions[bot]'
|
||||||
|
body-includes: Hey, just a bot checking in!
|
||||||
|
- name: Create comment if there is not
|
||||||
|
if: steps.fc.outputs.comment-id == ''
|
||||||
|
uses: peter-evans/create-or-update-comment@v4
|
||||||
|
with:
|
||||||
|
issue-number: ${{ github.event.pull_request.number }}
|
||||||
|
body: |
|
||||||
|
Hey, just a bot checking in! You edited files related to the configuration.
|
||||||
|
If you changed any of the default values or added a new config option, don't forget to update the [`doc_config.nu`](https://github.com/nushell/nushell/blob/main/crates/nu-utils/src/default_files/doc_config.nu) which documents the options for our users including the defaults provided by the Rust implementation.
|
||||||
|
If you didn't make a change here, you can just ignore me.
|
19
.github/workflows/labels.yml
vendored
Normal file
19
.github/workflows/labels.yml
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Automatically labels PRs based on the configuration file
|
||||||
|
# you are probably looking for 👉 `.github/labeler.yml`
|
||||||
|
name: Label PRs
|
||||||
|
|
||||||
|
on:
|
||||||
|
- pull_request_target
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
triage:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository_owner == 'nushell'
|
||||||
|
steps:
|
||||||
|
- uses: actions/labeler@v5
|
||||||
|
with:
|
||||||
|
repo-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
sync-labels: true
|
140
.github/workflows/nightly-build.yml
vendored
140
.github/workflows/nightly-build.yml
vendored
@ -4,6 +4,7 @@
|
|||||||
# 2. https://github.com/JasonEtco/create-an-issue
|
# 2. https://github.com/JasonEtco/create-an-issue
|
||||||
# 3. https://docs.github.com/en/actions/learn-github-actions/variables
|
# 3. https://docs.github.com/en/actions/learn-github-actions/variables
|
||||||
# 4. https://github.com/actions/github-script
|
# 4. https://github.com/actions/github-script
|
||||||
|
# 5. https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds
|
||||||
#
|
#
|
||||||
name: Nightly Build
|
name: Nightly Build
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ on:
|
|||||||
# This schedule will run only from the default branch
|
# This schedule will run only from the default branch
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '15 0 * * *' # run at 00:15 AM UTC
|
- cron: '15 0 * * *' # run at 00:15 AM UTC
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
defaults:
|
defaults:
|
||||||
run:
|
run:
|
||||||
@ -25,6 +27,11 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
# This job is required by the release job, so we should make it run both from Nushell repo and nightly repo
|
# This job is required by the release job, so we should make it run both from Nushell repo and nightly repo
|
||||||
# if: github.repository == 'nushell/nightly'
|
# if: github.repository == 'nushell/nightly'
|
||||||
|
# Map a step output to a job output
|
||||||
|
outputs:
|
||||||
|
skip: ${{ steps.vars.outputs.skip }}
|
||||||
|
build_date: ${{ steps.vars.outputs.build_date }}
|
||||||
|
nightly_tag: ${{ steps.vars.outputs.nightly_tag }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
@ -39,7 +46,7 @@ jobs:
|
|||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
if: github.repository == 'nushell/nightly'
|
if: github.repository == 'nushell/nightly'
|
||||||
with:
|
with:
|
||||||
version: 0.101.0
|
version: 0.105.1
|
||||||
|
|
||||||
# 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
|
||||||
@ -57,16 +64,53 @@ jobs:
|
|||||||
# All the changes will be overwritten by the upstream main branch
|
# All the changes will be overwritten by the upstream main branch
|
||||||
git reset --hard src/main
|
git reset --hard src/main
|
||||||
git push origin main -f
|
git push origin main -f
|
||||||
let sha_short = (git rev-parse --short origin/main | str trim | str substring 0..7)
|
|
||||||
let tag_name = $'nightly-($sha_short)'
|
- name: Create Tag and Output Tag Name
|
||||||
if (git ls-remote --tags origin $tag_name | is-empty) {
|
if: github.repository == 'nushell/nightly'
|
||||||
git tag -a $tag_name -m $'Nightly build from ($sha_short)'
|
id: vars
|
||||||
|
shell: nu {0}
|
||||||
|
run: |
|
||||||
|
let date = date now | format date %m%d
|
||||||
|
let version = open Cargo.toml | get package.version
|
||||||
|
let sha_short = (git rev-parse --short origin/main | str trim | str substring 0..6)
|
||||||
|
let latest_meta = http get https://api.github.com/repos/nushell/nightly/releases
|
||||||
|
| sort-by -r created_at
|
||||||
|
| where tag_name =~ nightly
|
||||||
|
| get tag_name?.0? | default ''
|
||||||
|
| parse '{version}-nightly.{build}+{hash}'
|
||||||
|
if ($latest_meta.0?.hash? | default '') == $sha_short {
|
||||||
|
print $'(ansi g)Latest nightly build is up-to-date, skip rebuilding.(ansi reset)'
|
||||||
|
$'skip=true(char nl)' o>> $env.GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
let prev_ver = $latest_meta.0?.version? | default '0.0.0'
|
||||||
|
let build = if ($latest_meta | is-empty) or ($version != $prev_ver) { 1 } else {
|
||||||
|
($latest_meta | get build?.0? | default 0 | into int) + 1
|
||||||
|
}
|
||||||
|
let nightly_tag = $'($version)-nightly.($build)+($sha_short)'
|
||||||
|
$'build_date=($date)(char nl)' o>> $env.GITHUB_OUTPUT
|
||||||
|
$'nightly_tag=($nightly_tag)(char nl)' o>> $env.GITHUB_OUTPUT
|
||||||
|
if (git ls-remote --tags origin $nightly_tag | is-empty) {
|
||||||
|
ls **/Cargo.toml | each {|file|
|
||||||
|
open --raw $file.name
|
||||||
|
| str replace --all $'version = "($version)"' $'version = "($version)-nightly.($build)"'
|
||||||
|
| save --force $file.name
|
||||||
|
}
|
||||||
|
# Disable the following two workflows for the automatic committed changes
|
||||||
|
rm .github/workflows/ci.yml
|
||||||
|
rm .github/workflows/audit.yml
|
||||||
|
|
||||||
|
git add .
|
||||||
|
git commit -m $'Update version to ($version)-nightly.($build)'
|
||||||
|
git tag -a $nightly_tag -m $'Nightly build from ($sha_short)'
|
||||||
git push origin --tags
|
git push origin --tags
|
||||||
|
git push origin main -f
|
||||||
}
|
}
|
||||||
|
|
||||||
standard:
|
release:
|
||||||
name: Nu
|
name: Nu
|
||||||
needs: prepare
|
needs: prepare
|
||||||
|
if: needs.prepare.outputs.skip != 'true'
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -83,24 +127,16 @@ jobs:
|
|||||||
- armv7-unknown-linux-musleabihf
|
- armv7-unknown-linux-musleabihf
|
||||||
- riscv64gc-unknown-linux-gnu
|
- riscv64gc-unknown-linux-gnu
|
||||||
- loongarch64-unknown-linux-gnu
|
- loongarch64-unknown-linux-gnu
|
||||||
extra: ['bin']
|
- loongarch64-unknown-linux-musl
|
||||||
include:
|
include:
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
- target: x86_64-apple-darwin
|
- target: x86_64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
- target: x86_64-pc-windows-msvc
|
- target: x86_64-pc-windows-msvc
|
||||||
extra: 'bin'
|
|
||||||
os: windows-latest
|
|
||||||
- target: x86_64-pc-windows-msvc
|
|
||||||
extra: msi
|
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
- target: aarch64-pc-windows-msvc
|
- target: aarch64-pc-windows-msvc
|
||||||
extra: 'bin'
|
os: windows-11-arm
|
||||||
os: windows-latest
|
|
||||||
- target: aarch64-pc-windows-msvc
|
|
||||||
extra: msi
|
|
||||||
os: windows-latest
|
|
||||||
- target: x86_64-unknown-linux-gnu
|
- target: x86_64-unknown-linux-gnu
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- target: x86_64-unknown-linux-musl
|
- target: x86_64-unknown-linux-musl
|
||||||
@ -117,29 +153,42 @@ jobs:
|
|||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- target: loongarch64-unknown-linux-gnu
|
- target: loongarch64-unknown-linux-gnu
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
|
- target: loongarch64-unknown-linux-musl
|
||||||
|
os: ubuntu-22.04
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Install Wix Toolset 6 for Windows
|
||||||
|
shell: pwsh
|
||||||
|
if: ${{ startsWith(matrix.os, 'windows') }}
|
||||||
|
run: |
|
||||||
|
dotnet tool install --global wix --version 6.0.0
|
||||||
|
dotnet workload install wix
|
||||||
|
$wixPath = "$env:USERPROFILE\.dotnet\tools"
|
||||||
|
echo "$wixPath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||||
|
$env:PATH = "$wixPath;$env:PATH"
|
||||||
|
wix --version
|
||||||
|
|
||||||
- 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 and cache
|
- name: Setup Rust toolchain and cache
|
||||||
uses: actions-rust-lang/setup-rust-toolchain@v1.11.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
# 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
|
||||||
rustflags: ''
|
rustflags: ''
|
||||||
|
|
||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
with:
|
with:
|
||||||
version: 0.101.0
|
version: 0.105.1
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
@ -148,11 +197,10 @@ jobs:
|
|||||||
OS: ${{ matrix.os }}
|
OS: ${{ matrix.os }}
|
||||||
REF: ${{ github.ref }}
|
REF: ${{ github.ref }}
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
_EXTRA_: ${{ matrix.extra }}
|
|
||||||
|
|
||||||
- name: Create an Issue for Release Failure
|
- name: Create an Issue for Release Failure
|
||||||
if: ${{ failure() }}
|
if: ${{ failure() }}
|
||||||
uses: JasonEtco/create-an-issue@v2.9.2
|
uses: JasonEtco/create-an-issue@v2
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
with:
|
with:
|
||||||
@ -160,13 +208,6 @@ jobs:
|
|||||||
search_existing: open
|
search_existing: open
|
||||||
filename: .github/AUTO_ISSUE_TEMPLATE/nightly-build-fail.md
|
filename: .github/AUTO_ISSUE_TEMPLATE/nightly-build-fail.md
|
||||||
|
|
||||||
- name: Set Outputs of Short SHA
|
|
||||||
id: vars
|
|
||||||
run: |
|
|
||||||
echo "date=$(date -u +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
|
||||||
sha_short=$(git rev-parse --short HEAD)
|
|
||||||
echo "sha_short=${sha_short:0:7}" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
# 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
|
||||||
@ -174,9 +215,37 @@ jobs:
|
|||||||
if: ${{ startsWith(github.repository, 'nushell/nightly') }}
|
if: ${{ startsWith(github.repository, 'nushell/nightly') }}
|
||||||
with:
|
with:
|
||||||
prerelease: true
|
prerelease: true
|
||||||
files: ${{ steps.nu.outputs.archive }}
|
files: |
|
||||||
tag_name: nightly-${{ steps.vars.outputs.sha_short }}
|
${{ steps.nu.outputs.msi }}
|
||||||
name: Nu-nightly-${{ steps.vars.outputs.date }}-${{ steps.vars.outputs.sha_short }}
|
${{ steps.nu.outputs.archive }}
|
||||||
|
tag_name: ${{ needs.prepare.outputs.nightly_tag }}
|
||||||
|
name: ${{ needs.prepare.outputs.build_date }}-${{ needs.prepare.outputs.nightly_tag }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
sha256sum:
|
||||||
|
needs: [prepare, release]
|
||||||
|
name: Create Sha256sum
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: github.repository == 'nushell/nightly'
|
||||||
|
steps:
|
||||||
|
- name: Download Release Archives
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: >-
|
||||||
|
gh release download ${{ needs.prepare.outputs.nightly_tag }}
|
||||||
|
--repo ${{ github.repository }}
|
||||||
|
--pattern '*'
|
||||||
|
--dir release
|
||||||
|
- name: Create Checksums
|
||||||
|
run: cd release && shasum -a 256 * > ../SHA256SUMS
|
||||||
|
- name: Publish Checksums
|
||||||
|
uses: softprops/action-gh-release@v2.0.9
|
||||||
|
with:
|
||||||
|
draft: false
|
||||||
|
prerelease: true
|
||||||
|
files: SHA256SUMS
|
||||||
|
tag_name: ${{ needs.prepare.outputs.nightly_tag }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
@ -184,12 +253,9 @@ jobs:
|
|||||||
name: Cleanup
|
name: Cleanup
|
||||||
# Should only run in nushell/nightly repo
|
# Should only run in nushell/nightly repo
|
||||||
if: github.repository == 'nushell/nightly'
|
if: github.repository == 'nushell/nightly'
|
||||||
|
needs: [release, sha256sum]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
# Sleep for 30 minutes, waiting for the release to be published
|
|
||||||
- name: Waiting for Release
|
|
||||||
run: sleep 1800
|
|
||||||
|
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
ref: main
|
ref: main
|
||||||
@ -197,14 +263,14 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
with:
|
with:
|
||||||
version: 0.101.0
|
version: 0.105.1
|
||||||
|
|
||||||
# Keep the last a few releases
|
# Keep the last a few releases
|
||||||
- name: Delete Older Releases
|
- name: Delete Older Releases
|
||||||
shell: nu {0}
|
shell: nu {0}
|
||||||
run: |
|
run: |
|
||||||
let KEEP_COUNT = 10
|
let KEEP_COUNT = 10
|
||||||
let deprecated = (http get https://api.github.com/repos/nushell/nightly/releases | sort-by -r created_at | select tag_name id | range $KEEP_COUNT..)
|
let deprecated = (http get https://api.github.com/repos/nushell/nightly/releases | sort-by -r created_at | select tag_name id | slice $KEEP_COUNT..)
|
||||||
for release in $deprecated {
|
for release in $deprecated {
|
||||||
print $'Deleting tag ($release.tag_name)'
|
print $'Deleting tag ($release.tag_name)'
|
||||||
git push origin --delete $release.tag_name
|
git push origin --delete $release.tag_name
|
||||||
|
62
.github/workflows/release-msi.nu
vendored
Executable file
62
.github/workflows/release-msi.nu
vendored
Executable file
@ -0,0 +1,62 @@
|
|||||||
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
|
# Created: 2025/05/21 19:05:20
|
||||||
|
# Description:
|
||||||
|
# A script to build Windows MSI packages for NuShell. Need wix 6.0 to be installed.
|
||||||
|
# The script will download the specified NuShell release, extract it, and create an MSI package.
|
||||||
|
# Can be run locally or in GitHub Actions.
|
||||||
|
# To run this script locally:
|
||||||
|
# load-env { TARGET: 'x86_64-pc-windows-msvc' REF: '0.103.0' GITHUB_REPOSITORY: 'nushell/nushell' }
|
||||||
|
# nu .github/workflows/release-msi.nu
|
||||||
|
|
||||||
|
def build-msi [] {
|
||||||
|
let target = $env.TARGET
|
||||||
|
# We should read the version from the environment variable first
|
||||||
|
# As we may build the MSI package for a specific version not the latest one
|
||||||
|
let version = $env.MSI_VERSION? | default (open Cargo.toml | get package.version)
|
||||||
|
let arch = if $nu.os-info.arch =~ 'x86_64' { 'x64' } else { 'arm64' }
|
||||||
|
|
||||||
|
print $'Building msi package for (ansi g)($target)(ansi reset) with version (ansi g)($version)(ansi reset) from tag (ansi g)($env.REF)(ansi reset)...'
|
||||||
|
fetch-nu-pkg
|
||||||
|
# Create extra Windows msi release package if dotnet and wix are available
|
||||||
|
let installed = [dotnet wix] | all { (which $in | length) > 0 }
|
||||||
|
if $installed and (wix --version | split row . | first | into int) >= 6 {
|
||||||
|
|
||||||
|
print $'(char nl)Start creating Windows msi package with the following contents...'
|
||||||
|
cd wix; hr-line
|
||||||
|
cp nu/README.txt .
|
||||||
|
ls -f nu/* | print
|
||||||
|
./nu/nu.exe -c $'NU_RELEASE_VERSION=($version) dotnet build -c Release -p:Platform=($arch)'
|
||||||
|
glob **/*.msi | print
|
||||||
|
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
||||||
|
let wixRelease = (glob **/*.msi | where $it =~ bin | get 0 | str replace --all '\' '/')
|
||||||
|
let msi = $'($wixRelease | path dirname)/nu-($version)-($target).msi'
|
||||||
|
mv $wixRelease $msi
|
||||||
|
print $'MSI archive: ---> ($msi)';
|
||||||
|
# Run only in GitHub Actions
|
||||||
|
if ($env.GITHUB_ACTIONS? | default false | into bool) {
|
||||||
|
echo $"msi=($msi)(char nl)" o>> $env.GITHUB_OUTPUT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def fetch-nu-pkg [] {
|
||||||
|
mkdir wix/nu
|
||||||
|
# See: https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/store-information-in-variables#default-environment-variables
|
||||||
|
gh release download $env.REF --repo $env.GITHUB_REPOSITORY --pattern $'*-($env.TARGET).zip' --dir wix/nu
|
||||||
|
cd wix/nu
|
||||||
|
let pkg = ls *.zip | get name.0
|
||||||
|
unzip $pkg
|
||||||
|
rm $pkg
|
||||||
|
ls | print
|
||||||
|
}
|
||||||
|
|
||||||
|
# Print a horizontal line marker
|
||||||
|
def 'hr-line' [
|
||||||
|
--blank-line(-b)
|
||||||
|
] {
|
||||||
|
print $'(ansi g)---------------------------------------------------------------------------->(ansi reset)'
|
||||||
|
if $blank_line { char nl }
|
||||||
|
}
|
||||||
|
|
||||||
|
alias main = build-msi
|
103
.github/workflows/release-msi.yml
vendored
Normal file
103
.github/workflows/release-msi.yml
vendored
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
#
|
||||||
|
# REF:
|
||||||
|
# 1. https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrixinclude
|
||||||
|
#
|
||||||
|
name: Build Windows MSI
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
required: true
|
||||||
|
description: 'Tag to Rebuild MSI'
|
||||||
|
version:
|
||||||
|
description: 'Version of Rebuild MSI'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
name: Nu
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
target:
|
||||||
|
- x86_64-pc-windows-msvc
|
||||||
|
- aarch64-pc-windows-msvc
|
||||||
|
extra: ['bin']
|
||||||
|
|
||||||
|
include:
|
||||||
|
- target: x86_64-pc-windows-msvc
|
||||||
|
os: windows-latest
|
||||||
|
- target: aarch64-pc-windows-msvc
|
||||||
|
os: windows-11-arm
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Wix Toolset 6 for Windows
|
||||||
|
shell: pwsh
|
||||||
|
if: ${{ startsWith(matrix.os, 'windows') }}
|
||||||
|
run: |
|
||||||
|
dotnet tool install --global wix --version 6.0.0
|
||||||
|
dotnet workload install wix
|
||||||
|
$wixPath = "$env:USERPROFILE\.dotnet\tools"
|
||||||
|
echo "$wixPath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||||
|
$env:PATH = "$wixPath;$env:PATH"
|
||||||
|
wix --version
|
||||||
|
|
||||||
|
- name: Setup Nushell
|
||||||
|
uses: hustcer/setup-nu@v3
|
||||||
|
with:
|
||||||
|
version: 0.105.1
|
||||||
|
|
||||||
|
- name: Release MSI Packages
|
||||||
|
id: nu
|
||||||
|
run: nu .github/workflows/release-msi.nu
|
||||||
|
env:
|
||||||
|
OS: ${{ matrix.os }}
|
||||||
|
REF: ${{ inputs.tag }}
|
||||||
|
TARGET: ${{ matrix.target }}
|
||||||
|
MSI_VERSION: ${{ inputs.version }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
# REF: https://github.com/marketplace/actions/gh-release
|
||||||
|
- name: Publish Archive
|
||||||
|
uses: softprops/action-gh-release@v2.0.5
|
||||||
|
with:
|
||||||
|
tag_name: ${{ inputs.tag }}
|
||||||
|
files: ${{ steps.nu.outputs.msi }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
sha256sum:
|
||||||
|
needs: release
|
||||||
|
name: Create Sha256sum
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Download Release Archives
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: >-
|
||||||
|
gh release download ${{ inputs.tag }}
|
||||||
|
--repo ${{ github.repository }}
|
||||||
|
--pattern '*'
|
||||||
|
--dir release
|
||||||
|
- name: Create Checksums
|
||||||
|
run: cd release && rm -f SHA256SUMS && shasum -a 256 * > ../SHA256SUMS
|
||||||
|
- name: Publish Checksums
|
||||||
|
uses: softprops/action-gh-release@v2.0.5
|
||||||
|
with:
|
||||||
|
files: SHA256SUMS
|
||||||
|
tag_name: ${{ inputs.tag }}
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
119
.github/workflows/release-pkg.nu
vendored
119
.github/workflows/release-pkg.nu
vendored
@ -8,10 +8,10 @@
|
|||||||
|
|
||||||
# Instructions for manually creating an MSI for Winget Releases when they fail
|
# Instructions for manually creating an MSI for Winget Releases when they fail
|
||||||
# Added 2022-11-29 when Windows packaging wouldn't work
|
# Added 2022-11-29 when Windows packaging wouldn't work
|
||||||
# Updated again on 2023-02-23 because msis are still failing validation
|
# Updated again on 2023-02-23 because MSIs are still failing validation
|
||||||
# To run this manual for windows here are the steps I take
|
# To run this manual for windows here are the steps I take
|
||||||
# checkout the release you want to publish
|
# checkout the release you want to publish
|
||||||
# 1. git checkout 0.86.0
|
# 1. git checkout 0.103.0
|
||||||
# unset CARGO_TARGET_DIR if set (I have to do this in the parent shell to get it to work)
|
# unset CARGO_TARGET_DIR if set (I have to do this in the parent shell to get it to work)
|
||||||
# 2. $env:CARGO_TARGET_DIR = ""
|
# 2. $env:CARGO_TARGET_DIR = ""
|
||||||
# 2. hide-env CARGO_TARGET_DIR
|
# 2. hide-env CARGO_TARGET_DIR
|
||||||
@ -23,19 +23,13 @@
|
|||||||
# 7. $env.Path = ($env.Path | append 'c:\apps\7-zip')
|
# 7. $env.Path = ($env.Path | append 'c:\apps\7-zip')
|
||||||
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
|
# make sure aria2c.exe is in your path https://github.com/aria2/aria2
|
||||||
# 8. $env.Path = ($env.Path | append 'c:\path\to\aria2c')
|
# 8. $env.Path = ($env.Path | append 'c:\path\to\aria2c')
|
||||||
# make sure you have the wixtools installed https://wixtoolset.org/
|
# make sure you have the wix 6.0 installed: dotnet tool install --global wix --version 6.0.0
|
||||||
# 9. $env.Path = ($env.Path | append 'C:\Users\dschroeder\AppData\Local\tauri\WixTools')
|
# then build nu*.exe and the MSI installer by running:
|
||||||
# You need to run the release-pkg twice. The first pass, with _EXTRA_ as 'bin', makes the output
|
# 9. source .github\workflows\release-pkg.nu
|
||||||
# folder and builds everything. The second pass, that generates the msi file, with _EXTRA_ as 'msi'
|
|
||||||
# 10. $env._EXTRA_ = 'bin'
|
|
||||||
# 11. source .github\workflows\release-pkg.nu
|
|
||||||
# 12. cd ..
|
|
||||||
# 13. $env._EXTRA_ = 'msi'
|
|
||||||
# 14. source .github\workflows\release-pkg.nu
|
|
||||||
# After msi is generated, you have to update winget-pkgs repo, you'll need to patch the release
|
# After msi is generated, you have to update winget-pkgs repo, you'll need to patch the release
|
||||||
# by deleting the existing msi and uploading this new msi. Then you'll need to update the hash
|
# by deleting the existing msi and uploading this new msi. Then you'll need to update the hash
|
||||||
# on the winget-pkgs PR. To generate the hash, run this command
|
# on the winget-pkgs PR. To generate the hash, run this command
|
||||||
# 15. open target\wix\nu-0.74.0-x86_64-pc-windows-msvc.msi | hash sha256
|
# 10. open wix\bin\x64\Release\nu-0.103.0-x86_64-pc-windows-msvc.msi | hash sha256
|
||||||
# Then, just take the output and put it in the winget-pkgs PR for the hash on the msi
|
# Then, just take the output and put it in the winget-pkgs PR for the hash on the msi
|
||||||
|
|
||||||
|
|
||||||
@ -85,14 +79,14 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
|||||||
cargo-build-nu
|
cargo-build-nu
|
||||||
}
|
}
|
||||||
'aarch64-unknown-linux-musl' => {
|
'aarch64-unknown-linux-musl' => {
|
||||||
aria2c https://musl.cc/aarch64-linux-musl-cross.tgz
|
aria2c https://github.com/nushell/integrations/releases/download/build-tools/aarch64-linux-musl-cross.tgz
|
||||||
tar -xf aarch64-linux-musl-cross.tgz -C $env.HOME
|
tar -xf aarch64-linux-musl-cross.tgz -C $env.HOME
|
||||||
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.HOME)/aarch64-linux-musl-cross/bin')
|
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.HOME)/aarch64-linux-musl-cross/bin')
|
||||||
$env.CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER = 'aarch64-linux-musl-gcc'
|
$env.CARGO_TARGET_AARCH64_UNKNOWN_LINUX_MUSL_LINKER = 'aarch64-linux-musl-gcc'
|
||||||
cargo-build-nu
|
cargo-build-nu
|
||||||
}
|
}
|
||||||
'armv7-unknown-linux-musleabihf' => {
|
'armv7-unknown-linux-musleabihf' => {
|
||||||
aria2c https://musl.cc/armv7r-linux-musleabihf-cross.tgz
|
aria2c https://github.com/nushell/integrations/releases/download/build-tools/armv7r-linux-musleabihf-cross.tgz
|
||||||
tar -xf armv7r-linux-musleabihf-cross.tgz -C $env.HOME
|
tar -xf armv7r-linux-musleabihf-cross.tgz -C $env.HOME
|
||||||
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.HOME)/armv7r-linux-musleabihf-cross/bin')
|
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.HOME)/armv7r-linux-musleabihf-cross/bin')
|
||||||
$env.CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER = 'armv7r-linux-musleabihf-gcc'
|
$env.CARGO_TARGET_ARMV7_UNKNOWN_LINUX_MUSLEABIHF_LINKER = 'armv7r-linux-musleabihf-gcc'
|
||||||
@ -105,6 +99,14 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
|||||||
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER = 'loongarch64-unknown-linux-gnu-gcc'
|
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_GNU_LINKER = 'loongarch64-unknown-linux-gnu-gcc'
|
||||||
cargo-build-nu
|
cargo-build-nu
|
||||||
}
|
}
|
||||||
|
'loongarch64-unknown-linux-musl' => {
|
||||||
|
print $"(ansi g)Downloading LoongArch64 musl cross-compilation toolchain...(ansi reset)"
|
||||||
|
aria2c -q https://github.com/LoongsonLab/oscomp-toolchains-for-oskernel/releases/download/loongarch64-linux-musl-cross-gcc-13.2.0/loongarch64-linux-musl-cross.tgz
|
||||||
|
tar -xf loongarch64-linux-musl-cross.tgz
|
||||||
|
$env.PATH = ($env.PATH | split row (char esep) | prepend $'($env.PWD)/loongarch64-linux-musl-cross/bin')
|
||||||
|
$env.CARGO_TARGET_LOONGARCH64_UNKNOWN_LINUX_MUSL_LINKER = "loongarch64-linux-musl-gcc"
|
||||||
|
cargo-build-nu
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
|
||||||
# Actually just for x86_64-unknown-linux-musl target
|
# Actually just for x86_64-unknown-linux-musl target
|
||||||
@ -117,14 +119,14 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
|||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Build for Windows without static-link-openssl feature
|
# Build for Windows without static-link-openssl feature
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
if $os in ['windows-latest'] {
|
if $os =~ 'windows' {
|
||||||
cargo-build-nu
|
cargo-build-nu
|
||||||
}
|
}
|
||||||
|
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
# Prepare for the release archive
|
# Prepare for the release archive
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
let suffix = if $os == 'windows-latest' { '.exe' }
|
let suffix = if $os =~ 'windows' { '.exe' }
|
||||||
# nu, nu_plugin_* were all included
|
# nu, nu_plugin_* were all included
|
||||||
let executable = $'target/($target)/release/($bin)*($suffix)'
|
let executable = $'target/($target)/release/($bin)*($suffix)'
|
||||||
print $'Current executable file: ($executable)'
|
print $'Current executable file: ($executable)'
|
||||||
@ -148,10 +150,10 @@ For more information, refer to https://www.nushell.sh/book/plugins.html
|
|||||||
[LICENSE ...(glob $executable)] | each {|it| cp -rv $it $dist } | flatten
|
[LICENSE ...(glob $executable)] | each {|it| cp -rv $it $dist } | flatten
|
||||||
|
|
||||||
print $'(char nl)Check binary release version detail:'; hr-line
|
print $'(char nl)Check binary release version detail:'; hr-line
|
||||||
let ver = if $os == 'windows-latest' {
|
let ver = if $os =~ 'windows' {
|
||||||
(do -i { .\output\nu.exe -c 'version' }) | str join
|
(do -i { .\output\nu.exe -c 'version' }) | default '' | str join
|
||||||
} else {
|
} else {
|
||||||
(do -i { ./output/nu -c 'version' }) | str join
|
(do -i { ./output/nu -c 'version' }) | default '' | str join
|
||||||
}
|
}
|
||||||
if ($ver | str trim | is-empty) {
|
if ($ver | str trim | is-empty) {
|
||||||
print $'(ansi r)Incompatible Nu binary: The binary cross compiled is not runnable on current arch...(ansi reset)'
|
print $'(ansi r)Incompatible Nu binary: The binary cross compiled is not runnable on current arch...(ansi reset)'
|
||||||
@ -175,53 +177,60 @@ if $os in ['macos-latest'] or $USE_UBUNTU {
|
|||||||
tar -czf $archive $dest
|
tar -czf $archive $dest
|
||||||
print $'archive: ---> ($archive)'; ls $archive
|
print $'archive: ---> ($archive)'; ls $archive
|
||||||
# REF: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
|
# REF: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/
|
||||||
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
echo $"archive=($archive)(char nl)" o>> $env.GITHUB_OUTPUT
|
||||||
|
|
||||||
} else if $os == 'windows-latest' {
|
} else if $os =~ 'windows' {
|
||||||
|
|
||||||
let releaseStem = $'($bin)-($version)-($target)'
|
let releaseStem = $'($bin)-($version)-($target)'
|
||||||
|
let arch = if $nu.os-info.arch =~ 'x86_64' { 'x64' } else { 'arm64' }
|
||||||
|
fetch-less $arch
|
||||||
|
|
||||||
print $'(char nl)Download less related stuffs...'; hr-line
|
print $'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls | print
|
||||||
# todo: less-v661 is out but is released as a zip file. maybe we should switch to that and extract it?
|
let archive = $'($dist)/($releaseStem).zip'
|
||||||
aria2c https://github.com/jftuga/less-Windows/releases/download/less-v608/less.exe -o less.exe
|
7z a $archive ...(glob *)
|
||||||
# the below was renamed because it was failing to download for darren. it should work but it wasn't
|
let pkg = (ls -f $archive | get name)
|
||||||
# todo: maybe we should get rid of this aria2c dependency and just use http get?
|
if not ($pkg | is-empty) {
|
||||||
#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
|
|
||||||
if (get-env _EXTRA_) == 'msi' {
|
|
||||||
|
|
||||||
let wixRelease = $'($src)/target/wix/($releaseStem).msi'
|
|
||||||
print $'(char nl)Start creating Windows msi package with the following contents...'
|
|
||||||
cd $src; hr-line
|
|
||||||
# Wix need the binaries be stored in target/release/
|
|
||||||
cp -r ($'($dist)/*' | into glob) target/release/
|
|
||||||
ls target/release/* | print
|
|
||||||
cargo install cargo-wix --version 0.3.8
|
|
||||||
cargo wix --no-build --nocapture --package nu --output $wixRelease
|
|
||||||
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
||||||
let archive = ($wixRelease | str replace --all '\' '/')
|
let archive = ($pkg | get 0 | str replace --all '\' '/')
|
||||||
print $'archive: ---> ($archive)';
|
print $'archive: ---> ($archive)'
|
||||||
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
echo $"archive=($archive)(char nl)" o>> $env.GITHUB_OUTPUT
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
# Create extra Windows msi release package if dotnet and wix are available
|
||||||
|
let installed = [dotnet wix] | all { (which $in | length) > 0 }
|
||||||
|
if $installed and (wix --version | split row . | first | into int) >= 6 {
|
||||||
|
|
||||||
print $'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls | print
|
print $'(char nl)Start creating Windows msi package with the following contents...'
|
||||||
let archive = $'($dist)/($releaseStem).zip'
|
cd $src; cd wix; hr-line; mkdir nu
|
||||||
7z a $archive ...(glob *)
|
# Wix need the binaries be stored in nu folder
|
||||||
let pkg = (ls -f $archive | get name)
|
cp -r ($'($dist)/*' | into glob) nu/
|
||||||
if not ($pkg | is-empty) {
|
cp $'($dist)/README.txt' .
|
||||||
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
ls -f nu/* | print
|
||||||
let archive = ($pkg | get 0 | str replace --all '\' '/')
|
./nu/nu.exe -c $'NU_RELEASE_VERSION=($version) dotnet build -c Release -p:Platform=($arch)'
|
||||||
print $'archive: ---> ($archive)'
|
glob **/*.msi | print
|
||||||
echo $"archive=($archive)" | save --append $env.GITHUB_OUTPUT
|
# Workaround for https://github.com/softprops/action-gh-release/issues/280
|
||||||
}
|
let wixRelease = (glob **/*.msi | where $it =~ bin | get 0 | str replace --all '\' '/')
|
||||||
|
let msi = $'($wixRelease | path dirname)/nu-($version)-($target).msi'
|
||||||
|
mv $wixRelease $msi
|
||||||
|
print $'MSI archive: ---> ($msi)';
|
||||||
|
echo $"msi=($msi)(char nl)" o>> $env.GITHUB_OUTPUT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def fetch-less [
|
||||||
|
arch: string = 'x64' # The architecture to fetch
|
||||||
|
] {
|
||||||
|
let less_zip = $'less-($arch).zip'
|
||||||
|
print $'Fetching less archive: (ansi g)($less_zip)(ansi reset)'
|
||||||
|
let url = $'https://github.com/jftuga/less-Windows/releases/download/less-v668/($less_zip)'
|
||||||
|
http get https://github.com/jftuga/less-Windows/blob/master/LICENSE | save -rf LICENSE-for-less.txt
|
||||||
|
http get $url | save -rf $less_zip
|
||||||
|
unzip $less_zip
|
||||||
|
rm $less_zip lesskey.exe
|
||||||
|
}
|
||||||
|
|
||||||
def 'cargo-build-nu' [] {
|
def 'cargo-build-nu' [] {
|
||||||
if $os == 'windows-latest' {
|
if $os =~ 'windows' {
|
||||||
cargo build --release --all --target $target
|
cargo build --release --all --target $target
|
||||||
} else {
|
} else {
|
||||||
cargo build --release --all --target $target --features=static-link-openssl
|
cargo build --release --all --target $target --features=static-link-openssl
|
||||||
|
34
.github/workflows/release.yml
vendored
34
.github/workflows/release.yml
vendored
@ -35,24 +35,16 @@ jobs:
|
|||||||
- armv7-unknown-linux-musleabihf
|
- armv7-unknown-linux-musleabihf
|
||||||
- riscv64gc-unknown-linux-gnu
|
- riscv64gc-unknown-linux-gnu
|
||||||
- loongarch64-unknown-linux-gnu
|
- loongarch64-unknown-linux-gnu
|
||||||
extra: ['bin']
|
- loongarch64-unknown-linux-musl
|
||||||
include:
|
include:
|
||||||
- target: aarch64-apple-darwin
|
- target: aarch64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
- target: x86_64-apple-darwin
|
- target: x86_64-apple-darwin
|
||||||
os: macos-latest
|
os: macos-latest
|
||||||
- target: x86_64-pc-windows-msvc
|
- target: x86_64-pc-windows-msvc
|
||||||
extra: 'bin'
|
|
||||||
os: windows-latest
|
|
||||||
- target: x86_64-pc-windows-msvc
|
|
||||||
extra: msi
|
|
||||||
os: windows-latest
|
os: windows-latest
|
||||||
- target: aarch64-pc-windows-msvc
|
- target: aarch64-pc-windows-msvc
|
||||||
extra: 'bin'
|
os: windows-11-arm
|
||||||
os: windows-latest
|
|
||||||
- target: aarch64-pc-windows-msvc
|
|
||||||
extra: msi
|
|
||||||
os: windows-latest
|
|
||||||
- target: x86_64-unknown-linux-gnu
|
- target: x86_64-unknown-linux-gnu
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- target: x86_64-unknown-linux-musl
|
- target: x86_64-unknown-linux-musl
|
||||||
@ -69,18 +61,31 @@ jobs:
|
|||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
- target: loongarch64-unknown-linux-gnu
|
- target: loongarch64-unknown-linux-gnu
|
||||||
os: ubuntu-22.04
|
os: ubuntu-22.04
|
||||||
|
- target: loongarch64-unknown-linux-musl
|
||||||
|
os: ubuntu-22.04
|
||||||
|
|
||||||
runs-on: ${{matrix.os}}
|
runs-on: ${{matrix.os}}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Wix Toolset 6 for Windows
|
||||||
|
shell: pwsh
|
||||||
|
if: ${{ startsWith(matrix.os, 'windows') }}
|
||||||
|
run: |
|
||||||
|
dotnet tool install --global wix --version 6.0.0
|
||||||
|
dotnet workload install wix
|
||||||
|
$wixPath = "$env:USERPROFILE\.dotnet\tools"
|
||||||
|
echo "$wixPath" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||||
|
$env:PATH = "$wixPath;$env:PATH"
|
||||||
|
wix --version
|
||||||
|
|
||||||
- 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.11.0
|
uses: actions-rust-lang/setup-rust-toolchain@v1.12.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
|
||||||
@ -89,7 +94,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3
|
uses: hustcer/setup-nu@v3
|
||||||
with:
|
with:
|
||||||
version: 0.101.0
|
version: 0.105.1
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
@ -98,7 +103,6 @@ jobs:
|
|||||||
OS: ${{ matrix.os }}
|
OS: ${{ matrix.os }}
|
||||||
REF: ${{ github.ref }}
|
REF: ${{ github.ref }}
|
||||||
TARGET: ${{ matrix.target }}
|
TARGET: ${{ matrix.target }}
|
||||||
_EXTRA_: ${{ matrix.extra }}
|
|
||||||
|
|
||||||
# WARN: Don't upgrade this action due to the release per asset issue.
|
# WARN: Don't upgrade this action due to the release per asset issue.
|
||||||
# See: https://github.com/softprops/action-gh-release/issues/445
|
# See: https://github.com/softprops/action-gh-release/issues/445
|
||||||
@ -107,7 +111,9 @@ jobs:
|
|||||||
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
if: ${{ startsWith(github.ref, 'refs/tags/') }}
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
files: ${{ steps.nu.outputs.archive }}
|
files: |
|
||||||
|
${{ steps.nu.outputs.msi }}
|
||||||
|
${{ steps.nu.outputs.archive }}
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@ -10,4 +10,4 @@ jobs:
|
|||||||
uses: actions/checkout@v4.1.7
|
uses: actions/checkout@v4.1.7
|
||||||
|
|
||||||
- name: Check spelling
|
- name: Check spelling
|
||||||
uses: crate-ci/typos@v1.29.10
|
uses: crate-ci/typos@v1.34.0
|
||||||
|
7
.github/workflows/winget-submission.yml
vendored
7
.github/workflows/winget-submission.yml
vendored
@ -10,6 +10,11 @@ on:
|
|||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
packages: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
winget:
|
winget:
|
||||||
@ -26,4 +31,4 @@ jobs:
|
|||||||
version: ${{ inputs.tag_name || github.event.release.tag_name }}
|
version: ${{ inputs.tag_name || github.event.release.tag_name }}
|
||||||
release-tag: ${{ inputs.tag_name || github.event.release.tag_name }}
|
release-tag: ${{ inputs.tag_name || github.event.release.tag_name }}
|
||||||
token: ${{ secrets.NUSHELL_PAT }}
|
token: ${{ secrets.NUSHELL_PAT }}
|
||||||
fork-user: fdncred
|
fork-user: nushell
|
||||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -32,11 +32,17 @@ unstable_cargo_features.txt
|
|||||||
# Helix configuration folder
|
# Helix configuration folder
|
||||||
.helix/*
|
.helix/*
|
||||||
.helix
|
.helix
|
||||||
|
wix/bin/
|
||||||
|
wix/obj/
|
||||||
|
wix/nu/
|
||||||
|
|
||||||
# Coverage tools
|
# Coverage tools
|
||||||
lcov.info
|
lcov.info
|
||||||
tarpaulin-report.html
|
tarpaulin-report.html
|
||||||
|
|
||||||
|
# benchmarking
|
||||||
|
/tango
|
||||||
|
|
||||||
# Visual Studio
|
# Visual Studio
|
||||||
.vs/*
|
.vs/*
|
||||||
*.rsproj
|
*.rsproj
|
||||||
|
@ -31,7 +31,7 @@ The review process can be summarized as follows:
|
|||||||
1. You want to make some change to Nushell that is more involved than simple bug-fixing.
|
1. You want to make some change to Nushell that is more involved than simple bug-fixing.
|
||||||
2. Go to [Discord](https://discordapp.com/invite/NtAbbGn) or a [GitHub issue](https://github.com/nushell/nushell/issues/new/choose) and chat with some core team members and/or other contributors about it.
|
2. Go to [Discord](https://discordapp.com/invite/NtAbbGn) or a [GitHub issue](https://github.com/nushell/nushell/issues/new/choose) and chat with some core team members and/or other contributors about it.
|
||||||
3. After getting a green light from the core team, implement the feature, open a pull request (PR) and write a concise but comprehensive description of the change.
|
3. After getting a green light from the core team, implement the feature, open a pull request (PR) and write a concise but comprehensive description of the change.
|
||||||
4. If your PR includes any use-facing features (such as adding a flag to a command), clearly list them in the PR description.
|
4. If your PR includes any user-facing features (such as adding a flag to a command), clearly list them in the PR description.
|
||||||
5. Then, core team members and other regular contributors will review the PR and suggest changes.
|
5. Then, core team members and other regular contributors will review the PR and suggest changes.
|
||||||
6. When we all agree, the PR will be merged.
|
6. When we all agree, the PR will be merged.
|
||||||
7. If your PR includes any user-facing features, make sure the changes are also reflected in [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged.
|
7. If your PR includes any user-facing features, make sure the changes are also reflected in [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged.
|
||||||
|
1063
Cargo.lock
generated
1063
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
170
Cargo.toml
170
Cargo.toml
@ -4,14 +4,14 @@ build = "scripts/build.rs"
|
|||||||
default-run = "nu"
|
default-run = "nu"
|
||||||
description = "A new type of shell"
|
description = "A new type of shell"
|
||||||
documentation = "https://www.nushell.sh/book/"
|
documentation = "https://www.nushell.sh/book/"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
exclude = ["images"]
|
exclude = ["images"]
|
||||||
homepage = "https://www.nushell.sh"
|
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.83.0"
|
rust-version = "1.86.0"
|
||||||
version = "0.103.0"
|
version = "0.105.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
|
||||||
|
|
||||||
@ -24,36 +24,37 @@ pkg-fmt = "zip"
|
|||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
|
"crates/nu_plugin_custom_values",
|
||||||
|
"crates/nu_plugin_example",
|
||||||
|
"crates/nu_plugin_formats",
|
||||||
|
"crates/nu_plugin_gstat",
|
||||||
|
"crates/nu_plugin_inc",
|
||||||
|
"crates/nu_plugin_polars",
|
||||||
|
"crates/nu_plugin_query",
|
||||||
|
"crates/nu_plugin_stress_internals",
|
||||||
"crates/nu-cli",
|
"crates/nu-cli",
|
||||||
"crates/nu-engine",
|
|
||||||
"crates/nu-parser",
|
|
||||||
"crates/nu-system",
|
|
||||||
"crates/nu-cmd-base",
|
"crates/nu-cmd-base",
|
||||||
"crates/nu-cmd-extra",
|
"crates/nu-cmd-extra",
|
||||||
"crates/nu-cmd-lang",
|
"crates/nu-cmd-lang",
|
||||||
"crates/nu-cmd-plugin",
|
"crates/nu-cmd-plugin",
|
||||||
"crates/nu-command",
|
|
||||||
"crates/nu-color-config",
|
"crates/nu-color-config",
|
||||||
|
"crates/nu-command",
|
||||||
|
"crates/nu-derive-value",
|
||||||
|
"crates/nu-engine",
|
||||||
|
"crates/nu-experimental",
|
||||||
"crates/nu-explore",
|
"crates/nu-explore",
|
||||||
"crates/nu-json",
|
"crates/nu-json",
|
||||||
"crates/nu-lsp",
|
"crates/nu-lsp",
|
||||||
"crates/nu-pretty-hex",
|
"crates/nu-parser",
|
||||||
"crates/nu-protocol",
|
|
||||||
"crates/nu-derive-value",
|
|
||||||
"crates/nu-plugin",
|
|
||||||
"crates/nu-plugin-core",
|
"crates/nu-plugin-core",
|
||||||
"crates/nu-plugin-engine",
|
"crates/nu-plugin-engine",
|
||||||
"crates/nu-plugin-protocol",
|
"crates/nu-plugin-protocol",
|
||||||
"crates/nu-plugin-test-support",
|
"crates/nu-plugin-test-support",
|
||||||
"crates/nu_plugin_inc",
|
"crates/nu-plugin",
|
||||||
"crates/nu_plugin_gstat",
|
"crates/nu-pretty-hex",
|
||||||
"crates/nu_plugin_example",
|
"crates/nu-protocol",
|
||||||
"crates/nu_plugin_query",
|
|
||||||
"crates/nu_plugin_custom_values",
|
|
||||||
"crates/nu_plugin_formats",
|
|
||||||
"crates/nu_plugin_polars",
|
|
||||||
"crates/nu_plugin_stress_internals",
|
|
||||||
"crates/nu-std",
|
"crates/nu-std",
|
||||||
|
"crates/nu-system",
|
||||||
"crates/nu-table",
|
"crates/nu-table",
|
||||||
"crates/nu-term-grid",
|
"crates/nu-term-grid",
|
||||||
"crates/nu-test-support",
|
"crates/nu-test-support",
|
||||||
@ -63,15 +64,15 @@ members = [
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
alphanumeric-sort = "1.5"
|
alphanumeric-sort = "1.5"
|
||||||
ansi-str = "0.8"
|
ansi-str = "0.9"
|
||||||
anyhow = "1.0.82"
|
anyhow = "1.0.82"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
bracoxide = "0.1.5"
|
bracoxide = "0.1.6"
|
||||||
brotli = "7.0"
|
brotli = "7.0"
|
||||||
byteorder = "1.5"
|
byteorder = "1.5"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
bytesize = "1.3.1"
|
bytesize = "1.3.3"
|
||||||
calamine = "0.26.1"
|
calamine = "0.28"
|
||||||
chardetng = "0.1.17"
|
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"
|
||||||
@ -91,26 +92,26 @@ fancy-regex = "0.14"
|
|||||||
filesize = "0.2"
|
filesize = "0.2"
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
heck = "0.5.0"
|
heck = "0.5.0"
|
||||||
human-date-parser = "0.2.0"
|
human-date-parser = "0.3.0"
|
||||||
indexmap = "2.7"
|
indexmap = "2.10"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
interprocess = "2.2.0"
|
interprocess = "2.2.0"
|
||||||
is_executable = "1.0"
|
is_executable = "1.0"
|
||||||
itertools = "0.13"
|
itertools = "0.14"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
libproc = "0.14"
|
libproc = "0.14"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
lru = "0.12"
|
lru = "0.12"
|
||||||
lscolors = { version = "0.17", default-features = false }
|
lscolors = { version = "0.20", default-features = false }
|
||||||
lsp-server = "0.7.8"
|
lsp-server = "0.7.8"
|
||||||
lsp-types = { version = "0.97.0", features = ["proposed"] }
|
lsp-types = { version = "0.97.0", features = ["proposed"] }
|
||||||
lsp-textdocument = "0.4.2"
|
lsp-textdocument = "0.4.2"
|
||||||
mach2 = "0.4"
|
mach2 = "0.4"
|
||||||
md5 = { version = "0.10", package = "md-5" }
|
md5 = { version = "0.10", package = "md-5" }
|
||||||
miette = "7.5"
|
miette = "7.6"
|
||||||
mime = "0.3.17"
|
mime = "0.3.17"
|
||||||
mime_guess = "2.0"
|
mime_guess = "2.0"
|
||||||
mockito = { version = "1.6", default-features = false }
|
mockito = { version = "1.7", default-features = false }
|
||||||
multipart-rs = "0.1.13"
|
multipart-rs = "0.1.13"
|
||||||
native-tls = "0.2"
|
native-tls = "0.2"
|
||||||
nix = { version = "0.29", default-features = false }
|
nix = { version = "0.29", default-features = false }
|
||||||
@ -131,88 +132,94 @@ proc-macro-error2 = "2.0"
|
|||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
procfs = "0.17.0"
|
procfs = "0.17.0"
|
||||||
pwd = "1.3"
|
pwd = "1.3"
|
||||||
quick-xml = "0.37.0"
|
quick-xml = "0.37.5"
|
||||||
quickcheck = "1.0"
|
quickcheck = "1.0"
|
||||||
quickcheck_macros = "1.0"
|
quickcheck_macros = "1.1"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
rand = "0.8"
|
rand = "0.9"
|
||||||
getrandom = "0.2" # pick same version that rand requires
|
getrandom = "0.2" # pick same version that rand requires
|
||||||
rand_chacha = "0.3.1"
|
rand_chacha = "0.9"
|
||||||
ratatui = "0.29"
|
ratatui = "0.29"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
reedline = "0.39.0"
|
reedline = "0.40.0"
|
||||||
rmp = "0.8"
|
rmp = "0.8"
|
||||||
rmp-serde = "1.3"
|
rmp-serde = "1.3"
|
||||||
roxmltree = "0.20"
|
roxmltree = "0.20"
|
||||||
rstest = { version = "0.23", default-features = false }
|
rstest = { version = "0.23", default-features = false }
|
||||||
rstest_reuse = "0.7"
|
rstest_reuse = "0.7"
|
||||||
rusqlite = "0.31"
|
rusqlite = "0.31"
|
||||||
rust-embed = "8.6.0"
|
rust-embed = "8.7.0"
|
||||||
|
rustls = { version = "0.23", default-features = false, features = ["std", "tls12"] }
|
||||||
|
rustls-native-certs = "0.8"
|
||||||
scopeguard = { version = "1.2.0" }
|
scopeguard = { version = "1.2.0" }
|
||||||
serde = { version = "1.0" }
|
serde = { version = "1.0" }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0.97"
|
||||||
serde_urlencoded = "0.7.1"
|
serde_urlencoded = "0.7.1"
|
||||||
serde_yaml = "0.9.33"
|
serde_yaml = "0.9.33"
|
||||||
sha2 = "0.10"
|
sha2 = "0.10"
|
||||||
strip-ansi-escapes = "0.2.0"
|
strip-ansi-escapes = "0.2.1"
|
||||||
strum = "0.26"
|
strum = "0.26"
|
||||||
strum_macros = "0.26"
|
strum_macros = "0.26"
|
||||||
syn = "2.0"
|
syn = "2.0"
|
||||||
sysinfo = "0.33"
|
sysinfo = "0.33"
|
||||||
tabled = { version = "0.17.0", default-features = false }
|
tabled = { version = "0.20", default-features = false }
|
||||||
tempfile = "3.15"
|
tempfile = "3.20"
|
||||||
titlecase = "3.4"
|
thiserror = "2.0.12"
|
||||||
|
titlecase = "3.6"
|
||||||
toml = "0.8"
|
toml = "0.8"
|
||||||
trash = "5.2"
|
trash = "5.2"
|
||||||
update-informer = { version = "1.2.0", default-features = false, features = ["github", "native-tls", "ureq"] }
|
update-informer = { version = "1.2.0", default-features = false, features = ["github", "ureq"] }
|
||||||
umask = "2.1"
|
umask = "2.1"
|
||||||
unicode-segmentation = "1.12"
|
unicode-segmentation = "1.12"
|
||||||
unicode-width = "0.2"
|
unicode-width = "0.2"
|
||||||
ureq = { version = "2.12", default-features = false }
|
ureq = { version = "2.12", default-features = false, features = ["socks-proxy"] }
|
||||||
url = "2.2"
|
url = "2.2"
|
||||||
uu_cp = "0.0.29"
|
uu_cp = "0.0.30"
|
||||||
uu_mkdir = "0.0.29"
|
uu_mkdir = "0.0.30"
|
||||||
uu_mktemp = "0.0.29"
|
uu_mktemp = "0.0.30"
|
||||||
uu_mv = "0.0.29"
|
uu_mv = "0.0.30"
|
||||||
uu_touch = "0.0.29"
|
uu_touch = "0.0.30"
|
||||||
uu_whoami = "0.0.29"
|
uu_whoami = "0.0.30"
|
||||||
uu_uname = "0.0.29"
|
uu_uname = "0.0.30"
|
||||||
uucore = "0.0.29"
|
uucore = "0.0.30"
|
||||||
uuid = "1.12.0"
|
uuid = "1.16.0"
|
||||||
v_htmlescape = "0.15.0"
|
v_htmlescape = "0.15.0"
|
||||||
wax = "0.6"
|
wax = "0.6"
|
||||||
web-time = "1.1.0"
|
web-time = "1.1.0"
|
||||||
which = "7.0.0"
|
which = "8.0.0"
|
||||||
windows = "0.56"
|
windows = "0.56"
|
||||||
windows-sys = "0.48"
|
windows-sys = "0.48"
|
||||||
winreg = "0.52"
|
winreg = "0.52"
|
||||||
memchr = "2.7.4"
|
memchr = "2.7.4"
|
||||||
|
webpki-roots = "1.0"
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
# Warning: workspace lints affect library code as well as tests, so don't enable lints that would be too noisy in tests like that.
|
# Warning: workspace lints affect library code as well as tests, so don't enable lints that would be too noisy in tests like that.
|
||||||
# todo = "warn"
|
# todo = "warn"
|
||||||
unchecked_duration_subtraction = "warn"
|
unchecked_duration_subtraction = "warn"
|
||||||
|
used_underscore_binding = "warn"
|
||||||
|
|
||||||
[lints]
|
[lints]
|
||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.103.0" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.105.2" }
|
||||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.103.0" }
|
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.105.2" }
|
||||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.103.0" }
|
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.105.2" }
|
||||||
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.103.0", optional = true }
|
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.105.2" }
|
||||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.103.0" }
|
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.105.2", optional = true }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.103.0" }
|
nu-command = { path = "./crates/nu-command", version = "0.105.2", default-features = false, features = ["os"] }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.103.0" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.105.2" }
|
||||||
nu-explore = { path = "./crates/nu-explore", version = "0.103.0" }
|
nu-experimental = { path = "./crates/nu-experimental", version = "0.105.2" }
|
||||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.103.0" }
|
nu-explore = { path = "./crates/nu-explore", version = "0.105.2" }
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.103.0" }
|
nu-lsp = { path = "./crates/nu-lsp/", version = "0.105.2" }
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.103.0" }
|
nu-parser = { path = "./crates/nu-parser", version = "0.105.2" }
|
||||||
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.103.0" }
|
nu-path = { path = "./crates/nu-path", version = "0.105.2" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.103.0" }
|
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.105.2" }
|
||||||
nu-std = { path = "./crates/nu-std", version = "0.103.0" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.105.2" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.103.0" }
|
nu-std = { path = "./crates/nu-std", version = "0.105.2" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.103.0" }
|
nu-system = { path = "./crates/nu-system", version = "0.105.2" }
|
||||||
|
nu-utils = { path = "./crates/nu-utils", version = "0.105.2" }
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
crossterm = { workspace = true }
|
crossterm = { workspace = true }
|
||||||
@ -241,9 +248,9 @@ nix = { workspace = true, default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.103.0" }
|
nu-test-support = { path = "./crates/nu-test-support", version = "0.105.2" }
|
||||||
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.103.0" }
|
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.105.2" }
|
||||||
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.103.0" }
|
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.105.2" }
|
||||||
assert_cmd = "2.0"
|
assert_cmd = "2.0"
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
tango-bench = "0.6"
|
tango-bench = "0.6"
|
||||||
@ -254,10 +261,14 @@ serial_test = "3.2"
|
|||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
# Enable all features while still avoiding mutually exclusive features.
|
||||||
|
# Use this if `--all-features` fails.
|
||||||
|
full = ["plugin", "rustls-tls", "system-clipboard", "trash-support", "sqlite"]
|
||||||
|
|
||||||
plugin = [
|
plugin = [
|
||||||
# crates
|
# crates
|
||||||
"nu-cmd-plugin",
|
"dep:nu-cmd-plugin",
|
||||||
"nu-plugin-engine",
|
"dep:nu-plugin-engine",
|
||||||
|
|
||||||
# features
|
# features
|
||||||
"nu-cli/plugin",
|
"nu-cli/plugin",
|
||||||
@ -269,31 +280,34 @@ plugin = [
|
|||||||
"nu-protocol/plugin",
|
"nu-protocol/plugin",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
native-tls = ["nu-command/native-tls"]
|
||||||
|
rustls-tls = ["nu-command/rustls-tls"]
|
||||||
|
|
||||||
default = [
|
default = [
|
||||||
"plugin",
|
"plugin",
|
||||||
"trash-support",
|
"trash-support",
|
||||||
"sqlite",
|
"sqlite",
|
||||||
|
"rustls-tls"
|
||||||
]
|
]
|
||||||
stable = ["default"]
|
stable = ["default"]
|
||||||
# NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command
|
# NOTE: individual features are also passed to `nu-cmd-lang` that uses them to generate the feature matrix in the `version` command
|
||||||
|
|
||||||
# Enable to statically link OpenSSL (perl is required, to build OpenSSL https://docs.rs/openssl/latest/openssl/);
|
# Enable to statically link OpenSSL (perl is required, to build OpenSSL https://docs.rs/openssl/latest/openssl/);
|
||||||
# otherwise the system version will be used. Not enabled by default because it takes a while to build
|
# otherwise the system version will be used. Not enabled by default because it takes a while to build
|
||||||
static-link-openssl = ["dep:openssl", "nu-cmd-lang/static-link-openssl"]
|
static-link-openssl = ["dep:openssl"]
|
||||||
|
|
||||||
# Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems.
|
# Optional system clipboard support in `reedline`, this behavior has problematic compatibility with some systems.
|
||||||
# Missing X server/ Wayland can cause issues
|
# Missing X server/ Wayland can cause issues
|
||||||
system-clipboard = [
|
system-clipboard = [
|
||||||
"reedline/system_clipboard",
|
"reedline/system_clipboard",
|
||||||
"nu-cli/system-clipboard",
|
"nu-cli/system-clipboard",
|
||||||
"nu-cmd-lang/system-clipboard",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Stable (Default)
|
# Stable (Default)
|
||||||
trash-support = ["nu-command/trash-support", "nu-cmd-lang/trash-support"]
|
trash-support = ["nu-command/trash-support"]
|
||||||
|
|
||||||
# SQLite commands for nushell
|
# SQLite commands for nushell
|
||||||
sqlite = ["nu-command/sqlite", "nu-cmd-lang/sqlite", "nu-std/sqlite"]
|
sqlite = ["nu-command/sqlite", "nu-std/sqlite"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "s" # Optimize for size
|
opt-level = "s" # Optimize for size
|
||||||
@ -323,7 +337,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`
|
||||||
|
@ -222,6 +222,7 @@ Please submit an issue or PR to be added to this list.
|
|||||||
- [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)
|
- [x-cmd](https://x-cmd.com/mod/nu)
|
||||||
|
- [vfox](https://github.com/version-fox/vfox)
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ use nu_cli::{eval_source, evaluate_commands};
|
|||||||
use nu_plugin_core::{Encoder, EncodingType};
|
use nu_plugin_core::{Encoder, EncodingType};
|
||||||
use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
|
use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
|
||||||
PipelineData, Signals, Span, Spanned, Value,
|
PipelineData, Signals, Span, Spanned, Value,
|
||||||
|
engine::{EngineState, Stack},
|
||||||
};
|
};
|
||||||
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};
|
||||||
@ -11,9 +11,9 @@ use std::{
|
|||||||
fmt::Write,
|
fmt::Write,
|
||||||
hint::black_box,
|
hint::black_box,
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{atomic::AtomicBool, Arc},
|
sync::{Arc, atomic::AtomicBool},
|
||||||
};
|
};
|
||||||
use tango_bench::{benchmark_fn, tango_benchmarks, tango_main, IntoBenchmarks};
|
use tango_bench::{IntoBenchmarks, benchmark_fn, tango_benchmarks, tango_main};
|
||||||
|
|
||||||
fn load_bench_commands() -> EngineState {
|
fn load_bench_commands() -> 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())
|
||||||
@ -68,14 +68,14 @@ fn encoding_test_data(row_cnt: usize, col_cnt: usize) -> Value {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn bench_command(
|
fn bench_command(
|
||||||
name: &str,
|
name: impl Into<String>,
|
||||||
command: &str,
|
command: impl Into<String> + Clone,
|
||||||
stack: Stack,
|
stack: Stack,
|
||||||
engine: EngineState,
|
engine: EngineState,
|
||||||
) -> impl IntoBenchmarks {
|
) -> impl IntoBenchmarks {
|
||||||
let commands = Spanned {
|
let commands = Spanned {
|
||||||
span: Span::unknown(),
|
span: Span::unknown(),
|
||||||
item: command.to_string(),
|
item: command.into(),
|
||||||
};
|
};
|
||||||
[benchmark_fn(name, move |b| {
|
[benchmark_fn(name, move |b| {
|
||||||
let commands = commands.clone();
|
let commands = commands.clone();
|
||||||
@ -175,8 +175,8 @@ fn create_example_table_nrows(n: usize) -> String {
|
|||||||
|
|
||||||
fn bench_record_create(n: usize) -> impl IntoBenchmarks {
|
fn bench_record_create(n: usize) -> impl IntoBenchmarks {
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("record_create_{n}"),
|
format!("record_create_{n}"),
|
||||||
&create_flat_record_string(n),
|
create_flat_record_string(n),
|
||||||
Stack::new(),
|
Stack::new(),
|
||||||
setup_engine(),
|
setup_engine(),
|
||||||
)
|
)
|
||||||
@ -186,7 +186,7 @@ fn bench_record_flat_access(n: usize) -> impl IntoBenchmarks {
|
|||||||
let setup_command = create_flat_record_string(n);
|
let setup_command = create_flat_record_string(n);
|
||||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("record_flat_access_{n}"),
|
format!("record_flat_access_{n}"),
|
||||||
"$record.col_0 | ignore",
|
"$record.col_0 | ignore",
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
@ -198,8 +198,8 @@ fn bench_record_nested_access(n: usize) -> impl IntoBenchmarks {
|
|||||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||||
let nested_access = ".col".repeat(n);
|
let nested_access = ".col".repeat(n);
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("record_nested_access_{n}"),
|
format!("record_nested_access_{n}"),
|
||||||
&format!("$record{} | ignore", nested_access),
|
format!("$record{nested_access} | ignore"),
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
@ -213,13 +213,13 @@ fn bench_record_insert(n: usize, m: usize) -> impl IntoBenchmarks {
|
|||||||
write!(insert, " | insert col_{i} {i}").unwrap();
|
write!(insert, " | insert col_{i} {i}").unwrap();
|
||||||
}
|
}
|
||||||
insert.push_str(" | ignore");
|
insert.push_str(" | ignore");
|
||||||
bench_command(&format!("record_insert_{n}_{m}"), &insert, stack, engine)
|
bench_command(format!("record_insert_{n}_{m}"), insert, stack, engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_table_create(n: usize) -> impl IntoBenchmarks {
|
fn bench_table_create(n: usize) -> impl IntoBenchmarks {
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("table_create_{n}"),
|
format!("table_create_{n}"),
|
||||||
&create_example_table_nrows(n),
|
create_example_table_nrows(n),
|
||||||
Stack::new(),
|
Stack::new(),
|
||||||
setup_engine(),
|
setup_engine(),
|
||||||
)
|
)
|
||||||
@ -229,7 +229,7 @@ fn bench_table_get(n: usize) -> impl IntoBenchmarks {
|
|||||||
let setup_command = create_example_table_nrows(n);
|
let setup_command = create_example_table_nrows(n);
|
||||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("table_get_{n}"),
|
format!("table_get_{n}"),
|
||||||
"$table | get bar | math sum | ignore",
|
"$table | get bar | math sum | ignore",
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
@ -240,7 +240,7 @@ fn bench_table_select(n: usize) -> impl IntoBenchmarks {
|
|||||||
let setup_command = create_example_table_nrows(n);
|
let setup_command = create_example_table_nrows(n);
|
||||||
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
let (stack, engine) = setup_stack_and_engine_from_command(&setup_command);
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("table_select_{n}"),
|
format!("table_select_{n}"),
|
||||||
"$table | select foo baz | ignore",
|
"$table | select foo baz | ignore",
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
@ -255,7 +255,7 @@ fn bench_table_insert_row(n: usize, m: usize) -> impl IntoBenchmarks {
|
|||||||
write!(insert, " | insert {i} {{ foo: 0, bar: 1, baz: {i} }}").unwrap();
|
write!(insert, " | insert {i} {{ foo: 0, bar: 1, baz: {i} }}").unwrap();
|
||||||
}
|
}
|
||||||
insert.push_str(" | ignore");
|
insert.push_str(" | ignore");
|
||||||
bench_command(&format!("table_insert_row_{n}_{m}"), &insert, stack, engine)
|
bench_command(format!("table_insert_row_{n}_{m}"), insert, stack, engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_table_insert_col(n: usize, m: usize) -> impl IntoBenchmarks {
|
fn bench_table_insert_col(n: usize, m: usize) -> impl IntoBenchmarks {
|
||||||
@ -266,15 +266,15 @@ fn bench_table_insert_col(n: usize, m: usize) -> impl IntoBenchmarks {
|
|||||||
write!(insert, " | insert col_{i} {i}").unwrap();
|
write!(insert, " | insert col_{i} {i}").unwrap();
|
||||||
}
|
}
|
||||||
insert.push_str(" | ignore");
|
insert.push_str(" | ignore");
|
||||||
bench_command(&format!("table_insert_col_{n}_{m}"), &insert, stack, engine)
|
bench_command(format!("table_insert_col_{n}_{m}"), insert, stack, engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bench_eval_interleave(n: usize) -> impl IntoBenchmarks {
|
fn bench_eval_interleave(n: usize) -> impl IntoBenchmarks {
|
||||||
let engine = setup_engine();
|
let engine = setup_engine();
|
||||||
let stack = Stack::new();
|
let stack = Stack::new();
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("eval_interleave_{n}"),
|
format!("eval_interleave_{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,
|
||||||
)
|
)
|
||||||
@ -285,8 +285,8 @@ fn bench_eval_interleave_with_interrupt(n: usize) -> impl IntoBenchmarks {
|
|||||||
engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
|
engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
|
||||||
let stack = Stack::new();
|
let stack = Stack::new();
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("eval_interleave_with_interrupt_{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,
|
||||||
)
|
)
|
||||||
@ -296,8 +296,8 @@ fn bench_eval_for(n: usize) -> impl IntoBenchmarks {
|
|||||||
let engine = setup_engine();
|
let engine = setup_engine();
|
||||||
let stack = Stack::new();
|
let stack = Stack::new();
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("eval_for_{n}"),
|
format!("eval_for_{n}"),
|
||||||
&format!("(for $x in (1..{n}) {{ 1 }}) | ignore"),
|
format!("(for $x in (1..{n}) {{ 1 }}) | ignore"),
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
@ -307,8 +307,8 @@ fn bench_eval_each(n: usize) -> impl IntoBenchmarks {
|
|||||||
let engine = setup_engine();
|
let engine = setup_engine();
|
||||||
let stack = Stack::new();
|
let stack = Stack::new();
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("eval_each_{n}"),
|
format!("eval_each_{n}"),
|
||||||
&format!("(1..{n}) | each {{|_| 1 }} | ignore"),
|
format!("(1..{n}) | each {{|_| 1 }} | ignore"),
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
@ -318,8 +318,8 @@ fn bench_eval_par_each(n: usize) -> impl IntoBenchmarks {
|
|||||||
let engine = setup_engine();
|
let engine = setup_engine();
|
||||||
let stack = Stack::new();
|
let stack = Stack::new();
|
||||||
bench_command(
|
bench_command(
|
||||||
&format!("eval_par_each_{n}"),
|
format!("eval_par_each_{n}"),
|
||||||
&format!("(1..{}) | par-each -t 2 {{|_| 1 }} | ignore", n),
|
format!("(1..{n}) | par-each -t 2 {{|_| 1 }} | ignore"),
|
||||||
stack,
|
stack,
|
||||||
engine,
|
engine,
|
||||||
)
|
)
|
||||||
@ -357,7 +357,7 @@ fn encode_json(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
|||||||
let encoder = Rc::new(EncodingType::try_from_bytes(b"json").unwrap());
|
let encoder = Rc::new(EncodingType::try_from_bytes(b"json").unwrap());
|
||||||
|
|
||||||
[benchmark_fn(
|
[benchmark_fn(
|
||||||
format!("encode_json_{}_{}", row_cnt, col_cnt),
|
format!("encode_json_{row_cnt}_{col_cnt}"),
|
||||||
move |b| {
|
move |b| {
|
||||||
let encoder = encoder.clone();
|
let encoder = encoder.clone();
|
||||||
let test_data = test_data.clone();
|
let test_data = test_data.clone();
|
||||||
@ -377,7 +377,7 @@ fn encode_msgpack(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
|||||||
let encoder = Rc::new(EncodingType::try_from_bytes(b"msgpack").unwrap());
|
let encoder = Rc::new(EncodingType::try_from_bytes(b"msgpack").unwrap());
|
||||||
|
|
||||||
[benchmark_fn(
|
[benchmark_fn(
|
||||||
format!("encode_msgpack_{}_{}", row_cnt, col_cnt),
|
format!("encode_msgpack_{row_cnt}_{col_cnt}"),
|
||||||
move |b| {
|
move |b| {
|
||||||
let encoder = encoder.clone();
|
let encoder = encoder.clone();
|
||||||
let test_data = test_data.clone();
|
let test_data = test_data.clone();
|
||||||
@ -399,7 +399,7 @@ fn decode_json(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
|||||||
encoder.encode(&test_data, &mut res).unwrap();
|
encoder.encode(&test_data, &mut res).unwrap();
|
||||||
|
|
||||||
[benchmark_fn(
|
[benchmark_fn(
|
||||||
format!("decode_json_{}_{}", row_cnt, col_cnt),
|
format!("decode_json_{row_cnt}_{col_cnt}"),
|
||||||
move |b| {
|
move |b| {
|
||||||
let res = res.clone();
|
let res = res.clone();
|
||||||
b.iter(move || {
|
b.iter(move || {
|
||||||
@ -422,7 +422,7 @@ fn decode_msgpack(row_cnt: usize, col_cnt: usize) -> impl IntoBenchmarks {
|
|||||||
encoder.encode(&test_data, &mut res).unwrap();
|
encoder.encode(&test_data, &mut res).unwrap();
|
||||||
|
|
||||||
[benchmark_fn(
|
[benchmark_fn(
|
||||||
format!("decode_msgpack_{}_{}", row_cnt, col_cnt),
|
format!("decode_msgpack_{row_cnt}_{col_cnt}"),
|
||||||
move |b| {
|
move |b| {
|
||||||
let res = res.clone();
|
let res = res.clone();
|
||||||
b.iter(move || {
|
b.iter(move || {
|
||||||
|
@ -2,31 +2,32 @@
|
|||||||
authors = ["The Nushell Project Developers"]
|
authors = ["The Nushell Project Developers"]
|
||||||
description = "CLI-related functionality for Nushell"
|
description = "CLI-related functionality for Nushell"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.103.0"
|
version = "0.105.2"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.103.0" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.105.2" }
|
||||||
nu-command = { path = "../nu-command", version = "0.103.0" }
|
nu-command = { path = "../nu-command", version = "0.105.2" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.103.0" }
|
nu-std = { path = "../nu-std", version = "0.105.2" }
|
||||||
|
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }
|
||||||
rstest = { workspace = true, default-features = false }
|
rstest = { workspace = true, default-features = false }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.103.0" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.103.0", features = ["os"] }
|
nu-engine = { path = "../nu-engine", version = "0.105.2", features = ["os"] }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.103.0" }
|
nu-glob = { path = "../nu-glob", version = "0.105.2" }
|
||||||
nu-path = { path = "../nu-path", version = "0.103.0" }
|
nu-path = { path = "../nu-path", version = "0.105.2" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.103.0" }
|
nu-parser = { path = "../nu-parser", version = "0.105.2" }
|
||||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.103.0", optional = true }
|
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.105.2", optional = true }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.103.0", features = ["os"] }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", features = ["os"] }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.103.0" }
|
nu-utils = { path = "../nu-utils", version = "0.105.2" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.103.0" }
|
nu-color-config = { path = "../nu-color-config", version = "0.105.2" }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{shell_error::io::IoError, HistoryFileFormat};
|
use nu_protocol::{
|
||||||
|
HistoryFileFormat,
|
||||||
|
shell_error::{self, io::IoError},
|
||||||
|
};
|
||||||
use reedline::{
|
use reedline::{
|
||||||
FileBackedHistory, History as ReedlineHistory, HistoryItem, SearchDirection, SearchQuery,
|
FileBackedHistory, History as ReedlineHistory, HistoryItem, SearchDirection, SearchQuery,
|
||||||
SqliteBackedHistory,
|
SqliteBackedHistory,
|
||||||
@ -94,7 +97,7 @@ impl Command for History {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.ok_or(IoError::new(
|
.ok_or(IoError::new(
|
||||||
std::io::ErrorKind::NotFound,
|
shell_error::io::ErrorKind::FileNotFound,
|
||||||
head,
|
head,
|
||||||
history_path,
|
history_path,
|
||||||
))?
|
))?
|
||||||
@ -105,13 +108,12 @@ impl Command for History {
|
|||||||
.ok()
|
.ok()
|
||||||
})
|
})
|
||||||
.map(move |entries| {
|
.map(move |entries| {
|
||||||
entries
|
entries.into_iter().enumerate().map(move |(idx, entry)| {
|
||||||
.into_iter()
|
create_sqlite_history_record(idx, entry, long, head)
|
||||||
.enumerate()
|
})
|
||||||
.map(move |(idx, entry)| create_history_record(idx, entry, long, head))
|
|
||||||
})
|
})
|
||||||
.ok_or(IoError::new(
|
.ok_or(IoError::new(
|
||||||
std::io::ErrorKind::NotFound,
|
shell_error::io::ErrorKind::FileNotFound,
|
||||||
head,
|
head,
|
||||||
history_path,
|
history_path,
|
||||||
))?
|
))?
|
||||||
@ -140,7 +142,7 @@ impl Command for History {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span) -> Value {
|
fn create_sqlite_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span) -> Value {
|
||||||
//1. Format all the values
|
//1. Format all the values
|
||||||
//2. Create a record of either short or long columns and values
|
//2. Create a record of either short or long columns and values
|
||||||
|
|
||||||
@ -151,11 +153,8 @@ fn create_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span)
|
|||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
head,
|
head,
|
||||||
);
|
);
|
||||||
let start_timestamp_value = Value::string(
|
let start_timestamp_value = Value::date(
|
||||||
entry
|
entry.start_timestamp.unwrap_or_default().fixed_offset(),
|
||||||
.start_timestamp
|
|
||||||
.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);
|
||||||
|
@ -2,8 +2,8 @@ use std::path::{Path, PathBuf};
|
|||||||
|
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
shell_error::{self, io::IoError},
|
|
||||||
HistoryFileFormat,
|
HistoryFileFormat,
|
||||||
|
shell_error::{self, io::IoError},
|
||||||
};
|
};
|
||||||
|
|
||||||
use reedline::{
|
use reedline::{
|
||||||
@ -26,7 +26,7 @@ impl Command for HistoryImport {
|
|||||||
|
|
||||||
fn extra_description(&self) -> &str {
|
fn extra_description(&self) -> &str {
|
||||||
r#"Can import history from input, either successive command lines or more detailed records. If providing records, available fields are:
|
r#"Can import history from input, either successive command lines or more detailed records. If providing records, available fields are:
|
||||||
command_line, id, start_timestamp, hostname, cwd, duration, exit_status.
|
command, start_timestamp, hostname, cwd, duration, exit_status.
|
||||||
|
|
||||||
If no input is provided, will import all history items from existing history in the other format: if current history is stored in sqlite, it will store it in plain text and vice versa.
|
If no input is provided, will import all history items from existing history in the other format: if current history is stored in sqlite, it will store it in plain text and vice versa.
|
||||||
|
|
||||||
@ -48,8 +48,7 @@ Note that history item IDs are ignored when importing from file."#
|
|||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
example: "history import",
|
example: "history import",
|
||||||
description:
|
description: "Append all items from history in the other format to the current history",
|
||||||
"Append all items from history in the other format to the current history",
|
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
@ -198,7 +197,7 @@ fn item_from_record(mut rec: Record, span: Span) -> Result<HistoryItem, ShellErr
|
|||||||
return Err(ShellError::TypeMismatch {
|
return Err(ShellError::TypeMismatch {
|
||||||
err_message: format!("missing column: {}", fields::COMMAND_LINE),
|
err_message: format!("missing column: {}", fields::COMMAND_LINE),
|
||||||
span,
|
span,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -283,22 +282,22 @@ fn backup(path: &Path, span: Span) -> Result<Option<PathBuf>, ShellError> {
|
|||||||
PathBuf::from(path),
|
PathBuf::from(path),
|
||||||
"history path exists but is not a file",
|
"history path exists but is not a file",
|
||||||
)
|
)
|
||||||
.into())
|
.into());
|
||||||
}
|
}
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
|
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(None),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(IoError::new_internal(
|
return Err(IoError::new_internal(
|
||||||
e.kind(),
|
e,
|
||||||
"Could not get metadata",
|
"Could not get metadata",
|
||||||
nu_protocol::location!(),
|
nu_protocol::location!(),
|
||||||
)
|
)
|
||||||
.into())
|
.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let bak_path = find_backup_path(path, span)?;
|
let bak_path = find_backup_path(path, span)?;
|
||||||
std::fs::copy(path, &bak_path).map_err(|err| {
|
std::fs::copy(path, &bak_path).map_err(|err| {
|
||||||
IoError::new_internal(
|
IoError::new_internal(
|
||||||
err.kind(),
|
err.not_found_as(NotFound::File),
|
||||||
"Could not copy backup",
|
"Could not copy backup",
|
||||||
nu_protocol::location!(),
|
nu_protocol::location!(),
|
||||||
)
|
)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crossterm::{
|
use crossterm::{
|
||||||
event::Event, event::KeyCode, event::KeyEvent, execute, terminal, QueueableCommand,
|
QueueableCommand, event::Event, event::KeyCode, event::KeyEvent, execute, terminal,
|
||||||
};
|
};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::shell_error::io::IoError;
|
use nu_protocol::shell_error::io::IoError;
|
||||||
use std::io::{stdout, Write};
|
use std::io::{Write, stdout};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct KeybindingsListen;
|
pub struct KeybindingsListen;
|
||||||
@ -42,7 +42,7 @@ impl Command for KeybindingsListen {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
terminal::disable_raw_mode().map_err(|err| {
|
terminal::disable_raw_mode().map_err(|err| {
|
||||||
IoError::new_internal(
|
IoError::new_internal(
|
||||||
err.kind(),
|
err,
|
||||||
"Could not disable raw mode",
|
"Could not disable raw mode",
|
||||||
nu_protocol::location!(),
|
nu_protocol::location!(),
|
||||||
)
|
)
|
||||||
@ -71,18 +71,10 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
|||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
|
|
||||||
stdout().flush().map_err(|err| {
|
stdout().flush().map_err(|err| {
|
||||||
IoError::new_internal(
|
IoError::new_internal(err, "Could not flush stdout", nu_protocol::location!())
|
||||||
err.kind(),
|
|
||||||
"Could not flush stdout",
|
|
||||||
nu_protocol::location!(),
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
terminal::enable_raw_mode().map_err(|err| {
|
terminal::enable_raw_mode().map_err(|err| {
|
||||||
IoError::new_internal(
|
IoError::new_internal(err, "Could not enable raw mode", nu_protocol::location!())
|
||||||
err.kind(),
|
|
||||||
"Could not enable raw mode",
|
|
||||||
nu_protocol::location!(),
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if config.use_kitty_protocol {
|
if config.use_kitty_protocol {
|
||||||
@ -114,7 +106,7 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
let event = crossterm::event::read().map_err(|err| {
|
let event = crossterm::event::read().map_err(|err| {
|
||||||
IoError::new_internal(err.kind(), "Could not read event", nu_protocol::location!())
|
IoError::new_internal(err, "Could not read event", nu_protocol::location!())
|
||||||
})?;
|
})?;
|
||||||
if event == Event::Key(KeyCode::Esc.into()) {
|
if event == Event::Key(KeyCode::Esc.into()) {
|
||||||
break;
|
break;
|
||||||
@ -136,7 +128,7 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
|||||||
};
|
};
|
||||||
stdout.queue(crossterm::style::Print(o)).map_err(|err| {
|
stdout.queue(crossterm::style::Print(o)).map_err(|err| {
|
||||||
IoError::new_internal(
|
IoError::new_internal(
|
||||||
err.kind(),
|
err,
|
||||||
"Could not print output record",
|
"Could not print output record",
|
||||||
nu_protocol::location!(),
|
nu_protocol::location!(),
|
||||||
)
|
)
|
||||||
@ -144,14 +136,10 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
|||||||
stdout
|
stdout
|
||||||
.queue(crossterm::style::Print("\r\n"))
|
.queue(crossterm::style::Print("\r\n"))
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
IoError::new_internal(
|
IoError::new_internal(err, "Could not print linebreak", nu_protocol::location!())
|
||||||
err.kind(),
|
|
||||||
"Could not print linebreak",
|
|
||||||
nu_protocol::location!(),
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
stdout.flush().map_err(|err| {
|
stdout.flush().map_err(|err| {
|
||||||
IoError::new_internal(err.kind(), "Could not flush", nu_protocol::location!())
|
IoError::new_internal(err, "Could not flush", nu_protocol::location!())
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,11 +151,7 @@ pub fn print_events(engine_state: &EngineState) -> Result<Value, ShellError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
terminal::disable_raw_mode().map_err(|err| {
|
terminal::disable_raw_mode().map_err(|err| {
|
||||||
IoError::new_internal(
|
IoError::new_internal(err, "Could not disable raw mode", nu_protocol::location!())
|
||||||
err.kind(),
|
|
||||||
"Could not disable raw mode",
|
|
||||||
nu_protocol::location!(),
|
|
||||||
)
|
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(Value::nothing(Span::unknown()))
|
Ok(Value::nothing(Span::unknown()))
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use super::{completion_options::NuMatcher, SemanticSuggestion};
|
use super::{SemanticSuggestion, completion_options::NuMatcher};
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::{Completer, CompletionOptions},
|
|
||||||
SuggestionKind,
|
SuggestionKind,
|
||||||
|
completions::{Completer, CompletionOptions},
|
||||||
};
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Stack, StateWorkingSet},
|
|
||||||
Span,
|
Span,
|
||||||
|
engine::{Stack, StateWorkingSet},
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
@ -27,21 +27,20 @@ impl Completer for AttributeCompletion {
|
|||||||
let attr_commands =
|
let attr_commands =
|
||||||
working_set.find_commands_by_predicate(|s| s.starts_with(b"attr "), true);
|
working_set.find_commands_by_predicate(|s| s.starts_with(b"attr "), true);
|
||||||
|
|
||||||
for (name, desc, ty) in attr_commands {
|
for (decl_id, name, desc, ty) in attr_commands {
|
||||||
let name = name.strip_prefix(b"attr ").unwrap_or(&name);
|
let name = name.strip_prefix(b"attr ").unwrap_or(&name);
|
||||||
matcher.add_semantic_suggestion(SemanticSuggestion {
|
matcher.add_semantic_suggestion(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(name).into_owned(),
|
value: String::from_utf8_lossy(name).into_owned(),
|
||||||
description: desc,
|
description: desc,
|
||||||
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,
|
append_whitespace: false,
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Command(ty)),
|
kind: Some(SuggestionKind::Command(ty, Some(decl_id))),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,15 +69,14 @@ impl Completer for AttributableCompletion {
|
|||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: cmd.name().into(),
|
value: cmd.name().into(),
|
||||||
description: Some(cmd.description().into()),
|
description: Some(cmd.description().into()),
|
||||||
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,
|
append_whitespace: false,
|
||||||
|
..Default::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Command(cmd.command_type())),
|
kind: Some(SuggestionKind::Command(cmd.command_type(), None)),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::completions::CompletionOptions;
|
use crate::completions::CompletionOptions;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
DeclId, Span,
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
Span,
|
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ pub struct SemanticSuggestion {
|
|||||||
// TODO: think about name: maybe suggestion context?
|
// TODO: think about name: maybe suggestion context?
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum SuggestionKind {
|
pub enum SuggestionKind {
|
||||||
Command(nu_protocol::engine::CommandType),
|
Command(nu_protocol::engine::CommandType, Option<DeclId>),
|
||||||
Value(nu_protocol::Type),
|
Value(nu_protocol::Type),
|
||||||
CellPath,
|
CellPath,
|
||||||
Directory,
|
Directory,
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
|
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
|
||||||
use nu_engine::{column::get_columns, eval_variable};
|
use nu_engine::{column::get_columns, eval_variable};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
ShellError, Span, Value,
|
||||||
ast::{Expr, Expression, FullCellPath, PathMember},
|
ast::{Expr, Expression, FullCellPath, PathMember},
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
eval_const::eval_constant,
|
eval_const::eval_constant,
|
||||||
ShellError, Span, Value,
|
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
@ -17,14 +19,14 @@ pub struct CellPathCompletion<'a> {
|
|||||||
|
|
||||||
fn prefix_from_path_member(member: &PathMember, pos: usize) -> (String, Span) {
|
fn prefix_from_path_member(member: &PathMember, pos: usize) -> (String, Span) {
|
||||||
let (prefix_str, start) = match member {
|
let (prefix_str, start) = match member {
|
||||||
PathMember::String { val, span, .. } => (val.clone(), span.start),
|
PathMember::String { val, span, .. } => (val, span.start),
|
||||||
PathMember::Int { val, span, .. } => (val.to_string(), span.start),
|
PathMember::Int { val, span, .. } => (&val.to_string(), span.start),
|
||||||
};
|
};
|
||||||
let prefix_str = prefix_str
|
let prefix_str = prefix_str.get(..pos + 1 - start).unwrap_or(prefix_str);
|
||||||
.get(..pos + 1 - start)
|
// strip wrapping quotes
|
||||||
.map(str::to_string)
|
let quotations = ['"', '\'', '`'];
|
||||||
.unwrap_or(prefix_str);
|
let prefix_str = prefix_str.strip_prefix(quotations).unwrap_or(prefix_str);
|
||||||
(prefix_str, Span::new(start, pos + 1))
|
(prefix_str.to_string(), Span::new(start, pos + 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Completer for CellPathCompletion<'_> {
|
impl Completer for CellPathCompletion<'_> {
|
||||||
@ -101,21 +103,35 @@ pub(crate) fn eval_cell_path(
|
|||||||
} else {
|
} else {
|
||||||
eval_constant(working_set, head)
|
eval_constant(working_set, head)
|
||||||
}?;
|
}?;
|
||||||
head_value.follow_cell_path(path_members, false)
|
head_value
|
||||||
|
.follow_cell_path(path_members)
|
||||||
|
.map(Cow::into_owned)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_suggestions_by_value(
|
fn get_suggestions_by_value(
|
||||||
value: &Value,
|
value: &Value,
|
||||||
current_span: reedline::Span,
|
current_span: reedline::Span,
|
||||||
) -> Vec<SemanticSuggestion> {
|
) -> Vec<SemanticSuggestion> {
|
||||||
let to_suggestion = |s: String, v: Option<&Value>| SemanticSuggestion {
|
let to_suggestion = |s: String, v: Option<&Value>| {
|
||||||
suggestion: Suggestion {
|
// Check if the string needs quoting
|
||||||
value: s,
|
let value = if s.is_empty()
|
||||||
span: current_span,
|
|| s.chars()
|
||||||
description: v.map(|v| v.get_type().to_string()),
|
.any(|c: char| !(c.is_ascii_alphabetic() || ['_', '-'].contains(&c)))
|
||||||
..Suggestion::default()
|
{
|
||||||
},
|
format!("{s:?}")
|
||||||
kind: Some(SuggestionKind::CellPath),
|
} else {
|
||||||
|
s
|
||||||
|
};
|
||||||
|
|
||||||
|
SemanticSuggestion {
|
||||||
|
suggestion: Suggestion {
|
||||||
|
value,
|
||||||
|
span: current_span,
|
||||||
|
description: v.map(|v| v.get_type().to_string()),
|
||||||
|
..Suggestion::default()
|
||||||
|
},
|
||||||
|
kind: Some(SuggestionKind::CellPath),
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match value {
|
match value {
|
||||||
Value::Record { val, .. } => val
|
Value::Record { val, .. } => val
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::{Completer, CompletionOptions},
|
|
||||||
SuggestionKind,
|
SuggestionKind,
|
||||||
|
completions::{Completer, CompletionOptions},
|
||||||
};
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{CommandType, Stack, StateWorkingSet},
|
|
||||||
Span,
|
Span,
|
||||||
|
engine::{CommandType, Stack, StateWorkingSet},
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
use super::{completion_options::NuMatcher, SemanticSuggestion};
|
use super::{SemanticSuggestion, completion_options::NuMatcher};
|
||||||
|
|
||||||
pub struct CommandCompletion {
|
pub struct CommandCompletion {
|
||||||
/// Whether to include internal commands
|
/// Whether to include internal commands
|
||||||
@ -52,7 +52,7 @@ impl CommandCompletion {
|
|||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
let value = if matched_internal(&name) {
|
let value = if matched_internal(&name) {
|
||||||
format!("^{}", name)
|
format!("^{name}")
|
||||||
} else {
|
} else {
|
||||||
name.clone()
|
name.clone()
|
||||||
};
|
};
|
||||||
@ -75,7 +75,10 @@ impl CommandCompletion {
|
|||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Command(CommandType::External)),
|
kind: Some(SuggestionKind::Command(
|
||||||
|
CommandType::External,
|
||||||
|
None,
|
||||||
|
)),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -112,7 +115,7 @@ impl Completer for CommandCompletion {
|
|||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
for (name, description, typ) in filtered_commands {
|
for (decl_id, name, description, typ) in filtered_commands {
|
||||||
let name = String::from_utf8_lossy(&name);
|
let name = String::from_utf8_lossy(&name);
|
||||||
internal_suggs.insert(
|
internal_suggs.insert(
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
@ -124,7 +127,7 @@ impl Completer for CommandCompletion {
|
|||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
..Suggestion::default()
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Command(typ)),
|
kind: Some(SuggestionKind::Command(typ, Some(decl_id))),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
AttributableCompletion, AttributeCompletion, CellPathCompletion, CommandCompletion, Completer,
|
AttributableCompletion, AttributeCompletion, CellPathCompletion, CommandCompletion, Completer,
|
||||||
CompletionOptions, CustomCompletion, DirectoryCompletion, DotNuCompletion, FileCompletion,
|
CompletionOptions, CustomCompletion, DirectoryCompletion, DotNuCompletion,
|
||||||
FlagCompletion, OperatorCompletion, VariableCompletion,
|
ExportableCompletion, FileCompletion, FlagCompletion, OperatorCompletion, VariableCompletion,
|
||||||
|
base::{SemanticSuggestion, SuggestionKind},
|
||||||
};
|
};
|
||||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::{flatten_expression, parse};
|
use nu_parser::{flatten_expression, parse, parse_module_file_or_dir};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Argument, Block, Expr, Expression, FindMapResult, Traverse},
|
PipelineData, Span, Type, Value,
|
||||||
|
ast::{Argument, Block, Expr, Expression, FindMapResult, ListItem, Traverse},
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
||||||
PipelineData, Span, Type, Value,
|
|
||||||
};
|
};
|
||||||
use reedline::{Completer as ReedlineCompleter, Suggestion};
|
use reedline::{Completer as ReedlineCompleter, Suggestion};
|
||||||
use std::{str, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::base::{SemanticSuggestion, SuggestionKind};
|
|
||||||
|
|
||||||
/// Used as the function `f` in find_map Traverse
|
/// Used as the function `f` in find_map Traverse
|
||||||
///
|
///
|
||||||
@ -57,8 +56,13 @@ fn find_pipeline_element_by_position<'a>(
|
|||||||
Expr::FullCellPath(fcp) => fcp
|
Expr::FullCellPath(fcp) => fcp
|
||||||
.head
|
.head
|
||||||
.find_map(working_set, &closure)
|
.find_map(working_set, &closure)
|
||||||
.or(Some(expr))
|
|
||||||
.map(FindMapResult::Found)
|
.map(FindMapResult::Found)
|
||||||
|
// e.g. use std/util [<tab>
|
||||||
|
.or_else(|| {
|
||||||
|
(fcp.head.span.contains(pos) && matches!(fcp.head.expr, Expr::List(_)))
|
||||||
|
.then_some(FindMapResult::Continue)
|
||||||
|
})
|
||||||
|
.or(Some(FindMapResult::Found(expr)))
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
Expr::Var(_) => FindMapResult::Found(expr),
|
Expr::Var(_) => FindMapResult::Found(expr),
|
||||||
Expr::AttributeBlock(ab) => ab
|
Expr::AttributeBlock(ab) => ab
|
||||||
@ -127,6 +131,18 @@ struct Context<'a> {
|
|||||||
offset: usize,
|
offset: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// For argument completion
|
||||||
|
struct PositionalArguments<'a> {
|
||||||
|
/// command name
|
||||||
|
command_head: &'a str,
|
||||||
|
/// indices of positional arguments
|
||||||
|
positional_arg_indices: Vec<usize>,
|
||||||
|
/// argument list
|
||||||
|
arguments: &'a [Argument],
|
||||||
|
/// expression of current argument
|
||||||
|
expr: &'a Expression,
|
||||||
|
}
|
||||||
|
|
||||||
impl Context<'_> {
|
impl Context<'_> {
|
||||||
fn new<'a>(
|
fn new<'a>(
|
||||||
working_set: &'a StateWorkingSet,
|
working_set: &'a StateWorkingSet,
|
||||||
@ -160,7 +176,7 @@ impl NuCompleter {
|
|||||||
&mut working_set,
|
&mut working_set,
|
||||||
Some("completer"),
|
Some("completer"),
|
||||||
// Add a placeholder `a` to the end
|
// Add a placeholder `a` to the end
|
||||||
format!("{}a", line).as_bytes(),
|
format!("{line}a").as_bytes(),
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
self.fetch_completions_by_block(block, &working_set, pos, offset, line, true)
|
self.fetch_completions_by_block(block, &working_set, pos, offset, line, true)
|
||||||
@ -328,7 +344,8 @@ impl NuCompleter {
|
|||||||
// NOTE: the argument to complete is not necessarily the last one
|
// NOTE: the argument to complete is not necessarily the last one
|
||||||
// for lsp completion, we don't trim the text,
|
// for lsp completion, we don't trim the text,
|
||||||
// so that `def`s after pos can be completed
|
// so that `def`s after pos can be completed
|
||||||
for arg in call.arguments.iter() {
|
let mut positional_arg_indices = Vec::new();
|
||||||
|
for (arg_idx, arg) in call.arguments.iter().enumerate() {
|
||||||
let span = arg.span();
|
let span = arg.span();
|
||||||
if span.contains(pos) {
|
if span.contains(pos) {
|
||||||
// if customized completion specified, it has highest priority
|
// if customized completion specified, it has highest priority
|
||||||
@ -378,10 +395,16 @@ impl NuCompleter {
|
|||||||
Argument::Positional(_) if prefix == b"-" => flag_completion_helper(),
|
Argument::Positional(_) if prefix == b"-" => flag_completion_helper(),
|
||||||
// complete according to expression type and command head
|
// complete according to expression type and command head
|
||||||
Argument::Positional(expr) => {
|
Argument::Positional(expr) => {
|
||||||
let command_head = working_set.get_span_contents(call.head);
|
let command_head = working_set.get_decl(call.decl_id).name();
|
||||||
|
positional_arg_indices.push(arg_idx);
|
||||||
self.argument_completion_helper(
|
self.argument_completion_helper(
|
||||||
command_head,
|
PositionalArguments {
|
||||||
expr,
|
command_head,
|
||||||
|
positional_arg_indices,
|
||||||
|
arguments: &call.arguments,
|
||||||
|
expr,
|
||||||
|
},
|
||||||
|
pos,
|
||||||
&ctx,
|
&ctx,
|
||||||
suggestions.is_empty(),
|
suggestions.is_empty(),
|
||||||
)
|
)
|
||||||
@ -389,6 +412,8 @@ impl NuCompleter {
|
|||||||
_ => vec![],
|
_ => vec![],
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
} else if !matches!(arg, Argument::Named(_)) {
|
||||||
|
positional_arg_indices.push(arg_idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -441,6 +466,14 @@ impl NuCompleter {
|
|||||||
return suggestions;
|
return suggestions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// for external path arguments with spaces, please check issue #15790
|
||||||
|
if suggestions.is_empty() {
|
||||||
|
let (new_span, prefix) =
|
||||||
|
strip_placeholder_if_any(working_set, &span, strip);
|
||||||
|
let ctx = Context::new(working_set, new_span, prefix, offset);
|
||||||
|
suggestions.extend(self.process_completion(&mut FileCompletion, &ctx));
|
||||||
|
return suggestions;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -486,9 +519,10 @@ impl NuCompleter {
|
|||||||
externals: bool,
|
externals: bool,
|
||||||
strip: bool,
|
strip: bool,
|
||||||
) -> Vec<SemanticSuggestion> {
|
) -> Vec<SemanticSuggestion> {
|
||||||
|
let config = self.engine_state.get_config();
|
||||||
let mut command_completions = CommandCompletion {
|
let mut command_completions = CommandCompletion {
|
||||||
internals,
|
internals,
|
||||||
externals,
|
externals: !internals || (externals && config.completions.external.enable),
|
||||||
};
|
};
|
||||||
let (new_span, prefix) = strip_placeholder_if_any(working_set, &span, strip);
|
let (new_span, prefix) = strip_placeholder_if_any(working_set, &span, strip);
|
||||||
let ctx = Context::new(working_set, new_span, prefix, offset);
|
let ctx = Context::new(working_set, new_span, prefix, offset);
|
||||||
@ -497,20 +531,97 @@ impl NuCompleter {
|
|||||||
|
|
||||||
fn argument_completion_helper(
|
fn argument_completion_helper(
|
||||||
&self,
|
&self,
|
||||||
command_head: &[u8],
|
argument_info: PositionalArguments,
|
||||||
expr: &Expression,
|
pos: usize,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
need_fallback: bool,
|
need_fallback: bool,
|
||||||
) -> Vec<SemanticSuggestion> {
|
) -> Vec<SemanticSuggestion> {
|
||||||
|
let PositionalArguments {
|
||||||
|
command_head,
|
||||||
|
positional_arg_indices,
|
||||||
|
arguments,
|
||||||
|
expr,
|
||||||
|
} = argument_info;
|
||||||
// special commands
|
// special commands
|
||||||
match command_head {
|
match command_head {
|
||||||
// complete module file/directory
|
// complete module file/directory
|
||||||
// TODO: if module file already specified,
|
"use" | "export use" | "overlay use" | "source-env"
|
||||||
// should parse it to get modules/commands/consts to complete
|
if positional_arg_indices.len() == 1 =>
|
||||||
b"use" | b"export use" | b"overlay use" | b"source-env" => {
|
{
|
||||||
return self.process_completion(&mut DotNuCompletion, ctx);
|
return self.process_completion(
|
||||||
|
&mut DotNuCompletion {
|
||||||
|
std_virtual_path: command_head != "source-env",
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
b"which" => {
|
// NOTE: if module file already specified,
|
||||||
|
// should parse it to get modules/commands/consts to complete
|
||||||
|
"use" | "export use" => {
|
||||||
|
let Some(Argument::Positional(Expression {
|
||||||
|
expr: Expr::String(module_name),
|
||||||
|
span,
|
||||||
|
..
|
||||||
|
})) = positional_arg_indices
|
||||||
|
.first()
|
||||||
|
.and_then(|i| arguments.get(*i))
|
||||||
|
else {
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
let module_name = module_name.as_bytes();
|
||||||
|
let (module_id, temp_working_set) = match ctx.working_set.find_module(module_name) {
|
||||||
|
Some(module_id) => (module_id, None),
|
||||||
|
None => {
|
||||||
|
let mut temp_working_set =
|
||||||
|
StateWorkingSet::new(ctx.working_set.permanent_state);
|
||||||
|
let Some(module_id) = parse_module_file_or_dir(
|
||||||
|
&mut temp_working_set,
|
||||||
|
module_name,
|
||||||
|
*span,
|
||||||
|
None,
|
||||||
|
) else {
|
||||||
|
return vec![];
|
||||||
|
};
|
||||||
|
(module_id, Some(temp_working_set))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut exportable_completion = ExportableCompletion {
|
||||||
|
module_id,
|
||||||
|
temp_working_set,
|
||||||
|
};
|
||||||
|
let mut complete_on_list_items = |items: &[ListItem]| -> Vec<SemanticSuggestion> {
|
||||||
|
for item in items {
|
||||||
|
let span = item.expr().span;
|
||||||
|
if span.contains(pos) {
|
||||||
|
let offset = span.start.saturating_sub(ctx.span.start);
|
||||||
|
let end_offset =
|
||||||
|
ctx.prefix.len().min(pos.min(span.end) - ctx.span.start + 1);
|
||||||
|
let new_ctx = Context::new(
|
||||||
|
ctx.working_set,
|
||||||
|
Span::new(span.start, ctx.span.end.min(span.end)),
|
||||||
|
ctx.prefix.get(offset..end_offset).unwrap_or_default(),
|
||||||
|
ctx.offset,
|
||||||
|
);
|
||||||
|
return self.process_completion(&mut exportable_completion, &new_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
|
||||||
|
match &expr.expr {
|
||||||
|
Expr::String(_) => {
|
||||||
|
return self.process_completion(&mut exportable_completion, ctx);
|
||||||
|
}
|
||||||
|
Expr::FullCellPath(fcp) => match &fcp.head.expr {
|
||||||
|
Expr::List(items) => {
|
||||||
|
return complete_on_list_items(items);
|
||||||
|
}
|
||||||
|
_ => return vec![],
|
||||||
|
},
|
||||||
|
_ => return vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"which" => {
|
||||||
let mut completer = CommandCompletion {
|
let mut completer = CommandCompletion {
|
||||||
internals: true,
|
internals: true,
|
||||||
externals: true,
|
externals: true,
|
||||||
@ -543,7 +654,6 @@ impl NuCompleter {
|
|||||||
case_sensitive: config.completions.case_sensitive,
|
case_sensitive: config.completions.case_sensitive,
|
||||||
match_algorithm: config.completions.algorithm.into(),
|
match_algorithm: config.completions.algorithm.into(),
|
||||||
sort: config.completions.sort,
|
sort: config.completions.sort,
|
||||||
..Default::default()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
completer.fetch(
|
completer.fetch(
|
||||||
@ -740,7 +850,7 @@ mod completer_tests {
|
|||||||
for (line, has_result, begins_with, expected_values) in dataset {
|
for (line, has_result, begins_with, expected_values) in dataset {
|
||||||
let result = completer.fetch_completions_at(line, line.len());
|
let result = completer.fetch_completions_at(line, line.len());
|
||||||
// Test whether the result is empty or not
|
// Test whether the result is empty or not
|
||||||
assert_eq!(!result.is_empty(), has_result, "line: {}", line);
|
assert_eq!(!result.is_empty(), has_result, "line: {line}");
|
||||||
|
|
||||||
// Test whether the result begins with the expected value
|
// Test whether the result begins with the expected value
|
||||||
result
|
result
|
||||||
@ -755,8 +865,7 @@ mod completer_tests {
|
|||||||
.filter(|x| *x)
|
.filter(|x| *x)
|
||||||
.count(),
|
.count(),
|
||||||
expected_values.len(),
|
expected_values.len(),
|
||||||
"line: {}",
|
"line: {line}"
|
||||||
line
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,16 @@
|
|||||||
use super::{completion_options::NuMatcher, MatchAlgorithm};
|
use super::{MatchAlgorithm, completion_options::NuMatcher};
|
||||||
use crate::completions::CompletionOptions;
|
use crate::completions::CompletionOptions;
|
||||||
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::dots::expand_ndots;
|
use nu_path::dots::expand_ndots;
|
||||||
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},
|
|
||||||
Span,
|
Span,
|
||||||
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
};
|
};
|
||||||
use nu_utils::get_ls_colors;
|
|
||||||
use nu_utils::IgnoreCaseExt;
|
use nu_utils::IgnoreCaseExt;
|
||||||
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
|
use nu_utils::get_ls_colors;
|
||||||
|
use std::path::{Component, MAIN_SEPARATOR as SEP, Path, PathBuf, is_separator};
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct PathBuiltFromString {
|
pub struct PathBuiltFromString {
|
||||||
@ -22,21 +22,27 @@ pub struct PathBuiltFromString {
|
|||||||
/// Recursively goes through paths that match a given `partial`.
|
/// Recursively goes through paths that match a given `partial`.
|
||||||
/// built: State struct for a valid matching path built so far.
|
/// built: State struct for a valid matching path built so far.
|
||||||
///
|
///
|
||||||
|
/// `want_directory`: Whether we want only directories as completion matches.
|
||||||
|
/// Some commands like `cd` can only be run on directories whereas others
|
||||||
|
/// like `ls` can be run on regular files as well.
|
||||||
|
///
|
||||||
/// `isdir`: whether the current partial path has a trailing slash.
|
/// `isdir`: whether the current partial path has a trailing slash.
|
||||||
/// Parsing a path string into a pathbuf loses that bit of information.
|
/// Parsing a path string into a pathbuf loses that bit of information.
|
||||||
///
|
///
|
||||||
/// want_directory: Whether we want only directories as completion matches.
|
/// `enable_exact_match`: Whether match algorithm is Prefix and all previous components
|
||||||
/// Some commands like `cd` can only be run on directories whereas others
|
/// of the path matched a directory exactly.
|
||||||
/// like `ls` can be run on regular files as well.
|
|
||||||
fn complete_rec(
|
fn complete_rec(
|
||||||
partial: &[&str],
|
partial: &[&str],
|
||||||
built_paths: &[PathBuiltFromString],
|
built_paths: &[PathBuiltFromString],
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
want_directory: bool,
|
want_directory: bool,
|
||||||
isdir: bool,
|
isdir: bool,
|
||||||
|
enable_exact_match: bool,
|
||||||
) -> Vec<PathBuiltFromString> {
|
) -> Vec<PathBuiltFromString> {
|
||||||
|
let has_more = !partial.is_empty() && (partial.len() > 1 || isdir);
|
||||||
|
|
||||||
if let Some((&base, rest)) = partial.split_first() {
|
if let Some((&base, rest)) = partial.split_first() {
|
||||||
if base.chars().all(|c| c == '.') && (isdir || !rest.is_empty()) {
|
if base.chars().all(|c| c == '.') && has_more {
|
||||||
let built_paths: Vec<_> = built_paths
|
let built_paths: Vec<_> = built_paths
|
||||||
.iter()
|
.iter()
|
||||||
.map(|built| {
|
.map(|built| {
|
||||||
@ -46,13 +52,23 @@ fn complete_rec(
|
|||||||
built
|
built
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
return complete_rec(rest, &built_paths, options, want_directory, isdir);
|
return complete_rec(
|
||||||
|
rest,
|
||||||
|
&built_paths,
|
||||||
|
options,
|
||||||
|
want_directory,
|
||||||
|
isdir,
|
||||||
|
enable_exact_match,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let prefix = partial.first().unwrap_or(&"");
|
let prefix = partial.first().unwrap_or(&"");
|
||||||
let mut matcher = NuMatcher::new(prefix, options);
|
let mut matcher = NuMatcher::new(prefix, options);
|
||||||
|
|
||||||
|
let mut exact_match = None;
|
||||||
|
// Only relevant for case insensitive matching
|
||||||
|
let mut multiple_exact_matches = false;
|
||||||
for built in built_paths {
|
for built in built_paths {
|
||||||
let mut path = built.cwd.clone();
|
let mut path = built.cwd.clone();
|
||||||
for part in &built.parts {
|
for part in &built.parts {
|
||||||
@ -72,49 +88,56 @@ fn complete_rec(
|
|||||||
built.isdir = entry_isdir && !entry.path().is_symlink();
|
built.isdir = entry_isdir && !entry.path().is_symlink();
|
||||||
|
|
||||||
if !want_directory || entry_isdir {
|
if !want_directory || entry_isdir {
|
||||||
matcher.add(entry_name.clone(), (entry_name, built));
|
if enable_exact_match && !multiple_exact_matches && has_more {
|
||||||
}
|
let matches = if options.case_sensitive {
|
||||||
}
|
entry_name.eq(prefix)
|
||||||
}
|
|
||||||
|
|
||||||
let mut completions = vec![];
|
|
||||||
for (entry_name, built) in matcher.results() {
|
|
||||||
match partial.split_first() {
|
|
||||||
Some((base, rest)) => {
|
|
||||||
// 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],
|
|
||||||
options,
|
|
||||||
want_directory,
|
|
||||||
isdir,
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
completions.push(built);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For https://github.com/nushell/nushell/issues/13204
|
|
||||||
if isdir && options.match_algorithm == MatchAlgorithm::Prefix {
|
|
||||||
let exact_match = if options.case_sensitive {
|
|
||||||
entry_name.eq(base)
|
|
||||||
} else {
|
} else {
|
||||||
entry_name.to_folded_case().eq(&base.to_folded_case())
|
entry_name.eq_ignore_case(prefix)
|
||||||
};
|
};
|
||||||
if exact_match {
|
if matches {
|
||||||
break;
|
if exact_match.is_none() {
|
||||||
|
exact_match = Some(built.clone());
|
||||||
|
} else {
|
||||||
|
multiple_exact_matches = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None => {
|
matcher.add(entry_name, built);
|
||||||
completions.push(built);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
completions
|
|
||||||
|
// Don't show longer completions if we have a single exact match (#13204, #14794)
|
||||||
|
if !multiple_exact_matches {
|
||||||
|
if let Some(built) = exact_match {
|
||||||
|
return complete_rec(
|
||||||
|
&partial[1..],
|
||||||
|
&[built],
|
||||||
|
options,
|
||||||
|
want_directory,
|
||||||
|
isdir,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_more {
|
||||||
|
let mut completions = vec![];
|
||||||
|
for built in matcher.results() {
|
||||||
|
completions.extend(complete_rec(
|
||||||
|
&partial[1..],
|
||||||
|
&[built],
|
||||||
|
options,
|
||||||
|
want_directory,
|
||||||
|
isdir,
|
||||||
|
false,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
completions
|
||||||
|
} else {
|
||||||
|
matcher.results()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -140,7 +163,7 @@ impl OriginalCwd {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn surround_remove(partial: &str) -> String {
|
pub fn surround_remove(partial: &str) -> String {
|
||||||
for c in ['`', '"', '\''] {
|
for c in ['`', '"', '\''] {
|
||||||
if partial.starts_with(c) {
|
if partial.starts_with(c) {
|
||||||
let ret = partial.strip_prefix(c).unwrap_or(partial);
|
let ret = partial.strip_prefix(c).unwrap_or(partial);
|
||||||
@ -199,10 +222,9 @@ pub fn complete_item(
|
|||||||
let ls_colors = (engine_state.config.completions.use_ls_colors
|
let ls_colors = (engine_state.config.completions.use_ls_colors
|
||||||
&& engine_state.config.use_ansi_coloring.get(engine_state))
|
&& engine_state.config.use_ansi_coloring.get(engine_state))
|
||||||
.then(|| {
|
.then(|| {
|
||||||
let ls_colors_env_str = match stack.get_env_var(engine_state, "LS_COLORS") {
|
let ls_colors_env_str = stack
|
||||||
Some(v) => env_to_string("LS_COLORS", v, engine_state, stack).ok(),
|
.get_env_var(engine_state, "LS_COLORS")
|
||||||
None => None,
|
.and_then(|v| env_to_string("LS_COLORS", v, engine_state, stack).ok());
|
||||||
};
|
|
||||||
get_ls_colors(ls_colors_env_str)
|
get_ls_colors(ls_colors_env_str)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -256,6 +278,7 @@ pub fn complete_item(
|
|||||||
options,
|
options,
|
||||||
want_directory,
|
want_directory,
|
||||||
isdir,
|
isdir,
|
||||||
|
options.match_algorithm == MatchAlgorithm::Prefix,
|
||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|mut p| {
|
.map(|mut p| {
|
||||||
@ -264,15 +287,12 @@ pub fn complete_item(
|
|||||||
}
|
}
|
||||||
let is_dir = p.isdir;
|
let is_dir = p.isdir;
|
||||||
let path = original_cwd.apply(p, path_separator);
|
let path = original_cwd.apply(p, path_separator);
|
||||||
|
let real_path = expand_to_real_path(&path);
|
||||||
|
let metadata = std::fs::symlink_metadata(&real_path).ok();
|
||||||
let style = ls_colors.as_ref().map(|lsc| {
|
let style = ls_colors.as_ref().map(|lsc| {
|
||||||
lsc.style_for_path_with_metadata(
|
lsc.style_for_path_with_metadata(&real_path, metadata.as_ref())
|
||||||
&path,
|
.map(lscolors::Style::to_nu_ansi_term_style)
|
||||||
std::fs::symlink_metadata(expand_to_real_path(&path))
|
.unwrap_or_default()
|
||||||
.ok()
|
|
||||||
.as_ref(),
|
|
||||||
)
|
|
||||||
.map(lscolors::Style::to_nu_ansi_term_style)
|
|
||||||
.unwrap_or_default()
|
|
||||||
});
|
});
|
||||||
FileSuggestion {
|
FileSuggestion {
|
||||||
span,
|
span,
|
||||||
@ -294,7 +314,7 @@ pub fn escape_path(path: String) -> String {
|
|||||||
if path.contains('\'') {
|
if path.contains('\'') {
|
||||||
// decide to use double quotes
|
// decide to use double quotes
|
||||||
// Path as Debug will do the escaping for `"`, `\`
|
// Path as Debug will do the escaping for `"`, `\`
|
||||||
format!("{:?}", path)
|
format!("{path:?}")
|
||||||
} else {
|
} else {
|
||||||
format!("'{path}'")
|
format!("'{path}'")
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ use nu_parser::trim_quotes_str;
|
|||||||
use nu_protocol::{CompletionAlgorithm, CompletionSort};
|
use nu_protocol::{CompletionAlgorithm, CompletionSort};
|
||||||
use nu_utils::IgnoreCaseExt;
|
use nu_utils::IgnoreCaseExt;
|
||||||
use nucleo_matcher::{
|
use nucleo_matcher::{
|
||||||
pattern::{Atom, AtomKind, CaseMatching, Normalization},
|
|
||||||
Config, Matcher, Utf32Str,
|
Config, Matcher, Utf32Str,
|
||||||
|
pattern::{Atom, AtomKind, CaseMatching, Normalization},
|
||||||
};
|
};
|
||||||
use std::{borrow::Cow, fmt::Display};
|
use std::{borrow::Cow, fmt::Display};
|
||||||
|
|
||||||
@ -18,6 +18,12 @@ pub enum MatchAlgorithm {
|
|||||||
/// "git switch" is matched by "git sw"
|
/// "git switch" is matched by "git sw"
|
||||||
Prefix,
|
Prefix,
|
||||||
|
|
||||||
|
/// Only show suggestions which have a substring matching with the given input
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// "git checkout" is matched by "checkout"
|
||||||
|
Substring,
|
||||||
|
|
||||||
/// Only show suggestions which contain the input chars at any place
|
/// Only show suggestions which contain the input chars at any place
|
||||||
///
|
///
|
||||||
/// Example:
|
/// Example:
|
||||||
@ -36,6 +42,10 @@ enum State<T> {
|
|||||||
/// Holds (haystack, item)
|
/// Holds (haystack, item)
|
||||||
items: Vec<(String, T)>,
|
items: Vec<(String, T)>,
|
||||||
},
|
},
|
||||||
|
Substring {
|
||||||
|
/// Holds (haystack, item)
|
||||||
|
items: Vec<(String, T)>,
|
||||||
|
},
|
||||||
Fuzzy {
|
Fuzzy {
|
||||||
matcher: Matcher,
|
matcher: Matcher,
|
||||||
atom: Atom,
|
atom: Atom,
|
||||||
@ -64,6 +74,18 @@ impl<T> NuMatcher<'_, T> {
|
|||||||
state: State::Prefix { items: Vec::new() },
|
state: State::Prefix { items: Vec::new() },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
MatchAlgorithm::Substring => {
|
||||||
|
let lowercase_needle = if options.case_sensitive {
|
||||||
|
needle.to_owned()
|
||||||
|
} else {
|
||||||
|
needle.to_folded_case()
|
||||||
|
};
|
||||||
|
NuMatcher {
|
||||||
|
options,
|
||||||
|
needle: lowercase_needle,
|
||||||
|
state: State::Substring { items: Vec::new() },
|
||||||
|
}
|
||||||
|
}
|
||||||
MatchAlgorithm::Fuzzy => {
|
MatchAlgorithm::Fuzzy => {
|
||||||
let atom = Atom::new(
|
let atom = Atom::new(
|
||||||
needle,
|
needle,
|
||||||
@ -102,11 +124,21 @@ impl<T> NuMatcher<'_, T> {
|
|||||||
} else {
|
} else {
|
||||||
Cow::Owned(haystack.to_folded_case())
|
Cow::Owned(haystack.to_folded_case())
|
||||||
};
|
};
|
||||||
let matches = if self.options.positional {
|
let matches = haystack_folded.starts_with(self.needle.as_str());
|
||||||
haystack_folded.starts_with(self.needle.as_str())
|
if matches {
|
||||||
|
if let Some(item) = item {
|
||||||
|
items.push((haystack.to_string(), item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
matches
|
||||||
|
}
|
||||||
|
State::Substring { items } => {
|
||||||
|
let haystack_folded = if self.options.case_sensitive {
|
||||||
|
Cow::Borrowed(haystack)
|
||||||
} else {
|
} else {
|
||||||
haystack_folded.contains(self.needle.as_str())
|
Cow::Owned(haystack.to_folded_case())
|
||||||
};
|
};
|
||||||
|
let matches = haystack_folded.contains(self.needle.as_str());
|
||||||
if matches {
|
if matches {
|
||||||
if let Some(item) = item {
|
if let Some(item) = item {
|
||||||
items.push((haystack.to_string(), item));
|
items.push((haystack.to_string(), item));
|
||||||
@ -148,7 +180,7 @@ impl<T> NuMatcher<'_, T> {
|
|||||||
/// Get all the items that matched (sorted)
|
/// Get all the items that matched (sorted)
|
||||||
pub fn results(self) -> Vec<T> {
|
pub fn results(self) -> Vec<T> {
|
||||||
match self.state {
|
match self.state {
|
||||||
State::Prefix { mut items, .. } => {
|
State::Prefix { mut items, .. } | State::Substring { mut items, .. } => {
|
||||||
items.sort_by(|(haystack1, _), (haystack2, _)| {
|
items.sort_by(|(haystack1, _), (haystack2, _)| {
|
||||||
let cmp_sensitive = haystack1.cmp(haystack2);
|
let cmp_sensitive = haystack1.cmp(haystack2);
|
||||||
if self.options.case_sensitive {
|
if self.options.case_sensitive {
|
||||||
@ -195,6 +227,7 @@ impl From<CompletionAlgorithm> for MatchAlgorithm {
|
|||||||
fn from(value: CompletionAlgorithm) -> Self {
|
fn from(value: CompletionAlgorithm) -> Self {
|
||||||
match value {
|
match value {
|
||||||
CompletionAlgorithm::Prefix => MatchAlgorithm::Prefix,
|
CompletionAlgorithm::Prefix => MatchAlgorithm::Prefix,
|
||||||
|
CompletionAlgorithm::Substring => MatchAlgorithm::Substring,
|
||||||
CompletionAlgorithm::Fuzzy => MatchAlgorithm::Fuzzy,
|
CompletionAlgorithm::Fuzzy => MatchAlgorithm::Fuzzy,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,6 +239,7 @@ impl TryFrom<String> for MatchAlgorithm {
|
|||||||
fn try_from(value: String) -> Result<Self, Self::Error> {
|
fn try_from(value: String) -> Result<Self, Self::Error> {
|
||||||
match value.as_str() {
|
match value.as_str() {
|
||||||
"prefix" => Ok(Self::Prefix),
|
"prefix" => Ok(Self::Prefix),
|
||||||
|
"substring" => Ok(Self::Substring),
|
||||||
"fuzzy" => Ok(Self::Fuzzy),
|
"fuzzy" => Ok(Self::Fuzzy),
|
||||||
_ => Err(InvalidMatchAlgorithm::Unknown),
|
_ => Err(InvalidMatchAlgorithm::Unknown),
|
||||||
}
|
}
|
||||||
@ -230,7 +264,6 @@ impl std::error::Error for InvalidMatchAlgorithm {}
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CompletionOptions {
|
pub struct CompletionOptions {
|
||||||
pub case_sensitive: bool,
|
pub case_sensitive: bool,
|
||||||
pub positional: bool,
|
|
||||||
pub match_algorithm: MatchAlgorithm,
|
pub match_algorithm: MatchAlgorithm,
|
||||||
pub sort: CompletionSort,
|
pub sort: CompletionSort,
|
||||||
}
|
}
|
||||||
@ -239,7 +272,6 @@ impl Default for CompletionOptions {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
case_sensitive: true,
|
case_sensitive: true,
|
||||||
positional: true,
|
|
||||||
match_algorithm: MatchAlgorithm::Prefix,
|
match_algorithm: MatchAlgorithm::Prefix,
|
||||||
sort: Default::default(),
|
sort: Default::default(),
|
||||||
}
|
}
|
||||||
@ -256,6 +288,9 @@ mod test {
|
|||||||
#[case(MatchAlgorithm::Prefix, "example text", "", true)]
|
#[case(MatchAlgorithm::Prefix, "example text", "", true)]
|
||||||
#[case(MatchAlgorithm::Prefix, "example text", "examp", true)]
|
#[case(MatchAlgorithm::Prefix, "example text", "examp", true)]
|
||||||
#[case(MatchAlgorithm::Prefix, "example text", "text", false)]
|
#[case(MatchAlgorithm::Prefix, "example text", "text", false)]
|
||||||
|
#[case(MatchAlgorithm::Substring, "example text", "", true)]
|
||||||
|
#[case(MatchAlgorithm::Substring, "example text", "text", true)]
|
||||||
|
#[case(MatchAlgorithm::Substring, "example text", "mplxt", false)]
|
||||||
#[case(MatchAlgorithm::Fuzzy, "example text", "", true)]
|
#[case(MatchAlgorithm::Fuzzy, "example text", "", true)]
|
||||||
#[case(MatchAlgorithm::Fuzzy, "example text", "examp", true)]
|
#[case(MatchAlgorithm::Fuzzy, "example text", "examp", true)]
|
||||||
#[case(MatchAlgorithm::Fuzzy, "example text", "ext", true)]
|
#[case(MatchAlgorithm::Fuzzy, "example text", "ext", true)]
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
completer::map_value_completions, Completer, CompletionOptions, SemanticSuggestion,
|
Completer, CompletionOptions, MatchAlgorithm, SemanticSuggestion,
|
||||||
|
completer::map_value_completions,
|
||||||
};
|
};
|
||||||
use nu_engine::eval_call;
|
use nu_engine::eval_call;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
DeclId, PipelineData, Span, Type, Value,
|
||||||
ast::{Argument, Call, Expr, Expression},
|
ast::{Argument, Call, Expr, Expression},
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
DeclId, PipelineData, Span, Type, Value,
|
|
||||||
};
|
};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
@ -42,28 +43,37 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
|||||||
) -> Vec<SemanticSuggestion> {
|
) -> Vec<SemanticSuggestion> {
|
||||||
// Call custom declaration
|
// Call custom declaration
|
||||||
let mut stack_mut = stack.clone();
|
let mut stack_mut = stack.clone();
|
||||||
let result = eval_call::<WithoutDebug>(
|
let mut eval = |engine_state: &EngineState| {
|
||||||
working_set.permanent_state,
|
eval_call::<WithoutDebug>(
|
||||||
&mut stack_mut,
|
engine_state,
|
||||||
&Call {
|
&mut stack_mut,
|
||||||
decl_id: self.decl_id,
|
&Call {
|
||||||
head: span,
|
decl_id: self.decl_id,
|
||||||
arguments: vec![
|
head: span,
|
||||||
Argument::Positional(Expression::new_unknown(
|
arguments: vec![
|
||||||
Expr::String(self.line.clone()),
|
Argument::Positional(Expression::new_unknown(
|
||||||
Span::unknown(),
|
Expr::String(self.line.clone()),
|
||||||
Type::String,
|
Span::unknown(),
|
||||||
)),
|
Type::String,
|
||||||
Argument::Positional(Expression::new_unknown(
|
)),
|
||||||
Expr::Int(self.line_pos as i64),
|
Argument::Positional(Expression::new_unknown(
|
||||||
Span::unknown(),
|
Expr::Int(self.line_pos as i64),
|
||||||
Type::Int,
|
Span::unknown(),
|
||||||
)),
|
Type::Int,
|
||||||
],
|
)),
|
||||||
parser_info: HashMap::new(),
|
],
|
||||||
},
|
parser_info: HashMap::new(),
|
||||||
PipelineData::empty(),
|
},
|
||||||
);
|
PipelineData::empty(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let result = if self.decl_id.get() < working_set.permanent_state.num_decls() {
|
||||||
|
eval(working_set.permanent_state)
|
||||||
|
} else {
|
||||||
|
let mut engine_state = working_set.permanent_state.clone();
|
||||||
|
let _ = engine_state.merge_delta(working_set.delta.clone());
|
||||||
|
eval(&engine_state)
|
||||||
|
};
|
||||||
|
|
||||||
let mut completion_options = orig_options.clone();
|
let mut completion_options = orig_options.clone();
|
||||||
let mut should_sort = true;
|
let mut should_sort = true;
|
||||||
@ -93,10 +103,12 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
|||||||
{
|
{
|
||||||
completion_options.case_sensitive = case_sensitive;
|
completion_options.case_sensitive = case_sensitive;
|
||||||
}
|
}
|
||||||
if let Some(positional) =
|
let positional =
|
||||||
options.get("positional").and_then(|val| val.as_bool().ok())
|
options.get("positional").and_then(|val| val.as_bool().ok());
|
||||||
{
|
if positional.is_some() {
|
||||||
completion_options.positional = positional;
|
log::warn!(
|
||||||
|
"Use of the positional option is deprecated. Use the substring match algorithm instead."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if let Some(algorithm) = options
|
if let Some(algorithm) = options
|
||||||
.get("completion_algorithm")
|
.get("completion_algorithm")
|
||||||
@ -104,6 +116,11 @@ impl<T: Completer> Completer for CustomCompletion<T> {
|
|||||||
.and_then(|option| option.try_into().ok())
|
.and_then(|option| option.try_into().ok())
|
||||||
{
|
{
|
||||||
completion_options.match_algorithm = algorithm;
|
completion_options.match_algorithm = algorithm;
|
||||||
|
if let Some(false) = positional {
|
||||||
|
if completion_options.match_algorithm == MatchAlgorithm::Prefix {
|
||||||
|
completion_options.match_algorithm = MatchAlgorithm::Substring
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
|
||||||
Completer, CompletionOptions,
|
Completer, CompletionOptions,
|
||||||
|
completion_common::{AdjustView, adjust_if_intermediate, complete_item},
|
||||||
};
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
|
||||||
Span,
|
Span,
|
||||||
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::{completion_common::FileSuggestion, SemanticSuggestion, SuggestionKind};
|
use super::{SemanticSuggestion, SuggestionKind, completion_common::FileSuggestion};
|
||||||
|
|
||||||
pub struct DirectoryCompletion;
|
pub struct DirectoryCompletion;
|
||||||
|
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
use crate::completions::{file_path_completion, Completer, CompletionOptions};
|
use crate::completions::{
|
||||||
|
Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||||
|
completion_common::{FileSuggestion, surround_remove},
|
||||||
|
completion_options::NuMatcher,
|
||||||
|
file_path_completion,
|
||||||
|
};
|
||||||
use nu_path::expand_tilde;
|
use nu_path::expand_tilde;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Stack, StateWorkingSet},
|
|
||||||
Span,
|
Span,
|
||||||
|
engine::{Stack, StateWorkingSet, VirtualPath},
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
path::{is_separator, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
|
path::{MAIN_SEPARATOR_STR, PathBuf, is_separator},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{SemanticSuggestion, SuggestionKind};
|
pub struct DotNuCompletion {
|
||||||
|
/// e.g. use std/a<tab>
|
||||||
pub struct DotNuCompletion;
|
pub std_virtual_path: bool,
|
||||||
|
}
|
||||||
|
|
||||||
impl Completer for DotNuCompletion {
|
impl Completer for DotNuCompletion {
|
||||||
fn fetch(
|
fn fetch(
|
||||||
@ -102,7 +108,7 @@ impl Completer for DotNuCompletion {
|
|||||||
|
|
||||||
// Fetch the files filtering the ones that ends with .nu
|
// Fetch the files filtering the ones that ends with .nu
|
||||||
// and transform them into suggestions
|
// and transform them into suggestions
|
||||||
let completions = file_path_completion(
|
let mut completions = file_path_completion(
|
||||||
span,
|
span,
|
||||||
partial,
|
partial,
|
||||||
&search_dirs
|
&search_dirs
|
||||||
@ -113,17 +119,60 @@ impl Completer for DotNuCompletion {
|
|||||||
working_set.permanent_state,
|
working_set.permanent_state,
|
||||||
stack,
|
stack,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if self.std_virtual_path {
|
||||||
|
let mut matcher = NuMatcher::new(partial, options);
|
||||||
|
let base_dir = surround_remove(&base_dir);
|
||||||
|
if base_dir == "." {
|
||||||
|
let surround_prefix = partial
|
||||||
|
.chars()
|
||||||
|
.take_while(|c| "`'\"".contains(*c))
|
||||||
|
.collect::<String>();
|
||||||
|
for path in ["std", "std-rfc"] {
|
||||||
|
let path = format!("{surround_prefix}{path}");
|
||||||
|
matcher.add(
|
||||||
|
path.clone(),
|
||||||
|
FileSuggestion {
|
||||||
|
span,
|
||||||
|
path,
|
||||||
|
style: None,
|
||||||
|
is_dir: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if let Some(VirtualPath::Dir(sub_paths)) =
|
||||||
|
working_set.find_virtual_path(&base_dir)
|
||||||
|
{
|
||||||
|
for sub_vp_id in sub_paths {
|
||||||
|
let (path, sub_vp) = working_set.get_virtual_path(*sub_vp_id);
|
||||||
|
let path = path
|
||||||
|
.strip_prefix(&format!("{base_dir}/"))
|
||||||
|
.unwrap_or(path)
|
||||||
|
.to_string();
|
||||||
|
matcher.add(
|
||||||
|
path.clone(),
|
||||||
|
FileSuggestion {
|
||||||
|
path,
|
||||||
|
span,
|
||||||
|
style: None,
|
||||||
|
is_dir: matches!(sub_vp, VirtualPath::Dir(_)),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
completions.extend(matcher.results());
|
||||||
|
}
|
||||||
|
|
||||||
completions
|
completions
|
||||||
.into_iter()
|
.into_iter()
|
||||||
// Different base dir, so we list the .nu files or folders
|
// Different base dir, so we list the .nu files or folders
|
||||||
.filter(|it| {
|
.filter(|it| {
|
||||||
// for paths with spaces in them
|
// for paths with spaces in them
|
||||||
let path = it.path.trim_end_matches('`');
|
let path = it.path.trim_end_matches('`');
|
||||||
path.ends_with(".nu") || path.ends_with(SEP)
|
path.ends_with(".nu") || it.is_dir
|
||||||
})
|
})
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
let append_whitespace =
|
let append_whitespace = !x.is_dir && (!start_with_backquote || end_with_backquote);
|
||||||
x.path.ends_with(".nu") && (!start_with_backquote || end_with_backquote);
|
|
||||||
// Re-calculate the span to replace
|
// Re-calculate the span to replace
|
||||||
let mut span_offset = 0;
|
let mut span_offset = 0;
|
||||||
let mut value = x.path.to_string();
|
let mut value = x.path.to_string();
|
||||||
|
112
crates/nu-cli/src/completions/exportable_completions.rs
Normal file
112
crates/nu-cli/src/completions/exportable_completions.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
use crate::completions::{
|
||||||
|
Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
||||||
|
completion_common::surround_remove, completion_options::NuMatcher,
|
||||||
|
};
|
||||||
|
use nu_protocol::{
|
||||||
|
ModuleId, Span,
|
||||||
|
engine::{Stack, StateWorkingSet},
|
||||||
|
};
|
||||||
|
use reedline::Suggestion;
|
||||||
|
|
||||||
|
pub struct ExportableCompletion<'a> {
|
||||||
|
pub module_id: ModuleId,
|
||||||
|
pub temp_working_set: Option<StateWorkingSet<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If name contains space, wrap it in quotes
|
||||||
|
fn wrapped_name(name: String) -> String {
|
||||||
|
if !name.contains(' ') {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
if name.contains('\'') {
|
||||||
|
format!("\"{}\"", name.replace('"', r#"\""#))
|
||||||
|
} else {
|
||||||
|
format!("'{name}'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Completer for ExportableCompletion<'_> {
|
||||||
|
fn fetch(
|
||||||
|
&mut self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
_stack: &Stack,
|
||||||
|
prefix: impl AsRef<str>,
|
||||||
|
span: Span,
|
||||||
|
offset: usize,
|
||||||
|
options: &CompletionOptions,
|
||||||
|
) -> Vec<SemanticSuggestion> {
|
||||||
|
let mut matcher = NuMatcher::<()>::new(surround_remove(prefix.as_ref()), options);
|
||||||
|
let mut results = Vec::new();
|
||||||
|
let span = reedline::Span {
|
||||||
|
start: span.start - offset,
|
||||||
|
end: span.end - offset,
|
||||||
|
};
|
||||||
|
// TODO: use matcher.add_lazy to lazy evaluate an item if it matches the prefix
|
||||||
|
let mut add_suggestion = |value: String,
|
||||||
|
description: Option<String>,
|
||||||
|
extra: Option<Vec<String>>,
|
||||||
|
kind: SuggestionKind| {
|
||||||
|
results.push(SemanticSuggestion {
|
||||||
|
suggestion: Suggestion {
|
||||||
|
value,
|
||||||
|
span,
|
||||||
|
description,
|
||||||
|
extra,
|
||||||
|
..Suggestion::default()
|
||||||
|
},
|
||||||
|
kind: Some(kind),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
let working_set = self.temp_working_set.as_ref().unwrap_or(working_set);
|
||||||
|
let module = working_set.get_module(self.module_id);
|
||||||
|
|
||||||
|
for (name, decl_id) in &module.decls {
|
||||||
|
let name = String::from_utf8_lossy(name).to_string();
|
||||||
|
if matcher.matches(&name) {
|
||||||
|
let cmd = working_set.get_decl(*decl_id);
|
||||||
|
add_suggestion(
|
||||||
|
wrapped_name(name),
|
||||||
|
Some(cmd.description().to_string()),
|
||||||
|
None,
|
||||||
|
// `None` here avoids arguments being expanded by snippet edit style for lsp
|
||||||
|
SuggestionKind::Command(cmd.command_type(), None),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (name, module_id) in &module.submodules {
|
||||||
|
let name = String::from_utf8_lossy(name).to_string();
|
||||||
|
if matcher.matches(&name) {
|
||||||
|
let comments = working_set.get_module_comments(*module_id).map(|spans| {
|
||||||
|
spans
|
||||||
|
.iter()
|
||||||
|
.map(|sp| {
|
||||||
|
String::from_utf8_lossy(working_set.get_span_contents(*sp)).into()
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
});
|
||||||
|
add_suggestion(
|
||||||
|
wrapped_name(name),
|
||||||
|
Some("Submodule".into()),
|
||||||
|
comments,
|
||||||
|
SuggestionKind::Module,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (name, var_id) in &module.constants {
|
||||||
|
let name = String::from_utf8_lossy(name).to_string();
|
||||||
|
if matcher.matches(&name) {
|
||||||
|
let var = working_set.get_variable(*var_id);
|
||||||
|
add_suggestion(
|
||||||
|
wrapped_name(name),
|
||||||
|
var.const_val
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|v| v.clone().coerce_into_string().ok()),
|
||||||
|
None,
|
||||||
|
SuggestionKind::Variable,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
results
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,15 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
|
|
||||||
Completer, CompletionOptions,
|
Completer, CompletionOptions,
|
||||||
|
completion_common::{AdjustView, adjust_if_intermediate, complete_item},
|
||||||
};
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
|
||||||
Span,
|
Span,
|
||||||
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use super::{completion_common::FileSuggestion, SemanticSuggestion, SuggestionKind};
|
use super::{SemanticSuggestion, SuggestionKind, completion_common::FileSuggestion};
|
||||||
|
|
||||||
pub struct FileCompletion;
|
pub struct FileCompletion;
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use crate::completions::{completion_options::NuMatcher, Completer, CompletionOptions};
|
use crate::completions::{
|
||||||
|
Completer, CompletionOptions, SemanticSuggestion, SuggestionKind, completion_options::NuMatcher,
|
||||||
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Stack, StateWorkingSet},
|
|
||||||
DeclId, Span,
|
DeclId, Span,
|
||||||
|
engine::{Stack, StateWorkingSet},
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
use super::{SemanticSuggestion, SuggestionKind};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FlagCompletion {
|
pub struct FlagCompletion {
|
||||||
pub decl_id: DeclId,
|
pub decl_id: DeclId,
|
||||||
|
@ -8,6 +8,7 @@ mod completion_options;
|
|||||||
mod custom_completions;
|
mod custom_completions;
|
||||||
mod directory_completions;
|
mod directory_completions;
|
||||||
mod dotnu_completions;
|
mod dotnu_completions;
|
||||||
|
mod exportable_completions;
|
||||||
mod file_completions;
|
mod file_completions;
|
||||||
mod flag_completions;
|
mod flag_completions;
|
||||||
mod operator_completions;
|
mod operator_completions;
|
||||||
@ -22,7 +23,8 @@ 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;
|
||||||
pub use file_completions::{file_path_completion, FileCompletion};
|
pub use exportable_completions::ExportableCompletion;
|
||||||
|
pub use file_completions::{FileCompletion, file_path_completion};
|
||||||
pub use flag_completions::FlagCompletion;
|
pub use flag_completions::FlagCompletion;
|
||||||
pub use operator_completions::OperatorCompletion;
|
pub use operator_completions::OperatorCompletion;
|
||||||
pub use variable_completions::VariableCompletion;
|
pub use variable_completions::VariableCompletion;
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
completion_options::NuMatcher, Completer, CompletionOptions, SemanticSuggestion, SuggestionKind,
|
Completer, CompletionOptions, SemanticSuggestion, SuggestionKind, completion_options::NuMatcher,
|
||||||
};
|
};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
ENV_VARIABLE_ID, Span, Type, Value,
|
||||||
ast::{self, Comparison, Expr, Expression},
|
ast::{self, Comparison, Expr, Expression},
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
Span, Type, Value, ENV_VARIABLE_ID,
|
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use strum::{EnumMessage, IntoEnumIterator};
|
use strum::{EnumMessage, IntoEnumIterator};
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
|
use crate::completions::{Completer, CompletionOptions, SemanticSuggestion, SuggestionKind};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Stack, StateWorkingSet},
|
|
||||||
Span, VarId,
|
Span, VarId,
|
||||||
|
engine::{Stack, StateWorkingSet},
|
||||||
};
|
};
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
|
|
||||||
|
@ -2,10 +2,11 @@ use crate::util::eval_source;
|
|||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_protocol::{engine::StateWorkingSet, ParseError, PluginRegistryFile, Spanned};
|
use nu_protocol::{ParseError, PluginRegistryFile, Spanned, engine::StateWorkingSet};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
PipelineData,
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
report_shell_error, PipelineData,
|
report_shell_error,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use nu_utils::perf;
|
use nu_utils::perf;
|
||||||
@ -18,7 +19,7 @@ const OLD_PLUGIN_FILE: &str = "plugin.nu";
|
|||||||
|
|
||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
pub fn read_plugin_file(engine_state: &mut EngineState, plugin_file: Option<Spanned<String>>) {
|
pub fn read_plugin_file(engine_state: &mut EngineState, plugin_file: Option<Spanned<String>>) {
|
||||||
use nu_protocol::{shell_error::io::IoError, ShellError};
|
use nu_protocol::{ShellError, shell_error::io::IoError};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
let span = plugin_file.as_ref().map(|s| s.span);
|
let span = plugin_file.as_ref().map(|s| s.span);
|
||||||
@ -79,7 +80,7 @@ pub fn read_plugin_file(engine_state: &mut EngineState, plugin_file: Option<Span
|
|||||||
report_shell_error(
|
report_shell_error(
|
||||||
engine_state,
|
engine_state,
|
||||||
&ShellError::Io(IoError::new_internal_with_path(
|
&ShellError::Io(IoError::new_internal_with_path(
|
||||||
err.kind(),
|
err,
|
||||||
"Could not open plugin registry file",
|
"Could not open plugin registry file",
|
||||||
nu_protocol::location!(),
|
nu_protocol::location!(),
|
||||||
plugin_path,
|
plugin_path,
|
||||||
@ -230,8 +231,8 @@ pub fn eval_config_contents(
|
|||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
pub fn migrate_old_plugin_file(engine_state: &EngineState) -> bool {
|
pub fn migrate_old_plugin_file(engine_state: &EngineState) -> bool {
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
shell_error::io::IoError, PluginExample, PluginIdentity, PluginRegistryItem,
|
PluginExample, PluginIdentity, PluginRegistryItem, PluginRegistryItemData, PluginSignature,
|
||||||
PluginRegistryItemData, PluginSignature, ShellError,
|
ShellError, shell_error::io::IoError,
|
||||||
};
|
};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
@ -322,7 +323,7 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState) -> bool {
|
|||||||
if let Err(err) = std::fs::File::create(&new_plugin_file_path)
|
if let Err(err) = std::fs::File::create(&new_plugin_file_path)
|
||||||
.map_err(|err| {
|
.map_err(|err| {
|
||||||
IoError::new_internal_with_path(
|
IoError::new_internal_with_path(
|
||||||
err.kind(),
|
err,
|
||||||
"Could not create new plugin file",
|
"Could not create new plugin file",
|
||||||
nu_protocol::location!(),
|
nu_protocol::location!(),
|
||||||
new_plugin_file_path.clone(),
|
new_plugin_file_path.clone(),
|
||||||
|
@ -2,10 +2,11 @@ use log::info;
|
|||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
PipelineData, ShellError, Spanned, Value,
|
||||||
cli_error::report_compile_error,
|
cli_error::report_compile_error,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_parse_error, report_parse_warning, PipelineData, ShellError, Spanned, Value,
|
report_parse_error, report_parse_warning,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -4,12 +4,12 @@ use nu_engine::eval_block;
|
|||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
PipelineData, ShellError, Span, Value,
|
||||||
cli_error::report_compile_error,
|
cli_error::report_compile_error,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_parse_error, report_parse_warning,
|
report_parse_error, report_parse_warning,
|
||||||
shell_error::io::*,
|
shell_error::io::*,
|
||||||
PipelineData, ShellError, Span, Value,
|
|
||||||
};
|
};
|
||||||
use std::{path::PathBuf, sync::Arc};
|
use std::{path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ pub fn evaluate_file(
|
|||||||
|
|
||||||
let file_path = canonicalize_with(&path, cwd).map_err(|err| {
|
let file_path = canonicalize_with(&path, cwd).map_err(|err| {
|
||||||
IoError::new_internal_with_path(
|
IoError::new_internal_with_path(
|
||||||
err.kind().not_found_as(NotFound::File),
|
err.not_found_as(NotFound::File),
|
||||||
"Could not access file",
|
"Could not access file",
|
||||||
nu_protocol::location!(),
|
nu_protocol::location!(),
|
||||||
PathBuf::from(&path),
|
PathBuf::from(&path),
|
||||||
@ -47,7 +47,7 @@ pub fn evaluate_file(
|
|||||||
|
|
||||||
let file = std::fs::read(&file_path).map_err(|err| {
|
let file = std::fs::read(&file_path).map_err(|err| {
|
||||||
IoError::new_internal_with_path(
|
IoError::new_internal_with_path(
|
||||||
err.kind().not_found_as(NotFound::File),
|
err.not_found_as(NotFound::File),
|
||||||
"Could not read file",
|
"Could not read file",
|
||||||
nu_protocol::location!(),
|
nu_protocol::location!(),
|
||||||
file_path.clone(),
|
file_path.clone(),
|
||||||
|
@ -18,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, EvaluateCommandsOpts};
|
pub use eval_cmds::{EvaluateCommandsOpts, evaluate_commands};
|
||||||
pub use eval_file::evaluate_file;
|
pub use eval_file::evaluate_file;
|
||||||
pub use menus::NuHelpCompleter;
|
pub use menus::NuHelpCompleter;
|
||||||
pub use nu_highlight::NuHighlight;
|
pub use nu_highlight::NuHighlight;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::documentation::{get_flags_section, HelpStyle};
|
use nu_engine::documentation::{FormatterValue, HelpStyle, get_flags_section};
|
||||||
use nu_protocol::{engine::EngineState, levenshtein_distance, Config};
|
use nu_protocol::{Config, engine::EngineState, levenshtein_distance};
|
||||||
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};
|
||||||
@ -66,8 +66,11 @@ 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(&sig, &help_style, |v| {
|
long_desc.push_str(&get_flags_section(&sig, &help_style, |v| match v {
|
||||||
v.to_parsable_string(", ", &self.config)
|
FormatterValue::DefaultValue(value) => {
|
||||||
|
value.to_parsable_string(", ", &self.config)
|
||||||
|
}
|
||||||
|
FormatterValue::CodeString(text) => text.to_string(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
BlockId, IntoPipelineData, Span, Value,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
BlockId, IntoPipelineData, Span, Value,
|
|
||||||
};
|
};
|
||||||
use reedline::{menu_functions::parse_selection_char, Completer, Suggestion};
|
use reedline::{Completer, Suggestion, menu_functions::parse_selection_char};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
const SELECTION_CHAR: char = '!';
|
const SELECTION_CHAR: char = '!';
|
||||||
|
@ -3,6 +3,8 @@ use std::sync::Arc;
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
|
|
||||||
|
use crate::syntax_highlight::highlight_syntax;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NuHighlight;
|
pub struct NuHighlight;
|
||||||
|
|
||||||
@ -14,6 +16,11 @@ impl Command for NuHighlight {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("nu-highlight")
|
Signature::build("nu-highlight")
|
||||||
.category(Category::Strings)
|
.category(Category::Strings)
|
||||||
|
.switch(
|
||||||
|
"reject-garbage",
|
||||||
|
"Return an error if invalid syntax (garbage) was encountered",
|
||||||
|
Some('r'),
|
||||||
|
)
|
||||||
.input_output_types(vec![(Type::String, Type::String)])
|
.input_output_types(vec![(Type::String, Type::String)])
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,19 +39,33 @@ impl Command for NuHighlight {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let reject_garbage = call.has_flag(engine_state, stack, "reject-garbage")?;
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
let signals = engine_state.signals();
|
let signals = engine_state.signals();
|
||||||
|
|
||||||
let highlighter = crate::NuHighlighter {
|
let engine_state = Arc::new(engine_state.clone());
|
||||||
engine_state: Arc::new(engine_state.clone()),
|
let stack = Arc::new(stack.clone());
|
||||||
stack: Arc::new(stack.clone()),
|
|
||||||
};
|
|
||||||
|
|
||||||
input.map(
|
input.map(
|
||||||
move |x| match x.coerce_into_string() {
|
move |x| match x.coerce_into_string() {
|
||||||
Ok(line) => {
|
Ok(line) => {
|
||||||
let highlights = highlighter.highlight(&line, line.len());
|
let result = highlight_syntax(&engine_state, &stack, &line, line.len());
|
||||||
|
|
||||||
|
let highlights = match (reject_garbage, result.found_garbage) {
|
||||||
|
(false, _) => result.text,
|
||||||
|
(true, None) => result.text,
|
||||||
|
(true, Some(span)) => {
|
||||||
|
let error = ShellError::OutsideSpannedLabeledError {
|
||||||
|
src: line,
|
||||||
|
error: "encountered invalid syntax while highlighting".into(),
|
||||||
|
msg: "invalid syntax".into(),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
return Value::error(error, head);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Value::string(highlights.render_simple(), head)
|
Value::string(highlights.render_simple(), head)
|
||||||
}
|
}
|
||||||
Err(err) => Value::error(err, head),
|
Err(err) => Value::error(err, head),
|
||||||
|
@ -2,8 +2,9 @@ use crate::NushellPrompt;
|
|||||||
use log::{trace, warn};
|
use log::{trace, warn};
|
||||||
use nu_engine::ClosureEvalOnce;
|
use nu_engine::ClosureEvalOnce;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
Config, PipelineData, Value,
|
||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
report_shell_error, Config, PipelineData, Value,
|
report_shell_error,
|
||||||
};
|
};
|
||||||
use reedline::Prompt;
|
use reedline::Prompt;
|
||||||
|
|
||||||
|
@ -1,19 +1,20 @@
|
|||||||
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
use crate::{NuHelpCompleter, menus::NuMenuCompleter};
|
||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
Config, EditBindings, FromValue, ParsedKeybinding, ParsedMenu, PipelineData, Record,
|
||||||
|
ShellError, Span, Type, Value,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
extract_value, Config, EditBindings, FromValue, ParsedKeybinding, ParsedMenu, PipelineData,
|
extract_value,
|
||||||
Record, ShellError, Span, Type, Value,
|
|
||||||
};
|
};
|
||||||
use reedline::{
|
use reedline::{
|
||||||
default_emacs_keybindings, default_vi_insert_keybindings, default_vi_normal_keybindings,
|
|
||||||
ColumnarMenu, DescriptionMenu, DescriptionMode, EditCommand, IdeMenu, Keybindings, ListMenu,
|
ColumnarMenu, DescriptionMenu, DescriptionMode, EditCommand, IdeMenu, Keybindings, ListMenu,
|
||||||
MenuBuilder, Reedline, ReedlineEvent, ReedlineMenu,
|
MenuBuilder, Reedline, ReedlineEvent, ReedlineMenu, default_emacs_keybindings,
|
||||||
|
default_vi_insert_keybindings, default_vi_normal_keybindings,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -6,12 +6,12 @@ use crate::prompt_update::{
|
|||||||
VSCODE_PRE_EXECUTION_MARKER,
|
VSCODE_PRE_EXECUTION_MARKER,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
NuHighlighter, NuValidator, NushellPrompt,
|
||||||
completions::NuCompleter,
|
completions::NuCompleter,
|
||||||
nu_highlight::NoOpHighlighter,
|
nu_highlight::NoOpHighlighter,
|
||||||
prompt_update,
|
prompt_update,
|
||||||
reedline_config::{add_menus, create_keybindings, KeybindingsMode},
|
reedline_config::{KeybindingsMode, add_menus, create_keybindings},
|
||||||
util::eval_source,
|
util::eval_source,
|
||||||
NuHighlighter, NuValidator, NushellPrompt,
|
|
||||||
};
|
};
|
||||||
use crossterm::cursor::SetCursorStyle;
|
use crossterm::cursor::SetCursorStyle;
|
||||||
use log::{error, trace, warn};
|
use log::{error, trace, warn};
|
||||||
@ -23,14 +23,15 @@ use nu_engine::env_to_strings;
|
|||||||
use nu_engine::exit::cleanup_exit;
|
use nu_engine::exit::cleanup_exit;
|
||||||
use nu_parser::{lex, parse, trim_quotes_str};
|
use nu_parser::{lex, parse, trim_quotes_str};
|
||||||
use nu_protocol::shell_error::io::IoError;
|
use nu_protocol::shell_error::io::IoError;
|
||||||
|
use nu_protocol::{BannerKind, shell_error};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, Value,
|
||||||
config::NuCursorShape,
|
config::NuCursorShape,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_shell_error, HistoryConfig, HistoryFileFormat, PipelineData, ShellError, Span, Spanned,
|
report_shell_error,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
use nu_utils::{
|
use nu_utils::{
|
||||||
filesystem::{have_permission, PermissionResult},
|
filesystem::{PermissionResult, have_permission},
|
||||||
perf,
|
perf,
|
||||||
};
|
};
|
||||||
use reedline::{
|
use reedline::{
|
||||||
@ -42,7 +43,7 @@ use std::{
|
|||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
env::temp_dir,
|
env::temp_dir,
|
||||||
io::{self, IsTerminal, Write},
|
io::{self, IsTerminal, Write},
|
||||||
panic::{catch_unwind, AssertUnwindSafe},
|
panic::{AssertUnwindSafe, catch_unwind},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
@ -144,8 +145,8 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
if load_std_lib.is_none() {
|
if load_std_lib.is_none() {
|
||||||
match engine_state.get_config().show_banner {
|
match engine_state.get_config().show_banner {
|
||||||
Value::Bool { val: false, .. } => {}
|
BannerKind::None => {}
|
||||||
Value::String { ref val, .. } if val == "short" => {
|
BannerKind::Short => {
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut unique_stack,
|
&mut unique_stack,
|
||||||
@ -155,7 +156,7 @@ pub fn evaluate_repl(
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
BannerKind::Full => {
|
||||||
eval_source(
|
eval_source(
|
||||||
engine_state,
|
engine_state,
|
||||||
&mut unique_stack,
|
&mut unique_stack,
|
||||||
@ -238,7 +239,7 @@ fn escape_special_vscode_bytes(input: &str) -> Result<String, ShellError> {
|
|||||||
|
|
||||||
match byte {
|
match byte {
|
||||||
// Escape bytes below 0x20
|
// Escape bytes below 0x20
|
||||||
b if b < 0x20 => format!("\\x{:02X}", byte).into_bytes(),
|
b if b < 0x20 => format!("\\x{byte:02X}").into_bytes(),
|
||||||
// Escape semicolon as \x3B
|
// Escape semicolon as \x3B
|
||||||
b';' => "\\x3B".to_string().into_bytes(),
|
b';' => "\\x3B".to_string().into_bytes(),
|
||||||
// Escape backslash as \\
|
// Escape backslash as \\
|
||||||
@ -854,7 +855,7 @@ fn do_auto_cd(
|
|||||||
report_shell_error(
|
report_shell_error(
|
||||||
engine_state,
|
engine_state,
|
||||||
&ShellError::Io(IoError::new_with_additional_context(
|
&ShellError::Io(IoError::new_with_additional_context(
|
||||||
std::io::ErrorKind::NotFound,
|
shell_error::io::ErrorKind::DirectoryNotFound,
|
||||||
span,
|
span,
|
||||||
PathBuf::from(&path),
|
PathBuf::from(&path),
|
||||||
"Cannot change directory",
|
"Cannot change directory",
|
||||||
@ -864,11 +865,11 @@ fn do_auto_cd(
|
|||||||
path.to_string_lossy().to_string()
|
path.to_string_lossy().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
if let PermissionResult::PermissionDenied(_) = have_permission(path.clone()) {
|
if let PermissionResult::PermissionDenied = have_permission(path.clone()) {
|
||||||
report_shell_error(
|
report_shell_error(
|
||||||
engine_state,
|
engine_state,
|
||||||
&ShellError::Io(IoError::new_with_additional_context(
|
&ShellError::Io(IoError::new_with_additional_context(
|
||||||
std::io::ErrorKind::PermissionDenied,
|
shell_error::io::ErrorKind::from_std(std::io::ErrorKind::PermissionDenied),
|
||||||
span,
|
span,
|
||||||
PathBuf::from(path),
|
PathBuf::from(path),
|
||||||
"Cannot change directory",
|
"Cannot change directory",
|
||||||
@ -1096,8 +1097,7 @@ fn run_shell_integration_osc633(
|
|||||||
// If we're in vscode, run their specific ansi escape sequence.
|
// If we're in vscode, run their specific ansi escape sequence.
|
||||||
// This is helpful for ctrl+g to change directories in the terminal.
|
// This is helpful for ctrl+g to change directories in the terminal.
|
||||||
run_ansi_sequence(&format!(
|
run_ansi_sequence(&format!(
|
||||||
"{}{}{}",
|
"{VSCODE_CWD_PROPERTY_MARKER_PREFIX}{path}{VSCODE_CWD_PROPERTY_MARKER_SUFFIX}"
|
||||||
VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX
|
|
||||||
));
|
));
|
||||||
|
|
||||||
perf!(
|
perf!(
|
||||||
@ -1113,10 +1113,7 @@ fn run_shell_integration_osc633(
|
|||||||
|
|
||||||
//OSC 633 ; E ; <commandline> [; <nonce] ST - Explicitly set the command line with an optional nonce.
|
//OSC 633 ; E ; <commandline> [; <nonce] ST - Explicitly set the command line with an optional nonce.
|
||||||
run_ansi_sequence(&format!(
|
run_ansi_sequence(&format!(
|
||||||
"{}{}{}",
|
"{VSCODE_COMMANDLINE_MARKER_PREFIX}{replaced_cmd_text}{VSCODE_COMMANDLINE_MARKER_SUFFIX}"
|
||||||
VSCODE_COMMANDLINE_MARKER_PREFIX,
|
|
||||||
replaced_cmd_text,
|
|
||||||
VSCODE_COMMANDLINE_MARKER_SUFFIX
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1451,7 +1448,7 @@ fn are_session_ids_in_sync() {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_auto_cd {
|
mod test_auto_cd {
|
||||||
use super::{do_auto_cd, escape_special_vscode_bytes, parse_operation, ReplOperation};
|
use super::{ReplOperation, do_auto_cd, escape_special_vscode_bytes, parse_operation};
|
||||||
use nu_path::AbsolutePath;
|
use nu_path::AbsolutePath;
|
||||||
use nu_protocol::engine::{EngineState, Stack};
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
@ -1492,7 +1489,7 @@ mod test_auto_cd {
|
|||||||
// 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();
|
||||||
let ReplOperation::AutoCd { cwd, target, span } = op else {
|
let ReplOperation::AutoCd { cwd, target, span } = op else {
|
||||||
panic!("'{}' was not parsed into an auto-cd operation", input)
|
panic!("'{input}' was not parsed into an auto-cd operation")
|
||||||
};
|
};
|
||||||
|
|
||||||
// Perform the auto-cd operation.
|
// Perform the auto-cd operation.
|
||||||
|
@ -2,11 +2,11 @@ use log::trace;
|
|||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use nu_color_config::{get_matching_brackets_style, get_shape_color};
|
use nu_color_config::{get_matching_brackets_style, get_shape_color};
|
||||||
use nu_engine::env;
|
use nu_engine::env;
|
||||||
use nu_parser::{flatten_block, parse, FlatShape};
|
use nu_parser::{FlatShape, flatten_block, parse};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
Span,
|
||||||
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
|
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Span,
|
|
||||||
};
|
};
|
||||||
use reedline::{Highlighter, StyledText};
|
use reedline::{Highlighter, StyledText};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -17,147 +17,173 @@ pub struct NuHighlighter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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);
|
let result = highlight_syntax(&self.engine_state, &self.stack, line, cursor);
|
||||||
|
result.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let config = self.stack.get_config(&self.engine_state);
|
/// Result of a syntax highlight operation
|
||||||
let highlight_resolved_externals = config.highlight_resolved_externals;
|
#[derive(Default)]
|
||||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
pub(crate) struct HighlightResult {
|
||||||
let block = parse(&mut working_set, None, line.as_bytes(), false);
|
/// The highlighted text
|
||||||
let (shapes, global_span_offset) = {
|
pub(crate) text: StyledText,
|
||||||
let mut shapes = flatten_block(&working_set, &block);
|
/// The span of any garbage that was highlighted
|
||||||
// Highlighting externals has a config point because of concerns that using which to resolve
|
pub(crate) found_garbage: Option<Span>,
|
||||||
// externals may slow down things too much.
|
}
|
||||||
if highlight_resolved_externals {
|
|
||||||
for (span, shape) in shapes.iter_mut() {
|
|
||||||
if *shape == FlatShape::External {
|
|
||||||
let str_contents =
|
|
||||||
working_set.get_span_contents(Span::new(span.start, span.end));
|
|
||||||
|
|
||||||
let str_word = String::from_utf8_lossy(str_contents).to_string();
|
pub(crate) fn highlight_syntax(
|
||||||
let paths = env::path_str(&self.engine_state, &self.stack, *span).ok();
|
engine_state: &EngineState,
|
||||||
#[allow(deprecated)]
|
stack: &Stack,
|
||||||
let res = if let Ok(cwd) =
|
line: &str,
|
||||||
env::current_dir_str(&self.engine_state, &self.stack)
|
cursor: usize,
|
||||||
{
|
) -> HighlightResult {
|
||||||
which::which_in(str_word, paths.as_ref(), cwd).ok()
|
trace!("highlighting: {}", line);
|
||||||
} else {
|
|
||||||
which::which_in_global(str_word, paths.as_ref())
|
let config = stack.get_config(engine_state);
|
||||||
.ok()
|
let highlight_resolved_externals = config.highlight_resolved_externals;
|
||||||
.and_then(|mut i| i.next())
|
let mut working_set = StateWorkingSet::new(engine_state);
|
||||||
};
|
let block = parse(&mut working_set, None, line.as_bytes(), false);
|
||||||
if res.is_some() {
|
let (shapes, global_span_offset) = {
|
||||||
*shape = FlatShape::ExternalResolved;
|
let mut shapes = flatten_block(&working_set, &block);
|
||||||
}
|
// Highlighting externals has a config point because of concerns that using which to resolve
|
||||||
|
// externals may slow down things too much.
|
||||||
|
if highlight_resolved_externals {
|
||||||
|
for (span, shape) in shapes.iter_mut() {
|
||||||
|
if *shape == FlatShape::External {
|
||||||
|
let str_contents =
|
||||||
|
working_set.get_span_contents(Span::new(span.start, span.end));
|
||||||
|
|
||||||
|
let str_word = String::from_utf8_lossy(str_contents).to_string();
|
||||||
|
let paths = env::path_str(engine_state, stack, *span).ok();
|
||||||
|
let res = if let Ok(cwd) = engine_state.cwd(Some(stack)) {
|
||||||
|
which::which_in(str_word, paths.as_ref(), cwd).ok()
|
||||||
|
} else {
|
||||||
|
which::which_in_global(str_word, paths.as_ref())
|
||||||
|
.ok()
|
||||||
|
.and_then(|mut i| i.next())
|
||||||
|
};
|
||||||
|
if res.is_some() {
|
||||||
|
*shape = FlatShape::ExternalResolved;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(shapes, self.engine_state.next_span_start())
|
}
|
||||||
|
(shapes, engine_state.next_span_start())
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut result = HighlightResult::default();
|
||||||
|
let mut last_seen_span = global_span_offset;
|
||||||
|
|
||||||
|
let global_cursor_offset = cursor + global_span_offset;
|
||||||
|
let matching_brackets_pos = find_matching_brackets(
|
||||||
|
line,
|
||||||
|
&working_set,
|
||||||
|
&block,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
);
|
||||||
|
|
||||||
|
for shape in &shapes {
|
||||||
|
if shape.0.end <= last_seen_span
|
||||||
|
|| last_seen_span < global_span_offset
|
||||||
|
|| shape.0.start < global_span_offset
|
||||||
|
{
|
||||||
|
// We've already output something for this span
|
||||||
|
// so just skip this one
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if shape.0.start > last_seen_span {
|
||||||
|
let gap = line
|
||||||
|
[(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)]
|
||||||
|
.to_string();
|
||||||
|
result.text.push((Style::new(), gap));
|
||||||
|
}
|
||||||
|
let next_token = line
|
||||||
|
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
||||||
|
result
|
||||||
|
.text
|
||||||
|
.push((get_shape_color(shape.as_str(), &config), text));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut output = StyledText::default();
|
match shape.1 {
|
||||||
let mut last_seen_span = global_span_offset;
|
FlatShape::Garbage => {
|
||||||
|
result.found_garbage.get_or_insert_with(|| {
|
||||||
let global_cursor_offset = _cursor + global_span_offset;
|
Span::new(
|
||||||
let matching_brackets_pos = find_matching_brackets(
|
shape.0.start - global_span_offset,
|
||||||
line,
|
shape.0.end - global_span_offset,
|
||||||
&working_set,
|
)
|
||||||
&block,
|
});
|
||||||
global_span_offset,
|
add_colored_token(&shape.1, next_token)
|
||||||
global_cursor_offset,
|
|
||||||
);
|
|
||||||
|
|
||||||
for shape in &shapes {
|
|
||||||
if shape.0.end <= last_seen_span
|
|
||||||
|| last_seen_span < global_span_offset
|
|
||||||
|| shape.0.start < global_span_offset
|
|
||||||
{
|
|
||||||
// We've already output something for this span
|
|
||||||
// so just skip this one
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
if shape.0.start > last_seen_span {
|
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
|
||||||
let gap = line
|
FlatShape::Binary => add_colored_token(&shape.1, next_token),
|
||||||
[(last_seen_span - global_span_offset)..(shape.0.start - global_span_offset)]
|
FlatShape::Bool => add_colored_token(&shape.1, next_token),
|
||||||
.to_string();
|
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
||||||
output.push((Style::new(), gap));
|
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
||||||
}
|
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
||||||
let next_token = line
|
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
|
||||||
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
FlatShape::External => add_colored_token(&shape.1, next_token),
|
||||||
.to_string();
|
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::ExternalResolved => add_colored_token(&shape.1, next_token),
|
||||||
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
|
||||||
output.push((get_shape_color(shape.as_str(), &config), text));
|
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
||||||
};
|
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
||||||
match shape.1 {
|
FlatShape::String => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Garbage => add_colored_token(&shape.1, next_token),
|
FlatShape::RawString => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Nothing => add_colored_token(&shape.1, next_token),
|
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Binary => add_colored_token(&shape.1, next_token),
|
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Bool => add_colored_token(&shape.1, next_token),
|
FlatShape::List
|
||||||
FlatShape::Int => add_colored_token(&shape.1, next_token),
|
| FlatShape::Table
|
||||||
FlatShape::Float => add_colored_token(&shape.1, next_token),
|
| FlatShape::Record
|
||||||
FlatShape::Range => add_colored_token(&shape.1, next_token),
|
| FlatShape::Block
|
||||||
FlatShape::InternalCall(_) => add_colored_token(&shape.1, next_token),
|
| FlatShape::Closure => {
|
||||||
FlatShape::External => add_colored_token(&shape.1, next_token),
|
let span = shape.0;
|
||||||
FlatShape::ExternalArg => add_colored_token(&shape.1, next_token),
|
let shape = &shape.1;
|
||||||
FlatShape::ExternalResolved => add_colored_token(&shape.1, next_token),
|
let spans = split_span_by_highlight_positions(
|
||||||
FlatShape::Keyword => add_colored_token(&shape.1, next_token),
|
line,
|
||||||
FlatShape::Literal => add_colored_token(&shape.1, next_token),
|
span,
|
||||||
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
&matching_brackets_pos,
|
||||||
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
global_span_offset,
|
||||||
FlatShape::String => add_colored_token(&shape.1, next_token),
|
);
|
||||||
FlatShape::RawString => add_colored_token(&shape.1, next_token),
|
for (part, highlight) in spans {
|
||||||
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
let start = part.start - span.start;
|
||||||
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
let end = part.end - span.start;
|
||||||
FlatShape::List
|
let text = next_token[start..end].to_string();
|
||||||
| FlatShape::Table
|
let mut style = get_shape_color(shape.as_str(), &config);
|
||||||
| FlatShape::Record
|
if highlight {
|
||||||
| FlatShape::Block
|
style = get_matching_brackets_style(style, &config);
|
||||||
| FlatShape::Closure => {
|
|
||||||
let span = shape.0;
|
|
||||||
let shape = &shape.1;
|
|
||||||
let spans = split_span_by_highlight_positions(
|
|
||||||
line,
|
|
||||||
span,
|
|
||||||
&matching_brackets_pos,
|
|
||||||
global_span_offset,
|
|
||||||
);
|
|
||||||
for (part, highlight) in spans {
|
|
||||||
let start = part.start - span.start;
|
|
||||||
let end = part.end - span.start;
|
|
||||||
let text = next_token[start..end].to_string();
|
|
||||||
let mut style = get_shape_color(shape.as_str(), &config);
|
|
||||||
if highlight {
|
|
||||||
style = get_matching_brackets_style(style, &config);
|
|
||||||
}
|
|
||||||
output.push((style, text));
|
|
||||||
}
|
}
|
||||||
|
result.text.push((style, text));
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::GlobInterpolation => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
|
||||||
add_colored_token(&shape.1, next_token)
|
|
||||||
}
|
|
||||||
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
|
|
||||||
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
|
|
||||||
}
|
}
|
||||||
last_seen_span = shape.0.end;
|
|
||||||
}
|
|
||||||
|
|
||||||
let remainder = line[(last_seen_span - global_span_offset)..].to_string();
|
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
||||||
if !remainder.is_empty() {
|
FlatShape::Directory => add_colored_token(&shape.1, next_token),
|
||||||
output.push((Style::new(), remainder));
|
FlatShape::GlobInterpolation => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::GlobPattern => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::Variable(_) | FlatShape::VarDecl(_) => {
|
||||||
|
add_colored_token(&shape.1, next_token)
|
||||||
|
}
|
||||||
|
FlatShape::Flag => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::Pipe => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::Redirection => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::Custom(..) => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::MatchPattern => add_colored_token(&shape.1, next_token),
|
||||||
}
|
}
|
||||||
|
last_seen_span = shape.0.end;
|
||||||
output
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let remainder = line[(last_seen_span - global_span_offset)..].to_string();
|
||||||
|
if !remainder.is_empty() {
|
||||||
|
result.text.push((Style::new(), remainder));
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_span_by_highlight_positions(
|
fn split_span_by_highlight_positions(
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
use nu_cmd_base::hook::eval_hook;
|
use nu_cmd_base::hook::eval_hook;
|
||||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
use nu_engine::{eval_block, eval_block_with_early_return};
|
||||||
use nu_parser::{lex, parse, unescape_unquote_string, Token, TokenContents};
|
use nu_parser::{Token, TokenContents, lex, parse, unescape_unquote_string};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
PipelineData, ShellError, Span, Value,
|
||||||
cli_error::report_compile_error,
|
cli_error::report_compile_error,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
report_parse_error, report_parse_warning, report_shell_error, PipelineData, ShellError, Span,
|
report_parse_error, report_parse_warning, report_shell_error,
|
||||||
Value,
|
|
||||||
};
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, StateWorkingSet},
|
|
||||||
ParseError,
|
ParseError,
|
||||||
|
engine::{EngineState, StateWorkingSet},
|
||||||
};
|
};
|
||||||
use reedline::{ValidationResult, Validator};
|
use reedline::{ValidationResult, Validator};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_protocol::HistoryFileFormat;
|
use nu_protocol::HistoryFileFormat;
|
||||||
use nu_test_support::{nu, Outcome};
|
use nu_test_support::{Outcome, nu};
|
||||||
use reedline::{
|
use reedline::{
|
||||||
FileBackedHistory, History, HistoryItem, HistoryItemId, ReedlineError, SearchQuery,
|
FileBackedHistory, History, HistoryItem, HistoryItemId, ReedlineError, SearchQuery,
|
||||||
SqliteBackedHistory,
|
SqliteBackedHistory,
|
||||||
|
@ -1,21 +1,24 @@
|
|||||||
pub mod support;
|
pub mod support;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fs::{read_dir, FileType, ReadDir},
|
fs::{FileType, ReadDir, read_dir},
|
||||||
path::{PathBuf, MAIN_SEPARATOR},
|
path::{MAIN_SEPARATOR, PathBuf},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
};
|
};
|
||||||
|
|
||||||
use nu_cli::NuCompleter;
|
use nu_cli::NuCompleter;
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::expand_tilde;
|
use nu_path::{AbsolutePathBuf, expand_tilde};
|
||||||
use nu_protocol::{debugger::WithoutDebug, engine::StateWorkingSet, PipelineData};
|
use nu_protocol::{Config, PipelineData, debugger::WithoutDebug, engine::StateWorkingSet};
|
||||||
|
use nu_std::load_standard_library;
|
||||||
|
use nu_test_support::fs;
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use support::{
|
use support::{
|
||||||
completions_helpers::{
|
completions_helpers::{
|
||||||
new_dotnu_engine, new_external_engine, new_partial_engine, new_quote_engine,
|
new_dotnu_engine, new_engine_helper, new_external_engine, new_partial_engine,
|
||||||
|
new_quote_engine,
|
||||||
},
|
},
|
||||||
file, folder, match_suggestions, match_suggestions_by_string, new_engine,
|
file, folder, match_suggestions, match_suggestions_by_string, new_engine,
|
||||||
};
|
};
|
||||||
@ -122,7 +125,7 @@ fn custom_completer_with_options(
|
|||||||
global_opts,
|
global_opts,
|
||||||
completions
|
completions
|
||||||
.iter()
|
.iter()
|
||||||
.map(|comp| format!("'{}'", comp))
|
.map(|comp| format!("'{comp}'"))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", "),
|
.join(", "),
|
||||||
completer_opts,
|
completer_opts,
|
||||||
@ -227,14 +230,12 @@ fn customcompletions_override_options() {
|
|||||||
let mut completer = custom_completer_with_options(
|
let mut completer = custom_completer_with_options(
|
||||||
r#"$env.config.completions.algorithm = "fuzzy"
|
r#"$env.config.completions.algorithm = "fuzzy"
|
||||||
$env.config.completions.case_sensitive = false"#,
|
$env.config.completions.case_sensitive = false"#,
|
||||||
r#"completion_algorithm: "prefix",
|
r#"completion_algorithm: "substring",
|
||||||
positional: false,
|
|
||||||
case_sensitive: true,
|
case_sensitive: true,
|
||||||
sort: true"#,
|
sort: true"#,
|
||||||
&["Foo Abcdef", "Abcdef", "Acd Bar"],
|
&["Foo Abcdef", "Abcdef", "Acd Bar"],
|
||||||
);
|
);
|
||||||
|
|
||||||
// positional: false should make it do substring matching
|
|
||||||
// sort: true should force sorting
|
// sort: true should force sorting
|
||||||
let expected: Vec<_> = vec!["Abcdef", "Foo Abcdef"];
|
let expected: Vec<_> = vec!["Abcdef", "Foo Abcdef"];
|
||||||
let suggestions = completer.complete("my-command Abcd", 15);
|
let suggestions = completer.complete("my-command Abcd", 15);
|
||||||
@ -350,9 +351,24 @@ fn custom_arguments_vs_subcommands() {
|
|||||||
match_suggestions(&expected, &suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn custom_completions_defined_inline() {
|
||||||
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
let completion_str = "def animals [] { [cat dog] }
|
||||||
|
export def say [
|
||||||
|
animal: string@animals
|
||||||
|
] { }; say ";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
// including only subcommand completions
|
||||||
|
let expected: Vec<_> = vec!["cat", "dog"];
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
/// External command only if starts with `^`
|
/// External command only if starts with `^`
|
||||||
#[test]
|
#[test]
|
||||||
fn external_commands_only() {
|
fn external_commands() {
|
||||||
let engine = new_external_engine();
|
let engine = new_external_engine();
|
||||||
let mut completer = NuCompleter::new(
|
let mut completer = NuCompleter::new(
|
||||||
Arc::new(engine),
|
Arc::new(engine),
|
||||||
@ -375,6 +391,31 @@ fn external_commands_only() {
|
|||||||
match_suggestions(&expected, &suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Disable external commands except for those start with `^`
|
||||||
|
#[test]
|
||||||
|
fn external_commands_disabled() {
|
||||||
|
let mut engine = new_external_engine();
|
||||||
|
|
||||||
|
let mut config = Config::default();
|
||||||
|
config.completions.external.enable = false;
|
||||||
|
engine.set_config(config);
|
||||||
|
|
||||||
|
let stack = nu_protocol::engine::Stack::new();
|
||||||
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
let completion_str = "ls; ^sleep";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
#[cfg(windows)]
|
||||||
|
let expected: Vec<_> = vec!["sleep.exe"];
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
let expected: Vec<_> = vec!["sleep"];
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "sleep";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
let expected: Vec<_> = vec!["sleep"];
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
/// Which completes both internals and externals
|
/// Which completes both internals and externals
|
||||||
#[test]
|
#[test]
|
||||||
fn which_command_completions() {
|
fn which_command_completions() {
|
||||||
@ -473,7 +514,7 @@ fn dotnu_completions() {
|
|||||||
|
|
||||||
match_suggestions(&vec!["sub.nu`"], &suggestions);
|
match_suggestions(&vec!["sub.nu`"], &suggestions);
|
||||||
|
|
||||||
let expected = vec![
|
let mut expected = vec![
|
||||||
"asdf.nu",
|
"asdf.nu",
|
||||||
"bar.nu",
|
"bar.nu",
|
||||||
"bat.nu",
|
"bat.nu",
|
||||||
@ -506,6 +547,8 @@ fn dotnu_completions() {
|
|||||||
match_suggestions(&expected, &suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test use completion
|
// Test use completion
|
||||||
|
expected.push("std");
|
||||||
|
expected.push("std-rfc");
|
||||||
let completion_str = "use ";
|
let completion_str = "use ";
|
||||||
let suggestions = completer.complete(completion_str, completion_str.len());
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
|
||||||
@ -537,6 +580,66 @@ fn dotnu_completions() {
|
|||||||
match_dir_content_for_dotnu(dir_content, &suggestions);
|
match_dir_content_for_dotnu(dir_content, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dotnu_stdlib_completions() {
|
||||||
|
let (_, _, mut engine, stack) = new_dotnu_engine();
|
||||||
|
assert!(load_standard_library(&mut engine).is_ok());
|
||||||
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
|
||||||
|
// `export use` should be recognized as command `export use`
|
||||||
|
let completion_str = "export use std/ass";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["assert"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "use `std-rfc/cli";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["clip"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "use \"std";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["\"std", "\"std-rfc"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "overlay use \'std-rfc/cli";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["clip"], &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn exportable_completions() {
|
||||||
|
let (_, _, mut engine, mut stack) = new_dotnu_engine();
|
||||||
|
let code = r#"export module "🤔🐘" {
|
||||||
|
export const foo = "🤔🐘";
|
||||||
|
}"#;
|
||||||
|
assert!(support::merge_input(code.as_bytes(), &mut engine, &mut stack).is_ok());
|
||||||
|
assert!(load_standard_library(&mut engine).is_ok());
|
||||||
|
|
||||||
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
|
||||||
|
let completion_str = "use std null";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["null-device", "null_device"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "export use std/assert eq";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["equal"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "use std/assert \"not eq";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["'not equal'"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "use std-rfc/clip ['prefi";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["prefix"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "use std/math [E, `TAU";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["TAU"], &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "use 🤔🐘 'foo";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&vec!["foo"], &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dotnu_completions_const_nu_lib_dirs() {
|
fn dotnu_completions_const_nu_lib_dirs() {
|
||||||
let (_, _, engine, stack) = new_dotnu_engine();
|
let (_, _, engine, stack) = new_dotnu_engine();
|
||||||
@ -615,6 +718,16 @@ fn external_completer_fallback() {
|
|||||||
let expected = [folder("test_a"), file("test_a_symlink"), folder("test_b")];
|
let expected = [folder("test_a"), file("test_a_symlink"), folder("test_b")];
|
||||||
let suggestions = run_external_completion(block, input);
|
let suggestions = run_external_completion(block, input);
|
||||||
match_suggestions_by_string(&expected, &suggestions);
|
match_suggestions_by_string(&expected, &suggestions);
|
||||||
|
|
||||||
|
// issue #15790
|
||||||
|
let input = "foo `dir with space/`";
|
||||||
|
let expected = vec!["`dir with space/bar baz`", "`dir with space/foo`"];
|
||||||
|
let suggestions = run_external_completion_within_pwd(
|
||||||
|
block,
|
||||||
|
input,
|
||||||
|
fs::fixtures().join("external_completions"),
|
||||||
|
);
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fallback to external completions for flags of `sudo`
|
/// Fallback to external completions for flags of `sudo`
|
||||||
@ -911,10 +1024,11 @@ fn partial_completions() {
|
|||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths = [
|
let expected_paths = [
|
||||||
file(dir.join("partial").join("hello.txt")),
|
file(dir.join("partial").join("hello.txt")),
|
||||||
|
folder(dir.join("partial").join("hol")),
|
||||||
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")),
|
||||||
file(dir.join("partial-a").join("hello")),
|
file(dir.join("partial-a").join("hello")),
|
||||||
file(dir.join("partial-a").join("hola")),
|
folder(dir.join("partial-a").join("hola")),
|
||||||
file(dir.join("partial-b").join("hello_b")),
|
file(dir.join("partial-b").join("hello_b")),
|
||||||
file(dir.join("partial-b").join("hi_b")),
|
file(dir.join("partial-b").join("hi_b")),
|
||||||
file(dir.join("partial-c").join("hello_c")),
|
file(dir.join("partial-c").join("hello_c")),
|
||||||
@ -931,11 +1045,12 @@ fn partial_completions() {
|
|||||||
// Create the expected values
|
// Create the expected values
|
||||||
let expected_paths = [
|
let expected_paths = [
|
||||||
file(dir.join("partial").join("hello.txt")),
|
file(dir.join("partial").join("hello.txt")),
|
||||||
|
folder(dir.join("partial").join("hol")),
|
||||||
file(dir.join("partial-a").join("anotherfile")),
|
file(dir.join("partial-a").join("anotherfile")),
|
||||||
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")),
|
||||||
file(dir.join("partial-a").join("hello")),
|
file(dir.join("partial-a").join("hello")),
|
||||||
file(dir.join("partial-a").join("hola")),
|
folder(dir.join("partial-a").join("hola")),
|
||||||
file(dir.join("partial-b").join("hello_b")),
|
file(dir.join("partial-b").join("hello_b")),
|
||||||
file(dir.join("partial-b").join("hi_b")),
|
file(dir.join("partial-b").join("hi_b")),
|
||||||
file(dir.join("partial-c").join("hello_c")),
|
file(dir.join("partial-c").join("hello_c")),
|
||||||
@ -1476,16 +1591,25 @@ fn attribute_completions() {
|
|||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
|
|
||||||
|
// Compile a list of built-in attribute names (without the "attr " prefix)
|
||||||
|
let attribute_names: Vec<String> = engine
|
||||||
|
.get_signatures_and_declids(false)
|
||||||
|
.into_iter()
|
||||||
|
.map(|(sig, _)| sig.name)
|
||||||
|
.filter(|name| name.starts_with("attr "))
|
||||||
|
.map(|name| name[5..].to_string())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// Make sure we actually found some attributes so the test is valid
|
||||||
|
assert!(attribute_names.contains(&String::from("example")));
|
||||||
|
|
||||||
// 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));
|
||||||
// Test completions for the 'ls' flags
|
// Test completions for the 'ls' flags
|
||||||
let suggestions = completer.complete("@", 1);
|
let suggestions = completer.complete("@", 1);
|
||||||
|
|
||||||
// Only checking for the builtins and not the std attributes
|
|
||||||
let expected: Vec<_> = vec!["category", "example", "search-terms"];
|
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(&expected, &suggestions);
|
match_suggestions_by_string(&attribute_names, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1902,6 +2026,35 @@ fn table_cell_path_completions() {
|
|||||||
match_suggestions(&expected, &suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn quoted_cell_path_completions() {
|
||||||
|
let (_, _, mut engine, mut stack) = new_engine();
|
||||||
|
let command = r#"let foo = {'foo bar':1 'foo\\"bar"': 1 '.': 1 '|': 1 1: 1 "": 1}"#;
|
||||||
|
assert!(support::merge_input(command.as_bytes(), &mut engine, &mut stack).is_ok());
|
||||||
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
|
||||||
|
let expected: Vec<_> = vec![
|
||||||
|
"\"\"",
|
||||||
|
"\".\"",
|
||||||
|
"\"1\"",
|
||||||
|
"\"foo bar\"",
|
||||||
|
"\"foo\\\\\\\\\\\"bar\\\"\"",
|
||||||
|
"\"|\"",
|
||||||
|
];
|
||||||
|
let completion_str = "$foo.";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
|
let expected: Vec<_> = vec!["\"foo bar\"", "\"foo\\\\\\\\\\\"bar\\\"\""];
|
||||||
|
let completion_str = "$foo.`foo";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
|
let completion_str = "$foo.foo";
|
||||||
|
let suggestions = completer.complete(completion_str, completion_str.len());
|
||||||
|
match_suggestions(&expected, &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn alias_of_command_and_flags() {
|
fn alias_of_command_and_flags() {
|
||||||
let (_, _, mut engine, mut stack) = new_engine();
|
let (_, _, mut engine, mut stack) = new_engine();
|
||||||
@ -1962,11 +2115,15 @@ fn alias_of_another_alias() {
|
|||||||
match_suggestions(&expected_paths, &suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
fn run_external_completion_within_pwd(
|
||||||
|
completer: &str,
|
||||||
|
input: &str,
|
||||||
|
pwd: AbsolutePathBuf,
|
||||||
|
) -> Vec<Suggestion> {
|
||||||
let completer = format!("$env.config.completions.external.completer = {completer}");
|
let completer = format!("$env.config.completions.external.completer = {completer}");
|
||||||
|
|
||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (_, _, mut engine_state, mut stack) = new_engine();
|
let (_, _, mut engine_state, mut stack) = new_engine_helper(pwd);
|
||||||
let (block, delta) = {
|
let (block, delta) = {
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let block = parse(&mut working_set, None, completer.as_bytes(), false);
|
let block = parse(&mut working_set, None, completer.as_bytes(), false);
|
||||||
@ -1990,6 +2147,10 @@ fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
|||||||
completer.complete(input, input.len())
|
completer.complete(input, input.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
||||||
|
run_external_completion_within_pwd(completer, input, fs::fixtures().join("completions"))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_command_completion() {
|
fn unknown_command_completion() {
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
@ -2175,15 +2336,69 @@ fn exact_match() {
|
|||||||
|
|
||||||
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
|
||||||
|
// Troll case to test if exact match logic works case insensitively
|
||||||
let target_dir = format!("open {}", folder(dir.join("pArTiAL")));
|
let target_dir = format!("open {}", folder(dir.join("pArTiAL")));
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
// Since it's an exact match, only 'partial' should be suggested, not
|
|
||||||
// 'partial-a' and stuff. Implemented in #13302
|
|
||||||
match_suggestions(
|
match_suggestions(
|
||||||
&vec![file(dir.join("partial").join("hello.txt")).as_str()],
|
&vec![
|
||||||
|
file(dir.join("partial").join("hello.txt")).as_str(),
|
||||||
|
folder(dir.join("partial").join("hol")).as_str(),
|
||||||
|
],
|
||||||
&suggestions,
|
&suggestions,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let target_dir = format!("open {}", file(dir.join("partial").join("h")));
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
match_suggestions(
|
||||||
|
&vec![
|
||||||
|
file(dir.join("partial").join("hello.txt")).as_str(),
|
||||||
|
folder(dir.join("partial").join("hol")).as_str(),
|
||||||
|
],
|
||||||
|
&suggestions,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Even though "hol" is an exact match, the first component ("part") wasn't an
|
||||||
|
// exact match, so we include partial-a/hola
|
||||||
|
let target_dir = format!("open {}", file(dir.join("part").join("hol")));
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
match_suggestions(
|
||||||
|
&vec![
|
||||||
|
folder(dir.join("partial").join("hol")).as_str(),
|
||||||
|
folder(dir.join("partial-a").join("hola")).as_str(),
|
||||||
|
],
|
||||||
|
&suggestions,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Exact match behavior shouldn't be enabled if the path has no slashes
|
||||||
|
let target_dir = format!("open {}", file(dir.join("partial")));
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
assert!(suggestions.len() > 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(not(windows), not(target_os = "macos")))]
|
||||||
|
#[test]
|
||||||
|
fn exact_match_case_insensitive() {
|
||||||
|
use nu_test_support::playground::Playground;
|
||||||
|
use support::completions_helpers::new_engine_helper;
|
||||||
|
|
||||||
|
Playground::setup("exact_match_case_insensitive", |dirs, playground| {
|
||||||
|
playground.mkdir("AA/foo");
|
||||||
|
playground.mkdir("aa/foo");
|
||||||
|
playground.mkdir("aaa/foo");
|
||||||
|
|
||||||
|
let (dir, _, engine, stack) = new_engine_helper(dirs.test().into());
|
||||||
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
|
||||||
|
let target = format!("open {}", folder(dir.join("aa")));
|
||||||
|
match_suggestions(
|
||||||
|
&vec![
|
||||||
|
folder(dir.join("AA").join("foo")).as_str(),
|
||||||
|
folder(dir.join("aa").join("foo")).as_str(),
|
||||||
|
folder(dir.join("aaa").join("foo")).as_str(),
|
||||||
|
],
|
||||||
|
&completer.complete(&target, target.len()),
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ignore = "was reverted, still needs fixing"]
|
#[ignore = "was reverted, still needs fixing"]
|
||||||
|
@ -2,9 +2,9 @@ use nu_engine::eval_block;
|
|||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::{AbsolutePathBuf, PathBuf};
|
use nu_path::{AbsolutePathBuf, PathBuf};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
PipelineData, ShellError, Span, Value,
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
PipelineData, ShellError, Span, Value,
|
|
||||||
};
|
};
|
||||||
use nu_test_support::fs;
|
use nu_test_support::fs;
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
@ -14,11 +14,8 @@ 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
|
pub fn new_engine_helper(pwd: AbsolutePathBuf) -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||||
pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
let pwd_str = pwd
|
||||||
// Target folder inside assets
|
|
||||||
let dir = fs::fixtures().join("completions");
|
|
||||||
let dir_str = dir
|
|
||||||
.clone()
|
.clone()
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.into_string()
|
.into_string()
|
||||||
@ -36,13 +33,13 @@ pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
|||||||
// Add pwd as env var
|
// Add pwd as env var
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
"PWD".to_string(),
|
"PWD".to_string(),
|
||||||
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
Value::string(pwd_str.clone(), nu_protocol::Span::new(0, pwd_str.len())),
|
||||||
);
|
);
|
||||||
stack.add_env_var(
|
stack.add_env_var(
|
||||||
"TEST".to_string(),
|
"TEST".to_string(),
|
||||||
Value::string(
|
Value::string(
|
||||||
"NUSHELL".to_string(),
|
"NUSHELL".to_string(),
|
||||||
nu_protocol::Span::new(0, dir_str.len()),
|
nu_protocol::Span::new(0, pwd_str.len()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
@ -50,7 +47,7 @@ pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
|||||||
"Path".to_string(),
|
"Path".to_string(),
|
||||||
Value::string(
|
Value::string(
|
||||||
"c:\\some\\path;c:\\some\\other\\path".to_string(),
|
"c:\\some\\path;c:\\some\\other\\path".to_string(),
|
||||||
nu_protocol::Span::new(0, dir_str.len()),
|
nu_protocol::Span::new(0, pwd_str.len()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
@ -58,7 +55,7 @@ pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
|||||||
"PATH".to_string(),
|
"PATH".to_string(),
|
||||||
Value::string(
|
Value::string(
|
||||||
"/some/path:/some/other/path".to_string(),
|
"/some/path:/some/other/path".to_string(),
|
||||||
nu_protocol::Span::new(0, dir_str.len()),
|
nu_protocol::Span::new(0, pwd_str.len()),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -66,7 +63,12 @@ pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
|||||||
let merge_result = engine_state.merge_env(&mut stack);
|
let merge_result = engine_state.merge_env(&mut stack);
|
||||||
assert!(merge_result.is_ok());
|
assert!(merge_result.is_ok());
|
||||||
|
|
||||||
(dir, dir_str, engine_state, stack)
|
(pwd, pwd_str, engine_state, stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// creates a new engine with the current path in the completions fixtures folder
|
||||||
|
pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||||
|
new_engine_helper(fs::fixtures().join("completions"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds pseudo PATH env for external completion tests
|
/// Adds pseudo PATH env for external completion tests
|
||||||
@ -88,23 +90,13 @@ pub fn new_external_engine() -> EngineState {
|
|||||||
engine
|
engine
|
||||||
}
|
}
|
||||||
|
|
||||||
/// creates a new engine with the current path into the completions fixtures folder
|
/// creates a new engine with the current path in the dotnu_completions fixtures folder
|
||||||
pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
// Target folder inside assets
|
||||||
let dir = fs::fixtures().join("dotnu_completions");
|
let dir = fs::fixtures().join("dotnu_completions");
|
||||||
let dir_str = dir
|
let (dir, dir_str, mut engine_state, mut stack) = new_engine_helper(dir);
|
||||||
.clone()
|
|
||||||
.into_os_string()
|
|
||||||
.into_string()
|
|
||||||
.unwrap_or_default();
|
|
||||||
let dir_span = nu_protocol::Span::new(0, dir_str.len());
|
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();
|
|
||||||
|
|
||||||
// const $NU_LIB_DIRS
|
// const $NU_LIB_DIRS
|
||||||
let mut working_set = StateWorkingSet::new(&engine_state);
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
let var_id = working_set.add_variable(
|
let var_id = working_set.add_variable(
|
||||||
@ -122,15 +114,6 @@ pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
|||||||
);
|
);
|
||||||
let _ = engine_state.merge_delta(working_set.render());
|
let _ = engine_state.merge_delta(working_set.render());
|
||||||
|
|
||||||
// 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(
|
stack.add_env_var(
|
||||||
"NU_LIB_DIRS".into(),
|
"NU_LIB_DIRS".into(),
|
||||||
Value::test_list(vec![
|
Value::test_list(vec![
|
||||||
@ -147,73 +130,11 @@ pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_quote_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
pub fn new_quote_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
new_engine_helper(fs::fixtures().join("quoted_completions"))
|
||||||
let dir = fs::fixtures().join("quoted_completions");
|
|
||||||
let dir_str = dir
|
|
||||||
.clone()
|
|
||||||
.into_os_string()
|
|
||||||
.into_string()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
// Create a new engine with default context
|
|
||||||
let mut engine_state = create_default_context();
|
|
||||||
|
|
||||||
// New stack
|
|
||||||
let mut stack = Stack::new();
|
|
||||||
|
|
||||||
// Add pwd as env var
|
|
||||||
stack.add_env_var(
|
|
||||||
"PWD".to_string(),
|
|
||||||
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
|
||||||
);
|
|
||||||
stack.add_env_var(
|
|
||||||
"TEST".to_string(),
|
|
||||||
Value::string(
|
|
||||||
"NUSHELL".to_string(),
|
|
||||||
nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Merge environment into the permanent state
|
|
||||||
let merge_result = engine_state.merge_env(&mut stack);
|
|
||||||
assert!(merge_result.is_ok());
|
|
||||||
|
|
||||||
(dir, dir_str, engine_state, stack)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
new_engine_helper(fs::fixtures().join("partial_completions"))
|
||||||
let dir = fs::fixtures().join("partial_completions");
|
|
||||||
let dir_str = dir
|
|
||||||
.clone()
|
|
||||||
.into_os_string()
|
|
||||||
.into_string()
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
// Create a new engine with default context
|
|
||||||
let mut engine_state = create_default_context();
|
|
||||||
|
|
||||||
// New stack
|
|
||||||
let mut stack = Stack::new();
|
|
||||||
|
|
||||||
// Add pwd as env var
|
|
||||||
stack.add_env_var(
|
|
||||||
"PWD".to_string(),
|
|
||||||
Value::string(dir_str.clone(), nu_protocol::Span::new(0, dir_str.len())),
|
|
||||||
);
|
|
||||||
stack.add_env_var(
|
|
||||||
"TEST".to_string(),
|
|
||||||
Value::string(
|
|
||||||
"NUSHELL".to_string(),
|
|
||||||
nu_protocol::Span::new(0, dir_str.len()),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Merge environment into the permanent state
|
|
||||||
let merge_result = engine_state.merge_env(&mut stack);
|
|
||||||
assert!(merge_result.is_ok());
|
|
||||||
|
|
||||||
(dir, dir_str, engine_state, stack)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// match a list of suggestions with the expected values
|
/// match a list of suggestions with the expected values
|
||||||
@ -273,13 +194,15 @@ pub fn merge_input(
|
|||||||
|
|
||||||
engine_state.merge_delta(delta)?;
|
engine_state.merge_delta(delta)?;
|
||||||
|
|
||||||
assert!(eval_block::<WithoutDebug>(
|
assert!(
|
||||||
engine_state,
|
eval_block::<WithoutDebug>(
|
||||||
stack,
|
engine_state,
|
||||||
&block,
|
stack,
|
||||||
PipelineData::Value(Value::nothing(Span::unknown()), None),
|
&block,
|
||||||
)
|
PipelineData::Value(Value::nothing(Span::unknown()), None),
|
||||||
.is_ok());
|
)
|
||||||
|
.is_ok()
|
||||||
|
);
|
||||||
|
|
||||||
// Merge environment into the permanent state
|
// Merge environment into the permanent state
|
||||||
engine_state.merge_env(stack)
|
engine_state.merge_env(stack)
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["The Nushell Project Developers"]
|
authors = ["The Nushell Project Developers"]
|
||||||
description = "The foundation tools to build Nushell commands."
|
description = "The foundation tools to build Nushell commands."
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
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.103.0"
|
version = "0.105.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,12 +13,12 @@ version = "0.103.0"
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.103.0", default-features = false }
|
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.103.0" }
|
nu-parser = { path = "../nu-parser", version = "0.105.2" }
|
||||||
nu-path = { path = "../nu-path", version = "0.103.0" }
|
nu-path = { path = "../nu-path", version = "0.105.2" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.103.0", default-features = false }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
|
||||||
|
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
miette = { workspace = true }
|
miette = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use indexmap::{indexset, IndexSet};
|
use indexmap::{IndexSet, indexset};
|
||||||
use nu_protocol::Value;
|
use nu_protocol::Value;
|
||||||
|
|
||||||
pub fn merge_descriptors(values: &[Value]) -> Vec<String> {
|
pub fn merge_descriptors(values: &[Value]) -> Vec<String> {
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use miette::Result;
|
use miette::Result;
|
||||||
use nu_engine::{eval_block, eval_block_with_early_return};
|
use nu_engine::{eval_block, eval_block_with_early_return, redirect_env};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
|
PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
|
||||||
cli_error::{report_parse_error, report_shell_error},
|
cli_error::{report_parse_error, report_shell_error},
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
engine::{Closure, EngineState, Stack, StateWorkingSet},
|
||||||
PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
|
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{collections::HashMap, sync::Arc};
|
||||||
|
|
||||||
@ -325,19 +325,7 @@ fn run_hook(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If all went fine, preserve the environment of the called block
|
// If all went fine, preserve the environment of the called block
|
||||||
let caller_env_vars = stack.get_env_var_names(engine_state);
|
redirect_env(engine_state, stack, &callee_stack);
|
||||||
|
|
||||||
// remove env vars that are present in the caller but not in the callee
|
|
||||||
// (the callee hid them)
|
|
||||||
for var in caller_env_vars.iter() {
|
|
||||||
if !callee_stack.has_env_var(engine_state, var) {
|
|
||||||
stack.remove_env_var(engine_state, var);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// add new env vars from callee to caller
|
|
||||||
for (var, value) in callee_stack.get_stack_env_vars() {
|
|
||||||
stack.add_env_var(var, value);
|
|
||||||
}
|
|
||||||
Ok(pipeline_data)
|
Ok(pipeline_data)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_protocol::{ast::CellPath, PipelineData, ShellError, Signals, Span, Value};
|
use nu_protocol::{PipelineData, ShellError, Signals, Span, Value, ast::CellPath};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub trait CmdArgument {
|
pub trait CmdArgument {
|
||||||
|
@ -3,3 +3,6 @@ pub mod formats;
|
|||||||
pub mod hook;
|
pub mod hook;
|
||||||
pub mod input_handler;
|
pub mod input_handler;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
mod wrap_call;
|
||||||
|
|
||||||
|
pub use wrap_call::*;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack},
|
|
||||||
Range, ShellError, Span, Value,
|
Range, ShellError, Span, Value,
|
||||||
|
engine::{EngineState, Stack},
|
||||||
};
|
};
|
||||||
use std::ops::Bound;
|
use std::ops::Bound;
|
||||||
|
|
||||||
|
101
crates/nu-cmd-base/src/wrap_call.rs
Normal file
101
crates/nu-cmd-base/src/wrap_call.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use nu_engine::CallExt;
|
||||||
|
use nu_protocol::{
|
||||||
|
DeclId, FromValue, ShellError, Span,
|
||||||
|
engine::{Call, EngineState, Stack, StateWorkingSet},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A helper utility to aid in implementing commands which have the same behavior for `run` and `run_const`.
|
||||||
|
///
|
||||||
|
/// Only supports functions in [`Call`] and [`CallExt`] which have a `const` suffix.
|
||||||
|
///
|
||||||
|
/// To use, the actual command logic should be moved to a function. Then, `eval` and `eval_const` can be implemented like this:
|
||||||
|
/// ```rust
|
||||||
|
/// # use nu_engine::command_prelude::*;
|
||||||
|
/// # use nu_cmd_base::WrapCall;
|
||||||
|
/// # fn do_command_logic(call: WrapCall) -> Result<PipelineData, ShellError> { Ok(PipelineData::Empty) }
|
||||||
|
///
|
||||||
|
/// # struct Command {}
|
||||||
|
/// # impl Command {
|
||||||
|
/// fn run(&self, engine_state: &EngineState, stack: &mut Stack, call: &Call) -> Result<PipelineData, ShellError> {
|
||||||
|
/// let call = WrapCall::Eval(engine_state, stack, call);
|
||||||
|
/// do_command_logic(call)
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn run_const(&self, working_set: &StateWorkingSet, call: &Call) -> Result<PipelineData, ShellError> {
|
||||||
|
/// let call = WrapCall::ConstEval(working_set, call);
|
||||||
|
/// do_command_logic(call)
|
||||||
|
/// }
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// Then, the typical [`Call`] and [`CallExt`] operations can be called using destructuring:
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use nu_engine::command_prelude::*;
|
||||||
|
/// # use nu_cmd_base::WrapCall;
|
||||||
|
/// # let call = WrapCall::Eval(&EngineState::new(), &mut Stack::new(), &Call::new(Span::unknown()));
|
||||||
|
/// # fn do_command_logic(call: WrapCall) -> Result<(), ShellError> {
|
||||||
|
/// let (call, required): (_, String) = call.req(0)?;
|
||||||
|
/// let (call, flag): (_, Option<i64>) = call.get_flag("number")?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// A new `WrapCall` instance has to be returned after each function to ensure
|
||||||
|
/// that there is only ever one copy of mutable [`Stack`] reference.
|
||||||
|
pub enum WrapCall<'a> {
|
||||||
|
Eval(&'a EngineState, &'a mut Stack, &'a Call<'a>),
|
||||||
|
ConstEval(&'a StateWorkingSet<'a>, &'a Call<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Macro to choose between the non-const and const versions of each [`Call`]/[`CallExt`] function
|
||||||
|
macro_rules! proxy {
|
||||||
|
($self:ident , $eval:ident , $const:ident , $( $args:expr ),*) => {
|
||||||
|
match $self {
|
||||||
|
WrapCall::Eval(engine_state, stack, call) => {
|
||||||
|
Call::$eval(call, engine_state, stack, $( $args ),*)
|
||||||
|
.map(|val| (WrapCall::Eval(engine_state, stack, call), val))
|
||||||
|
},
|
||||||
|
WrapCall::ConstEval(working_set, call) => {
|
||||||
|
Call::$const(call, working_set, $( $args ),*)
|
||||||
|
.map(|val| (WrapCall::ConstEval(working_set, call), val))
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WrapCall<'_> {
|
||||||
|
pub fn head(&self) -> Span {
|
||||||
|
match self {
|
||||||
|
WrapCall::Eval(_, _, call) => call.head,
|
||||||
|
WrapCall::ConstEval(_, call) => call.head,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decl_id(&self) -> DeclId {
|
||||||
|
match self {
|
||||||
|
WrapCall::Eval(_, _, call) => call.decl_id,
|
||||||
|
WrapCall::ConstEval(_, call) => call.decl_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_flag<T: FromValue>(self, flag_name: &str) -> Result<(Self, bool), ShellError> {
|
||||||
|
proxy!(self, has_flag, has_flag_const, flag_name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_flag<T: FromValue>(self, name: &str) -> Result<(Self, Option<T>), ShellError> {
|
||||||
|
proxy!(self, get_flag, get_flag_const, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn req<T: FromValue>(self, pos: usize) -> Result<(Self, T), ShellError> {
|
||||||
|
proxy!(self, req, req_const, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rest<T: FromValue>(self, pos: usize) -> Result<(Self, Vec<T>), ShellError> {
|
||||||
|
proxy!(self, rest, rest_const, pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn opt<T: FromValue>(self, pos: usize) -> Result<(Self, Option<T>), ShellError> {
|
||||||
|
proxy!(self, opt, opt_const, pos)
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
[package]
|
[package]
|
||||||
authors = ["The Nushell Project Developers"]
|
authors = ["The Nushell Project Developers"]
|
||||||
description = "Nushell's extra commands that are not part of the 1.0 api standard."
|
description = "Nushell's extra commands that are not part of the 1.0 api standard."
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
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.103.0"
|
version = "0.105.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
|
||||||
|
|
||||||
@ -16,13 +16,13 @@ bench = false
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.103.0" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.103.0", default-features = false }
|
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
|
||||||
nu-json = { version = "0.103.0", path = "../nu-json" }
|
nu-json = { version = "0.105.2", path = "../nu-json" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.103.0" }
|
nu-parser = { path = "../nu-parser", version = "0.105.2" }
|
||||||
nu-pretty-hex = { version = "0.103.0", path = "../nu-pretty-hex" }
|
nu-pretty-hex = { version = "0.105.2", path = "../nu-pretty-hex" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.103.0", default-features = false }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.103.0", default-features = false }
|
nu-utils = { path = "../nu-utils", version = "0.105.2", default-features = false }
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
heck = { workspace = true }
|
heck = { workspace = true }
|
||||||
@ -37,6 +37,6 @@ itertools = { workspace = true }
|
|||||||
mime = { workspace = true }
|
mime = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.103.0" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.105.2" }
|
||||||
nu-command = { path = "../nu-command", version = "0.103.0" }
|
nu-command = { path = "../nu-command", version = "0.105.2" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.103.0" }
|
nu-test-support = { path = "../nu-test-support", version = "0.105.2" }
|
||||||
|
@ -16,8 +16,8 @@ mod test_examples {
|
|||||||
};
|
};
|
||||||
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Command, EngineState, StateWorkingSet},
|
|
||||||
Type,
|
Type,
|
||||||
|
engine::{Command, EngineState, StateWorkingSet},
|
||||||
};
|
};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ impl Command for BitsAnd {
|
|||||||
return Err(ShellError::TypeMismatch {
|
return Err(ShellError::TypeMismatch {
|
||||||
err_message: "Endian must be one of native, little, big".to_string(),
|
err_message: "Endian must be one of native, little, big".to_string(),
|
||||||
span: endian.span,
|
span: endian.span,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -113,8 +113,7 @@ impl Command for BitsAnd {
|
|||||||
])),
|
])),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description: "Apply bitwise and to binary data of varying lengths with specified endianness",
|
||||||
"Apply bitwise and to binary data of varying lengths with specified endianness",
|
|
||||||
example: "0x[c0 ff ee] | bits and 0x[ff] --endian big",
|
example: "0x[c0 ff ee] | bits and 0x[ff] --endian big",
|
||||||
result: Some(Value::test_binary(vec![0x00, 0x00, 0xee])),
|
result: Some(Value::test_binary(vec![0x00, 0x00, 0xee])),
|
||||||
},
|
},
|
||||||
|
@ -135,7 +135,7 @@ where
|
|||||||
(min, max) => (rhs, lhs, max, min),
|
(min, max) => (rhs, lhs, max, min),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pad = iter::repeat(0).take(max_len - min_len);
|
let pad = iter::repeat_n(0, max_len - min_len);
|
||||||
|
|
||||||
let mut a;
|
let mut a;
|
||||||
let mut b;
|
let mut b;
|
||||||
@ -159,9 +159,10 @@ where
|
|||||||
}
|
}
|
||||||
(Value::Binary { .. }, Value::Int { .. }) | (Value::Int { .. }, Value::Binary { .. }) => {
|
(Value::Binary { .. }, Value::Int { .. }) | (Value::Int { .. }, Value::Binary { .. }) => {
|
||||||
Value::error(
|
Value::error(
|
||||||
ShellError::PipelineMismatch {
|
ShellError::OnlySupportsThisInputType {
|
||||||
exp_input_type: "input, and argument, to be both int or both binary"
|
exp_input_type: "input, and argument, to be both int or both binary"
|
||||||
.to_string(),
|
.to_string(),
|
||||||
|
wrong_type: "int and binary".to_string(),
|
||||||
dst_span: rhs.span(),
|
dst_span: rhs.span(),
|
||||||
src_span: span,
|
src_span: span,
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{get_number_bytes, NumberBytes};
|
use super::{NumberBytes, get_number_bytes};
|
||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -100,8 +100,7 @@ impl Command for BitsNot {
|
|||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description: "Apply logical negation to a list of numbers, treat input as 2 bytes number",
|
||||||
"Apply logical negation to a list of numbers, treat input as 2 bytes number",
|
|
||||||
example: "[4 3 2] | bits not --number-bytes 2",
|
example: "[4 3 2] | bits not --number-bytes 2",
|
||||||
result: Some(Value::list(
|
result: Some(Value::list(
|
||||||
vec![
|
vec![
|
||||||
@ -113,8 +112,7 @@ impl Command for BitsNot {
|
|||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description: "Apply logical negation to a list of numbers, treat input as signed number",
|
||||||
"Apply logical negation to a list of numbers, treat input as signed number",
|
|
||||||
example: "[4 3 2] | bits not --signed",
|
example: "[4 3 2] | bits not --signed",
|
||||||
result: Some(Value::list(
|
result: Some(Value::list(
|
||||||
vec![
|
vec![
|
||||||
|
@ -66,7 +66,7 @@ impl Command for BitsOr {
|
|||||||
return Err(ShellError::TypeMismatch {
|
return Err(ShellError::TypeMismatch {
|
||||||
err_message: "Endian must be one of native, little, big".to_string(),
|
err_message: "Endian must be one of native, little, big".to_string(),
|
||||||
span: endian.span,
|
span: endian.span,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -106,8 +106,7 @@ impl Command for BitsOr {
|
|||||||
result: Some(Value::test_binary(vec![0xca, 0xfe])),
|
result: Some(Value::test_binary(vec![0xca, 0xfe])),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description: "Apply bitwise or to binary data of varying lengths with specified endianness",
|
||||||
"Apply bitwise or to binary data of varying lengths with specified endianness",
|
|
||||||
example: "0x[c0 ff ee] | bits or 0x[ff] --endian big",
|
example: "0x[c0 ff ee] | bits or 0x[ff] --endian big",
|
||||||
result: Some(Value::test_binary(vec![0xc0, 0xff, 0xff])),
|
result: Some(Value::test_binary(vec![0xc0, 0xff, 0xff])),
|
||||||
},
|
},
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
use super::{InputNumType, NumberBytes, get_input_num_type, get_number_bytes};
|
||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
@ -222,7 +222,8 @@ fn rotate_bytes_and_bits_left(data: &[u8], byte_shift: usize, bit_shift: usize)
|
|||||||
debug_assert!(byte_shift < data.len());
|
debug_assert!(byte_shift < data.len());
|
||||||
debug_assert!(
|
debug_assert!(
|
||||||
(1..8).contains(&bit_shift),
|
(1..8).contains(&bit_shift),
|
||||||
"Bit shifts of 0 can't be handled by this impl and everything else should be part of the byteshift");
|
"Bit shifts of 0 can't be handled by this impl and everything else should be part of the byteshift"
|
||||||
|
);
|
||||||
let mut bytes = Vec::with_capacity(data.len());
|
let mut bytes = Vec::with_capacity(data.len());
|
||||||
let mut next_index = byte_shift;
|
let mut next_index = byte_shift;
|
||||||
for _ in 0..data.len() {
|
for _ in 0..data.len() {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
use super::{InputNumType, NumberBytes, get_input_num_type, get_number_bytes};
|
||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
use super::{InputNumType, NumberBytes, get_input_num_type, get_number_bytes};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
@ -237,7 +237,8 @@ fn shift_bytes_left(data: &[u8], byte_shift: usize) -> Vec<u8> {
|
|||||||
|
|
||||||
fn shift_bytes_and_bits_left(data: &[u8], byte_shift: usize, bit_shift: usize) -> Vec<u8> {
|
fn shift_bytes_and_bits_left(data: &[u8], byte_shift: usize, bit_shift: usize) -> Vec<u8> {
|
||||||
use itertools::Position::*;
|
use itertools::Position::*;
|
||||||
debug_assert!((1..8).contains(&bit_shift),
|
debug_assert!(
|
||||||
|
(1..8).contains(&bit_shift),
|
||||||
"Bit shifts of 0 can't be handled by this impl and everything else should be part of the byteshift"
|
"Bit shifts of 0 can't be handled by this impl and everything else should be part of the byteshift"
|
||||||
);
|
);
|
||||||
data.iter()
|
data.iter()
|
||||||
@ -249,7 +250,7 @@ fn shift_bytes_and_bits_left(data: &[u8], byte_shift: usize, bit_shift: usize) -
|
|||||||
Last | Only => lhs << bit_shift,
|
Last | Only => lhs << bit_shift,
|
||||||
_ => (lhs << bit_shift) | (rhs >> (8 - bit_shift)),
|
_ => (lhs << bit_shift) | (rhs >> (8 - bit_shift)),
|
||||||
})
|
})
|
||||||
.chain(iter::repeat(0).take(byte_shift))
|
.chain(iter::repeat_n(0, byte_shift))
|
||||||
.collect::<Vec<u8>>()
|
.collect::<Vec<u8>>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
|
use super::{InputNumType, NumberBytes, get_input_num_type, get_number_bytes};
|
||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
|
@ -66,7 +66,7 @@ impl Command for BitsXor {
|
|||||||
return Err(ShellError::TypeMismatch {
|
return Err(ShellError::TypeMismatch {
|
||||||
err_message: "Endian must be one of native, little, big".to_string(),
|
err_message: "Endian must be one of native, little, big".to_string(),
|
||||||
span: endian.span,
|
span: endian.span,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -106,8 +106,7 @@ impl Command for BitsXor {
|
|||||||
result: Some(Value::test_binary(vec![0x70, 0x40])),
|
result: Some(Value::test_binary(vec![0x70, 0x40])),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description: "Apply bitwise xor to binary data of varying lengths with specified endianness",
|
||||||
"Apply bitwise xor to binary data of varying lengths with specified endianness",
|
|
||||||
example: "0x[ca fe] | bits xor 0x[aa] --endian big",
|
example: "0x[ca fe] | bits xor 0x[aa] --endian big",
|
||||||
result: Some(Value::test_binary(vec![0xca, 0x54])),
|
result: Some(Value::test_binary(vec![0xca, 0x54])),
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::{command_prelude::*, ClosureEval, ClosureEvalOnce};
|
use nu_engine::{ClosureEval, ClosureEvalOnce, command_prelude::*};
|
||||||
use nu_protocol::engine::Closure;
|
use nu_protocol::engine::Closure;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::{vertical_rotate_value, VerticalDirection};
|
use super::{VerticalDirection, vertical_rotate_value};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::{horizontal_rotate_value, HorizontalDirection};
|
use super::{HorizontalDirection, horizontal_rotate_value};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::{horizontal_rotate_value, HorizontalDirection};
|
use super::{HorizontalDirection, horizontal_rotate_value};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use super::{vertical_rotate_value, VerticalDirection};
|
use super::{VerticalDirection, vertical_rotate_value};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -36,104 +36,93 @@ impl Command for Rotate {
|
|||||||
description: "Rotate a record clockwise, producing a table (like `transpose` but with column order reversed)",
|
description: "Rotate a record clockwise, producing a table (like `transpose` but with column order reversed)",
|
||||||
example: "{a:1, b:2} | rotate",
|
example: "{a:1, b:2} | rotate",
|
||||||
result: Some(Value::test_list(vec![
|
result: Some(Value::test_list(vec![
|
||||||
Value::test_record(record! {
|
Value::test_record(record! {
|
||||||
"column0" => Value::test_int(1),
|
"column0" => Value::test_int(1),
|
||||||
"column1" => Value::test_string("a"),
|
"column1" => Value::test_string("a"),
|
||||||
}),
|
}),
|
||||||
Value::test_record(record! {
|
Value::test_record(record! {
|
||||||
"column0" => Value::test_int(2),
|
"column0" => Value::test_int(2),
|
||||||
"column1" => Value::test_string("b"),
|
"column1" => Value::test_string("b"),
|
||||||
}),
|
}),
|
||||||
],
|
])),
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Rotate 2x3 table clockwise",
|
description: "Rotate 2x3 table clockwise",
|
||||||
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate",
|
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate",
|
||||||
result: Some(Value::test_list(
|
result: Some(Value::test_list(vec![
|
||||||
vec![
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"column0" => Value::test_int(5),
|
||||||
"column0" => Value::test_int(5),
|
"column1" => Value::test_int(3),
|
||||||
"column1" => Value::test_int(3),
|
"column2" => Value::test_int(1),
|
||||||
"column2" => Value::test_int(1),
|
"column3" => Value::test_string("a"),
|
||||||
"column3" => Value::test_string("a"),
|
}),
|
||||||
}),
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"column0" => Value::test_int(6),
|
||||||
"column0" => Value::test_int(6),
|
"column1" => Value::test_int(4),
|
||||||
"column1" => Value::test_int(4),
|
"column2" => Value::test_int(2),
|
||||||
"column2" => Value::test_int(2),
|
"column3" => Value::test_string("b"),
|
||||||
"column3" => Value::test_string("b"),
|
}),
|
||||||
}),
|
])),
|
||||||
],
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Rotate table clockwise and change columns names",
|
description: "Rotate table clockwise and change columns names",
|
||||||
example: "[[a b]; [1 2]] | rotate col_a col_b",
|
example: "[[a b]; [1 2]] | rotate col_a col_b",
|
||||||
result: Some(Value::test_list(
|
result: Some(Value::test_list(vec![
|
||||||
vec![
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"col_a" => Value::test_int(1),
|
||||||
"col_a" => Value::test_int(1),
|
"col_b" => Value::test_string("a"),
|
||||||
"col_b" => Value::test_string("a"),
|
}),
|
||||||
}),
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"col_a" => Value::test_int(2),
|
||||||
"col_a" => Value::test_int(2),
|
"col_b" => Value::test_string("b"),
|
||||||
"col_b" => Value::test_string("b"),
|
}),
|
||||||
}),
|
])),
|
||||||
],
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Rotate table counter clockwise",
|
description: "Rotate table counter clockwise",
|
||||||
example: "[[a b]; [1 2]] | rotate --ccw",
|
example: "[[a b]; [1 2]] | rotate --ccw",
|
||||||
result: Some(Value::test_list(
|
result: Some(Value::test_list(vec![
|
||||||
vec![
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"column0" => Value::test_string("b"),
|
||||||
"column0" => Value::test_string("b"),
|
"column1" => Value::test_int(2),
|
||||||
"column1" => Value::test_int(2),
|
}),
|
||||||
}),
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"column0" => Value::test_string("a"),
|
||||||
"column0" => Value::test_string("a"),
|
"column1" => Value::test_int(1),
|
||||||
"column1" => Value::test_int(1),
|
}),
|
||||||
}),
|
])),
|
||||||
],
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Rotate table counter-clockwise",
|
description: "Rotate table counter-clockwise",
|
||||||
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate --ccw",
|
example: "[[a b]; [1 2] [3 4] [5 6]] | rotate --ccw",
|
||||||
result: Some(Value::test_list(
|
result: Some(Value::test_list(vec![
|
||||||
vec![
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"column0" => Value::test_string("b"),
|
||||||
"column0" => Value::test_string("b"),
|
"column1" => Value::test_int(2),
|
||||||
"column1" => Value::test_int(2),
|
"column2" => Value::test_int(4),
|
||||||
"column2" => Value::test_int(4),
|
"column3" => Value::test_int(6),
|
||||||
"column3" => Value::test_int(6),
|
}),
|
||||||
}),
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"column0" => Value::test_string("a"),
|
||||||
"column0" => Value::test_string("a"),
|
"column1" => Value::test_int(1),
|
||||||
"column1" => Value::test_int(1),
|
"column2" => Value::test_int(3),
|
||||||
"column2" => Value::test_int(3),
|
"column3" => Value::test_int(5),
|
||||||
"column3" => Value::test_int(5),
|
}),
|
||||||
}),
|
])),
|
||||||
],
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Rotate table counter-clockwise and change columns names",
|
description: "Rotate table counter-clockwise and change columns names",
|
||||||
example: "[[a b]; [1 2]] | rotate --ccw col_a col_b",
|
example: "[[a b]; [1 2]] | rotate --ccw col_a col_b",
|
||||||
result: Some(Value::test_list(
|
result: Some(Value::test_list(vec![
|
||||||
vec![
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"col_a" => Value::test_string("b"),
|
||||||
"col_a" => Value::test_string("b"),
|
"col_b" => Value::test_int(2),
|
||||||
"col_b" => Value::test_int(2),
|
}),
|
||||||
}),
|
Value::test_record(record! {
|
||||||
Value::test_record(record! {
|
"col_a" => Value::test_string("a"),
|
||||||
"col_a" => Value::test_string("a"),
|
"col_b" => Value::test_int(1),
|
||||||
"col_b" => Value::test_int(1),
|
}),
|
||||||
}),
|
])),
|
||||||
],
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::{command_prelude::*, ClosureEval};
|
use nu_engine::{ClosureEval, command_prelude::*};
|
||||||
use nu_protocol::{engine::Closure, PipelineIterator};
|
use nu_protocol::{PipelineIterator, engine::Closure};
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -12,7 +12,10 @@ impl Command for UpdateCells {
|
|||||||
|
|
||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("update cells")
|
Signature::build("update cells")
|
||||||
.input_output_types(vec![(Type::table(), Type::table())])
|
.input_output_types(vec![
|
||||||
|
(Type::table(), Type::table()),
|
||||||
|
(Type::record(), Type::record()),
|
||||||
|
])
|
||||||
.required(
|
.required(
|
||||||
"closure",
|
"closure",
|
||||||
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
|
||||||
@ -77,6 +80,15 @@ impl Command for UpdateCells {
|
|||||||
"2021-11-18" => Value::test_string(""),
|
"2021-11-18" => Value::test_string(""),
|
||||||
})])),
|
})])),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
example: r#"{a: 1, b: 2, c: 3} | update cells { $in + 10 }"#,
|
||||||
|
description: "Update each value in a record.",
|
||||||
|
result: Some(Value::test_record(record! {
|
||||||
|
"a" => Value::test_int(11),
|
||||||
|
"b" => Value::test_int(12),
|
||||||
|
"c" => Value::test_int(13),
|
||||||
|
})),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +97,7 @@ impl Command for UpdateCells {
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
mut input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
let closure: Closure = call.req(engine_state, stack, 0)?;
|
let closure: Closure = call.req(engine_state, stack, 0)?;
|
||||||
@ -102,14 +114,51 @@ impl Command for UpdateCells {
|
|||||||
|
|
||||||
let metadata = input.metadata();
|
let metadata = input.metadata();
|
||||||
|
|
||||||
Ok(UpdateCellIterator {
|
match input {
|
||||||
iter: input.into_iter(),
|
PipelineData::Value(
|
||||||
closure: ClosureEval::new(engine_state, stack, closure),
|
Value::Record {
|
||||||
columns,
|
ref mut val,
|
||||||
span: head,
|
internal_span,
|
||||||
|
},
|
||||||
|
..,
|
||||||
|
) => {
|
||||||
|
let val = val.to_mut();
|
||||||
|
update_record(
|
||||||
|
val,
|
||||||
|
&mut ClosureEval::new(engine_state, stack, closure),
|
||||||
|
internal_span,
|
||||||
|
columns.as_ref(),
|
||||||
|
);
|
||||||
|
Ok(input)
|
||||||
|
}
|
||||||
|
_ => Ok(UpdateCellIterator {
|
||||||
|
iter: input.into_iter(),
|
||||||
|
closure: ClosureEval::new(engine_state, stack, closure),
|
||||||
|
columns,
|
||||||
|
span: head,
|
||||||
|
}
|
||||||
|
.into_pipeline_data(head, engine_state.signals().clone())
|
||||||
|
.set_metadata(metadata)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_record(
|
||||||
|
record: &mut Record,
|
||||||
|
closure: &mut ClosureEval,
|
||||||
|
span: Span,
|
||||||
|
cols: Option<&HashSet<String>>,
|
||||||
|
) {
|
||||||
|
if let Some(columns) = cols {
|
||||||
|
for (col, val) in record.iter_mut() {
|
||||||
|
if columns.contains(col) {
|
||||||
|
*val = eval_value(closure, span, std::mem::take(val));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (_, val) in record.iter_mut() {
|
||||||
|
*val = eval_value(closure, span, std::mem::take(val))
|
||||||
}
|
}
|
||||||
.into_pipeline_data(head, engine_state.signals().clone())
|
|
||||||
.set_metadata(metadata))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,18 +177,7 @@ impl Iterator for UpdateCellIterator {
|
|||||||
|
|
||||||
let value = if let Value::Record { val, .. } = &mut value {
|
let value = if let Value::Record { val, .. } = &mut value {
|
||||||
let val = val.to_mut();
|
let val = val.to_mut();
|
||||||
if let Some(columns) = &self.columns {
|
update_record(val, &mut self.closure, self.span, self.columns.as_ref());
|
||||||
for (col, val) in val.iter_mut() {
|
|
||||||
if columns.contains(col) {
|
|
||||||
*val = eval_value(&mut self.closure, self.span, std::mem::take(val));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (_, val) in val.iter_mut() {
|
|
||||||
*val = eval_value(&mut self.closure, self.span, std::mem::take(val))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value
|
value
|
||||||
} else {
|
} else {
|
||||||
eval_value(&mut self.closure, self.span, value)
|
eval_value(&mut self.closure, self.span, value)
|
||||||
|
@ -101,7 +101,7 @@ impl Command for ToHtml {
|
|||||||
.named(
|
.named(
|
||||||
"theme",
|
"theme",
|
||||||
SyntaxShape::String,
|
SyntaxShape::String,
|
||||||
"the name of the theme to use (github, blulocolight, ...)",
|
"the name of the theme to use (github, blulocolight, ...); case-insensitive",
|
||||||
Some('t'),
|
Some('t'),
|
||||||
)
|
)
|
||||||
.switch(
|
.switch(
|
||||||
@ -163,9 +163,16 @@ fn get_theme_from_asset_file(
|
|||||||
) -> Result<HashMap<&'static str, String>, ShellError> {
|
) -> Result<HashMap<&'static str, String>, ShellError> {
|
||||||
let theme_name = match theme {
|
let theme_name = match theme {
|
||||||
Some(s) => &s.item,
|
Some(s) => &s.item,
|
||||||
None => "default", // There is no theme named "default" so this will be HtmlTheme::default(), which is "nu_default".
|
None => {
|
||||||
|
return Ok(convert_html_theme_to_hash_map(
|
||||||
|
is_dark,
|
||||||
|
&HtmlTheme::default(),
|
||||||
|
));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let theme_span = theme.map(|s| s.span).unwrap_or(Span::unknown());
|
||||||
|
|
||||||
// 228 themes come from
|
// 228 themes come from
|
||||||
// https://github.com/mbadolato/iTerm2-Color-Schemes/tree/master/windowsterminal
|
// https://github.com/mbadolato/iTerm2-Color-Schemes/tree/master/windowsterminal
|
||||||
// we should find a hit on any name in there
|
// we should find a hit on any name in there
|
||||||
@ -175,8 +182,17 @@ fn get_theme_from_asset_file(
|
|||||||
let th = asset
|
let th = asset
|
||||||
.themes
|
.themes
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.find(|n| n.name.eq_ignore_case(theme_name)) // case insensitive search
|
.find(|n| n.name.eq_ignore_case(theme_name)); // case insensitive search
|
||||||
.unwrap_or_default();
|
|
||||||
|
let th = match th {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
return Err(ShellError::TypeMismatch {
|
||||||
|
err_message: format!("Unknown HTML theme '{theme_name}'"),
|
||||||
|
span: theme_span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
Ok(convert_html_theme_to_hash_map(is_dark, &th))
|
Ok(convert_html_theme_to_hash_map(is_dark, &th))
|
||||||
}
|
}
|
||||||
@ -257,18 +273,20 @@ fn to_html(
|
|||||||
None => head,
|
None => head,
|
||||||
};
|
};
|
||||||
|
|
||||||
let color_hm = get_theme_from_asset_file(dark, theme.as_ref());
|
let color_hm = match get_theme_from_asset_file(dark, theme.as_ref()) {
|
||||||
let color_hm = match color_hm {
|
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
_ => {
|
Err(e) => match e {
|
||||||
return Err(ShellError::GenericError {
|
ShellError::TypeMismatch {
|
||||||
error: "Error finding theme name".into(),
|
err_message,
|
||||||
msg: "Error finding theme name".into(),
|
span: _,
|
||||||
span: Some(theme_span),
|
} => {
|
||||||
help: None,
|
return Err(ShellError::TypeMismatch {
|
||||||
inner: vec![],
|
err_message,
|
||||||
})
|
span: theme_span,
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
_ => return Err(e),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// change the color of the page
|
// change the color of the page
|
||||||
@ -703,4 +721,87 @@ mod tests {
|
|||||||
|
|
||||||
test_examples(ToHtml {})
|
test_examples(ToHtml {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn get_theme_from_asset_file_returns_default() {
|
||||||
|
let result = super::get_theme_from_asset_file(false, None);
|
||||||
|
|
||||||
|
assert!(result.is_ok(), "Expected Ok result for None theme");
|
||||||
|
|
||||||
|
let theme_map = result.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
theme_map.get("background").map(String::as_str),
|
||||||
|
Some("white"),
|
||||||
|
"Expected default background color to be white"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
theme_map.get("foreground").map(String::as_str),
|
||||||
|
Some("black"),
|
||||||
|
"Expected default foreground color to be black"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
theme_map.contains_key("red"),
|
||||||
|
"Expected default theme to have a 'red' color"
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
theme_map.contains_key("bold_green"),
|
||||||
|
"Expected default theme to have a 'bold_green' color"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn returns_a_valid_theme() {
|
||||||
|
let theme_name = "Dracula".to_string().into_spanned(Span::new(0, 7));
|
||||||
|
let result = super::get_theme_from_asset_file(false, Some(&theme_name));
|
||||||
|
|
||||||
|
assert!(result.is_ok(), "Expected Ok result for valid theme");
|
||||||
|
let theme_map = result.unwrap();
|
||||||
|
let required_keys = [
|
||||||
|
"background",
|
||||||
|
"foreground",
|
||||||
|
"red",
|
||||||
|
"green",
|
||||||
|
"blue",
|
||||||
|
"bold_red",
|
||||||
|
"bold_green",
|
||||||
|
"bold_blue",
|
||||||
|
];
|
||||||
|
|
||||||
|
for key in required_keys {
|
||||||
|
assert!(
|
||||||
|
theme_map.contains_key(key),
|
||||||
|
"Expected theme to contain key '{key}'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fails_with_unknown_theme_name() {
|
||||||
|
let result = super::get_theme_from_asset_file(
|
||||||
|
false,
|
||||||
|
Some(&"doesnt-exist".to_string().into_spanned(Span::new(0, 13))),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(result.is_err(), "Expected error for invalid theme name");
|
||||||
|
|
||||||
|
if let Err(err) = result {
|
||||||
|
assert!(
|
||||||
|
matches!(err, ShellError::TypeMismatch { .. }),
|
||||||
|
"Expected TypeMismatch error, got: {err:?}"
|
||||||
|
);
|
||||||
|
|
||||||
|
if let ShellError::TypeMismatch { err_message, span } = err {
|
||||||
|
assert!(
|
||||||
|
err_message.contains("doesnt-exist"),
|
||||||
|
"Error message should mention theme name, got: {err_message}"
|
||||||
|
);
|
||||||
|
assert_eq!(span.start, 0);
|
||||||
|
assert_eq!(span.end, 13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_ansi_term::{build_all_gradient_text, gradient::TargetGround, Gradient, Rgb};
|
use nu_ansi_term::{Gradient, Rgb, build_all_gradient_text, gradient::TargetGround};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -70,29 +70,25 @@ impl Command for SubCommand {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "draw text in a gradient with foreground start and end colors",
|
description: "draw text in a gradient with foreground start and end colors",
|
||||||
example:
|
example: "'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff'",
|
||||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff'",
|
result: None,
|
||||||
result: None,
|
},
|
||||||
},
|
Example {
|
||||||
Example {
|
description: "draw text in a gradient with foreground start and end colors and background start and end colors",
|
||||||
description: "draw text in a gradient with foreground start and end colors and background start and end colors",
|
example: "'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff' --bgstart '0xe81cff' --bgend '0x40c9ff'",
|
||||||
example:
|
result: None,
|
||||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff' --fgend '0xe81cff' --bgstart '0xe81cff' --bgend '0x40c9ff'",
|
},
|
||||||
result: None,
|
Example {
|
||||||
},
|
description: "draw text in a gradient by specifying foreground start color - end color is assumed to be black",
|
||||||
Example {
|
example: "'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff'",
|
||||||
description: "draw text in a gradient by specifying foreground start color - end color is assumed to be black",
|
result: None,
|
||||||
example:
|
},
|
||||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgstart '0x40c9ff'",
|
Example {
|
||||||
result: None,
|
description: "draw text in a gradient by specifying foreground end color - start color is assumed to be black",
|
||||||
},
|
example: "'Hello, Nushell! This is a gradient.' | ansi gradient --fgend '0xe81cff'",
|
||||||
Example {
|
result: None,
|
||||||
description: "draw text in a gradient by specifying foreground end color - start color is assumed to be black",
|
},
|
||||||
example:
|
|
||||||
"'Hello, Nushell! This is a gradient.' | ansi gradient --fgend '0xe81cff'",
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -300,7 +296,7 @@ fn action(
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{action, SubCommand};
|
use super::{SubCommand, action};
|
||||||
use nu_ansi_term::Rgb;
|
use nu_ansi_term::Rgb;
|
||||||
use nu_protocol::{Span, Value};
|
use nu_protocol::{Span, Value};
|
||||||
|
|
||||||
@ -314,7 +310,9 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_fg_gradient() {
|
fn test_fg_gradient() {
|
||||||
let input_string = Value::test_string("Hello, World!");
|
let input_string = Value::test_string("Hello, World!");
|
||||||
let expected = Value::test_string("\u{1b}[38;2;64;201;255mH\u{1b}[38;2;76;187;254me\u{1b}[38;2;89;174;254ml\u{1b}[38;2;102;160;254ml\u{1b}[38;2;115;147;254mo\u{1b}[38;2;128;133;254m,\u{1b}[38;2;141;120;254m \u{1b}[38;2;153;107;254mW\u{1b}[38;2;166;94;254mo\u{1b}[38;2;179;80;254mr\u{1b}[38;2;192;67;254ml\u{1b}[38;2;205;53;254md\u{1b}[38;2;218;40;254m!\u{1b}[0m");
|
let expected = Value::test_string(
|
||||||
|
"\u{1b}[38;2;64;201;255mH\u{1b}[38;2;76;187;254me\u{1b}[38;2;89;174;254ml\u{1b}[38;2;102;160;254ml\u{1b}[38;2;115;147;254mo\u{1b}[38;2;128;133;254m,\u{1b}[38;2;141;120;254m \u{1b}[38;2;153;107;254mW\u{1b}[38;2;166;94;254mo\u{1b}[38;2;179;80;254mr\u{1b}[38;2;192;67;254ml\u{1b}[38;2;205;53;254md\u{1b}[38;2;218;40;254m!\u{1b}[0m",
|
||||||
|
);
|
||||||
let fg_start = Rgb::from_hex_string("0x40c9ff".to_string());
|
let fg_start = Rgb::from_hex_string("0x40c9ff".to_string());
|
||||||
let fg_end = Rgb::from_hex_string("0xe81cff".to_string());
|
let fg_end = Rgb::from_hex_string("0xe81cff".to_string());
|
||||||
let actual = action(
|
let actual = action(
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
use nu_cmd_base::input_handler::{operate, CmdArgument};
|
use nu_cmd_base::input_handler::{CmdArgument, operate};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
use nu_protocol::{shell_error::io::IoError, Signals};
|
use nu_protocol::{Signals, shell_error::io::IoError};
|
||||||
use num_traits::ToPrimitive;
|
use num_traits::ToPrimitive;
|
||||||
|
|
||||||
struct Arguments {
|
struct Arguments {
|
||||||
@ -68,42 +68,33 @@ impl Command for FormatBits {
|
|||||||
Example {
|
Example {
|
||||||
description: "convert a binary value into a string, padded to 8 places with 0s",
|
description: "convert a binary value into a string, padded to 8 places with 0s",
|
||||||
example: "0x[1] | format bits",
|
example: "0x[1] | format bits",
|
||||||
result: Some(Value::string("00000001",
|
result: Some(Value::string("00000001", Span::test_data())),
|
||||||
Span::test_data(),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert an int into a string, padded to 8 places with 0s",
|
description: "convert an int into a string, padded to 8 places with 0s",
|
||||||
example: "1 | format bits",
|
example: "1 | format bits",
|
||||||
result: Some(Value::string("00000001",
|
result: Some(Value::string("00000001", Span::test_data())),
|
||||||
Span::test_data(),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert a filesize value into a string, padded to 8 places with 0s",
|
description: "convert a filesize value into a string, padded to 8 places with 0s",
|
||||||
example: "1b | format bits",
|
example: "1b | format bits",
|
||||||
result: Some(Value::string("00000001",
|
result: Some(Value::string("00000001", Span::test_data())),
|
||||||
Span::test_data(),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert a duration value into a string, padded to 8 places with 0s",
|
description: "convert a duration value into a string, padded to 8 places with 0s",
|
||||||
example: "1ns | format bits",
|
example: "1ns | format bits",
|
||||||
result: Some(Value::string("00000001",
|
result: Some(Value::string("00000001", Span::test_data())),
|
||||||
Span::test_data(),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert a boolean value into a string, padded to 8 places with 0s",
|
description: "convert a boolean value into a string, padded to 8 places with 0s",
|
||||||
example: "true | format bits",
|
example: "true | format bits",
|
||||||
result: Some(Value::string("00000001",
|
result: Some(Value::string("00000001", Span::test_data())),
|
||||||
Span::test_data(),
|
|
||||||
)),
|
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "convert a string into a raw binary string, padded with 0s to 8 places",
|
description: "convert a string into a raw binary string, padded with 0s to 8 places",
|
||||||
example: "'nushell.sh' | format bits",
|
example: "'nushell.sh' | format bits",
|
||||||
result: Some(Value::string("01101110 01110101 01110011 01101000 01100101 01101100 01101100 00101110 01110011 01101000",
|
result: Some(Value::string(
|
||||||
|
"01101110 01110101 01110011 01101000 01100101 01101100 01101100 00101110 01110011 01101000",
|
||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
@ -143,7 +134,7 @@ fn byte_stream_to_bits(stream: ByteStream, head: Span) -> ByteStream {
|
|||||||
let mut byte = [0];
|
let mut byte = [0];
|
||||||
if reader
|
if reader
|
||||||
.read(&mut byte[..])
|
.read(&mut byte[..])
|
||||||
.map_err(|err| IoError::new(err.kind(), head, None))?
|
.map_err(|err| IoError::new(err, head, None))?
|
||||||
> 0
|
> 0
|
||||||
{
|
{
|
||||||
// Format the byte as bits
|
// Format the byte as bits
|
||||||
@ -170,28 +161,28 @@ fn convert_to_smallest_number_type(num: i64, span: Span) -> Value {
|
|||||||
let bytes = v.to_ne_bytes();
|
let bytes = v.to_ne_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in bytes {
|
for ch in bytes {
|
||||||
raw_string.push_str(&format!("{:08b} ", ch));
|
raw_string.push_str(&format!("{ch:08b} "));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
} else if let Some(v) = num.to_i16() {
|
} else if let Some(v) = num.to_i16() {
|
||||||
let bytes = v.to_ne_bytes();
|
let bytes = v.to_ne_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in bytes {
|
for ch in bytes {
|
||||||
raw_string.push_str(&format!("{:08b} ", ch));
|
raw_string.push_str(&format!("{ch:08b} "));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
} else if let Some(v) = num.to_i32() {
|
} else if let Some(v) = num.to_i32() {
|
||||||
let bytes = v.to_ne_bytes();
|
let bytes = v.to_ne_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in bytes {
|
for ch in bytes {
|
||||||
raw_string.push_str(&format!("{:08b} ", ch));
|
raw_string.push_str(&format!("{ch:08b} "));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
} else {
|
} else {
|
||||||
let bytes = num.to_ne_bytes();
|
let bytes = num.to_ne_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in bytes {
|
for ch in bytes {
|
||||||
raw_string.push_str(&format!("{:08b} ", ch));
|
raw_string.push_str(&format!("{ch:08b} "));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
}
|
}
|
||||||
@ -202,7 +193,7 @@ fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
|||||||
Value::Binary { val, .. } => {
|
Value::Binary { val, .. } => {
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in val {
|
for ch in val {
|
||||||
raw_string.push_str(&format!("{:08b} ", ch));
|
raw_string.push_str(&format!("{ch:08b} "));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
}
|
}
|
||||||
@ -213,7 +204,7 @@ fn action(input: &Value, _args: &Arguments, span: Span) -> Value {
|
|||||||
let raw_bytes = val.as_bytes();
|
let raw_bytes = val.as_bytes();
|
||||||
let mut raw_string = "".to_string();
|
let mut raw_string = "".to_string();
|
||||||
for ch in raw_bytes {
|
for ch in raw_bytes {
|
||||||
raw_string.push_str(&format!("{:08b} ", ch));
|
raw_string.push_str(&format!("{ch:08b} "));
|
||||||
}
|
}
|
||||||
Value::string(raw_string.trim(), span)
|
Value::string(raw_string.trim(), span)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{ast::PathMember, engine::StateWorkingSet, Config, ListStream};
|
use nu_protocol::{Config, ListStream, ast::PathMember, casing::Casing, engine::StateWorkingSet};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FormatPattern;
|
pub struct FormatPattern;
|
||||||
@ -214,7 +214,7 @@ fn format(
|
|||||||
wrong_type: val.get_type().to_string(),
|
wrong_type: val.get_type().to_string(),
|
||||||
dst_span: head_span,
|
dst_span: head_span,
|
||||||
src_span: val.span(),
|
src_span: val.span(),
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -251,14 +251,14 @@ fn format_record(
|
|||||||
val: path.to_string(),
|
val: path.to_string(),
|
||||||
span: *span,
|
span: *span,
|
||||||
optional: false,
|
optional: false,
|
||||||
|
casing: Casing::Sensitive,
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
match data_as_value.clone().follow_cell_path(&path_members, false) {
|
|
||||||
Ok(value_at_column) => {
|
let expanded_string = data_as_value
|
||||||
output.push_str(value_at_column.to_expanded_string(", ", config).as_str())
|
.follow_cell_path(&path_members)?
|
||||||
}
|
.to_expanded_string(", ", config);
|
||||||
Err(se) => return Err(se),
|
output.push_str(expanded_string.as_str())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_cmd_base::input_handler::{operate, CellPathOnlyArgs};
|
use nu_cmd_base::input_handler::{CellPathOnlyArgs, operate};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -16,6 +16,11 @@ impl Command for FormatNumber {
|
|||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("format number")
|
Signature::build("format number")
|
||||||
.input_output_types(vec![(Type::Number, Type::record())])
|
.input_output_types(vec![(Type::Number, Type::record())])
|
||||||
|
.switch(
|
||||||
|
"no-prefix",
|
||||||
|
"don't include the binary, hex or octal prefixes",
|
||||||
|
Some('n'),
|
||||||
|
)
|
||||||
.category(Category::Conversions)
|
.category(Category::Conversions)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,20 +29,36 @@ impl Command for FormatNumber {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![
|
||||||
description: "Get a record containing multiple formats for the number 42",
|
Example {
|
||||||
example: "42 | format number",
|
description: "Get a record containing multiple formats for the number 42",
|
||||||
result: Some(Value::test_record(record! {
|
example: "42 | format number",
|
||||||
"binary" => Value::test_string("0b101010"),
|
result: Some(Value::test_record(record! {
|
||||||
"debug" => Value::test_string("42"),
|
"debug" => Value::test_string("42"),
|
||||||
"display" => Value::test_string("42"),
|
"display" => Value::test_string("42"),
|
||||||
"lowerexp" => Value::test_string("4.2e1"),
|
"binary" => Value::test_string("0b101010"),
|
||||||
"lowerhex" => Value::test_string("0x2a"),
|
"lowerexp" => Value::test_string("4.2e1"),
|
||||||
"octal" => Value::test_string("0o52"),
|
"upperexp" => Value::test_string("4.2E1"),
|
||||||
"upperexp" => Value::test_string("4.2E1"),
|
"lowerhex" => Value::test_string("0x2a"),
|
||||||
"upperhex" => Value::test_string("0x2A"),
|
"upperhex" => Value::test_string("0x2A"),
|
||||||
})),
|
"octal" => Value::test_string("0o52"),
|
||||||
}]
|
})),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Format float without prefixes",
|
||||||
|
example: "3.14 | format number --no-prefix",
|
||||||
|
result: Some(Value::test_record(record! {
|
||||||
|
"debug" => Value::test_string("3.14"),
|
||||||
|
"display" => Value::test_string("3.14"),
|
||||||
|
"binary" => Value::test_string("100000000001001000111101011100001010001111010111000010100011111"),
|
||||||
|
"lowerexp" => Value::test_string("3.14e0"),
|
||||||
|
"upperexp" => Value::test_string("3.14E0"),
|
||||||
|
"lowerhex" => Value::test_string("40091eb851eb851f"),
|
||||||
|
"upperhex" => Value::test_string("40091EB851EB851F"),
|
||||||
|
"octal" => Value::test_string("400110753412172702437"),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -59,14 +80,24 @@ pub(crate) fn format_number(
|
|||||||
) -> 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.signals())
|
if call.has_flag(engine_state, stack, "no-prefix")? {
|
||||||
|
operate(
|
||||||
|
action_no_prefix,
|
||||||
|
args,
|
||||||
|
input,
|
||||||
|
call.head,
|
||||||
|
engine_state.signals(),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
operate(action, args, input, call.head, engine_state.signals())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
match input {
|
match input {
|
||||||
Value::Float { val, .. } => format_f64(*val, span),
|
Value::Float { val, .. } => format_f64(*val, false, span),
|
||||||
Value::Int { val, .. } => format_i64(*val, span),
|
Value::Int { val, .. } => format_i64(*val, false, span),
|
||||||
Value::Filesize { val, .. } => format_i64(val.get(), span),
|
Value::Filesize { val, .. } => format_i64(val.get(), false, span),
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
// Propagate errors by explicitly matching them before the final case.
|
||||||
Value::Error { .. } => input.clone(),
|
Value::Error { .. } => input.clone(),
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
@ -81,33 +112,80 @@ fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_i64(num: i64, span: Span) -> Value {
|
fn action_no_prefix(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||||
|
match input {
|
||||||
|
Value::Float { val, .. } => format_f64(*val, true, span),
|
||||||
|
Value::Int { val, .. } => format_i64(*val, true, span),
|
||||||
|
Value::Filesize { val, .. } => format_i64(val.get(), true, span),
|
||||||
|
// Propagate errors by explicitly matching them before the final case.
|
||||||
|
Value::Error { .. } => input.clone(),
|
||||||
|
other => Value::error(
|
||||||
|
ShellError::OnlySupportsThisInputType {
|
||||||
|
exp_input_type: "float, int, or filesize".into(),
|
||||||
|
wrong_type: other.get_type().to_string(),
|
||||||
|
dst_span: span,
|
||||||
|
src_span: other.span(),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn format_i64(num: i64, no_prefix: bool, span: Span) -> Value {
|
||||||
Value::record(
|
Value::record(
|
||||||
record! {
|
record! {
|
||||||
"binary" => Value::string(format!("{num:#b}"), span),
|
|
||||||
"debug" => Value::string(format!("{num:#?}"), span),
|
"debug" => Value::string(format!("{num:#?}"), span),
|
||||||
"display" => Value::string(format!("{num}"), span),
|
"display" => Value::string(format!("{num}"), span),
|
||||||
|
"binary" => Value::string(
|
||||||
|
if no_prefix { format!("{num:b}") } else { format!("{num:#b}") },
|
||||||
|
span,
|
||||||
|
),
|
||||||
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
||||||
"lowerhex" => Value::string(format!("{num:#x}"), span),
|
|
||||||
"octal" => Value::string(format!("{num:#o}"), span),
|
|
||||||
"upperexp" => Value::string(format!("{num:#E}"), span),
|
"upperexp" => Value::string(format!("{num:#E}"), span),
|
||||||
"upperhex" => Value::string(format!("{num:#X}"), span),
|
"lowerhex" => Value::string(
|
||||||
|
if no_prefix { format!("{num:x}") } else { format!("{num:#x}") },
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
"upperhex" => Value::string(
|
||||||
|
if no_prefix { format!("{num:X}") } else { format!("{num:#X}") },
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
"octal" => Value::string(
|
||||||
|
if no_prefix { format!("{num:o}") } else { format!("{num:#o}") },
|
||||||
|
span,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn format_f64(num: f64, span: Span) -> Value {
|
fn format_f64(num: f64, no_prefix: bool, span: Span) -> Value {
|
||||||
Value::record(
|
Value::record(
|
||||||
record! {
|
record! {
|
||||||
"binary" => Value::string(format!("{:b}", num.to_bits()), span),
|
|
||||||
"debug" => Value::string(format!("{num:#?}"), span),
|
"debug" => Value::string(format!("{num:#?}"), span),
|
||||||
"display" => Value::string(format!("{num}"), span),
|
"display" => Value::string(format!("{num}"), span),
|
||||||
|
"binary" => Value::string(
|
||||||
|
if no_prefix {
|
||||||
|
format!("{:b}", num.to_bits())
|
||||||
|
} else {
|
||||||
|
format!("{:#b}", num.to_bits())
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
),
|
||||||
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
"lowerexp" => Value::string(format!("{num:#e}"), span),
|
||||||
"lowerhex" => Value::string(format!("{:0x}", num.to_bits()), span),
|
|
||||||
"octal" => Value::string(format!("{:0o}", num.to_bits()), span),
|
|
||||||
"upperexp" => Value::string(format!("{num:#E}"), span),
|
"upperexp" => Value::string(format!("{num:#E}"), span),
|
||||||
"upperhex" => Value::string(format!("{:0X}", num.to_bits()), span),
|
"lowerhex" => Value::string(
|
||||||
|
if no_prefix { format!("{:x}", num.to_bits()) } else { format!("{:#x}", num.to_bits()) },
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
"upperhex" => Value::string(
|
||||||
|
if no_prefix { format!("{:X}", num.to_bits()) } else { format!("{:#X}", num.to_bits()) },
|
||||||
|
span,
|
||||||
|
),
|
||||||
|
"octal" => Value::string(
|
||||||
|
if no_prefix { format!("{:o}", num.to_bits()) } else { format!("{:#o}", num.to_bits()) },
|
||||||
|
span,
|
||||||
|
)
|
||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
|
@ -14,7 +14,7 @@ pub use snake_case::StrSnakeCase;
|
|||||||
pub use str_::Str;
|
pub use str_::Str;
|
||||||
pub use title_case::StrTitleCase;
|
pub use title_case::StrTitleCase;
|
||||||
|
|
||||||
use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument};
|
use nu_cmd_base::input_handler::{CmdArgument, operate as general_operate};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
struct Arguments<F: Fn(&str) -> String + Send + Sync + 'static> {
|
struct Arguments<F: Fn(&str) -> String + Send + Sync + 'static> {
|
||||||
|
@ -3,10 +3,10 @@ authors = ["The Nushell Project Developers"]
|
|||||||
build = "build.rs"
|
build = "build.rs"
|
||||||
description = "Nushell's core language commands"
|
description = "Nushell's core language commands"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.103.0"
|
version = "0.105.2"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
@ -15,20 +15,23 @@ bench = false
|
|||||||
workspace = true
|
workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.103.0", default-features = false }
|
nu-engine = { path = "../nu-engine", version = "0.105.2", default-features = false }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.103.0" }
|
nu-experimental = { path = "../nu-experimental", version = "0.105.2" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.103.0", default-features = false }
|
nu-parser = { path = "../nu-parser", version = "0.105.2" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.103.0", default-features = false }
|
nu-protocol = { path = "../nu-protocol", version = "0.105.2", default-features = false }
|
||||||
|
nu-utils = { path = "../nu-utils", version = "0.105.2", default-features = false }
|
||||||
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.105.2" }
|
||||||
|
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
shadow-rs = { version = "0.38", default-features = false }
|
shadow-rs = { version = "1.2", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.38", default-features = false }
|
shadow-rs = { version = "1.2", default-features = false, features = ["build"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = { workspace = true }
|
quickcheck = { workspace = true }
|
||||||
quickcheck_macros = { workspace = true }
|
quickcheck_macros = { workspace = true }
|
||||||
|
miette = { workspace = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["os"]
|
default = ["os"]
|
||||||
@ -41,8 +44,3 @@ plugin = [
|
|||||||
"nu-protocol/plugin",
|
"nu-protocol/plugin",
|
||||||
"os",
|
"os",
|
||||||
]
|
]
|
||||||
|
|
||||||
trash-support = []
|
|
||||||
sqlite = []
|
|
||||||
static-link-openssl = []
|
|
||||||
system-clipboard = []
|
|
||||||
|
@ -18,4 +18,4 @@ A base crate is one with minimal dependencies in our system so that other develo
|
|||||||
|
|
||||||
### Background on nu-cmd-lang
|
### Background on nu-cmd-lang
|
||||||
|
|
||||||
This crate was designed to be a small, concise set of tools or commands that serve as the *foundation layer* of both nu and nushell. These are the core commands needed to have a nice working version of the *nu language* without all of the support that the other commands provide inside nushell. Prior to the launch of this crate all of our commands were housed in the crate *nu-command*. Moving forward we would like to *slowly* break out the commands in nu-command into different crates; the naming and how this will work and where all the commands will be located is a "work in progress" especially now that the *standard library* is starting to become more popular as a location for commands. As time goes on some of our commands written in rust will be migrated to nu and when this happens they will be moved into the *standard library*.
|
This crate was designed to be a small, concise set of tools or commands that serve as the *foundation layer* of both nu and nushell. These are the core commands needed to have a nice working version of the *nu language* without all of the support that the other commands provide inside nushell. Prior to the launch of this crate all of our commands were housed in the crate *nu-command*. Moving forward we would like to *slowly* break out the commands in nu-command into different crates; the naming and how this will work and where all the commands will be located is a "work in progress" especially now that the *standard library* is starting to become more popular as a location for commands. As time goes on some of our commands written in rust will be migrated to nu and when this happens they will be moved into the *standard library*.
|
||||||
|
148
crates/nu-cmd-lang/src/core_commands/attr/deprecated.rs
Normal file
148
crates/nu-cmd-lang/src/core_commands/attr/deprecated.rs
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
use nu_cmd_base::WrapCall;
|
||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct AttrDeprecated;
|
||||||
|
|
||||||
|
impl Command for AttrDeprecated {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"attr deprecated"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("attr deprecated")
|
||||||
|
.input_output_types(vec![
|
||||||
|
(Type::Nothing, Type::Nothing),
|
||||||
|
(Type::Nothing, Type::String),
|
||||||
|
])
|
||||||
|
.optional(
|
||||||
|
"message",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"Help message to include with deprecation warning.",
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"flag",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"Mark a flag as deprecated rather than the command",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"since",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"Denote a version when this item was deprecated",
|
||||||
|
Some('s'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"remove",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"Denote a version when this item will be removed",
|
||||||
|
Some('r'),
|
||||||
|
)
|
||||||
|
.named(
|
||||||
|
"report",
|
||||||
|
SyntaxShape::String,
|
||||||
|
"How to warn about this item. One of: first (default), every",
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.category(Category::Core)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn description(&self) -> &str {
|
||||||
|
"Attribute for marking a command or flag as deprecated."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn extra_description(&self) -> &str {
|
||||||
|
"Mark a command (default) or flag/switch (--flag) as deprecated. By default, only the first usage will trigger a deprecation warning.
|
||||||
|
|
||||||
|
A help message can be included to provide more context for the deprecation, such as what to use as a replacement.
|
||||||
|
|
||||||
|
Also consider setting the category to deprecated with @category deprecated"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let call = WrapCall::Eval(engine_state, stack, call);
|
||||||
|
Ok(deprecated_record(call)?.into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let call = WrapCall::ConstEval(working_set, call);
|
||||||
|
Ok(deprecated_record(call)?.into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![
|
||||||
|
Example {
|
||||||
|
description: "Add a deprecation warning to a custom command",
|
||||||
|
example: r###"@deprecated
|
||||||
|
def outdated [] {}"###,
|
||||||
|
result: Some(Value::nothing(Span::test_data())),
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Add a deprecation warning with a custom message",
|
||||||
|
example: r###"@deprecated "Use my-new-command instead."
|
||||||
|
@category deprecated
|
||||||
|
def my-old-command [] {}"###,
|
||||||
|
result: Some(Value::string(
|
||||||
|
"Use my-new-command instead.",
|
||||||
|
Span::test_data(),
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deprecated_record(call: WrapCall) -> Result<Value, ShellError> {
|
||||||
|
let (call, message): (_, Option<Spanned<String>>) = call.opt(0)?;
|
||||||
|
let (call, flag): (_, Option<Spanned<String>>) = call.get_flag("flag")?;
|
||||||
|
let (call, since): (_, Option<Spanned<String>>) = call.get_flag("since")?;
|
||||||
|
let (call, remove): (_, Option<Spanned<String>>) = call.get_flag("remove")?;
|
||||||
|
let (call, report): (_, Option<Spanned<String>>) = call.get_flag("report")?;
|
||||||
|
|
||||||
|
let mut record = Record::new();
|
||||||
|
if let Some(message) = message {
|
||||||
|
record.push("help", Value::string(message.item, message.span))
|
||||||
|
}
|
||||||
|
if let Some(flag) = flag {
|
||||||
|
record.push("flag", Value::string(flag.item, flag.span))
|
||||||
|
}
|
||||||
|
if let Some(since) = since {
|
||||||
|
record.push("since", Value::string(since.item, since.span))
|
||||||
|
}
|
||||||
|
if let Some(remove) = remove {
|
||||||
|
record.push("expected_removal", Value::string(remove.item, remove.span))
|
||||||
|
}
|
||||||
|
|
||||||
|
let report = if let Some(Spanned { item, span }) = report {
|
||||||
|
match item.as_str() {
|
||||||
|
"every" => Value::string(item, span),
|
||||||
|
"first" => Value::string(item, span),
|
||||||
|
_ => {
|
||||||
|
return Err(ShellError::IncorrectValue {
|
||||||
|
msg: "The report mode must be one of: every, first".into(),
|
||||||
|
val_span: span,
|
||||||
|
call_span: call.head(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Value::string("first", call.head())
|
||||||
|
};
|
||||||
|
record.push("report", report);
|
||||||
|
|
||||||
|
Ok(Value::record(record, call.head()))
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
mod category;
|
mod category;
|
||||||
|
mod deprecated;
|
||||||
mod example;
|
mod example;
|
||||||
mod search_terms;
|
mod search_terms;
|
||||||
|
|
||||||
pub use category::AttrCategory;
|
pub use category::AttrCategory;
|
||||||
|
pub use deprecated::AttrDeprecated;
|
||||||
pub use example::AttrExample;
|
pub use example::AttrExample;
|
||||||
pub use search_terms::AttrSearchTerms;
|
pub use search_terms::AttrSearchTerms;
|
||||||
|
@ -34,10 +34,15 @@ impl Command for Break {
|
|||||||
&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> {
|
||||||
Err(ShellError::Break { span: call.head })
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
eprintln!(
|
||||||
|
"Tried to execute 'run' for the 'break' command: this code path should never be reached in IR mode"
|
||||||
|
);
|
||||||
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block, redirect_env};
|
use nu_engine::{command_prelude::*, get_eval_block, redirect_env};
|
||||||
use nu_protocol::{engine::Closure, DataSource, PipelineMetadata};
|
use nu_protocol::{DataSource, PipelineMetadata, engine::Closure};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Collect;
|
pub struct Collect;
|
||||||
|
@ -41,35 +41,17 @@ impl Command for Const {
|
|||||||
|
|
||||||
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> {
|
||||||
// This is compiled specially by the IR compiler. The code here is never used when
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
// running in IR mode.
|
// running in IR mode.
|
||||||
let call = call.assert_ast_call()?;
|
eprintln!(
|
||||||
let var_id = if let Some(id) = call.positional_nth(0).and_then(|pos| pos.as_var()) {
|
"Tried to execute 'run' for the 'const' command: this code path should never be reached in IR mode"
|
||||||
id
|
);
|
||||||
} else {
|
unreachable!()
|
||||||
return Err(ShellError::NushellFailedSpanned {
|
|
||||||
msg: "Could not get variable".to_string(),
|
|
||||||
label: "variable not added by the parser".to_string(),
|
|
||||||
span: call.head,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(constval) = &engine_state.get_var(var_id).const_val {
|
|
||||||
stack.add_var(var_id, constval.clone());
|
|
||||||
|
|
||||||
Ok(PipelineData::empty())
|
|
||||||
} else {
|
|
||||||
Err(ShellError::NushellFailedSpanned {
|
|
||||||
msg: "Missing Constant".to_string(),
|
|
||||||
label: "constant not added by the parser".to_string(),
|
|
||||||
span: call.head,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_const(
|
fn run_const(
|
||||||
|
@ -33,10 +33,15 @@ impl Command for Continue {
|
|||||||
&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> {
|
||||||
Err(ShellError::Continue { span: call.head })
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
|
// running in IR mode.
|
||||||
|
eprintln!(
|
||||||
|
"Tried to execute 'run' for the 'continue' command: this code path should never be reached in IR mode"
|
||||||
|
);
|
||||||
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{engine::StateWorkingSet, ByteStreamSource, PipelineMetadata};
|
use nu_protocol::{
|
||||||
|
BlockId, ByteStreamSource, Category, PipelineMetadata, Signature,
|
||||||
|
engine::{Closure, StateWorkingSet},
|
||||||
|
};
|
||||||
|
use std::any::type_name;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Describe;
|
pub struct Describe;
|
||||||
|
|
||||||
@ -69,74 +72,150 @@ impl Command for Describe {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Describe the type of a record in a detailed way",
|
description: "Describe the type of a record in a detailed way",
|
||||||
example:
|
example: "{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
|
||||||
"{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
|
|
||||||
result: Some(Value::test_record(record!(
|
result: Some(Value::test_record(record!(
|
||||||
"type" => Value::test_string("record"),
|
"type" => Value::test_string("record"),
|
||||||
|
"detailed_type" => Value::test_string("record<shell: string, uwu: bool, features: record<bugs: bool, multiplatform: bool, speed: int>, fib: list<int>, on_save: closure, first_commit: datetime, my_duration: duration>"),
|
||||||
"columns" => Value::test_record(record!(
|
"columns" => Value::test_record(record!(
|
||||||
"shell" => Value::test_string("string"),
|
"shell" => Value::test_record(record!(
|
||||||
"uwu" => Value::test_string("bool"),
|
"type" => Value::test_string("string"),
|
||||||
|
"detailed_type" => Value::test_string("string"),
|
||||||
|
"rust_type" => Value::test_string("&alloc::string::String"),
|
||||||
|
"value" => Value::test_string("true"),
|
||||||
|
)),
|
||||||
|
"uwu" => Value::test_record(record!(
|
||||||
|
"type" => Value::test_string("bool"),
|
||||||
|
"detailed_type" => Value::test_string("bool"),
|
||||||
|
"rust_type" => Value::test_string("bool"),
|
||||||
|
"value" => Value::test_bool(true),
|
||||||
|
)),
|
||||||
"features" => Value::test_record(record!(
|
"features" => Value::test_record(record!(
|
||||||
"type" => Value::test_string("record"),
|
"type" => Value::test_string("record"),
|
||||||
|
"detailed_type" => Value::test_string("record<bugs: bool, multiplatform: bool, speed: int>"),
|
||||||
"columns" => Value::test_record(record!(
|
"columns" => Value::test_record(record!(
|
||||||
"bugs" => Value::test_string("bool"),
|
"bugs" => Value::test_record(record!(
|
||||||
"multiplatform" => Value::test_string("bool"),
|
"type" => Value::test_string("bool"),
|
||||||
"speed" => Value::test_string("int"),
|
"detailed_type" => Value::test_string("bool"),
|
||||||
|
"rust_type" => Value::test_string("bool"),
|
||||||
|
"value" => Value::test_bool(false),
|
||||||
|
)),
|
||||||
|
"multiplatform" => Value::test_record(record!(
|
||||||
|
"type" => Value::test_string("bool"),
|
||||||
|
"detailed_type" => Value::test_string("bool"),
|
||||||
|
"rust_type" => Value::test_string("bool"),
|
||||||
|
"value" => Value::test_bool(true),
|
||||||
|
)),
|
||||||
|
"speed" => Value::test_record(record!(
|
||||||
|
"type" => Value::test_string("int"),
|
||||||
|
"detailed_type" => Value::test_string("int"),
|
||||||
|
"rust_type" => Value::test_string("i64"),
|
||||||
|
"value" => Value::test_int(10),
|
||||||
|
)),
|
||||||
)),
|
)),
|
||||||
|
"rust_type" => Value::test_string("&nu_utils::shared_cow::SharedCow<nu_protocol::value::record::Record>"),
|
||||||
)),
|
)),
|
||||||
"fib" => Value::test_record(record!(
|
"fib" => Value::test_record(record!(
|
||||||
"type" => Value::test_string("list"),
|
"type" => Value::test_string("list"),
|
||||||
|
"detailed_type" => Value::test_string("list<int>"),
|
||||||
"length" => Value::test_int(6),
|
"length" => Value::test_int(6),
|
||||||
"values" => Value::test_list(vec![
|
"rust_type" => Value::test_string("&mut alloc::vec::Vec<nu_protocol::value::Value>"),
|
||||||
Value::test_string("int"),
|
"value" => Value::test_list(vec![
|
||||||
Value::test_string("int"),
|
Value::test_record(record!(
|
||||||
Value::test_string("int"),
|
"type" => Value::test_string("int"),
|
||||||
Value::test_string("int"),
|
"detailed_type" => Value::test_string("int"),
|
||||||
Value::test_string("int"),
|
"rust_type" => Value::test_string("i64"),
|
||||||
Value::test_string("int"),
|
"value" => Value::test_int(1),
|
||||||
]),
|
)),
|
||||||
|
Value::test_record(record!(
|
||||||
|
"type" => Value::test_string("int"),
|
||||||
|
"detailed_type" => Value::test_string("int"),
|
||||||
|
"rust_type" => Value::test_string("i64"),
|
||||||
|
"value" => Value::test_int(1),
|
||||||
|
)),
|
||||||
|
Value::test_record(record!(
|
||||||
|
"type" => Value::test_string("int"),
|
||||||
|
"detailed_type" => Value::test_string("int"),
|
||||||
|
"rust_type" => Value::test_string("i64"),
|
||||||
|
"value" => Value::test_int(2),
|
||||||
|
)),
|
||||||
|
Value::test_record(record!(
|
||||||
|
"type" => Value::test_string("int"),
|
||||||
|
"detailed_type" => Value::test_string("int"),
|
||||||
|
"rust_type" => Value::test_string("i64"),
|
||||||
|
"value" => Value::test_int(3),
|
||||||
|
)),
|
||||||
|
Value::test_record(record!(
|
||||||
|
"type" => Value::test_string("int"),
|
||||||
|
"detailed_type" => Value::test_string("int"),
|
||||||
|
"rust_type" => Value::test_string("i64"),
|
||||||
|
"value" => Value::test_int(5),
|
||||||
|
)),
|
||||||
|
Value::test_record(record!(
|
||||||
|
"type" => Value::test_string("int"),
|
||||||
|
"detailed_type" => Value::test_string("int"),
|
||||||
|
"rust_type" => Value::test_string("i64"),
|
||||||
|
"value" => Value::test_int(8),
|
||||||
|
))]
|
||||||
|
),
|
||||||
)),
|
)),
|
||||||
"on_save" => Value::test_record(record!(
|
"on_save" => Value::test_record(record!(
|
||||||
"type" => Value::test_string("closure"),
|
"type" => Value::test_string("closure"),
|
||||||
|
"detailed_type" => Value::test_string("closure"),
|
||||||
|
"rust_type" => Value::test_string("&alloc::boxed::Box<nu_protocol::engine::closure::Closure>"),
|
||||||
|
"value" => Value::test_closure(Closure {
|
||||||
|
block_id: BlockId::new(1),
|
||||||
|
captures: vec![],
|
||||||
|
}),
|
||||||
"signature" => Value::test_record(record!(
|
"signature" => Value::test_record(record!(
|
||||||
"name" => Value::test_string(""),
|
"name" => Value::test_string(""),
|
||||||
"category" => Value::test_string("default"),
|
"category" => Value::test_string("default"),
|
||||||
)),
|
)),
|
||||||
)),
|
)),
|
||||||
"first_commit" => Value::test_string("date"),
|
"first_commit" => Value::test_record(record!(
|
||||||
"my_duration" => Value::test_string("duration"),
|
"type" => Value::test_string("datetime"),
|
||||||
|
"detailed_type" => Value::test_string("datetime"),
|
||||||
|
"rust_type" => Value::test_string("chrono::datetime::DateTime<chrono::offset::fixed::FixedOffset>"),
|
||||||
|
"value" => Value::test_date("2019-05-10 00:00:00Z".parse().unwrap_or_default()),
|
||||||
|
)),
|
||||||
|
"my_duration" => Value::test_record(record!(
|
||||||
|
"type" => Value::test_string("duration"),
|
||||||
|
"detailed_type" => Value::test_string("duration"),
|
||||||
|
"rust_type" => Value::test_string("i64"),
|
||||||
|
"value" => Value::test_duration(260_000_000_000),
|
||||||
|
))
|
||||||
)),
|
)),
|
||||||
|
"rust_type" => Value::test_string("&nu_utils::shared_cow::SharedCow<nu_protocol::value::record::Record>"),
|
||||||
))),
|
))),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Describe the type of a stream with detailed information",
|
description: "Describe the type of a stream with detailed information",
|
||||||
example: "[1 2 3] | each {|i| echo $i} | describe -d",
|
example: "[1 2 3] | each {|i| echo $i} | describe -d",
|
||||||
result: None // Give "Running external commands not supported" error
|
result: None, // Give "Running external commands not supported" error
|
||||||
// result: Some(Value::test_record(record!(
|
// result: Some(Value::test_record(record!(
|
||||||
// "type" => Value::test_string("stream"),
|
// "type" => Value::test_string("stream"),
|
||||||
// "origin" => Value::test_string("nushell"),
|
// "origin" => Value::test_string("nushell"),
|
||||||
// "subtype" => Value::test_record(record!(
|
// "subtype" => Value::test_record(record!(
|
||||||
// "type" => Value::test_string("list"),
|
// "type" => Value::test_string("list"),
|
||||||
// "length" => Value::test_int(3),
|
// "length" => Value::test_int(3),
|
||||||
// "values" => Value::test_list(vec![
|
// "values" => Value::test_list(vec![
|
||||||
// Value::test_string("int"),
|
// Value::test_string("int"),
|
||||||
// Value::test_string("int"),
|
// Value::test_string("int"),
|
||||||
// Value::test_string("int"),
|
// Value::test_string("int"),
|
||||||
// ])
|
// ])
|
||||||
// ))
|
// ))
|
||||||
// ))),
|
// ))),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Describe a stream of data, collecting it first",
|
description: "Describe a stream of data, collecting it first",
|
||||||
example: "[1 2 3] | each {|i| echo $i} | describe",
|
example: "[1 2 3] | each {|i| echo $i} | describe",
|
||||||
result: None // Give "Running external commands not supported" error
|
result: None, // Give "Running external commands not supported" error
|
||||||
// result: Some(Value::test_string("list<int> (stream)")),
|
// result: Some(Value::test_string("list<int> (stream)")),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Describe the input but do not collect streams",
|
description: "Describe the input but do not collect streams",
|
||||||
example: "[1 2 3] | each {|i| echo $i} | describe --no-collect",
|
example: "[1 2 3] | each {|i| echo $i} | describe --no-collect",
|
||||||
result: None // Give "Running external commands not supported" error
|
result: None, // Give "Running external commands not supported" error
|
||||||
// result: Some(Value::test_string("stream")),
|
// result: Some(Value::test_string("stream")),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -175,7 +254,9 @@ fn run(
|
|||||||
|
|
||||||
Value::record(
|
Value::record(
|
||||||
record! {
|
record! {
|
||||||
"type" => Value::string(type_, head),
|
"type" => Value::string("bytestream", head),
|
||||||
|
"detailed_type" => Value::string(type_, head),
|
||||||
|
"rust_type" => Value::string(type_of(&stream), head),
|
||||||
"origin" => Value::string(origin, head),
|
"origin" => Value::string(origin, head),
|
||||||
"metadata" => metadata_to_value(metadata, head),
|
"metadata" => metadata_to_value(metadata, head),
|
||||||
},
|
},
|
||||||
@ -192,6 +273,7 @@ fn run(
|
|||||||
description
|
description
|
||||||
}
|
}
|
||||||
PipelineData::ListStream(stream, ..) => {
|
PipelineData::ListStream(stream, ..) => {
|
||||||
|
let type_ = type_of(&stream);
|
||||||
if options.detailed {
|
if options.detailed {
|
||||||
let subtype = if options.no_collect {
|
let subtype = if options.no_collect {
|
||||||
Value::string("any", head)
|
Value::string("any", head)
|
||||||
@ -201,6 +283,8 @@ fn run(
|
|||||||
Value::record(
|
Value::record(
|
||||||
record! {
|
record! {
|
||||||
"type" => Value::string("stream", head),
|
"type" => Value::string("stream", head),
|
||||||
|
"detailed_type" => Value::string("list stream", head),
|
||||||
|
"rust_type" => Value::string(type_, head),
|
||||||
"origin" => Value::string("nushell", head),
|
"origin" => Value::string("nushell", head),
|
||||||
"subtype" => subtype,
|
"subtype" => subtype,
|
||||||
"metadata" => metadata_to_value(metadata, head),
|
"metadata" => metadata_to_value(metadata, head),
|
||||||
@ -212,7 +296,7 @@ fn run(
|
|||||||
} else {
|
} else {
|
||||||
let value = stream.into_value();
|
let value = stream.into_value();
|
||||||
let base_description = value.get_type().to_string();
|
let base_description = value.get_type().to_string();
|
||||||
Value::string(format!("{} (stream)", base_description), head)
|
Value::string(format!("{base_description} (stream)"), head)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PipelineData::Value(value, ..) => {
|
PipelineData::Value(value, ..) => {
|
||||||
@ -229,45 +313,95 @@ fn run(
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum Description {
|
enum Description {
|
||||||
String(String),
|
|
||||||
Record(Record),
|
Record(Record),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Description {
|
impl Description {
|
||||||
fn into_value(self, span: Span) -> Value {
|
fn into_value(self, span: Span) -> Value {
|
||||||
match self {
|
match self {
|
||||||
Description::String(ty) => Value::string(ty, span),
|
|
||||||
Description::Record(record) => Value::record(record, span),
|
Description::Record(record) => Value::record(record, span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe_value(value: Value, head: Span, engine_state: Option<&EngineState>) -> Value {
|
fn describe_value(value: Value, head: Span, engine_state: Option<&EngineState>) -> Value {
|
||||||
let record = match describe_value_inner(value, head, engine_state) {
|
let Description::Record(record) = describe_value_inner(value, head, engine_state);
|
||||||
Description::String(ty) => record! { "type" => Value::string(ty, head) },
|
|
||||||
Description::Record(record) => record,
|
|
||||||
};
|
|
||||||
Value::record(record, head)
|
Value::record(record, head)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn type_of<T>(_: &T) -> String {
|
||||||
|
type_name::<T>().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
fn describe_value_inner(
|
fn describe_value_inner(
|
||||||
value: Value,
|
mut value: Value,
|
||||||
head: Span,
|
head: Span,
|
||||||
engine_state: Option<&EngineState>,
|
engine_state: Option<&EngineState>,
|
||||||
) -> Description {
|
) -> Description {
|
||||||
|
let value_type = value.get_type().to_string();
|
||||||
match value {
|
match value {
|
||||||
Value::Bool { .. }
|
Value::Bool { val, .. } => Description::Record(record! {
|
||||||
| Value::Int { .. }
|
"type" => Value::string("bool", head),
|
||||||
| Value::Float { .. }
|
"detailed_type" => Value::string(value_type, head),
|
||||||
| Value::Filesize { .. }
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
| Value::Duration { .. }
|
"value" => value,
|
||||||
| Value::Date { .. }
|
}),
|
||||||
| Value::Range { .. }
|
Value::Int { val, .. } => Description::Record(record! {
|
||||||
| Value::String { .. }
|
"type" => Value::string("int", head),
|
||||||
| Value::Glob { .. }
|
"detailed_type" => Value::string(value_type, head),
|
||||||
| Value::Nothing { .. } => Description::String(value.get_type().to_string()),
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
Value::Record { val, .. } => {
|
"value" => value,
|
||||||
let mut columns = val.into_owned();
|
}),
|
||||||
|
Value::Float { val, .. } => Description::Record(record! {
|
||||||
|
"type" => Value::string("float", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value,
|
||||||
|
}),
|
||||||
|
Value::Filesize { val, .. } => Description::Record(record! {
|
||||||
|
"type" => Value::string("filesize", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value,
|
||||||
|
}),
|
||||||
|
Value::Duration { val, .. } => Description::Record(record! {
|
||||||
|
"type" => Value::string("duration", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value,
|
||||||
|
}),
|
||||||
|
Value::Date { val, .. } => Description::Record(record! {
|
||||||
|
"type" => Value::string("datetime", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value,
|
||||||
|
}),
|
||||||
|
Value::Range { ref val, .. } => Description::Record(record! {
|
||||||
|
"type" => Value::string("range", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value,
|
||||||
|
}),
|
||||||
|
Value::String { ref val, .. } => Description::Record(record! {
|
||||||
|
"type" => Value::string("string", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value,
|
||||||
|
}),
|
||||||
|
Value::Glob { ref val, .. } => Description::Record(record! {
|
||||||
|
"type" => Value::string("glob", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value,
|
||||||
|
}),
|
||||||
|
Value::Nothing { .. } => Description::Record(record! {
|
||||||
|
"type" => Value::string("nothing", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"rust_type" => Value::string("", head),
|
||||||
|
"value" => value,
|
||||||
|
}),
|
||||||
|
Value::Record { ref val, .. } => {
|
||||||
|
let mut columns = val.clone().into_owned();
|
||||||
for (_, val) in &mut columns {
|
for (_, val) in &mut columns {
|
||||||
*val =
|
*val =
|
||||||
describe_value_inner(std::mem::take(val), head, engine_state).into_value(head);
|
describe_value_inner(std::mem::take(val), head, engine_state).into_value(head);
|
||||||
@ -275,25 +409,34 @@ fn describe_value_inner(
|
|||||||
|
|
||||||
Description::Record(record! {
|
Description::Record(record! {
|
||||||
"type" => Value::string("record", head),
|
"type" => Value::string("record", head),
|
||||||
"columns" => Value::record(columns, head),
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"columns" => Value::record(columns.clone(), head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Value::List { mut vals, .. } => {
|
Value::List { ref mut vals, .. } => {
|
||||||
for val in &mut vals {
|
for val in &mut *vals {
|
||||||
*val =
|
*val =
|
||||||
describe_value_inner(std::mem::take(val), head, engine_state).into_value(head);
|
describe_value_inner(std::mem::take(val), head, engine_state).into_value(head);
|
||||||
}
|
}
|
||||||
|
|
||||||
Description::Record(record! {
|
Description::Record(record! {
|
||||||
"type" => Value::string("list", head),
|
"type" => Value::string("list", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
"length" => Value::int(vals.len() as i64, head),
|
"length" => Value::int(vals.len() as i64, head),
|
||||||
"values" => Value::list(vals, head),
|
"rust_type" => Value::string(type_of(&vals), head),
|
||||||
|
"value" => value,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Value::Closure { val, .. } => {
|
Value::Closure { ref val, .. } => {
|
||||||
let block = engine_state.map(|engine_state| engine_state.get_block(val.block_id));
|
let block = engine_state.map(|engine_state| engine_state.get_block(val.block_id));
|
||||||
|
|
||||||
let mut record = record! { "type" => Value::string("closure", head) };
|
let mut record = record! {
|
||||||
|
"type" => Value::string("closure", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value,
|
||||||
|
};
|
||||||
if let Some(block) = block {
|
if let Some(block) = block {
|
||||||
record.push(
|
record.push(
|
||||||
"signature",
|
"signature",
|
||||||
@ -308,21 +451,37 @@ fn describe_value_inner(
|
|||||||
}
|
}
|
||||||
Description::Record(record)
|
Description::Record(record)
|
||||||
}
|
}
|
||||||
Value::Error { error, .. } => Description::Record(record! {
|
Value::Error { ref error, .. } => Description::Record(record! {
|
||||||
"type" => Value::string("error", head),
|
"type" => Value::string("error", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
"subtype" => Value::string(error.to_string(), head),
|
"subtype" => Value::string(error.to_string(), head),
|
||||||
|
"rust_type" => Value::string(type_of(&error), head),
|
||||||
|
"value" => value,
|
||||||
}),
|
}),
|
||||||
Value::Binary { val, .. } => Description::Record(record! {
|
Value::Binary { ref val, .. } => Description::Record(record! {
|
||||||
"type" => Value::string("binary", head),
|
"type" => Value::string("binary", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
"length" => Value::int(val.len() as i64, head),
|
"length" => Value::int(val.len() as i64, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value,
|
||||||
}),
|
}),
|
||||||
Value::CellPath { val, .. } => Description::Record(record! {
|
Value::CellPath { ref val, .. } => Description::Record(record! {
|
||||||
"type" => Value::string("cell-path", head),
|
"type" => Value::string("cell-path", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
"length" => Value::int(val.members.len() as i64, head),
|
"length" => Value::int(val.members.len() as i64, head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" => value
|
||||||
}),
|
}),
|
||||||
Value::Custom { val, .. } => Description::Record(record! {
|
Value::Custom { ref val, .. } => Description::Record(record! {
|
||||||
"type" => Value::string("custom", head),
|
"type" => Value::string("custom", head),
|
||||||
|
"detailed_type" => Value::string(value_type, head),
|
||||||
"subtype" => Value::string(val.type_name(), head),
|
"subtype" => Value::string(val.type_name(), head),
|
||||||
|
"rust_type" => Value::string(type_of(&val), head),
|
||||||
|
"value" =>
|
||||||
|
match val.to_base_value(head) {
|
||||||
|
Ok(base_value) => base_value,
|
||||||
|
Err(err) => Value::error(err, head),
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use nu_engine::{command_prelude::*, get_eval_block_with_early_return, redirect_e
|
|||||||
#[cfg(feature = "os")]
|
#[cfg(feature = "os")]
|
||||||
use nu_protocol::process::{ChildPipe, ChildProcess};
|
use nu_protocol::process::{ChildPipe, ChildProcess};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::Closure, shell_error::io::IoError, ByteStream, ByteStreamSource, OutDest,
|
ByteStream, ByteStreamSource, OutDest, engine::Closure, shell_error::io::IoError,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
@ -31,16 +31,6 @@ impl Command for Do {
|
|||||||
"ignore errors as the closure runs",
|
"ignore errors as the closure runs",
|
||||||
Some('i'),
|
Some('i'),
|
||||||
)
|
)
|
||||||
.switch(
|
|
||||||
"ignore-shell-errors",
|
|
||||||
"ignore shell errors as the closure runs",
|
|
||||||
Some('s'),
|
|
||||||
)
|
|
||||||
.switch(
|
|
||||||
"ignore-program-errors",
|
|
||||||
"ignore external program errors as the closure runs",
|
|
||||||
Some('p'),
|
|
||||||
)
|
|
||||||
.switch(
|
.switch(
|
||||||
"capture-errors",
|
"capture-errors",
|
||||||
"catch errors as the closure runs, and return them",
|
"catch errors as the closure runs, and return them",
|
||||||
@ -71,36 +61,6 @@ impl Command for Do {
|
|||||||
let rest: Vec<Value> = call.rest(engine_state, caller_stack, 1)?;
|
let rest: Vec<Value> = call.rest(engine_state, caller_stack, 1)?;
|
||||||
let ignore_all_errors = call.has_flag(engine_state, caller_stack, "ignore-errors")?;
|
let ignore_all_errors = call.has_flag(engine_state, caller_stack, "ignore-errors")?;
|
||||||
|
|
||||||
if call.has_flag(engine_state, caller_stack, "ignore-shell-errors")? {
|
|
||||||
nu_protocol::report_shell_warning(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "Deprecated option".into(),
|
|
||||||
msg: "`--ignore-shell-errors` is deprecated and will be removed in 0.102.0."
|
|
||||||
.into(),
|
|
||||||
span: Some(call.head),
|
|
||||||
help: Some("Please use the `--ignore-errors(-i)`".into()),
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if call.has_flag(engine_state, caller_stack, "ignore-program-errors")? {
|
|
||||||
nu_protocol::report_shell_warning(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "Deprecated option".into(),
|
|
||||||
msg: "`--ignore-program-errors` is deprecated and will be removed in 0.102.0."
|
|
||||||
.into(),
|
|
||||||
span: Some(call.head),
|
|
||||||
help: Some("Please use the `--ignore-errors(-i)`".into()),
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
let ignore_shell_errors = ignore_all_errors
|
|
||||||
|| call.has_flag(engine_state, caller_stack, "ignore-shell-errors")?;
|
|
||||||
let ignore_program_errors = ignore_all_errors
|
|
||||||
|| call.has_flag(engine_state, caller_stack, "ignore-program-errors")?;
|
|
||||||
let capture_errors = call.has_flag(engine_state, caller_stack, "capture-errors")?;
|
let capture_errors = call.has_flag(engine_state, caller_stack, "capture-errors")?;
|
||||||
let has_env = call.has_flag(engine_state, caller_stack, "env")?;
|
let has_env = call.has_flag(engine_state, caller_stack, "env")?;
|
||||||
|
|
||||||
@ -147,14 +107,14 @@ impl Command for Do {
|
|||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
stdout.read_to_end(&mut buf).map_err(|err| {
|
stdout.read_to_end(&mut buf).map_err(|err| {
|
||||||
IoError::new_internal(
|
IoError::new_internal(
|
||||||
err.kind(),
|
err,
|
||||||
"Could not read stdout to end",
|
"Could not read stdout to end",
|
||||||
nu_protocol::location!(),
|
nu_protocol::location!(),
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
Ok::<_, ShellError>(buf)
|
Ok::<_, ShellError>(buf)
|
||||||
})
|
})
|
||||||
.map_err(|err| IoError::new(err.kind(), head, None))
|
.map_err(|err| IoError::new(err, head, None))
|
||||||
})
|
})
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|
||||||
@ -166,7 +126,7 @@ impl Command for Do {
|
|||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
stderr
|
stderr
|
||||||
.read_to_string(&mut buf)
|
.read_to_string(&mut buf)
|
||||||
.map_err(|err| IoError::new(err.kind(), span, None))?;
|
.map_err(|err| IoError::new(err, span, None))?;
|
||||||
buf
|
buf
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -206,7 +166,7 @@ impl Command for Do {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(PipelineData::ByteStream(mut stream, metadata))
|
Ok(PipelineData::ByteStream(mut stream, metadata))
|
||||||
if ignore_program_errors
|
if ignore_all_errors
|
||||||
&& !matches!(
|
&& !matches!(
|
||||||
caller_stack.stdout(),
|
caller_stack.stdout(),
|
||||||
OutDest::Pipe | OutDest::PipeSeparate | OutDest::Value
|
OutDest::Pipe | OutDest::PipeSeparate | OutDest::Value
|
||||||
@ -218,10 +178,10 @@ impl Command for Do {
|
|||||||
}
|
}
|
||||||
Ok(PipelineData::ByteStream(stream, metadata))
|
Ok(PipelineData::ByteStream(stream, metadata))
|
||||||
}
|
}
|
||||||
Ok(PipelineData::Value(Value::Error { .. }, ..)) | Err(_) if ignore_shell_errors => {
|
Ok(PipelineData::Value(Value::Error { .. }, ..)) | Err(_) if ignore_all_errors => {
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
}
|
}
|
||||||
Ok(PipelineData::ListStream(stream, metadata)) if ignore_shell_errors => {
|
Ok(PipelineData::ListStream(stream, metadata)) if ignore_all_errors => {
|
||||||
let stream = stream.map(move |value| {
|
let stream = stream.map(move |value| {
|
||||||
if let Value::Error { .. } = value {
|
if let Value::Error { .. } = value {
|
||||||
Value::nothing(head)
|
Value::nothing(head)
|
||||||
|
@ -63,8 +63,7 @@ little reason to use this over just writing the values as-is."#
|
|||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description: "Returns the piped-in value, by using the special $in variable to obtain it.",
|
||||||
"Returns the piped-in value, by using the special $in variable to obtain it.",
|
|
||||||
example: "echo $in",
|
example: "echo $in",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
@ -76,8 +76,7 @@ impl Command for ErrorMake {
|
|||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description:
|
description: "Create a custom error for a custom command that shows the span of the argument",
|
||||||
"Create a custom error for a custom command that shows the span of the argument",
|
|
||||||
example: r#"def foo [x] {
|
example: r#"def foo [x] {
|
||||||
error make {
|
error make {
|
||||||
msg: "this is fishy"
|
msg: "this is fishy"
|
||||||
@ -106,7 +105,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
span: throw_span,
|
span: throw_span,
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -119,7 +118,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
span: Some(span),
|
span: Some(span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return ShellError::GenericError {
|
return ShellError::GenericError {
|
||||||
@ -128,7 +127,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
span: Some(span),
|
span: Some(span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -146,7 +145,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
span: Some(span),
|
span: Some(span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
// correct return: no label
|
// correct return: no label
|
||||||
None => {
|
None => {
|
||||||
@ -156,7 +155,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
span: throw_span,
|
span: throw_span,
|
||||||
help,
|
help,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -180,7 +179,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
span: Some(label_span),
|
span: Some(label_span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return ShellError::GenericError {
|
return ShellError::GenericError {
|
||||||
@ -189,7 +188,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
span: Some(label_span),
|
span: Some(label_span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -202,7 +201,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
span: Some(value.span()),
|
span: Some(value.span()),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
// correct return: label, no span
|
// correct return: label, no span
|
||||||
None => {
|
None => {
|
||||||
@ -212,7 +211,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
span: throw_span,
|
span: throw_span,
|
||||||
help,
|
help,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -230,7 +229,7 @@ fn make_other_error(value: &Value, throw_span: Option<Span>) -> ShellError {
|
|||||||
error: "invalid error format.".into(),
|
error: "invalid error format.".into(),
|
||||||
msg: "`$.label.start` should be smaller than `$.label.end`".into(),
|
msg: "`$.label.start` should be smaller than `$.label.end`".into(),
|
||||||
span: Some(label_span),
|
span: Some(label_span),
|
||||||
help: Some(format!("{} > {}", span_start, span_end)),
|
help: Some(format!("{span_start} > {span_end}")),
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{engine::CommandType, Signals};
|
use nu_protocol::engine::CommandType;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct For;
|
pub struct For;
|
||||||
@ -43,83 +43,17 @@ impl Command for For {
|
|||||||
|
|
||||||
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> {
|
||||||
// This is compiled specially by the IR compiler. The code here is never used when
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
// running in IR mode.
|
// running in IR mode.
|
||||||
let call = call.assert_ast_call()?;
|
eprintln!(
|
||||||
let head = call.head;
|
"Tried to execute 'run' for the 'for' command: this code path should never be reached in IR mode"
|
||||||
let var_id = call
|
);
|
||||||
.positional_nth(0)
|
unreachable!()
|
||||||
.expect("checked through parser")
|
|
||||||
.as_var()
|
|
||||||
.expect("internal error: missing variable");
|
|
||||||
|
|
||||||
let keyword_expr = call
|
|
||||||
.positional_nth(1)
|
|
||||||
.expect("checked through parser")
|
|
||||||
.as_keyword()
|
|
||||||
.expect("internal error: missing keyword");
|
|
||||||
|
|
||||||
let block_id = call
|
|
||||||
.positional_nth(2)
|
|
||||||
.expect("checked through parser")
|
|
||||||
.as_block()
|
|
||||||
.expect("internal error: missing block");
|
|
||||||
|
|
||||||
let eval_expression = get_eval_expression(engine_state);
|
|
||||||
let eval_block = get_eval_block(engine_state);
|
|
||||||
|
|
||||||
let value = eval_expression(engine_state, stack, keyword_expr)?;
|
|
||||||
|
|
||||||
let engine_state = engine_state.clone();
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
|
|
||||||
let stack = &mut stack.push_redirection(None, None);
|
|
||||||
|
|
||||||
let span = value.span();
|
|
||||||
match value {
|
|
||||||
Value::List { vals, .. } => {
|
|
||||||
for x in vals.into_iter() {
|
|
||||||
engine_state.signals().check(head)?;
|
|
||||||
|
|
||||||
// with_env() is used here to ensure that each iteration uses
|
|
||||||
// a different set of environment variables.
|
|
||||||
// Hence, a 'cd' in the first loop won't affect the next loop.
|
|
||||||
|
|
||||||
stack.add_var(var_id, x);
|
|
||||||
|
|
||||||
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
|
||||||
Err(ShellError::Break { .. }) => break,
|
|
||||||
Err(ShellError::Continue { .. }) => continue,
|
|
||||||
Err(err) => return Err(err),
|
|
||||||
Ok(data) => data.drain()?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::Range { val, .. } => {
|
|
||||||
for x in val.into_range_iter(span, Signals::empty()) {
|
|
||||||
engine_state.signals().check(head)?;
|
|
||||||
stack.add_var(var_id, x);
|
|
||||||
|
|
||||||
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
|
|
||||||
Err(ShellError::Break { .. }) => break,
|
|
||||||
Err(ShellError::Continue { .. }) => continue,
|
|
||||||
Err(err) => return Err(err),
|
|
||||||
Ok(data) => data.drain()?,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
x => {
|
|
||||||
stack.add_var(var_id, x);
|
|
||||||
|
|
||||||
eval_block(&engine_state, stack, block, PipelineData::empty())?.into_value(head)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(PipelineData::empty())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use nu_engine::{
|
use nu_engine::command_prelude::*;
|
||||||
command_prelude::*, get_eval_block, get_eval_expression, get_eval_expression_with_input,
|
|
||||||
};
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{CommandType, 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},
|
||||||
@ -60,8 +58,6 @@ 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 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
|
||||||
@ -97,43 +93,17 @@ impl Command for If {
|
|||||||
|
|
||||||
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> {
|
||||||
// This is compiled specially by the IR compiler. The code here is never used when
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
// running in IR mode.
|
// running in IR mode.
|
||||||
let call = call.assert_ast_call()?;
|
eprintln!(
|
||||||
let cond = call.positional_nth(0).expect("checked through parser");
|
"Tried to execute 'run' for the 'if' command: this code path should never be reached in IR mode"
|
||||||
let then_block = call
|
);
|
||||||
.positional_nth(1)
|
unreachable!()
|
||||||
.expect("checked through parser")
|
|
||||||
.as_block()
|
|
||||||
.expect("internal error: missing block");
|
|
||||||
let else_case = call.positional_nth(2);
|
|
||||||
|
|
||||||
let eval_expression = get_eval_expression(engine_state);
|
|
||||||
let eval_expression_with_input = get_eval_expression_with_input(engine_state);
|
|
||||||
let eval_block = get_eval_block(engine_state);
|
|
||||||
|
|
||||||
if eval_expression(engine_state, stack, cond)?.as_bool()? {
|
|
||||||
let block = engine_state.get_block(then_block);
|
|
||||||
eval_block(engine_state, stack, block, input)
|
|
||||||
} else if let Some(else_case) = else_case {
|
|
||||||
if let Some(else_expr) = else_case.as_keyword() {
|
|
||||||
if let Some(block_id) = else_expr.as_block() {
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
eval_block(engine_state, stack, block, input)
|
|
||||||
} else {
|
|
||||||
eval_expression_with_input(engine_state, stack, else_expr, input)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eval_expression_with_input(engine_state, stack, else_case, input)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(PipelineData::empty())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{engine::StateWorkingSet, ByteStreamSource, OutDest};
|
use nu_protocol::{ByteStreamSource, OutDest, engine::StateWorkingSet};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Ignore;
|
pub struct Ignore;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::{command_prelude::*, get_eval_block};
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::engine::CommandType;
|
use nu_protocol::engine::CommandType;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -41,47 +41,17 @@ impl Command for Let {
|
|||||||
|
|
||||||
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> {
|
||||||
// This is compiled specially by the IR compiler. The code here is never used when
|
// This is compiled specially by the IR compiler. The code here is never used when
|
||||||
// running in IR mode.
|
// running in IR mode.
|
||||||
let call = call.assert_ast_call()?;
|
eprintln!(
|
||||||
let var_id = call
|
"Tried to execute 'run' for the 'let' command: this code path should never be reached in IR mode"
|
||||||
.positional_nth(0)
|
);
|
||||||
.expect("checked through parser")
|
unreachable!()
|
||||||
.as_var()
|
|
||||||
.expect("internal error: missing variable");
|
|
||||||
|
|
||||||
let block_id = call
|
|
||||||
.positional_nth(1)
|
|
||||||
.expect("checked through parser")
|
|
||||||
.as_block()
|
|
||||||
.expect("internal error: missing right hand side");
|
|
||||||
|
|
||||||
let block = engine_state.get_block(block_id);
|
|
||||||
let eval_block = get_eval_block(engine_state);
|
|
||||||
let stack = &mut stack.start_collect_value();
|
|
||||||
let pipeline_data = eval_block(engine_state, stack, block, input)?;
|
|
||||||
let value = pipeline_data.into_value(call.head)?;
|
|
||||||
|
|
||||||
// if given variable type is Glob, and our result is string
|
|
||||||
// then nushell need to convert from Value::String to Value::Glob
|
|
||||||
// it's assigned by demand, then it's not quoted, and it's required to expand
|
|
||||||
// if we pass it to other commands.
|
|
||||||
let var_type = &engine_state.get_var(var_id).ty;
|
|
||||||
let val_span = value.span();
|
|
||||||
let value = match value {
|
|
||||||
Value::String { val, .. } if var_type == &Type::Glob => {
|
|
||||||
Value::glob(val, false, val_span)
|
|
||||||
}
|
|
||||||
value => value,
|
|
||||||
};
|
|
||||||
|
|
||||||
stack.add_var(var_id, value);
|
|
||||||
Ok(PipelineData::empty())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user