Compare commits

...

90 Commits

Author SHA1 Message Date
e76b3d61de Require static path for source-env (#6526) 2022-09-08 23:41:49 +03:00
1adebefc3e Improve wording around all and any (#6524)
* Improve wording around `all` and `any`

The role of the `predicate` for `all` and `any` was not as clear.

See #6499

* type-o

* type-o

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-09-08 08:45:01 -05:00
d1e1d0ac3e remove panic from lpad and rpad, change truncation behaviour for lpad (#6495)
* condense `lpad` and `rpad` into `pad`

* change description

* back to original names, add change
2022-09-08 14:29:56 +02:00
b398448cd9 Stop panic when typing module spam { export def-env (#6523)
* Stop `panic` when typing `module spam { export def-env`

same goes for `export extern` and `export alias`

* fmt
2022-09-08 12:27:11 +03:00
02f92fa527 remove tests (#6515) 2022-09-07 20:19:29 -05:00
773d167449 update regsiter-plugins script to not use encoding (#6512) 2022-09-07 11:54:28 -05:00
aa92141ad7 Remove --encoding argument during register plugin (#6486)
* first implement new plugin protocol core logic

* fix debug body construct

* fix output message from plugin

* finish plugin commands calling

* fix tests and adjust plugin_custom_value call

* fmt code

* fmt code, fix clippy

* add FIXME comment

* change from FIXME to TODO
2022-09-07 09:07:42 -05:00
80624267fd Pass TERM environment var to clear (#6500)
* Pass `TERM` environment var to clear

* don't panic

* use IOErrorSpanned instead of IOError
2022-09-07 10:40:44 +02:00
2030e25ddc fix typo (#6508) 2022-09-07 16:16:55 +08:00
247fff424d update to nu v0.68 for release workflow (#6505) 2022-09-07 15:36:42 +12:00
c902d8bc0c bump dev version to v0.68.1 (#6504) 2022-09-07 14:27:33 +12:00
JT
b0e5723a68 move back to old names for upcoming release 2022-09-07 06:42:11 +12:00
JT
9273bb3f72 bump to 0.68 (#6501) 2022-09-07 06:29:01 +12:00
f7d3ccfc70 Pin reedline to 0.11.0 release (#6497)
Includes minor bugfixes around the history

Release notes:

https://github.com/nushell/reedline/releases/tag/v0.11.0
2022-09-06 11:29:51 +02:00
JT
d86350af80 Revert "Make $ on variable names optional (#6434)" (#6446)
This reverts commit 3cb9147f22.
2022-09-06 05:42:47 +12:00
14512988ba Rename all?, any? and empty? (#6464)
Rename `all?`, `any?` and `empty?` to `all`, `any` and `is-empty` for sake of simplicity and consistency.

- More understandable for newcomers, that these commands are no special to others.
- `?` syntax did not really aprove readability. For me it made it worse.
- We can reserve `?` syntax for any other nushell feature.
2022-09-05 16:41:06 +02:00
33e1120add Terminate REPL if not connected to tty input (#6480)
* Terminate REPL if not connected to tty input

If the standard input stream is not a TTY abort the REPL execution.

Solves a problem as the current REPL tries to be IO fault tolerant and
would indefinetely fail when crossterm tries to handle the STDIN.

Fixes nushell/nushell#6452

* Improve the error message
2022-09-05 13:33:54 +02:00
3278d290be Avoid update_last_command_context "No command run" error (#6483)
* Avoid update_last_command_context "No command run" error

When using `executehostcommand` bindings without having run actual user input commands yet,
update_last_command_context is guaranteed to fail. A function has been added to reedline
that allows checking for this case.

* Update to most recent reedline

Includes bugfixes around the (SQlite) history

Co-authored-by: sholderbach <sholderbach@users.noreply.github.com>
2022-09-05 13:31:26 +02:00
daec3fc3d3 let path split keeps 'C:\' together (#6485)
* `path split` keeps 'C:\' together

* fmt

* fix clippt

* fix match arm
2022-09-04 23:32:09 -07:00
a6ba58ec41 restrict plugin file name (#6479) 2022-09-04 18:00:20 -05:00
65327e0e7e Disable cyclical module imports (#6477) 2022-09-04 23:19:20 +03:00
3ed3712fdc Fix overlays not preserving hidden env vars (#6475)
* Fix overlays not preserving hidden env vars

* Add a few more test

* Add one more test of resetting hidden env vars

* Move removed source-env tests
2022-09-04 20:32:06 +03:00
f46962d236 Fix scoped overlay use not finding a module (#6474)
* Add source-env test for dynamic path

* Use correct module ID for env overlay imports

* Remove parser check from "overlay list"

It would cause unnecessary errors from some inner scope if some
overlay module was also defined in some inner scope.

* Restore Cargo.lock back

* Remove comments
2022-09-04 18:36:42 +03:00
aa4778ff07 remove useless file (#6472) 2022-09-04 06:39:29 -05:00
e81689f2c0 Allow for rejecting nested record cells (#6463)
* add new function to remove data at a cellpath; allow reject to use cellpath

* add tests

* fmt

* fix clippt

* get it working properly with lists of records

* fix clippy, hopefully

* fix clippy, hopefully 2
2022-09-03 07:35:36 -05:00
4656310a1c Bump lz4-sys from 1.9.3 to 1.9.4 (#6462)
Bumps [lz4-sys](https://github.com/10xGenomics/lz4-rs) from 1.9.3 to 1.9.4.
- [Release notes](https://github.com/10xGenomics/lz4-rs/releases)
- [Changelog](https://github.com/10XGenomics/lz4-rs/blob/master/CHANGELOG.md)
- [Commits](https://github.com/10xGenomics/lz4-rs/commits)

---
updated-dependencies:
- dependency-name: lz4-sys
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-09-03 07:32:12 -05:00
34e58bc5d6 add tests, deal with pipes, newlines, tabs for to nuon (#6391)
* remove unnecessary FlatShape

* add proptest

* remove files that belonged in another PR

* more tests, more chars

* add exception for parser error unrelated ot PR
2022-09-01 14:08:19 +02:00
fbe9d6f529 Highlight source value as well as failure point. (#6442)
* show multiple errors at once for some commands

* change from invalid item to source value
2022-09-01 12:20:22 +02:00
e266590813 Fix ps command CPU usage on Apple Silicon M1 macs. #4142 (#6457)
* Fix ps command CPU usage on Apple Silicon M1 macs. #4142

The cpu user and system times returned my libproc are not in
nanoseconds; they are in mach ticks units.  This is not documented very
well.  The convert from mach ticks to ns, the kernel provides a timebase
info function and datatype:

https://developer.apple.com/documentation/driverkit/3433733-mach_timebase_info

The commit makes the PS command work for me.

* Cargo fmt of previous commit.

* Clippy format suggestion

Co-authored-by: Ondrej Baudys <ondrej.baudys@nextgen.net>
2022-09-01 18:09:52 +12:00
b27148d14b Fix build on *BSD, illumos, etc. (#6456)
* nu-path: use 'linux' code on all non-macOS unix

* nu-command: cfg() the Ps command to platforms it's actually implemented on

* nu-system: cfg() the Ps test to the platforms Ps is implemented on
2022-09-01 12:34:26 +12:00
4858a9a817 Revert "Add support for optional list stream output formatting (#6325)" (#6454)
This reverts commit ec4e3a6d5c.
2022-08-31 18:09:40 -05:00
3ec53e544c remove capnp protocol for plugin... (#6421)
* remove capnp protocol for plugin...

* remove relative doc
2022-08-31 17:33:30 -05:00
JT
c52d45cb97 Move from source to source-env (#6277)
* start working on source-env

* WIP

* Get most tests working, still one to go

* Fix file-relative paths; Report parser error

* Fix merge conflicts; Restore source as deprecated

* Tests: Use source-env; Remove redundant tests

* Fmt

* Respect hidden env vars

* Fix file-relative eval for source-env

* Add file-relative eval to "overlay use"

* Use FILE_PWD only in source-env and "overlay use"

* Ignore new tests for now

This will be another issue

* Throw an error if setting FILE_PWD manually

* Fix source-related test failures

* Fix nu-check to respect FILE_PWD

* Fix corrupted spans in source-env shell errors

* Fix up some references to old source

* Remove deprecation message

* Re-introduce deleted tests

Co-authored-by: kubouch <kubouch@gmail.com>
2022-09-01 08:32:56 +12:00
11531b7630 Upgrade which dependency to fix case on Windows (#6453) 2022-08-31 09:50:18 -07:00
a098a27837 Bump iana-time-zone from 0.1.44 to 0.1.47 (#6448)
Bumps [iana-time-zone](https://github.com/strawlab/iana-time-zone) from 0.1.44 to 0.1.47.
- [Release notes](https://github.com/strawlab/iana-time-zone/releases)
- [Changelog](https://github.com/strawlab/iana-time-zone/blob/main/CHANGELOG.md)
- [Commits](https://github.com/strawlab/iana-time-zone/compare/0.1.44...v0.1.47)

---
updated-dependencies:
- dependency-name: iana-time-zone
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-08-31 06:19:20 -05:00
2591bd8c63 add more color highlighting to help (#6449) 2022-08-31 20:15:03 +12:00
JT
a03fb946d9 Allow parens around signatures (#6444)
* DRAFT: make var dollar optional

* couple fixes

* fix some tests + cleanup

* allow parens around signature

* clippy
2022-08-30 16:17:10 +12:00
9c58f2a522 Disable clickable links in SSH sessions (#6439)
* Disable clickable links in WSL and SSH sessions

* Revert WSL change; disable links in SSH only
2022-08-29 07:52:55 -07:00
JT
3cb9147f22 Make $ on variable names optional (#6434)
* DRAFT: make var dollar optional

* couple fixes

* fix some tests + cleanup
2022-08-29 14:35:55 +12:00
f1d72e2670 better error handling for nu_command::env::conig::utils::get_editor (#6430) 2022-08-28 12:56:55 +03:00
f1e7a01b2e shows wrong item when each command runs to failed. (#6437)
* add --wrong-item for each command

* fix test

* show multiple errors at once
2022-08-28 11:40:14 +03:00
b88ace4cde keep raw for variable inputed argument (#6426)
* keep raw for variable inputed argument

* fix clippy for windows

* make test runs on windows
2022-08-27 08:22:02 -05:00
34d7c17e78 Bring module's environment when activating overlay (#6425) 2022-08-27 01:32:19 +03:00
3f1824111d add the ast command to peek at the internals of nushell (#6423)
* add the ast command to peak at the internals of nushell

* fixed a bug in an example
2022-08-26 14:48:48 -05:00
fbae137442 Try to make argument with quotes for external command better (#6420)
* fix arg quote for external

* adjust comment
2022-08-26 06:50:41 -05:00
9850424251 Make run_external parameter required (#6418) 2022-08-26 06:31:33 -05:00
918ec9daa8 nu-command/filters: drop column check positive value (#6412) 2022-08-25 19:03:18 +03:00
7b502a4c7f register-plugin.nu: refactor register plugin (#6409) 2022-08-25 06:57:48 -05:00
7b07e976b8 Fix the span of "invalid time zone" (#6411)
Signed-off-by: nibon7 <nibon7@163.com>

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-25 13:21:54 +02:00
e45b169cba default to file completion after first command, add command option for completions (#6257)
* remove unnecessary FlatShape

* add test
2022-08-24 22:46:00 +03:00
5ebfa10495 convert string duration to named duration (#6406) 2022-08-24 14:45:51 -05:00
3f93dc2f1d Always report errors in cp (#6404) 2022-08-24 10:39:28 -07:00
a43514deb2 register-plugin.nu: remove .exe extension match to simplify code (#6400)
Signed-off-by: nibon7 <nibon7@163.com>

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-24 06:43:21 -05:00
ab77bf3289 Fix search terms for str distance (#6398)
Redundancy with the command name is unnecessary and now tested since #6380 
Fixes CI failure
2022-08-24 11:49:03 +02:00
0afe1e4e67 Test command names and search terms for redundancy (#6380)
* Test commands for proper names and search terms

Assert that the `Command.name()` is equal to `Signature.name`

Check that search terms are not just substrings of the command name as
they would not help finding the command.

* Clean up search terms

Remove redundant terms that just replicate the command name.
Try to eliminate substring between search terms, clean up where
necessary.
2022-08-24 11:16:47 +02:00
ef26d539a7 Make cp errors more specific (#6396) 2022-08-23 21:32:41 -07:00
fce8581321 add a plugin registration script (#6395) 2022-08-23 19:38:02 -05:00
ba6abd77c9 add another split words example (#6394) 2022-08-23 13:27:06 -05:00
a7295c8f1b Plugin: Add benchmark for different encoding protocol (#6384)
* add MessagePack as a plugin protocol

* tmp merge from remote

* add benchmark

* use less benchmark group, and add README for analysing benchmark result

* update README

* update README

* rewrite

* remove comment

* rename

* fmt

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-08-23 11:49:51 -05:00
2a310ef187 [Experiment] Reenable CI build cache for tests (#6390)
Let's see, if we can use `cargo-cache` again for the tests after #6389
reduced the number of test binaries to build that are quite large due as
they statically link copies of the same engine.
This might be one of the reasons why the tests on windows exceeded the
allotted disk space.
2022-08-23 17:17:33 +02:00
530e250573 nu-cli: merge completions tests into one file (#6389)
This PR merges all the completions tests into one file.

The reason for them to be separated was organization, so we wouldn't need to scroll a huge file.
But that came with another issue, because rust generates a new binary for each completion test file and each completion test depends on Nu looks like all the dataframes were coming into each test file as well (as pointed by @rgwood
2022-08-23 16:24:24 +02:00
6fbc76bc0f add edit distance/levenshtein command (#6383)
* add edit distance/levenshtein command

* change output to a record

* update test
2022-08-23 08:53:14 -05:00
884382bac4 preserve space by letting to nuon only add quotes when necessary (#6379)
* preserve space by letting `to nuon` only add quotes when necessary

* fix CI, add quotes with colon

* fmt

* add more chars to blacklist
2022-08-23 06:51:07 -05:00
d97975e9fa Allow "export-env" parsing in modules (#6382)
* Allow "export-env" parsing in modules

* Fmt

* Add test for importing module's environment
2022-08-23 10:45:17 +03:00
839b264261 Add test cases for $nu.config-path change (#6385)
* Add test cases for $nu.config-path change

Part of #6366

Signed-off-by: nibon7 <nibon7@163.com>

* do not start a new process to test default config path

Signed-off-by: nibon7 <nibon7@163.com>

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-23 10:18:14 +03:00
7ef4e5f940 Allow parsing modules as scripts (#6357)
* Allow parsing modules as scripts

* Remove 'export env' from the supported keywords

* Add test for export in blocks; Allow "export use"

* Allow evaluating "export alias"

* Fmt; Clippy

* Allow running "export extern" in scripts
2022-08-23 00:19:47 +03:00
646aace05b feat: external completions for commands/flags (#6295)
* wip

* wip

* cleanup

* error message

* cleanup

* cleanup

* fix clippy

* add test

* fix span

* cleanup

* cleanup

* cleanup

* fixed completion

* push char

* wip

* small fixes

* fix remove last span

* fmt

* cleanup

* fixes + more tests

* fix test

* only complete for commands

* also complete flags

* change decl_id to block_id

* use nu completion first

* fix test

* ignore test

* update config section
2022-08-22 21:38:51 +03:00
772ad896c8 Get $nu.config-path and $nu.env-path from EngineState (#6366)
* Get `$nu.config-path` and `$nu.env-path` from `EngineState`

Signed-off-by: nibon7 <nibon7@163.com>

* replace tuple with hashmap

Signed-off-by: nibon7 <nibon7@163.com>

* refactor set_config_path

Signed-off-by: nibon7 <nibon7@163.com>

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-22 19:30:09 +03:00
9c4bbe3c63 Rename overlay commands (#6375)
* rename from overlay add to overlay use

* rename from overlay remove to overlay hide

* rename add to use_
2022-08-21 17:27:56 +03:00
c5ca839294 Add pause and cls to cmd.exe exceptions (#6371) 2022-08-21 07:21:27 -07:00
5337a6dffa add MessagePack as a plugin protocol (#6370) 2022-08-21 06:13:38 -05:00
56ce10347e let to nuon convert column names with spaces (#6376)
* let `to nuon` convert column names with spaces

* change test
2022-08-21 13:12:13 +03:00
37bc90c62a fix the way lists are rendered in markdown (#6369) 2022-08-20 21:04:30 -05:00
ad7522bba0 Use string interpolation to construct log file path (#6365)
Signed-off-by: nibon7 <nibon7@163.com>

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-19 20:06:45 -05:00
bbcf374886 Update nu version for release workflow (#6361) 2022-08-20 08:05:58 +08:00
99c42582fe add a split words command (#6363)
* add a split words command

* changed regex
2022-08-20 05:55:54 +12:00
5a56d47f25 Add export-env command (#6355)
* WIP Start export-env

* Add missing file

* Do not modify the parser

Let's leave that for later

* Enable tests for export-env; Fmt
2022-08-18 23:24:39 +03:00
529c98085a Return error when kill didn't terminate successfully (#6354)
* Return error when `kill` didn't terminate successfully

Signed-off-by: nibon7 <nibon7@163.com>

* add test

Signed-off-by: nibon7 <nibon7@163.com>

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-18 11:58:51 -05:00
2b955f82b7 Fix #6330 (#6332) 2022-08-18 10:53:46 -05:00
1843fdc060 create clickable links in ls output if configured (#6333)
* create clickable links in ls output if configured

* move some comments
2022-08-18 05:45:49 -05:00
ec4e3a6d5c Add support for optional list stream output formatting (#6325)
* add support for optional list stream output formatting

* cargo fmt

* table: add ValueFormatter test
2022-08-18 05:44:53 -05:00
4ab468e65f Fix slice indexing (#6322)
* Return empty suggestions if no span contents is present

* Fix slice indexing
2022-08-18 05:44:09 -05:00
1d18f6947e Try again: in unix like system, set foreground process while running external command (#6273)
* Revert "Fix intermittent test crash (#6268)"

This reverts commit 555d9ee763.

* make a working version again

* try second impl

* add

* fmt

* check stdin is atty before acquire stdin

* add libc

* clean comment

* fix typo
2022-08-18 05:41:01 -05:00
df3b6d9d26 Add --execute option (#6302) 2022-08-18 12:25:52 +03:00
4bbdb73668 Bump dev version (#6350) 2022-08-18 21:14:17 +12:00
62d3497bbb fix links to the "think in nu" page in --help (#6348)
This commit uses `sed` on all the files of the code base to
replace each and every instance of https://www.nushell.sh/book/thinking_in_nushell.html,
which is a broken link, to https://www.nushell.sh/book/thinking_in_nu.html,
which is the new URL to the book page.

This exact command was
```nushell
ls **/* -f |
    where type == file |
    each {
        |it|
        sed -i 's|https://www.nushell.sh/book/thinking_in_nushell.html|https://www.nushell.sh/book/thinking_in_nu.html|' $it.name
    }
```

Co-authored-by: amtoine <44101798+AntoineStevan@users.noreply.github.com>
2022-08-17 13:51:07 -04:00
e614970c08 Use an older version of wingetcreate to do the msi package submission (#6347) 2022-08-18 00:11:19 +08:00
d931331b57 Add a manual run workflow for winget submission (#6345) 2022-08-17 23:01:34 +08:00
f18da2609a this pr fixes the wix building (#6343) 2022-08-17 09:10:13 -05:00
JT
2ef9cc118e Update engine_state.rs 2022-08-17 09:18:17 +12:00
274 changed files with 6350 additions and 9530 deletions

View File

@ -82,11 +82,9 @@ jobs:
# toolchain: ${{ matrix.rust }}
# override: true
# Temporarily disabled; the cache was getting huge (2.6GB compressed) on Windows and causing issues.
# TODO: investigate why the cache was so big
# - uses: Swatinem/rust-cache@v1
# with:
# key: ${{ matrix.style }}v3 # increment this to bust the cache if needed
- uses: Swatinem/rust-cache@v1
with:
key: ${{ matrix.style }}v3 # increment this to bust the cache if needed
- name: Tests
uses: actions-rs/cargo@v1

41
.github/workflows/manual.yml vendored Normal file
View File

@ -0,0 +1,41 @@
# This is a basic workflow that is manually triggered
# Don't run it unless you know what you are doing
name: Manual Workflow for Winget Submission
# Controls when the action will run. Workflow runs when manually triggered using the UI
# or API.
on:
workflow_dispatch:
# Inputs the workflow accepts.
inputs:
ver:
# Friendly description to be shown in the UI instead of 'ver'
description: 'The nushell version to release'
# Default value if no value is explicitly provided
default: '0.66.0'
# Input has to be provided for the workflow to run
required: true
uri:
# Friendly description to be shown in the UI instead of 'uri'
description: 'The nushell windows .msi package URI to publish'
# Default value if no value is explicitly provided
default: 'https://github.com/nushell/nushell/releases/download/0.66.0/nu-0.66.0-x86_64-pc-windows-msvc.msi'
# Input has to be provided for the workflow to run
required: true
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job
rls-winget-pkg:
name: Publish winget package manually
# The type of runner that the job will run on
runs-on: windows-latest
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Runs commands using the runners shell
- name: Submit package to Windows Package Manager Community Repository Manually
run: |
iwr https://github.com/microsoft/winget-create/releases/download/v1.0.4.0/wingetcreate.exe -OutFile wingetcreate.exe
.\wingetcreate.exe update Nushell.Nushell -s -v ${{ github.event.inputs.ver }} -u ${{ github.event.inputs.uri }} -t ${{ secrets.NUSHELL_PAT }}

View File

@ -41,7 +41,7 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
} else {
# musl-tools to fix 'Failed to find tool. Is `musl-gcc` installed?'
# Actually just for x86_64-unknown-linux-musl target
sudo apt install musl-tools -y
if $os == 'ubuntu-latest' { sudo apt install musl-tools -y }
cargo-build-nu $flags
}
}
@ -50,7 +50,7 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
# Build for Windows without static-link-openssl feature
# ----------------------------------------------------------------------------
if $os in ['windows-latest'] {
if ($flags | str trim | empty?) {
if ($flags | str trim | is-empty) {
cargo build --release --all --target $target --features=extra
} else {
cargo build --release --all --target $target --features=extra $flags
@ -80,7 +80,7 @@ let ver = if $os == 'windows-latest' {
} else {
(do -i { ./output/nu -c 'version' }) | str collect
}
if ($ver | str trim | empty?) {
if ($ver | str trim | is-empty) {
$'(ansi r)Incompatible nu binary...(ansi reset)'
} else { $ver }
@ -113,7 +113,7 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
cd $src; hr-line
# Wix need the binaries be stored in target/release/
cp -r $'($dist)/*' target/release/
cargo install cargo-wix --version 0.3.2
cargo install cargo-wix --version 0.3.3
cargo wix --no-build --nocapture --package nu --output $wixRelease
echo $'::set-output name=archive::($wixRelease)'
@ -124,14 +124,14 @@ if $os in ['ubuntu-latest', 'macos-latest'] {
7z a $archive *
print $'archive: ---> ($archive)';
let pkg = (ls -f $archive | get name)
if not ($pkg | empty?) {
if not ($pkg | is-empty) {
echo $'::set-output name=archive::($pkg | get 0)'
}
}
}
def 'cargo-build-nu' [ options: string ] {
if ($options | str trim | empty?) {
if ($options | str trim | is-empty) {
cargo build --release --all --target $target --features=extra,static-link-openssl
} else {
cargo build --release --all --target $target --features=extra,static-link-openssl $options
@ -143,7 +143,7 @@ def 'hr-line' [
--blank-line(-b): bool
] {
print $'(ansi g)---------------------------------------------------------------------------->(ansi reset)'
if $blank-line { char nl }
if $blank_line { char nl }
}
# Get the specified env key's value or ''

View File

@ -70,9 +70,9 @@ jobs:
target: ${{ matrix.target }}
- name: Setup Nushell
uses: hustcer/setup-nu@v1
uses: hustcer/setup-nu@v2.1
with:
version: 0.63.0
version: 0.68.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -13,7 +13,7 @@ jobs:
steps:
- name: Submit package to Windows Package Manager Community Repository
run: |
iwr https://aka.ms/wingetcreate/latest -OutFile wingetcreate.exe
iwr https://github.com/microsoft/winget-create/releases/download/v1.0.4.0/wingetcreate.exe -OutFile wingetcreate.exe
$github = Get-Content '${{ github.event_path }}' | ConvertFrom-Json
$installerUrl = $github.release.assets | Where-Object -Property name -match 'windows-msvc.msi' | Select -ExpandProperty browser_download_url -First 1
.\wingetcreate.exe update Nushell.Nushell -s -v $github.release.tag_name -u $installerUrl -t ${{ secrets.NUSHELL_PAT }}

View File

@ -70,5 +70,5 @@ cargo build
- To redirect trace logs to a file, enable the `--log-target file` switch.
```shell
cargo run --release --features=extra -- --log-level trace --log-target file
[($nu.temp-path) nu-($nu.pid).log] | path join | open
open $"($nu.temp-path)/nu-($nu.pid).log"
```

318
Cargo.lock generated
View File

@ -70,9 +70,9 @@ checksum = "77e9c9abb82613923ec78d7a461595d52491ba7240f3c64c0bbe0e6d98e0fce0"
[[package]]
name = "android_system_properties"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
@ -419,6 +419,12 @@ version = "3.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37ccbd214614c6783386c1af30caf03192f17891059cecc394b4fb119e363de3"
[[package]]
name = "byte-order"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b021a13e4bf34a5679ada4609a01337ae82f2c4c97493b9d8cbf8aa9af9bd0f4"
[[package]]
name = "byte-slice-cast"
version = "1.2.1"
@ -494,10 +500,10 @@ dependencies = [
]
[[package]]
name = "capnp"
version = "0.14.8"
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82efa3b0ab5e7e32b786334b052560ec0094135f906975d7481651b9ecf31a6a"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
@ -588,6 +594,17 @@ dependencies = [
"libloading",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"bitflags",
"textwrap 0.11.0",
"unicode-width",
]
[[package]]
name = "codepage"
version = "0.1.1"
@ -702,6 +719,42 @@ dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "criterion"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b01d6de93b2b6c65e17c634a26653a29d107b3c98c607c765bf38d041531cd8f"
dependencies = [
"atty",
"cast",
"clap",
"criterion-plot",
"csv",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_cbor",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2673cc8207403546f45f5fd319a974b1e6983ad1a3ee7e6041650013be041876"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "critical-section"
version = "0.2.7"
@ -1530,6 +1583,12 @@ dependencies = [
"tracing",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hamcrest2"
version = "0.3.0"
@ -1710,13 +1769,14 @@ dependencies = [
[[package]]
name = "iana-time-zone"
version = "0.1.44"
version = "0.1.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf7d67cf4a22adc5be66e75ebdf769b3f2ea032041437a7061f97a63dad4b"
checksum = "4c495f162af0bf17656d0014a0eded5f3cd2f365fdd204548c2869db89359dc7"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"js-sys",
"once_cell",
"wasm-bindgen",
"winapi 0.3.9",
]
@ -2145,9 +2205,9 @@ dependencies = [
[[package]]
name = "lz4-sys"
version = "1.9.3"
version = "1.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7be8908e2ed6f31c02db8a9fa962f03e36c53fbfde437363eae3306b85d7e17"
checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900"
dependencies = [
"cc",
"libc",
@ -2159,6 +2219,15 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
[[package]]
name = "mach2"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d0d1830bcd151a6fc4aea1369af235b36c1528fe976b8ff678683c9995eade8"
dependencies = [
"libc",
]
[[package]]
name = "malloc_buf"
version = "0.0.6"
@ -2258,7 +2327,7 @@ dependencies = [
"supports-hyperlinks",
"supports-unicode",
"terminal_size 0.1.17",
"textwrap",
"textwrap 0.15.0",
"thiserror",
"unicode-width",
]
@ -2440,6 +2509,7 @@ dependencies = [
"bitflags",
"cfg-if 1.0.0",
"libc",
"memoffset",
]
[[package]]
@ -2506,7 +2576,7 @@ dependencies = [
[[package]]
name = "nu"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"assert_cmd",
"chrono",
@ -2558,8 +2628,9 @@ dependencies = [
[[package]]
name = "nu-cli"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"atty",
"chrono",
"crossterm 0.24.0",
"fancy-regex",
@ -2586,7 +2657,7 @@ dependencies = [
[[package]]
name = "nu-color-config"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"nu-ansi-term",
"nu-json",
@ -2597,7 +2668,7 @@ dependencies = [
[[package]]
name = "nu-command"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"Inflector",
"alphanumeric-sort",
@ -2653,6 +2724,7 @@ dependencies = [
"pathdiff",
"polars",
"powierza-coefficient",
"proptest",
"quick-xml 0.23.0",
"quickcheck",
"quickcheck_macros",
@ -2690,19 +2762,20 @@ dependencies = [
[[package]]
name = "nu-engine"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"chrono",
"nu-glob",
"nu-path",
"nu-protocol",
"nu-utils",
"strip-ansi-escapes",
"sysinfo",
]
[[package]]
name = "nu-glob"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"doc-comment",
"tempdir",
@ -2710,7 +2783,7 @@ dependencies = [
[[package]]
name = "nu-json"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"fancy-regex",
"lazy_static",
@ -2723,7 +2796,7 @@ dependencies = [
[[package]]
name = "nu-parser"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"chrono",
"itertools",
@ -2739,7 +2812,7 @@ dependencies = [
[[package]]
name = "nu-path"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"dirs-next",
"dunce",
@ -2748,19 +2821,23 @@ dependencies = [
[[package]]
name = "nu-plugin"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"bincode",
"capnp",
"byte-order",
"criterion",
"nu-engine",
"nu-protocol",
"rmp",
"rmp-serde",
"rmpv",
"serde",
"serde_json",
]
[[package]]
name = "nu-pretty-hex"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"heapless",
"nu-ansi-term",
@ -2769,7 +2846,7 @@ dependencies = [
[[package]]
name = "nu-protocol"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"byte-unit",
"chrono",
@ -2790,12 +2867,15 @@ dependencies = [
[[package]]
name = "nu-system"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"atty",
"chrono",
"errno",
"libc",
"libproc",
"mach2",
"nix",
"ntapi",
"once_cell",
"procfs",
@ -2804,7 +2884,7 @@ dependencies = [
[[package]]
name = "nu-table"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"atty",
"nu-ansi-term",
@ -2815,7 +2895,7 @@ dependencies = [
[[package]]
name = "nu-term-grid"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"strip-ansi-escapes",
"unicode-width",
@ -2823,7 +2903,7 @@ dependencies = [
[[package]]
name = "nu-test-support"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"getset",
"hamcrest2",
@ -2838,7 +2918,7 @@ dependencies = [
[[package]]
name = "nu-utils"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"crossterm_winapi",
"lscolors",
@ -2858,7 +2938,7 @@ dependencies = [
[[package]]
name = "nu_plugin_example"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"nu-plugin",
"nu-protocol",
@ -2866,7 +2946,7 @@ dependencies = [
[[package]]
name = "nu_plugin_gstat"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"git2",
"nu-engine",
@ -2876,7 +2956,7 @@ dependencies = [
[[package]]
name = "nu_plugin_inc"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"nu-plugin",
"nu-protocol",
@ -2885,7 +2965,7 @@ dependencies = [
[[package]]
name = "nu_plugin_query"
version = "0.67.0"
version = "0.68.1"
dependencies = [
"gjson",
"nu-engine",
@ -3070,9 +3150,15 @@ dependencies = [
[[package]]
name = "once_cell"
version = "1.13.0"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1"
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "opaque-debug"
@ -3230,6 +3316,12 @@ dependencies = [
"regex",
]
[[package]]
name = "paste"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9423e2b32f7a043629287a536f21951e8c6a82482d0acb1eeebfc90bc2225b22"
[[package]]
name = "pathdiff"
version = "0.2.1"
@ -3421,6 +3513,34 @@ dependencies = [
"array-init-cursor",
]
[[package]]
name = "plotters"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "716b4eeb6c4a1d3ecc956f75b43ec2e8e8ba80026413e70a3f41fd3313d3492b"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
[[package]]
name = "plotters-svg"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
dependencies = [
"plotters-backend",
]
[[package]]
name = "polars"
version = "0.23.2"
@ -3677,6 +3797,26 @@ dependencies = [
"rustix",
]
[[package]]
name = "proptest"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5"
dependencies = [
"bit-set",
"bitflags",
"byteorder",
"lazy_static",
"num-traits",
"quick-error 2.0.1",
"rand 0.8.5",
"rand_chacha 0.3.1",
"rand_xorshift",
"regex-syntax",
"rusty-fork",
"tempfile",
]
[[package]]
name = "pure-rust-locales"
version = "0.5.6"
@ -3699,6 +3839,12 @@ version = "1.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0"
[[package]]
name = "quick-error"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
[[package]]
name = "quick-xml"
version = "0.19.0"
@ -3868,6 +4014,15 @@ dependencies = [
"rand_core 0.5.1",
]
[[package]]
name = "rand_xorshift"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f"
dependencies = [
"rand_core 0.6.3",
]
[[package]]
name = "rayon"
version = "1.5.3"
@ -3923,9 +4078,9 @@ dependencies = [
[[package]]
name = "reedline"
version = "0.10.0"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d84e8704e9eb141e73ac426c72af95eb195d4c3221a11ea92d5709f4a025adb5"
checksum = "5559b5ab4817b0da0c6fc6814edfae537209e01d955a2f3e7595606e3d039691"
dependencies = [
"chrono",
"crossterm 0.24.0",
@ -4040,6 +4195,38 @@ dependencies = [
"regex",
]
[[package]]
name = "rmp"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44519172358fd6d58656c86ab8e7fbc9e1490c3e8f14d35ed78ca0dd07403c9f"
dependencies = [
"byteorder",
"num-traits",
"paste",
]
[[package]]
name = "rmp-serde"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25786b0d276110195fa3d6f3f31299900cf71dfbd6c28450f3f58a0e7f7a347e"
dependencies = [
"byteorder",
"rmp",
"serde",
]
[[package]]
name = "rmpv"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de8813b3a2f95c5138fe5925bfb8784175d88d6bff059ba8ce090aa891319754"
dependencies = [
"num-traits",
"rmp",
]
[[package]]
name = "roxmltree"
version = "0.14.1"
@ -4180,6 +4367,18 @@ version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24c8ad4f0c00e1eb5bc7614d236a7f1300e3dbd76b68cac8e06fb00b015ad8d8"
[[package]]
name = "rusty-fork"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f"
dependencies = [
"fnv",
"quick-error 1.2.3",
"tempfile",
"wait-timeout",
]
[[package]]
name = "ryu"
version = "1.0.10"
@ -4311,18 +4510,28 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.140"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.140"
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
@ -4725,7 +4934,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36e39da5d30887b5690e29de4c5ebb8ddff64ebd9933f98a01daaa4fd11b36ea"
dependencies = [
"peresil",
"quick-error",
"quick-error 1.2.3",
"sxd-document",
]
@ -4865,6 +5074,15 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "507e9898683b6c43a9aa55b64259b721b52ba226e0f3779137e50ad114a4c90b"
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "textwrap"
version = "0.15.0"
@ -4940,6 +5158,16 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792"
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -5462,13 +5690,13 @@ dependencies = [
[[package]]
name = "which"
version = "4.2.5"
version = "4.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae"
checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b"
dependencies = [
"either",
"lazy_static",
"libc",
"once_cell",
]
[[package]]

View File

@ -11,7 +11,7 @@ name = "nu"
readme = "README.md"
repository = "https://github.com/nushell/nushell"
rust-version = "1.60"
version = "0.67.0"
version = "0.68.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -39,21 +39,21 @@ ctrlc = "3.2.1"
log = "0.4"
miette = "5.1.0"
nu-ansi-term = "0.46.0"
nu-cli = { path="./crates/nu-cli", version = "0.67.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.67.0" }
nu-command = { path="./crates/nu-command", version = "0.67.0" }
nu-engine = { path="./crates/nu-engine", version = "0.67.0" }
nu-json = { path="./crates/nu-json", version = "0.67.0" }
nu-parser = { path="./crates/nu-parser", version = "0.67.0" }
nu-path = { path="./crates/nu-path", version = "0.67.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.67.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.67.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.67.0" }
nu-system = { path = "./crates/nu-system", version = "0.67.0" }
nu-table = { path = "./crates/nu-table", version = "0.67.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.67.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.67.0" }
reedline = { version = "0.10.0", features = ["bashisms", "sqlite"]}
nu-cli = { path="./crates/nu-cli", version = "0.68.1" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.68.1" }
nu-command = { path="./crates/nu-command", version = "0.68.1" }
nu-engine = { path="./crates/nu-engine", version = "0.68.1" }
nu-json = { path="./crates/nu-json", version = "0.68.1" }
nu-parser = { path="./crates/nu-parser", version = "0.68.1" }
nu-path = { path="./crates/nu-path", version = "0.68.1" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.68.1" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.68.1" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.68.1" }
nu-system = { path = "./crates/nu-system", version = "0.68.1" }
nu-table = { path = "./crates/nu-table", version = "0.68.1" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.68.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.68.1" }
reedline = { version = "0.11.0", features = ["bashisms", "sqlite"]}
rayon = "1.5.1"
is_executable = "1.0.1"
simplelog = "0.12.0"
@ -65,7 +65,7 @@ openssl = { version = "0.10.38", features = ["vendored"], optional = true }
signal-hook = { version = "0.3.14", default-features = false }
[dev-dependencies]
nu-test-support = { path="./crates/nu-test-support", version = "0.67.0" }
nu-test-support = { path="./crates/nu-test-support", version = "0.68.1" }
tempfile = "3.2.0"
assert_cmd = "2.0.2"
pretty_assertions = "1.0.0"
@ -121,3 +121,4 @@ debug = false
[[bin]]
name = "nu"
path = "src/main.rs"

View File

@ -5,23 +5,24 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.67.0"
version = "0.68.1"
[dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.67.0" }
nu-command = { path = "../nu-command", version = "0.67.0" }
nu-test-support = { path="../nu-test-support", version = "0.68.1" }
nu-command = { path = "../nu-command", version = "0.68.1" }
rstest = {version = "0.15.0", default-features = false}
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.67.0" }
nu-path = { path = "../nu-path", version = "0.67.0" }
nu-parser = { path = "../nu-parser", version = "0.67.0" }
nu-protocol = { path = "../nu-protocol", version = "0.67.0" }
nu-utils = { path = "../nu-utils", version = "0.67.0" }
nu-engine = { path = "../nu-engine", version = "0.68.1" }
nu-path = { path = "../nu-path", version = "0.68.1" }
nu-parser = { path = "../nu-parser", version = "0.68.1" }
nu-protocol = { path = "../nu-protocol", version = "0.68.1" }
nu-utils = { path = "../nu-utils", version = "0.68.1" }
nu-ansi-term = "0.46.0"
nu-color-config = { path = "../nu-color-config", version = "0.67.0" }
reedline = { version = "0.10.0", features = ["bashisms", "sqlite"]}
nu-color-config = { path = "../nu-color-config", version = "0.68.1" }
reedline = { version = "0.11.0", features = ["bashisms", "sqlite"]}
atty = "0.2.14"
chrono = "0.4.21"
crossterm = "0.24.0"
fancy-regex = "0.10.0"

View File

@ -11,6 +11,7 @@ pub struct CommandCompletion {
engine_state: Arc<EngineState>,
flattened: Vec<(Span, FlatShape)>,
flat_shape: FlatShape,
force_completion_after_space: bool,
}
impl CommandCompletion {
@ -19,11 +20,13 @@ impl CommandCompletion {
_: &StateWorkingSet,
flattened: Vec<(Span, FlatShape)>,
flat_shape: FlatShape,
force_completion_after_space: bool,
) -> Self {
Self {
engine_state,
flattened,
flat_shape,
force_completion_after_space,
}
}
@ -206,6 +209,10 @@ impl Completer for CommandCompletion {
|| ((span.end - span.start) == 0)
{
// we're in a gap or at a command
if working_set.get_span_contents(span).is_empty() && !self.force_completion_after_space
{
return vec![];
}
self.complete_commands(
working_set,
span,

View File

@ -2,10 +2,11 @@ use crate::completions::{
CommandCompletion, Completer, CompletionOptions, CustomCompletion, DirectoryCompletion,
DotNuCompletion, FileCompletion, FlagCompletion, MatchAlgorithm, VariableCompletion,
};
use nu_engine::eval_block;
use nu_parser::{flatten_expression, parse, FlatShape};
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
Span,
BlockId, PipelineData, Span, Value,
};
use reedline::{Completer as ReedlineCompleter, Suggestion};
use std::str;
@ -56,6 +57,67 @@ impl NuCompleter {
suggestions
}
fn external_completion(
&self,
block_id: BlockId,
spans: Vec<String>,
offset: usize,
span: Span,
) -> Vec<Suggestion> {
let stack = self.stack.clone();
let block = self.engine_state.get_block(block_id);
let mut callee_stack = stack.gather_captures(&block.captures);
// Line
if let Some(pos_arg) = block.signature.required_positional.get(0) {
if let Some(var_id) = pos_arg.var_id {
callee_stack.add_var(
var_id,
Value::List {
vals: spans
.into_iter()
.map(|it| Value::String {
val: it,
span: Span::unknown(),
})
.collect(),
span: Span::unknown(),
},
);
}
}
let result = eval_block(
&self.engine_state,
&mut callee_stack,
block,
PipelineData::new(span),
true,
true,
);
match result {
Ok(pd) => {
let value = pd.into_value(span);
if let Value::List { vals, span: _ } = value {
let result = map_value_completions(
vals.iter(),
Span {
start: span.start,
end: span.end,
},
offset,
);
return result;
}
}
Err(err) => println!("failed to eval completer block: {}", err),
}
vec![]
}
fn completion_helper(&mut self, line: &str, pos: usize) -> Vec<Suggestion> {
let mut working_set = StateWorkingSet::new(&self.engine_state);
let offset = working_set.next_span_start();
@ -63,14 +125,32 @@ impl NuCompleter {
let initial_line = line.to_string();
new_line.push(b'a');
let pos = offset + pos;
let config = self.engine_state.get_config();
let (output, _err) = parse(&mut working_set, Some("completer"), &new_line, false, &[]);
for pipeline in output.pipelines.into_iter() {
for expr in pipeline.expressions {
let flattened: Vec<_> = flatten_expression(&working_set, &expr);
let span_offset: usize = alias_offset.iter().sum();
let mut spans: Vec<String> = vec![];
for (flat_idx, flat) in flattened.iter().enumerate() {
// Read the current spam to string
let current_span = working_set.get_span_contents(flat.0).to_vec();
let current_span_str = String::from_utf8_lossy(&current_span);
// Skip the last 'a' as span item
if flat_idx == flattened.len() - 1 {
let mut chars = current_span_str.chars();
chars.next_back();
let current_span_str = chars.as_str().to_owned();
spans.push(current_span_str.to_string());
} else {
spans.push(current_span_str.to_string());
}
// Complete based on the last span
if pos + span_offset >= flat.0.start && pos + span_offset < flat.0.end {
// Context variables
let most_left_var =
@ -113,8 +193,38 @@ impl NuCompleter {
// Flags completion
if prefix.starts_with(b"-") {
let mut completer = FlagCompletion::new(expr);
// Try to complete flag internally
let mut completer = FlagCompletion::new(expr.clone());
let result = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
new_span,
offset,
pos,
);
if !result.is_empty() {
return result;
}
// We got no results for internal completion
// now we can check if external completer is set and use it
if let Some(block_id) = config.external_completer {
return self.external_completion(block_id, spans, offset, new_span);
}
}
// specially check if it is currently empty - always complete commands
if flat_idx == 0 && working_set.get_span_contents(new_span).is_empty() {
let mut completer = CommandCompletion::new(
self.engine_state.clone(),
&working_set,
flattened.clone(),
// flat_idx,
FlatShape::String,
true,
);
return self.process_completion(
&mut completer,
&working_set,
@ -125,7 +235,7 @@ impl NuCompleter {
);
}
// Completions that depends on the previous expression (e.g: use, source)
// Completions that depends on the previous expression (e.g: use, source-env)
if flat_idx > 0 {
if let Some(previous_expr) = flattened.get(flat_idx - 1) {
// Read the content for the previous expression
@ -133,7 +243,7 @@ impl NuCompleter {
working_set.get_span_contents(previous_expr.0).to_vec();
// Completion for .nu files
if prev_expr_str == b"use" || prev_expr_str == b"source" {
if prev_expr_str == b"use" || prev_expr_str == b"source-env" {
let mut completer =
DotNuCompletion::new(self.engine_state.clone());
@ -212,9 +322,10 @@ impl NuCompleter {
flattened.clone(),
// flat_idx,
flat_shape.clone(),
false,
);
let out: Vec<_> = self.process_completion(
let mut out: Vec<_> = self.process_completion(
&mut completer,
&working_set,
prefix.clone(),
@ -223,21 +334,30 @@ impl NuCompleter {
pos,
);
if out.is_empty() {
let mut completer =
FileCompletion::new(self.engine_state.clone());
return self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
}
return out;
// Check for file completion
let mut completer = FileCompletion::new(self.engine_state.clone());
out = self.process_completion(
&mut completer,
&working_set,
prefix,
new_span,
offset,
pos,
);
if !out.is_empty() {
return out;
}
// Try to complete using an exnternal compelter (if set)
if let Some(block_id) = config.external_completer {
return self
.external_completion(block_id, spans, offset, new_span);
}
}
};
}
@ -383,3 +503,65 @@ fn most_left_variable(
Some((var, sublevels))
}
pub fn map_value_completions<'a>(
list: impl Iterator<Item = &'a Value>,
span: Span,
offset: usize,
) -> Vec<Suggestion> {
list.filter_map(move |x| {
// Match for string values
if let Ok(s) = x.as_string() {
return Some(Suggestion {
value: s,
description: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: false,
});
}
// Match for record values
if let Ok((cols, vals)) = x.as_record() {
let mut suggestion = Suggestion {
value: String::from(""), // Initialize with empty string
description: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: false,
};
// Iterate the cols looking for `value` and `description`
cols.iter().zip(vals).for_each(|it| {
// Match `value` column
if it.0 == "value" {
// Convert the value to string
if let Ok(val_str) = it.1.as_string() {
// Update the suggestion value
suggestion.value = val_str;
}
}
// Match `description` column
if it.0 == "description" {
// Convert the value to string
if let Ok(desc_str) = it.1.as_string() {
// Update the suggestion value
suggestion.description = Some(desc_str);
}
}
});
return Some(suggestion);
}
None
})
.collect()
}

View File

@ -8,6 +8,8 @@ use nu_protocol::{
use reedline::Suggestion;
use std::sync::Arc;
use super::completer::map_value_completions;
pub struct CustomCompletion {
engine_state: Arc<EngineState>,
stack: Stack,
@ -26,69 +28,6 @@ impl CustomCompletion {
sort_by: SortBy::None,
}
}
fn map_completions<'a>(
&self,
list: impl Iterator<Item = &'a Value>,
span: Span,
offset: usize,
) -> Vec<Suggestion> {
list.filter_map(move |x| {
// Match for string values
if let Ok(s) = x.as_string() {
return Some(Suggestion {
value: s,
description: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: false,
});
}
// Match for record values
if let Ok((cols, vals)) = x.as_record() {
let mut suggestion = Suggestion {
value: String::from(""), // Initialize with empty string
description: None,
extra: None,
span: reedline::Span {
start: span.start - offset,
end: span.end - offset,
},
append_whitespace: false,
};
// Iterate the cols looking for `value` and `description`
cols.iter().zip(vals).for_each(|it| {
// Match `value` column
if it.0 == "value" {
// Convert the value to string
if let Ok(val_str) = it.1.as_string() {
// Update the suggestion value
suggestion.value = val_str;
}
}
// Match `description` column
if it.0 == "description" {
// Convert the value to string
if let Ok(desc_str) = it.1.as_string() {
// Update the suggestion value
suggestion.description = Some(desc_str);
}
}
});
return Some(suggestion);
}
None
})
.collect()
}
}
impl Completer for CustomCompletion {
@ -144,7 +83,7 @@ impl Completer for CustomCompletion {
.and_then(|val| {
val.as_list()
.ok()
.map(|it| self.map_completions(it.iter(), span, offset))
.map(|it| map_value_completions(it.iter(), span, offset))
})
.unwrap_or_default();
let options = value.get_data_by_key("options");
@ -189,7 +128,7 @@ impl Completer for CustomCompletion {
completions
}
Value::List { vals, .. } => self.map_completions(vals.iter(), span, offset),
Value::List { vals, .. } => map_value_completions(vals.iter(), span, offset),
_ => vec![],
}
}

View File

@ -16,7 +16,7 @@ use nu_protocol::{
ast::PathMember,
engine::{EngineState, Stack, StateWorkingSet},
format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span,
Type, Value, VarId,
Spanned, Type, Value, VarId,
};
use reedline::{DefaultHinter, Emacs, SqliteBackedHistory, Vi};
use std::io::{self, Write};
@ -39,9 +39,20 @@ pub fn evaluate_repl(
stack: &mut Stack,
nushell_path: &str,
is_perf_true: bool,
prerun_command: Option<Spanned<String>>,
) -> Result<()> {
use reedline::{FileBackedHistory, Reedline, Signal};
// Guard against invocation without a connected terminal.
// reedline / crossterm event polling will fail without a connected tty
if !atty::is(atty::Stream::Stdin) {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
"Nushell launched as interactive REPL but STDIN is not a TTY, either launch in a valid terminal or provide arguments to invoke a script!",
))
.into_diagnostic();
}
let mut entry_num = 0;
let mut nu_prompt = NushellPrompt::new();
@ -140,6 +151,17 @@ pub fn evaluate_repl(
}
}
if let Some(s) = prerun_command {
eval_source(
engine_state,
stack,
s.item.as_bytes(),
&format!("entry #{}", entry_num),
PipelineData::new(Span::new(0, 0)),
);
engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?;
}
loop {
if is_perf_true {
info!(
@ -312,7 +334,8 @@ pub fn evaluate_repl(
Ok(Signal::Success(s)) => {
let history_supports_meta =
matches!(config.history_file_format, HistoryFileFormat::Sqlite);
if history_supports_meta && !s.is_empty() {
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
{
line_editor
.update_last_command_context(&|mut c| {
c.start_timestamp = Some(chrono::Utc::now());
@ -433,7 +456,8 @@ pub fn evaluate_repl(
},
);
if history_supports_meta && !s.is_empty() {
if history_supports_meta && !s.is_empty() && line_editor.has_last_command_context()
{
line_editor
.update_last_command_context(&|mut c| {
c.duration = Some(cmd_duration);
@ -484,6 +508,10 @@ pub fn evaluate_repl(
let message = err.to_string();
if !message.contains("duration") {
println!("Error: {:?}", err);
// TODO: Identify possible error cases where a hard failure is preferable
// Ignoring and reporting could hide bigger problems
// e.g. https://github.com/nushell/nushell/issues/6452
// Alternatively only allow that expected failures let the REPL loop
}
if shell_integration {
run_ansi_sequence(&get_command_finished_marker(stack, engine_state))?;

View File

@ -1,65 +0,0 @@
pub mod support;
use nu_cli::NuCompleter;
use reedline::Completer;
use support::{match_suggestions, new_engine};
#[test]
fn alias_of_command_and_flags() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls -l"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("ll t", 4);
#[cfg(windows)]
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn alias_of_basic_command() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls "#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("ll t", 4);
#[cfg(windows)]
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn alias_of_another_alias() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls -la"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir.clone()).is_ok());
// Create the second alias
let alias = r#"alias lf = ll -f"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("lf t", 4);
#[cfg(windows)]
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
}

View File

@ -0,0 +1,660 @@
pub mod support;
use nu_cli::NuCompleter;
use nu_parser::parse;
use nu_protocol::engine::StateWorkingSet;
use reedline::{Completer, Suggestion};
use rstest::{fixture, rstest};
use support::{file, folder, match_suggestions, new_engine};
#[fixture]
fn completer() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example
let record = "def tst [--mod -s] {}";
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(std::sync::Arc::new(engine), stack)
}
#[fixture]
fn completer_strings() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
def my-command [animal: string@animals] { print $animal }"#;
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(std::sync::Arc::new(engine), stack)
}
#[rstest]
fn variables_completions_double_dash_argument(mut completer: NuCompleter) {
let suggestions = completer.complete("tst --", 6);
let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
// dbg!(&expected, &suggestions);
match_suggestions(expected, suggestions);
}
#[rstest]
fn variables_completions_single_dash_argument(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn variables_completions_command(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command ", 9);
let expected: Vec<String> = vec!["my-command".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn variables_completions_subcommands(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn variables_completions_subcommands_2(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[test]
fn dotnu_completions() {
// Create a new engine
let (_, _, engine, stack) = new_engine();
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test source completion
let completion_str = "source-env ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(1, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.get(0).unwrap().value);
// Test use completion
let completion_str = "use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(1, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.get(0).unwrap().value);
}
#[test]
#[ignore]
fn external_completer_trailing_space() {
// https://github.com/nushell/nushell/issues/6378
let block = "let external_completer = {|spans| $spans}";
let input = "gh alias ".to_string();
let suggestions = run_external_completion(&block, &input);
assert_eq!(3, suggestions.len());
assert_eq!("gh", suggestions.get(0).unwrap().value);
assert_eq!("alias", suggestions.get(1).unwrap().value);
assert_eq!("", suggestions.get(2).unwrap().value);
}
#[test]
fn external_completer_no_trailing_space() {
let block = "let external_completer = {|spans| $spans}";
let input = "gh alias".to_string();
let suggestions = run_external_completion(&block, &input);
assert_eq!(2, suggestions.len());
assert_eq!("gh", suggestions.get(0).unwrap().value);
assert_eq!("alias", suggestions.get(1).unwrap().value);
}
#[test]
fn external_completer_pass_flags() {
let block = "let external_completer = {|spans| $spans}";
let input = "gh api --".to_string();
let suggestions = run_external_completion(&block, &input);
assert_eq!(3, suggestions.len());
assert_eq!("gh", suggestions.get(0).unwrap().value);
assert_eq!("api", suggestions.get(1).unwrap().value);
assert_eq!("--", suggestions.get(2).unwrap().value);
}
#[test]
fn file_completions() {
// Create a new engine
let (dir, dir_str, engine, stack) = new_engine();
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the current folder
let target_dir = format!("cp {}", dir_str);
let suggestions = completer.complete(&target_dir, target_dir.len());
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("nushell")),
folder(dir.join("test_a")),
folder(dir.join("test_b")),
folder(dir.join("another")),
file(dir.join("custom_completion.nu")),
file(dir.join(".hidden_file")),
folder(dir.join(".hidden_folder")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
// Test completions for a file
let target_dir = format!("cp {}", folder(dir.join("another")));
let suggestions = completer.complete(&target_dir, target_dir.len());
// Create the expected values
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
// Match the results
match_suggestions(expected_paths, suggestions);
}
#[test]
fn command_ls_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "ls ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_open_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "open ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_rm_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "rm ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_cp_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "cp ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_save_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "save ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_touch_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "touch ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_watch_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "watch ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn flag_completions() {
// Create a new engine
let (_, _, engine, stack) = new_engine();
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the 'ls' flags
let suggestions = completer.complete("ls -", 4);
assert_eq!(14, suggestions.len());
let expected: Vec<String> = vec![
"--all".into(),
"--directory".into(),
"--du".into(),
"--full-paths".into(),
"--help".into(),
"--long".into(),
"--short-names".into(),
"-D".into(),
"-a".into(),
"-d".into(),
"-f".into(),
"-h".into(),
"-l".into(),
"-s".into(),
];
// Match results
match_suggestions(expected, suggestions);
}
#[test]
fn folder_completions() {
// Create a new engine
let (dir, dir_str, engine, stack) = new_engine();
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the current folder
let target_dir = format!("cd {}", dir_str);
let suggestions = completer.complete(&target_dir, target_dir.len());
// Create the expected values
let expected_paths: Vec<String> = vec![
folder(dir.join("test_a")),
folder(dir.join("test_b")),
folder(dir.join("another")),
folder(dir.join(".hidden_folder")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
}
#[test]
fn variables_completions() {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example
let record = "let actor = { name: 'Tom Hardy', age: 44 }";
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for $nu
let suggestions = completer.complete("$nu.", 4);
assert_eq!(9, suggestions.len());
let expected: Vec<String> = vec![
"config-path".into(),
"env-path".into(),
"history-path".into(),
"home-path".into(),
"loginshell-path".into(),
"os-info".into(),
"pid".into(),
"scope".into(),
"temp-path".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.h (filter)
let suggestions = completer.complete("$nu.h", 5);
assert_eq!(2, suggestions.len());
let expected: Vec<String> = vec!["history-path".into(), "home-path".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for custom var
let suggestions = completer.complete("$actor.", 7);
assert_eq!(2, suggestions.len());
let expected: Vec<String> = vec!["age".into(), "name".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for custom var (filtering)
let suggestions = completer.complete("$actor.n", 8);
assert_eq!(1, suggestions.len());
let expected: Vec<String> = vec!["name".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $env
let suggestions = completer.complete("$env.", 5);
assert_eq!(2, suggestions.len());
let expected: Vec<String> = vec!["PWD".into(), "TEST".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $env
let suggestions = completer.complete("$env.T", 6);
assert_eq!(1, suggestions.len());
let expected: Vec<String> = vec!["TEST".into()];
// Match results
match_suggestions(expected, suggestions);
}
#[test]
fn alias_of_command_and_flags() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls -l"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("ll t", 4);
#[cfg(windows)]
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn alias_of_basic_command() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls "#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("ll t", 4);
#[cfg(windows)]
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn alias_of_another_alias() {
let (dir, _, mut engine, mut stack) = new_engine();
// Create an alias
let alias = r#"alias ll = ls -la"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir.clone()).is_ok());
// Create the second alias
let alias = r#"alias lf = ll -f"#;
assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok());
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("lf t", 4);
#[cfg(windows)]
let expected_paths: Vec<String> = vec!["test_a\\".to_string(), "test_b\\".to_string()];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
match_suggestions(expected_paths, suggestions)
}
fn run_external_completion(block: &str, input: &str) -> Vec<Suggestion> {
// Create a new engine
let (dir, _, mut engine_state, mut stack) = new_engine();
let (_, delta) = {
let mut working_set = StateWorkingSet::new(&engine_state);
let (block, err) = parse(&mut working_set, None, block.as_bytes(), false, &[]);
assert!(err.is_none());
(block, working_set.render())
};
assert!(engine_state.merge_delta(delta).is_ok());
// Merge environment into the permanent state
assert!(engine_state.merge_env(&mut stack, &dir).is_ok());
let latest_block_id = engine_state.num_blocks() - 1;
// Change config adding the external completer
let mut config = engine_state.get_config().clone();
config.external_completer = Some(latest_block_id);
engine_state.set_config(&config);
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine_state), stack);
completer.complete(&input, input.len())
}
#[test]
fn unknown_command_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "thiscommanddoesnotexist ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}

View File

@ -1,69 +0,0 @@
pub mod support;
use nu_cli::NuCompleter;
use reedline::Completer;
use rstest::{fixture, rstest};
use support::{match_suggestions, new_engine};
#[fixture]
fn completer() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example
let record = "def tst [--mod -s] {}";
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(std::sync::Arc::new(engine), stack)
}
#[fixture]
fn completer_strings() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
def my-command [animal: string@animals] { print $animal }"#;
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(std::sync::Arc::new(engine), stack)
}
#[rstest]
fn variables_completions_double_dash_argument(mut completer: NuCompleter) {
let suggestions = completer.complete("tst --", 6);
let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
// dbg!(&expected, &suggestions);
match_suggestions(expected, suggestions);
}
#[rstest]
fn variables_completions_single_dash_argument(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn variables_completions_command(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command ", 9);
let expected: Vec<String> = vec!["my-command".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn variables_completions_subcommands(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn variables_completions_subcommands_2(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command ", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}

View File

@ -1,28 +0,0 @@
pub mod support;
use nu_cli::NuCompleter;
use reedline::Completer;
use support::new_engine;
#[test]
fn dotnu_completions() {
// Create a new engine
let (_, _, engine, stack) = new_engine();
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test source completion
let completion_str = "source ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(1, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.get(0).unwrap().value);
// Test use completion
let completion_str = "use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(1, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.get(0).unwrap().value);
}

View File

@ -1,272 +0,0 @@
pub mod support;
use nu_cli::NuCompleter;
use reedline::Completer;
use support::{file, folder, match_suggestions, new_engine};
#[test]
fn file_completions() {
// Create a new engine
let (dir, dir_str, engine, stack) = new_engine();
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the current folder
let target_dir = format!("cp {}", dir_str);
let suggestions = completer.complete(&target_dir, target_dir.len());
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("nushell")),
folder(dir.join("test_a")),
folder(dir.join("test_b")),
folder(dir.join("another")),
file(dir.join("custom_completion.nu")),
file(dir.join(".hidden_file")),
folder(dir.join(".hidden_folder")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
// Test completions for a file
let target_dir = format!("cp {}", folder(dir.join("another")));
let suggestions = completer.complete(&target_dir, target_dir.len());
// Create the expected values
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
// Match the results
match_suggestions(expected_paths, suggestions);
}
#[test]
fn command_ls_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "ls ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_open_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "open ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_rm_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "rm ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_cp_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "cp ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_save_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "save ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_touch_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "touch ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}
#[test]
fn command_watch_completion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "watch ";
let suggestions = completer.complete(target_dir, target_dir.len());
#[cfg(windows)]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a\\".to_string(),
"test_b\\".to_string(),
"another\\".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder\\".to_string(),
];
#[cfg(not(windows))]
let expected_paths: Vec<String> = vec![
"nushell".to_string(),
"test_a/".to_string(),
"test_b/".to_string(),
"another/".to_string(),
"custom_completion.nu".to_string(),
".hidden_file".to_string(),
".hidden_folder/".to_string(),
];
match_suggestions(expected_paths, suggestions)
}

View File

@ -1,38 +0,0 @@
pub mod support;
use nu_cli::NuCompleter;
use reedline::Completer;
use support::{match_suggestions, new_engine};
#[test]
fn flag_completions() {
// Create a new engine
let (_, _, engine, stack) = new_engine();
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the 'ls' flags
let suggestions = completer.complete("ls -", 4);
assert_eq!(14, suggestions.len());
let expected: Vec<String> = vec![
"--all".into(),
"--directory".into(),
"--du".into(),
"--full-paths".into(),
"--help".into(),
"--long".into(),
"--short-names".into(),
"-D".into(),
"-a".into(),
"-d".into(),
"-f".into(),
"-h".into(),
"-l".into(),
"-s".into(),
];
// Match results
match_suggestions(expected, suggestions);
}

View File

@ -1,29 +0,0 @@
pub mod support;
use nu_cli::NuCompleter;
use reedline::Completer;
use support::{folder, match_suggestions, new_engine};
#[test]
fn folder_completions() {
// Create a new engine
let (dir, dir_str, engine, stack) = new_engine();
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for the current folder
let target_dir = format!("cd {}", dir_str);
let suggestions = completer.complete(&target_dir, target_dir.len());
// Create the expected values
let expected_paths: Vec<String> = vec![
folder(dir.join("test_a")),
folder(dir.join("test_b")),
folder(dir.join("another")),
folder(dir.join(".hidden_folder")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
}

View File

@ -1,88 +0,0 @@
pub mod support;
use nu_cli::NuCompleter;
use reedline::Completer;
use support::{match_suggestions, new_engine};
#[test]
fn variables_completions() {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Add record value as example
let record = "let actor = { name: 'Tom Hardy', age: 44 }";
assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instatiate a new completer
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// Test completions for $nu
let suggestions = completer.complete("$nu.", 4);
assert_eq!(9, suggestions.len());
let expected: Vec<String> = vec![
"config-path".into(),
"env-path".into(),
"history-path".into(),
"home-path".into(),
"loginshell-path".into(),
"os-info".into(),
"pid".into(),
"scope".into(),
"temp-path".into(),
];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $nu.h (filter)
let suggestions = completer.complete("$nu.h", 5);
assert_eq!(2, suggestions.len());
let expected: Vec<String> = vec!["history-path".into(), "home-path".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for custom var
let suggestions = completer.complete("$actor.", 7);
assert_eq!(2, suggestions.len());
let expected: Vec<String> = vec!["age".into(), "name".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for custom var (filtering)
let suggestions = completer.complete("$actor.n", 8);
assert_eq!(1, suggestions.len());
let expected: Vec<String> = vec!["name".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $env
let suggestions = completer.complete("$env.", 5);
assert_eq!(2, suggestions.len());
let expected: Vec<String> = vec!["PWD".into(), "TEST".into()];
// Match results
match_suggestions(expected, suggestions);
// Test completions for $env
let suggestions = completer.complete("$env.T", 6);
assert_eq!(1, suggestions.len());
let expected: Vec<String> = vec!["TEST".into()];
// Match results
match_suggestions(expected, suggestions);
}

View File

@ -5,11 +5,11 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
edition = "2021"
license = "MIT"
name = "nu-color-config"
version = "0.67.0"
version = "0.68.1"
[dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.67.0" }
nu-protocol = { path = "../nu-protocol", version = "0.68.1" }
nu-ansi-term = "0.46.0"
nu-json = { path = "../nu-json", version = "0.67.0" }
nu-table = { path = "../nu-table", version = "0.67.0" }
nu-json = { path = "../nu-json", version = "0.68.1" }
nu-table = { path = "../nu-table", version = "0.68.1" }
serde = { version="1.0.123", features=["derive"] }

View File

@ -5,25 +5,25 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
edition = "2021"
license = "MIT"
name = "nu-command"
version = "0.67.0"
version = "0.68.1"
build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-color-config = { path = "../nu-color-config", version = "0.67.0" }
nu-engine = { path = "../nu-engine", version = "0.67.0" }
nu-glob = { path = "../nu-glob", version = "0.67.0" }
nu-json = { path = "../nu-json", version = "0.67.0" }
nu-parser = { path = "../nu-parser", version = "0.67.0" }
nu-path = { path = "../nu-path", version = "0.67.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.67.0" }
nu-protocol = { path = "../nu-protocol", version = "0.67.0" }
nu-system = { path = "../nu-system", version = "0.67.0" }
nu-table = { path = "../nu-table", version = "0.67.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.67.0" }
nu-test-support = { path = "../nu-test-support", version = "0.67.0" }
nu-utils = { path = "../nu-utils", version = "0.67.0" }
nu-color-config = { path = "../nu-color-config", version = "0.68.1" }
nu-engine = { path = "../nu-engine", version = "0.68.1" }
nu-glob = { path = "../nu-glob", version = "0.68.1" }
nu-json = { path = "../nu-json", version = "0.68.1" }
nu-parser = { path = "../nu-parser", version = "0.68.1" }
nu-path = { path = "../nu-path", version = "0.68.1" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.68.1" }
nu-protocol = { path = "../nu-protocol", version = "0.68.1" }
nu-system = { path = "../nu-system", version = "0.68.1" }
nu-table = { path = "../nu-table", version = "0.68.1" }
nu-term-grid = { path = "../nu-term-grid", version = "0.68.1" }
nu-test-support = { path = "../nu-test-support", version = "0.68.1" }
nu-utils = { path = "../nu-utils", version = "0.68.1" }
nu-ansi-term = "0.46.0"
num-format = { version = "0.4.0" }
@ -86,8 +86,8 @@ toml = "0.5.8"
unicode-segmentation = "1.8.0"
url = "2.2.1"
uuid = { version = "1.1.2", features = ["v4"] }
which = { version = "4.2.2", optional = true }
reedline = { version = "0.10.0", features = ["bashisms", "sqlite"]}
which = { version = "4.3.0", optional = true }
reedline = { version = "0.11.0", features = ["bashisms", "sqlite"]}
wax = { version = "0.5.0", features = ["diagnostics"] }
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
sqlparser = { version = "0.16.0", features = ["serde"], optional = true }
@ -152,6 +152,7 @@ shadow-rs = { version = "0.16.1", default-features = false }
[dev-dependencies]
hamcrest2 = "0.3.0"
dirs-next = "2.0.0"
proptest = "1.0.0"
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
rstest = {version = "0.15.0", default-features = false}

View File

@ -0,0 +1,23 @@
# Seeds for failure cases proptest has generated in the past. It is
# automatically read and these particular cases re-run before any
# novel cases are generated.
#
# It is recommended to check this file in to source control so that
# everyone who runs the test benefits from these saved cases.
cc 96a80ecd19729fb43a7b7bb2766b37d6083ba73b16abb97075875e3cfcdc763f # shrinks to c = '"'
cc 4146602559ea717a02bcef3c6d73cdf613c30d0c3f92c48e26c79b9a1544e027 # shrinks to c = '\\'
cc 80532a0ee73df456a719b9e3cce1ae5f3d26009dde819cbaf16f8e0cb6709705 # shrinks to c = ':'
cc cdb88505686eea3c74c36f282fd29b2b68bc118ded4ebfc36f9838d174bd7653 # shrinks to c = '`'
cc 0f534d55f9771e8810b9c4252a4168abfaec1a35e1b0cac12dbaf726d295a08c # shrinks to c = '\0'
cc 5d31bcbab722acd1f4e23ca3a4f95ff309a636b45a73ca8ae9f820d93ff57acc # shrinks to c = '{'
cc 5afec063bc96160d681d77f90041b67ef5cfdea4dcbd12d984fd828fbeb4b421 # shrinks to c = '#'
cc f919beb3ee5c70e756a15635d65ded7d44f3ae58b5e86b6c09e814d5d8cdd506 # shrinks to c = ';'
cc ec00f39b8d45dfd8808947a56af5e50ba5a0ef7c951723b45377815a02e515b1 # shrinks to c = '('
cc 25b773cdf4c24179151fa86244c7de4136e05df9e94e6ee77a336ebfd8764444 # shrinks to c = '|'
cc 94dc0d54b97d59e1c0f4cb11bdccb3823a1bb908cbc3fd643ee8f067169fad72 # shrinks to c = '0'
cc c9d0051fb1e5a8bdc1d4f5a3dceac1b4b465827d1dff4fc3a3755baae6a7bb48 # shrinks to c = '$'
cc 14ec40d2eb5bd2663e9b11bb49fb2120852f9ea71678c69d732161412b55a3ec # shrinks to s = ""
cc d4afccc51ed9d421bdb7e1723e273dfb6e77c3a449489671a496db234e87c5ed # shrinks to c = '\r'
cc 515a56d73eb1b69290ef4c714b629421989879aebd57991bd2c2bf11294353b1 # shrinks to s = "\\\\𐊀{"
cc 111566990fffa432acd2dbc845141b0e7870f97125c7621e3ddf142204568246 # shrinks to s = "'\":`"
cc 0424c33000d9188be96b3049046eb052770b2158bf5ebb0c98656d7145e8aca9 # shrinks to s = "0"

View File

@ -49,7 +49,7 @@ impl Command for BytesIndexOf {
}
fn search_terms(&self) -> Vec<&str> {
vec!["pattern", "match", "find", "search", "index"]
vec!["pattern", "match", "find", "search"]
}
fn run(

View File

@ -39,7 +39,7 @@ impl Command for BytesLen {
}
fn search_terms(&self) -> Vec<&str> {
vec!["len", "size", "count"]
vec!["size", "count"]
}
fn run(

View File

@ -29,7 +29,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["convert", "binary", "bytes", "bin"]
vec!["convert", "bytes"]
}
fn run(

View File

@ -103,7 +103,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["convert", "date", "time", "timezone", "UTC"]
vec!["convert", "timezone", "UTC"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -3,7 +3,8 @@ use nu_parser::parse_duration_bytes;
use nu_protocol::{
ast::{Call, CellPath, Expr},
engine::{Command, EngineState, Stack},
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Unit, Value,
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Unit,
Value,
};
#[derive(Clone)]
@ -16,6 +17,12 @@ impl Command for SubCommand {
fn signature(&self) -> Signature {
Signature::build("into duration")
.named(
"convert",
SyntaxShape::String,
"convert duration into another duration",
Some('c'),
)
.rest(
"rest",
SyntaxShape::CellPath,
@ -106,6 +113,15 @@ impl Command for SubCommand {
span,
}),
},
Example {
description: "Convert string to a named duration",
example: "'7min' | into duration --convert sec",
result: Some(Value::String {
val: "420 sec".to_string(),
span,
}),
},
]
}
}
@ -117,17 +133,21 @@ fn into_duration(
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let convert_to_unit: Option<Spanned<String>> = call.get_flag(engine_state, stack, "convert")?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, head)
action(&v, &convert_to_unit, head)
} else {
let mut ret = v;
for path in &column_paths {
let r =
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
let d = convert_to_unit.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &d, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
@ -140,6 +160,148 @@ fn into_duration(
)
}
fn convert_str_from_unit_to_unit(
val: i64,
from_unit: &str,
to_unit: &str,
span: Span,
value_span: Span,
) -> Result<i64, ShellError> {
match (from_unit, to_unit) {
("ns", "ns") => Ok(val),
("ns", "us") => Ok(val / 1000),
("ns", "ms") => Ok(val / 1000 / 1000),
("ns", "sec") => Ok(val / 1000 / 1000 / 1000),
("ns", "min") => Ok(val / 1000 / 1000 / 1000 / 60),
("ns", "hr") => Ok(val / 1000 / 1000 / 1000 / 60 / 60),
("ns", "day") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24),
("ns", "wk") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24 / 7),
("ns", "month") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24 / 30),
("ns", "yr") => Ok(val / 1000 / 1000 / 1000 / 60 / 60 / 24 / 365),
("ns", "dec") => Ok(val / 10 / 1000 / 1000 / 1000 / 60 / 60 / 24 / 365),
("us", "ns") => Ok(val * 1000),
("us", "us") => Ok(val),
("us", "ms") => Ok(val / 1000),
("us", "sec") => Ok(val / 1000 / 1000),
("us", "min") => Ok(val / 1000 / 1000 / 60),
("us", "hr") => Ok(val / 1000 / 1000 / 60 / 60),
("us", "day") => Ok(val / 1000 / 1000 / 60 / 60 / 24),
("us", "wk") => Ok(val / 1000 / 1000 / 60 / 60 / 24 / 7),
("us", "month") => Ok(val / 1000 / 1000 / 60 / 60 / 24 / 30),
("us", "yr") => Ok(val / 1000 / 1000 / 60 / 60 / 24 / 365),
("us", "dec") => Ok(val / 10 / 1000 / 1000 / 60 / 60 / 24 / 365),
("ms", "ns") => Ok(val * 1000 * 1000),
("ms", "us") => Ok(val * 1000),
("ms", "ms") => Ok(val),
("ms", "sec") => Ok(val / 1000),
("ms", "min") => Ok(val / 1000 / 60),
("ms", "hr") => Ok(val / 1000 / 60 / 60),
("ms", "day") => Ok(val / 1000 / 60 / 60 / 24),
("ms", "wk") => Ok(val / 1000 / 60 / 60 / 24 / 7),
("ms", "month") => Ok(val / 1000 / 60 / 60 / 24 / 30),
("ms", "yr") => Ok(val / 1000 / 60 / 60 / 24 / 365),
("ms", "dec") => Ok(val / 10 / 1000 / 60 / 60 / 24 / 365),
("sec", "ns") => Ok(val * 1000 * 1000 * 1000),
("sec", "us") => Ok(val * 1000 * 1000),
("sec", "ms") => Ok(val * 1000),
("sec", "sec") => Ok(val),
("sec", "min") => Ok(val / 60),
("sec", "hr") => Ok(val / 60 / 60),
("sec", "day") => Ok(val / 60 / 60 / 24),
("sec", "wk") => Ok(val / 60 / 60 / 24 / 7),
("sec", "month") => Ok(val / 60 / 60 / 24 / 30),
("sec", "yr") => Ok(val / 60 / 60 / 24 / 365),
("sec", "dec") => Ok(val / 10 / 60 / 60 / 24 / 365),
("min", "ns") => Ok(val * 1000 * 1000 * 1000 * 60),
("min", "us") => Ok(val * 1000 * 1000 * 60),
("min", "ms") => Ok(val * 1000 * 60),
("min", "sec") => Ok(val * 60),
("min", "min") => Ok(val),
("min", "hr") => Ok(val / 60),
("min", "day") => Ok(val / 60 / 24),
("min", "wk") => Ok(val / 60 / 24 / 7),
("min", "month") => Ok(val / 60 / 24 / 30),
("min", "yr") => Ok(val / 60 / 24 / 365),
("min", "dec") => Ok(val / 10 / 60 / 24 / 365),
("hr", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60),
("hr", "us") => Ok(val * 1000 * 1000 * 60 * 60),
("hr", "ms") => Ok(val * 1000 * 60 * 60),
("hr", "sec") => Ok(val * 60 * 60),
("hr", "min") => Ok(val * 60),
("hr", "hr") => Ok(val),
("hr", "day") => Ok(val / 24),
("hr", "wk") => Ok(val / 24 / 7),
("hr", "month") => Ok(val / 24 / 30),
("hr", "yr") => Ok(val / 24 / 365),
("hr", "dec") => Ok(val / 10 / 24 / 365),
("day", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24),
("day", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24),
("day", "ms") => Ok(val * 1000 * 60 * 60 * 24),
("day", "sec") => Ok(val * 60 * 60 * 24),
("day", "min") => Ok(val * 60 * 24),
("day", "hr") => Ok(val * 24),
("day", "day") => Ok(val),
("day", "wk") => Ok(val / 7),
("day", "month") => Ok(val / 30),
("day", "yr") => Ok(val / 365),
("day", "dec") => Ok(val / 10 / 365),
("wk", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 7),
("wk", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24 * 7),
("wk", "ms") => Ok(val * 1000 * 60 * 60 * 24 * 7),
("wk", "sec") => Ok(val * 60 * 60 * 24 * 7),
("wk", "min") => Ok(val * 60 * 24 * 7),
("wk", "hr") => Ok(val * 24 * 7),
("wk", "day") => Ok(val * 7),
("wk", "wk") => Ok(val),
("wk", "month") => Ok(val / 4),
("wk", "yr") => Ok(val / 52),
("wk", "dec") => Ok(val / 10 / 52),
("month", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 30),
("month", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24 * 30),
("month", "ms") => Ok(val * 1000 * 60 * 60 * 24 * 30),
("month", "sec") => Ok(val * 60 * 60 * 24 * 30),
("month", "min") => Ok(val * 60 * 24 * 30),
("month", "hr") => Ok(val * 24 * 30),
("month", "day") => Ok(val * 30),
("month", "wk") => Ok(val * 4),
("month", "month") => Ok(val),
("month", "yr") => Ok(val / 12),
("month", "dec") => Ok(val / 10 / 12),
("yr", "ns") => Ok(val * 1000 * 1000 * 1000 * 60 * 60 * 24 * 365),
("yr", "us") => Ok(val * 1000 * 1000 * 60 * 60 * 24 * 365),
("yr", "ms") => Ok(val * 1000 * 60 * 60 * 24 * 365),
("yr", "sec") => Ok(val * 60 * 60 * 24 * 365),
("yr", "min") => Ok(val * 60 * 24 * 365),
("yr", "hr") => Ok(val * 24 * 365),
("yr", "day") => Ok(val * 365),
("yr", "wk") => Ok(val * 52),
("yr", "month") => Ok(val * 12),
("yr", "yr") => Ok(val),
("yr", "dec") => Ok(val / 10),
_ => Err(ShellError::CantConvertWithValue(
"string duration".to_string(),
"string duration".to_string(),
to_unit.to_string(),
span,
value_span,
Some(
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec"
.to_string(),
),
)),
}
}
fn string_to_duration(s: &str, span: Span, value_span: Span) -> Result<i64, ShellError> {
if let Some(expression) = parse_duration_bytes(s.as_bytes(), span) {
if let Expr::ValueWithUnit(value, unit) = expression.expr {
@ -168,20 +330,105 @@ fn string_to_duration(s: &str, span: Span, value_span: Span) -> Result<i64, Shel
s.to_string(),
span,
value_span,
Some("supported units are ns, us, ms, sec, min, hr, day, and wk".to_string()),
Some(
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec".to_string(),
),
))
}
fn action(input: &Value, span: Span) -> Value {
fn string_to_unit_duration(
s: &str,
span: Span,
value_span: Span,
) -> Result<(&str, i64), ShellError> {
if let Some(expression) = parse_duration_bytes(s.as_bytes(), span) {
if let Expr::ValueWithUnit(value, unit) = expression.expr {
if let Expr::Int(x) = value.expr {
match unit.item {
Unit::Nanosecond => return Ok(("ns", x)),
Unit::Microsecond => return Ok(("us", x)),
Unit::Millisecond => return Ok(("ms", x)),
Unit::Second => return Ok(("sec", x)),
Unit::Minute => return Ok(("min", x)),
Unit::Hour => return Ok(("hr", x)),
Unit::Day => return Ok(("day", x)),
Unit::Week => return Ok(("wk", x)),
Unit::Month => return Ok(("month", x)), //30 days to a month
Unit::Year => return Ok(("yr", x)), //365 days to a year
Unit::Decade => return Ok(("dec", x)), //365 days to a year
_ => return Ok(("ns", 0)),
}
}
}
}
Err(ShellError::CantConvertWithValue(
"duration".to_string(),
"string".to_string(),
s.to_string(),
span,
value_span,
Some(
"supported units are ns, us, ms, sec, min, hr, day, wk, month, yr and dec".to_string(),
),
))
}
fn action(input: &Value, convert_to_unit: &Option<Spanned<String>>, span: Span) -> Value {
match input {
Value::Duration { .. } => input.clone(),
Value::Duration {
val: _val_num,
span: _value_span,
} => {
if let Some(_to_unit) = convert_to_unit {
Value::Error {
error: ShellError::UnsupportedInput(
"Cannot convert from a Value::Duration right now. Try making it a string."
.into(),
span,
),
}
} else {
input.clone()
}
}
Value::String {
val,
span: value_span,
} => match string_to_duration(val, span, *value_span) {
Ok(val) => Value::Duration { val, span },
Err(error) => Value::Error { error },
},
} => {
if let Some(to_unit) = convert_to_unit {
if let Ok(dur) = string_to_unit_duration(val, span, *value_span) {
let from_unit = dur.0;
let duration = dur.1;
match convert_str_from_unit_to_unit(
duration,
from_unit,
&to_unit.item,
span,
*value_span,
) {
Ok(d) => Value::String {
val: format!("{} {}", d, &to_unit.item),
span: *value_span,
},
Err(e) => Value::Error { error: e },
}
} else {
Value::Error {
error: ShellError::UnsupportedInput(
"'into duration' does not support this string input".into(),
span,
),
}
}
} else {
match string_to_duration(val, span, *value_span) {
Ok(val) => Value::Duration { val, span },
Err(error) => Value::Error { error },
}
}
}
_ => Value::Error {
error: ShellError::UnsupportedInput(
"'into duration' does not support this input".into(),
@ -207,8 +454,9 @@ mod test {
let span = Span::test_data();
let word = Value::test_string("3ns");
let expected = Value::Duration { val: 3, span };
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, span);
assert_eq!(actual, expected);
}
@ -220,8 +468,9 @@ mod test {
val: 4 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, span);
assert_eq!(actual, expected);
}
@ -233,8 +482,9 @@ mod test {
val: 5 * 1000 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, span);
assert_eq!(actual, expected);
}
@ -246,8 +496,9 @@ mod test {
val: 1000 * 1000 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, span);
assert_eq!(actual, expected);
}
@ -259,8 +510,9 @@ mod test {
val: 7 * 60 * 1000 * 1000 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, span);
assert_eq!(actual, expected);
}
@ -272,8 +524,9 @@ mod test {
val: 42 * 60 * 60 * 1000 * 1000 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, span);
assert_eq!(actual, expected);
}
@ -285,8 +538,9 @@ mod test {
val: 123 * 24 * 60 * 60 * 1000 * 1000 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, span);
assert_eq!(actual, expected);
}
@ -298,8 +552,9 @@ mod test {
val: 3 * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, span);
assert_eq!(actual, expected);
}
}

View File

@ -28,7 +28,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["convert", "number", "size", "bytes"]
vec!["convert", "number", "bytes"]
}
fn run(

View File

@ -38,7 +38,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["convert", "str", "text"]
vec!["convert", "text"]
}
fn run(

View File

@ -27,7 +27,7 @@ impl Command for Alias {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -0,0 +1,77 @@
use nu_engine::CallExt;
use nu_parser::parse;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack, StateWorkingSet},
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape,
};
#[derive(Clone)]
pub struct Ast;
impl Command for Ast {
fn name(&self) -> &str {
"ast"
}
fn usage(&self) -> &str {
"Print the abstract syntax tree (ast) for a pipeline."
}
fn signature(&self) -> Signature {
Signature::build("ast")
.required(
"pipeline",
SyntaxShape::String,
"the pipeline to print the ast for",
)
.category(Category::Core)
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let pipeline: Spanned<String> = call.req(engine_state, stack, 0)?;
let mut working_set = StateWorkingSet::new(engine_state);
let (output, err) = parse(&mut working_set, None, pipeline.item.as_bytes(), false, &[]);
eprintln!("output: {:#?}\nerror: {:#?}", output, err);
Ok(PipelineData::new(head))
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Print the ast of a string",
example: "ast 'hello'",
result: None,
},
Example {
description: "Print the ast of a pipeline",
example: "ast 'ls | where name =~ README'",
result: None,
},
Example {
description: "Print the ast of a pipeline with an error",
example: "ast 'for x in 1..10 { echo $x '",
result: None,
},
]
}
}
#[cfg(test)]
mod test {
#[test]
fn test_examples() {
use super::Ast;
use crate::test_examples;
test_examples(Ast {})
}
}

View File

@ -28,7 +28,7 @@ impl Command for Def {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -28,7 +28,7 @@ impl Command for DefEnv {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html
https://www.nushell.sh/book/thinking_in_nu.html
=== EXTRA NOTE ===
All blocks are scoped, including variable definition and environment variable changes.

View File

@ -29,7 +29,7 @@ impl Command for ErrorMake {
}
fn search_terms(&self) -> Vec<&str> {
vec!["err", "panic", "crash", "throw"]
vec!["panic", "crash", "throw"]
}
fn run(

View File

@ -23,7 +23,7 @@ impl Command for ExportCommand {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -27,7 +27,7 @@ impl Command for ExportAlias {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -28,7 +28,7 @@ impl Command for ExportDef {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -28,7 +28,7 @@ impl Command for ExportDefEnv {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html
https://www.nushell.sh/book/thinking_in_nu.html
=== EXTRA NOTE ===
All blocks are scoped, including variable definition and environment variable changes.

View File

@ -3,9 +3,9 @@ use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
#[derive(Clone)]
pub struct ExportEnv;
pub struct ExportEnvModule;
impl Command for ExportEnv {
impl Command for ExportEnvModule {
fn name(&self) -> &str {
"export env"
}
@ -31,7 +31,7 @@ impl Command for ExportEnv {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -23,7 +23,7 @@ impl Command for ExportExtern {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -22,7 +22,7 @@ impl Command for ExportUse {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -23,7 +23,7 @@ impl Command for Extern {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -46,7 +46,7 @@ impl Command for For {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -26,7 +26,7 @@ impl Command for Hide {
r#"Definitions are hidden by priority: First aliases, then custom commands.
This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -35,7 +35,7 @@ impl Command for If {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -28,7 +28,7 @@ impl Command for Let {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -1,4 +1,5 @@
mod alias;
mod ast;
mod debug;
mod def;
mod def_env;
@ -24,11 +25,11 @@ mod let_;
mod metadata;
mod module;
pub(crate) mod overlay;
mod source;
mod use_;
mod version;
pub use alias::Alias;
pub use ast::Ast;
pub use debug::Debug;
pub use def::Def;
pub use def_env::DefEnv;
@ -40,7 +41,7 @@ pub use export::ExportCommand;
pub use export_alias::ExportAlias;
pub use export_def::ExportDef;
pub use export_def_env::ExportDefEnv;
pub use export_env::ExportEnv;
pub use export_env::ExportEnvModule;
pub use export_extern::ExportExtern;
pub use export_use::ExportUse;
pub use extern_::Extern;
@ -54,7 +55,6 @@ pub use let_::Let;
pub use metadata::Metadata;
pub use module::Module;
pub use overlay::*;
pub use source::Source;
pub use use_::Use;
pub use version::Version;
#[cfg(feature = "plugin")]

View File

@ -27,7 +27,7 @@ impl Command for Module {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -1,180 +0,0 @@
use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape};
use std::path::Path;
#[derive(Clone)]
pub struct OverlayAdd;
impl Command for OverlayAdd {
fn name(&self) -> &str {
"overlay add"
}
fn usage(&self) -> &str {
"Add definitions from a module as an overlay"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay add")
.required(
"name",
SyntaxShape::String,
"Module name to create overlay for",
)
.optional(
"as",
SyntaxShape::Keyword(b"as".to_vec(), Box::new(SyntaxShape::String)),
"as keyword followed by a new name",
)
.switch(
"prefix",
"Prepend module name to the imported commands and aliases",
Some('p'),
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let name_arg: Spanned<String> = call.req(engine_state, stack, 0)?;
let (overlay_name, overlay_name_span) = if let Some(kw_expression) = call.positional_nth(1)
{
// If renamed via the 'as' keyword, use the new name as the overlay name
if let Some(new_name_expression) = kw_expression.as_keyword() {
if let Some(new_name) = new_name_expression.as_string() {
(new_name, new_name_expression.span)
} else {
return Err(ShellError::NushellFailedSpanned(
"Wrong keyword type".to_string(),
"keyword argument not a string".to_string(),
new_name_expression.span,
));
}
} else {
return Err(ShellError::NushellFailedSpanned(
"Wrong keyword type".to_string(),
"keyword argument not a keyword".to_string(),
kw_expression.span,
));
}
} else if engine_state
.find_overlay(name_arg.item.as_bytes())
.is_some()
{
(name_arg.item, name_arg.span)
} else if let Some(os_str) = Path::new(&name_arg.item).file_stem() {
if let Some(name) = os_str.to_str() {
(name.to_string(), name_arg.span)
} else {
return Err(ShellError::NonUtf8(name_arg.span));
}
} else {
return Err(ShellError::OverlayNotFoundAtRuntime(
name_arg.item,
name_arg.span,
));
};
if let Some(overlay_id) = engine_state.find_overlay(overlay_name.as_bytes()) {
let old_module_id = engine_state.get_overlay(overlay_id).origin;
stack.add_overlay(overlay_name.clone());
if let Some(new_module_id) = engine_state.find_module(overlay_name.as_bytes(), &[]) {
if !stack.has_env_overlay(&overlay_name, engine_state)
|| (old_module_id != new_module_id)
{
// Add environment variables only if:
// a) adding a new overlay
// b) refreshing an active overlay (the origin module changed)
let module = engine_state.get_module(new_module_id);
for (name, block_id) in module.env_vars() {
let name = if let Ok(s) = String::from_utf8(name.clone()) {
s
} else {
return Err(ShellError::NonUtf8(call.head));
};
let block = engine_state.get_block(block_id);
let val = eval_block(
engine_state,
stack,
block,
PipelineData::new(call.head),
false,
true,
)?
.into_value(call.head);
stack.add_env_var(name, val);
}
}
}
} else {
return Err(ShellError::OverlayNotFoundAtRuntime(
overlay_name,
overlay_name_span,
));
}
Ok(PipelineData::new(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create an overlay from a module",
example: r#"module spam { export def foo [] { "foo" } }
overlay add spam
foo"#,
result: None,
},
Example {
description: "Create an overlay with a prefix",
example: r#"echo 'export def foo { "foo" }'
overlay add --prefix spam
spam foo"#,
result: None,
},
Example {
description: "Create an overlay from a file",
example: r#"echo 'export env FOO { "foo" }' | save spam.nu
overlay add spam.nu
$env.FOO"#,
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(OverlayAdd {})
}
}

View File

@ -23,7 +23,7 @@ impl Command for Overlay {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -4,20 +4,20 @@ use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape};
#[derive(Clone)]
pub struct OverlayRemove;
pub struct OverlayHide;
impl Command for OverlayRemove {
impl Command for OverlayHide {
fn name(&self) -> &str {
"overlay remove"
"overlay hide"
}
fn usage(&self) -> &str {
"Remove an active overlay"
"Hide an active overlay"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay remove")
.optional("name", SyntaxShape::String, "Overlay to remove")
Signature::build("overlay hide")
.optional("name", SyntaxShape::String, "Overlay to hide")
.switch(
"keep-custom",
"Keep all newly added symbols within the next activated overlay",
@ -26,7 +26,7 @@ impl Command for OverlayRemove {
.named(
"keep-env",
SyntaxShape::List(Box::new(SyntaxShape::String)),
"List of environment variables to keep from the removed overlay",
"List of environment variables to keep from the hidden overlay",
Some('e'),
)
.category(Category::Core)
@ -34,7 +34,7 @@ impl Command for OverlayRemove {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
@ -110,31 +110,31 @@ impl Command for OverlayRemove {
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Remove an overlay created from a module",
description: "Hide an overlay created from a module",
example: r#"module spam { export def foo [] { "foo" } }
overlay add spam
overlay remove spam"#,
overlay use spam
overlay hide spam"#,
result: None,
},
Example {
description: "Remove an overlay created from a file",
description: "Hide an overlay created from a file",
example: r#"echo 'export alias f = "foo"' | save spam.nu
overlay add spam.nu
overlay remove spam"#,
overlay use spam.nu
overlay hide spam"#,
result: None,
},
Example {
description: "Remove the last activated overlay",
description: "Hide the last activated overlay",
example: r#"module spam { export env FOO { "foo" } }
overlay add spam
overlay remove"#,
overlay use spam
overlay hide"#,
result: None,
},
Example {
description: "Keep the current working directory when removing an overlay",
example: r#"overlay new spam
cd some-dir
overlay remove --keep-env [ PWD ] spam"#,
overlay hide --keep-env [ PWD ] spam"#,
result: None,
},
]

View File

@ -4,8 +4,6 @@ use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Value,
};
use log::trace;
#[derive(Clone)]
pub struct OverlayList;
@ -28,41 +26,17 @@ impl Command for OverlayList {
fn run(
&self,
engine_state: &EngineState,
_engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let active_overlays_parser: Vec<Value> = engine_state
.active_overlay_names(&[])
.iter()
.map(|s| Value::string(String::from_utf8_lossy(s), call.head))
.collect();
let active_overlays_engine: Vec<Value> = stack
.active_overlays
.iter()
.map(|s| Value::string(s, call.head))
.collect();
// Check if the overlays in the engine match the overlays in the parser
if (active_overlays_parser.len() != active_overlays_engine.len())
|| active_overlays_parser
.iter()
.zip(active_overlays_engine.iter())
.any(|(op, oe)| op != oe)
{
trace!("parser overlays: {:?}", active_overlays_parser);
trace!("engine overlays: {:?}", active_overlays_engine);
return Err(ShellError::NushellFailedSpannedHelp(
"Overlay mismatch".into(),
"Active overlays do not match between the engine and the parser.".into(),
call.head,
"Run Nushell with --log-level=trace to see what went wrong.".into(),
));
}
Ok(Value::List {
vals: active_overlays_engine,
span: call.head,
@ -74,7 +48,7 @@ impl Command for OverlayList {
vec![Example {
description: "Get the last activated overlay",
example: r#"module spam { export def foo [] { "foo" } }
overlay add spam
overlay use spam
overlay list | last"#,
result: Some(Value::String {
val: "spam".to_string(),

View File

@ -1,11 +1,11 @@
mod add;
mod command;
mod hide;
mod list;
mod new;
mod remove;
mod use_;
pub use add::OverlayAdd;
pub use command::Overlay;
pub use hide::OverlayHide;
pub use list::OverlayList;
pub use new::OverlayNew;
pub use remove::OverlayRemove;
pub use use_::OverlayUse;

View File

@ -31,7 +31,7 @@ impl Command for OverlayNew {
r#"The command will first create an empty module, then add it as an overlay.
This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -0,0 +1,225 @@
use nu_engine::{eval_block, find_in_dirs_env, redirect_env, CallExt};
use nu_protocol::ast::{Call, Expr};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
};
use std::path::Path;
#[derive(Clone)]
pub struct OverlayUse;
impl Command for OverlayUse {
fn name(&self) -> &str {
"overlay use"
}
fn usage(&self) -> &str {
"Use definitions from a module as an overlay"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("overlay use")
.required(
"name",
SyntaxShape::String,
"Module name to use overlay for",
)
.optional(
"as",
SyntaxShape::Keyword(b"as".to_vec(), Box::new(SyntaxShape::String)),
"as keyword followed by a new name",
)
.switch(
"prefix",
"Prepend module name to the imported commands and aliases",
Some('p'),
)
.category(Category::Core)
}
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
caller_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let name_arg: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
let origin_module_id = if let Some(overlay_expr) = call.positional_nth(0) {
if let Expr::Overlay(module_id) = overlay_expr.expr {
module_id
} else {
return Err(ShellError::NushellFailedSpanned(
"Not an overlay".to_string(),
"requires an overlay (path or a string)".to_string(),
overlay_expr.span,
));
}
} else {
return Err(ShellError::NushellFailedSpanned(
"Missing positional".to_string(),
"missing required overlay".to_string(),
call.head,
));
};
let overlay_name = if let Some(kw_expression) = call.positional_nth(1) {
// If renamed via the 'as' keyword, use the new name as the overlay name
if let Some(new_name_expression) = kw_expression.as_keyword() {
if let Some(new_name) = new_name_expression.as_string() {
new_name
} else {
return Err(ShellError::NushellFailedSpanned(
"Wrong keyword type".to_string(),
"keyword argument not a string".to_string(),
new_name_expression.span,
));
}
} else {
return Err(ShellError::NushellFailedSpanned(
"Wrong keyword type".to_string(),
"keyword argument not a keyword".to_string(),
kw_expression.span,
));
}
} else if engine_state
.find_overlay(name_arg.item.as_bytes())
.is_some()
{
name_arg.item.clone()
} else if let Some(os_str) = Path::new(&name_arg.item).file_stem() {
if let Some(name) = os_str.to_str() {
name.to_string()
} else {
return Err(ShellError::NonUtf8(name_arg.span));
}
} else {
return Err(ShellError::OverlayNotFoundAtRuntime(
name_arg.item,
name_arg.span,
));
};
caller_stack.add_overlay(overlay_name);
if let Some(module_id) = origin_module_id {
// Add environment variables only if:
// a) adding a new overlay
// b) refreshing an active overlay (the origin module changed)
let module = engine_state.get_module(module_id);
for (name, block_id) in module.env_vars() {
let name = if let Ok(s) = String::from_utf8(name.clone()) {
s
} else {
return Err(ShellError::NonUtf8(call.head));
};
let block = engine_state.get_block(block_id);
let val = eval_block(
engine_state,
caller_stack,
block,
PipelineData::new(call.head),
false,
true,
)?
.into_value(call.head);
caller_stack.add_env_var(name, val);
}
// Evaluate the export-env block (if any) and keep its environment
if let Some(block_id) = module.env_block {
let maybe_path = find_in_dirs_env(&name_arg.item, engine_state, caller_stack)?;
if let Some(path) = &maybe_path {
// Set the currently evaluated directory, if the argument is a valid path
let mut parent = path.clone();
parent.pop();
let file_pwd = Value::String {
val: parent.to_string_lossy().to_string(),
span: call.head,
};
caller_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
}
let block = engine_state.get_block(block_id);
let mut callee_stack = caller_stack.gather_captures(&block.captures);
let _ = eval_block(
engine_state,
&mut callee_stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
);
// Merge the block's environment to the current stack
redirect_env(engine_state, caller_stack, &callee_stack);
if maybe_path.is_some() {
// Remove the file-relative PWD, if the argument is a valid path
caller_stack.remove_env_var(engine_state, "FILE_PWD");
}
}
}
Ok(PipelineData::new(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Create an overlay from a module",
example: r#"module spam { export def foo [] { "foo" } }
overlay use spam
foo"#,
result: None,
},
Example {
description: "Create an overlay with a prefix",
example: r#"echo 'export def foo { "foo" }'
overlay use --prefix spam
spam foo"#,
result: None,
},
Example {
description: "Create an overlay from a file",
example: r#"echo 'export env FOO { "foo" }' | save spam.nu
overlay use spam.nu
$env.FOO"#,
result: None,
},
]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(OverlayUse {})
}
}

View File

@ -21,12 +21,6 @@ impl Command for Register {
SyntaxShape::Filepath,
"path of executable for plugin",
)
.required_named(
"encoding",
SyntaxShape::String,
"Encoding used to communicate with plugin. Options: [capnp, json]",
Some('e'),
)
.optional(
"signature",
SyntaxShape::Any,
@ -43,7 +37,7 @@ impl Command for Register {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
@ -64,12 +58,12 @@ impl Command for Register {
vec![
Example {
description: "Register `nu_plugin_query` plugin from ~/.cargo/bin/ dir",
example: r#"register -e json ~/.cargo/bin/nu_plugin_query"#,
example: r#"register ~/.cargo/bin/nu_plugin_query"#,
result: None,
},
Example {
description: "Register `nu_plugin_query` plugin from `nu -c`(plugin will be available in that nu session only)",
example: r#"let plugin = ((which nu).path.0 | path dirname | path join 'nu_plugin_query'); nu -c $'register -e json ($plugin); version'"#,
example: r#"let plugin = ((which nu).path.0 | path dirname | path join 'nu_plugin_query'); nu -c $'register ($plugin); version'"#,
result: None,
},
]

View File

@ -25,7 +25,7 @@ impl Command for Use {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -34,7 +34,7 @@ impl Command for CollectDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "collect"]
vec!["database"]
}
fn run(

View File

@ -47,7 +47,7 @@ impl Command for DescribeDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "SQLite", "describe"]
vec!["database", "SQLite"]
}
fn run(

View File

@ -41,7 +41,7 @@ impl Command for FromDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "from"]
vec!["database"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -49,7 +49,7 @@ impl Command for IntoSqliteDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["convert", "sqlite", "database"]
vec!["convert", "database"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -47,7 +47,7 @@ impl Command for JoinDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "join"]
vec!["database"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -34,7 +34,7 @@ impl Command for LimitDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "limit"]
vec!["database", "head", "tail"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -29,7 +29,7 @@ impl Command for OpenDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "open"]
vec!["database"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -37,7 +37,7 @@ impl Command for OrderByDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "order-by"]
vec!["database"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -34,7 +34,7 @@ impl Command for SchemaDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "info", "SQLite", "schema"]
vec!["database", "info", "SQLite"]
}
fn run(

View File

@ -33,7 +33,7 @@ impl Command for ProjectionDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "select"]
vec!["database"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -30,7 +30,7 @@ impl Command for ToDataBase {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "into", "db"]
vec!["database", "convert"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -31,7 +31,7 @@ impl Command for WhereDb {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "where"]
vec!["database"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -30,7 +30,7 @@ impl Command for AndExpr {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "and", "expression"]
vec!["database", "expression"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -30,7 +30,7 @@ impl Command for OrExpr {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "or", "expression"]
vec!["database", "expression"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -92,7 +92,7 @@ impl Command for OverExpr {
}
fn search_terms(&self) -> Vec<&str> {
vec!["database", "over", "expression"]
vec!["database", "expression"]
}
fn run(

View File

@ -23,7 +23,6 @@ impl Command for Date {
fn search_terms(&self) -> Vec<&str> {
vec![
"date",
"time",
"now",
"today",

View File

@ -35,7 +35,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["date", "format", "strftime"]
vec!["fmt", "strftime"]
}
fn run(

View File

@ -22,8 +22,6 @@ impl Command for SubCommand {
fn search_terms(&self) -> Vec<&str> {
vec![
"date",
"humanize",
"relative",
"now",
"today",

View File

@ -22,7 +22,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["UTC", "GMT", "timezone", "list", "list-timezone"]
vec!["UTC", "GMT", "tz"]
}
fn run(

View File

@ -19,7 +19,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["date", "now", "present", "current-time"]
vec!["present", "current-time"]
}
fn run(

View File

@ -23,7 +23,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["date", "to", "record", "structured", "table"]
vec!["structured", "table"]
}
fn run(

View File

@ -23,7 +23,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["date", "to", "record", "structured", "table"]
vec!["structured"]
}
fn run(

View File

@ -34,9 +34,7 @@ impl Command for SubCommand {
fn search_terms(&self) -> Vec<&str> {
vec![
"date",
"to",
"timezone",
"tz",
"transform",
"convert",
"UTC",
@ -128,7 +126,7 @@ fn _to_timezone(dt: DateTime<FixedOffset>, timezone: &Spanned<String>, span: Spa
match datetime_in_timezone(&dt, timezone.item.as_str()) {
Ok(dt) => Value::Date { val: dt, span },
Err(_) => Value::Error {
error: ShellError::UnsupportedInput(String::from("invalid time zone"), span),
error: ShellError::UnsupportedInput(String::from("invalid time zone"), timezone.span),
},
}
}

View File

@ -29,6 +29,7 @@ pub fn create_default_context() -> EngineState {
// Core
bind_command! {
Alias,
Ast,
Debug,
Def,
DefEnv,
@ -40,7 +41,7 @@ pub fn create_default_context() -> EngineState {
ExportCommand,
ExportDef,
ExportDefEnv,
ExportEnv,
ExportEnvModule,
ExportExtern,
ExportUse,
Extern,
@ -51,14 +52,13 @@ pub fn create_default_context() -> EngineState {
If,
Ignore,
Overlay,
OverlayAdd,
OverlayUse,
OverlayList,
OverlayNew,
OverlayRemove,
OverlayHide,
Let,
Metadata,
Module,
Source,
Use,
Version,
};
@ -160,10 +160,17 @@ pub fn create_default_context() -> EngineState {
Exec,
External,
NuCheck,
Ps,
Sys,
};
#[cfg(any(
target_os = "android",
target_os = "linux",
target_os = "macos",
target_os = "windows"
))]
bind_command! { Ps };
#[cfg(feature = "which-support")]
bind_command! { Which };
@ -184,11 +191,13 @@ pub fn create_default_context() -> EngineState {
SplitChars,
SplitColumn,
SplitRow,
SplitWords,
Str,
StrCamelCase,
StrCapitalize,
StrCollect,
StrContains,
StrDistance,
StrDowncase,
StrEndswith,
StrReplace,
@ -352,8 +361,10 @@ pub fn create_default_context() -> EngineState {
// Env
bind_command! {
Env,
ExportEnv,
LetEnv,
LoadEnv,
SourceEnv,
WithEnv,
ConfigNu,
ConfigEnv,
@ -428,6 +439,7 @@ pub fn create_default_context() -> EngineState {
// Deprecated
bind_command! {
HashBase64,
Source,
StrDatetimeDeprecated,
StrDecimalDeprecated,
StrIntDeprecated,

View File

@ -5,11 +5,14 @@ use std::collections::HashMap;
/// subcommands like `foo bar` where `foo` is still a valid command.
/// For those, it's currently easiest to have a "stub" command that just returns an error.
pub fn deprecated_commands() -> HashMap<String, String> {
let mut commands = HashMap::new();
commands.insert("keep".to_string(), "take".to_string());
commands.insert("match".to_string(), "find".to_string());
commands.insert("nth".to_string(), "select".to_string());
commands.insert("pivot".to_string(), "transpose".to_string());
commands.insert("unalias".to_string(), "hide".to_string());
commands
HashMap::from([
("keep".to_string(), "take".to_string()),
("match".to_string(), "find".to_string()),
("nth".to_string(), "select".to_string()),
("pivot".to_string(), "transpose".to_string()),
("unalias".to_string(), "hide".to_string()),
("all?".to_string(), "all".to_string()),
("any?".to_string(), "any".to_string()),
("empty?".to_string(), "is-empty".to_string()),
])
}

View File

@ -1,5 +1,6 @@
mod deprecated_commands;
mod hash_base64;
mod source;
mod str_datetime;
mod str_decimal;
mod str_find_replace;
@ -7,6 +8,7 @@ mod str_int;
pub use deprecated_commands::*;
pub use hash_base64::HashBase64;
pub use source::Source;
pub use str_datetime::StrDatetimeDeprecated;
pub use str_decimal::StrDecimalDeprecated;
pub use str_find_replace::StrFindReplaceDeprecated;

View File

@ -28,7 +28,7 @@ impl Command for Source {
fn extra_usage(&self) -> &str {
r#"This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nushell.html"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {

View File

@ -58,7 +58,7 @@ impl Command for ConfigEnv {
nu_config.push("env.nu");
let name = Spanned {
item: get_editor(engine_state, stack),
item: get_editor(engine_state, stack)?,
span: Span { start: 0, end: 0 },
};
@ -70,6 +70,7 @@ impl Command for ConfigEnv {
let command = ExternalCommand {
name,
args,
arg_keep_raw: vec![false],
redirect_stdout: false,
redirect_stderr: false,
env_vars: env_vars_str,

View File

@ -58,7 +58,7 @@ impl Command for ConfigNu {
nu_config.push("config.nu");
let name = Spanned {
item: get_editor(engine_state, stack),
item: get_editor(engine_state, stack)?,
span: Span { start: 0, end: 0 },
};
@ -70,6 +70,7 @@ impl Command for ConfigNu {
let command = ExternalCommand {
name,
args,
arg_keep_raw: vec![false],
redirect_stdout: false,
redirect_stderr: false,
env_vars: env_vars_str,

View File

@ -1,17 +1,20 @@
use nu_protocol::engine::{EngineState, Stack};
pub(crate) fn get_editor(engine_state: &EngineState, stack: &mut Stack) -> String {
pub(crate) fn get_editor(
engine_state: &EngineState,
stack: &mut Stack,
) -> Result<String, nu_protocol::ShellError> {
let config = engine_state.get_config();
let env_vars = stack.get_env_vars(engine_state);
if !config.buffer_editor.is_empty() {
config.buffer_editor.clone()
Ok(config.buffer_editor.clone())
} else if let Some(value) = env_vars.get("EDITOR") {
value.as_string().expect("Unknown type")
value.as_string()
} else if let Some(value) = env_vars.get("VISUAL") {
value.as_string().expect("Unknown type")
value.as_string()
} else if cfg!(target_os = "windows") {
"notepad".to_string()
Ok("notepad".to_string())
} else {
"nano".to_string()
Ok("nano".to_string())
}
}

View File

@ -75,7 +75,7 @@ impl Command for Env {
},
Example {
description: "Check whether the env variable `MY_ENV_ABC` exists",
example: r#"env | any? name == MY_ENV_ABC"#,
example: r#"env | any name == MY_ENV_ABC"#,
result: Some(Value::test_bool(false)),
},
Example {

74
crates/nu-command/src/env/export_env.rs vendored Normal file
View File

@ -0,0 +1,74 @@
use nu_engine::{eval_block, redirect_env, CallExt};
use nu_protocol::{
ast::Call,
engine::{CaptureBlock, Command, EngineState, Stack},
Category, Example, PipelineData, Signature, Span, SyntaxShape, Value,
};
#[derive(Clone)]
pub struct ExportEnv;
impl Command for ExportEnv {
fn name(&self) -> &str {
"export-env"
}
fn signature(&self) -> Signature {
Signature::build("export-env")
.required(
"block",
SyntaxShape::Block(Some(vec![])),
"the block to run to set the environment",
)
.category(Category::Env)
}
fn usage(&self) -> &str {
"Run a block and preserve its environment in a current scope."
}
fn run(
&self,
engine_state: &EngineState,
caller_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let capture_block: CaptureBlock = call.req(engine_state, caller_stack, 0)?;
let block = engine_state.get_block(capture_block.block_id);
let mut callee_stack = caller_stack.captures_to_stack(&capture_block.captures);
let _ = eval_block(
engine_state,
&mut callee_stack,
block,
input,
call.redirect_stdout,
call.redirect_stderr,
);
redirect_env(engine_state, caller_stack, &callee_stack);
Ok(PipelineData::new(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Set an environment",
example: r#"export-env { let-env SPAM = 'eggs' }; $env.SPAM"#,
result: Some(Value::string("eggs", Span::test_data())),
}]
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(ExportEnv {})
}
}

View File

@ -1,7 +1,9 @@
use nu_engine::{current_dir, eval_expression_with_input, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, SyntaxShape, Value};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
};
#[derive(Clone)]
pub struct LetEnv;
@ -34,7 +36,7 @@ impl Command for LetEnv {
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
// TODO: find and require the crossplatform restrictions on environment names
let env_var = call.req(engine_state, stack, 0)?;
let env_var: Spanned<String> = call.req(engine_state, stack, 0)?;
let keyword_expr = call
.positional_nth(1)
@ -47,19 +49,26 @@ impl Command for LetEnv {
.0
.into_value(call.head);
if env_var == "PWD" {
if env_var.item == "FILE_PWD" {
return Err(ShellError::AutomaticEnvVarSetManually(
env_var.item,
env_var.span,
));
}
if env_var.item == "PWD" {
let cwd = current_dir(engine_state, stack)?;
let rhs = rhs.as_string()?;
let rhs = nu_path::expand_path_with(rhs, cwd);
stack.add_env_var(
env_var,
env_var.item,
Value::String {
val: rhs.to_string_lossy().to_string(),
span: call.head,
},
);
} else {
stack.add_env_var(env_var, rhs);
stack.add_env_var(env_var.item, rhs);
}
Ok(PipelineData::new(call.head))
}

View File

@ -38,6 +38,10 @@ impl Command for LoadEnv {
match arg {
Some((cols, vals)) => {
for (env_var, rhs) in cols.into_iter().zip(vals) {
if env_var == "FILE_PWD" {
return Err(ShellError::AutomaticEnvVarSetManually(env_var, call.head));
}
if env_var == "PWD" {
let cwd = current_dir(engine_state, stack)?;
let rhs = rhs.as_string()?;
@ -58,6 +62,10 @@ impl Command for LoadEnv {
None => match input {
PipelineData::Value(Value::Record { cols, vals, .. }, ..) => {
for (env_var, rhs) in cols.into_iter().zip(vals) {
if env_var == "FILE_PWD" {
return Err(ShellError::AutomaticEnvVarSetManually(env_var, call.head));
}
if env_var == "PWD" {
let cwd = current_dir(engine_state, stack)?;
let rhs = rhs.as_string()?;

View File

@ -1,7 +1,9 @@
mod config;
mod env_command;
mod export_env;
mod let_env;
mod load_env;
mod source_env;
mod with_env;
pub use config::ConfigEnv;
@ -9,6 +11,8 @@ pub use config::ConfigMeta;
pub use config::ConfigNu;
pub use config::ConfigReset;
pub use env_command::Env;
pub use export_env::ExportEnv;
pub use let_env::LetEnv;
pub use load_env::LoadEnv;
pub use source_env::SourceEnv;
pub use with_env::WithEnv;

92
crates/nu-command/src/env/source_env.rs vendored Normal file
View File

@ -0,0 +1,92 @@
use std::path::PathBuf;
use nu_engine::{eval_block, find_in_dirs_env, redirect_env, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
};
/// Source a file for environment variables.
#[derive(Clone)]
pub struct SourceEnv;
impl Command for SourceEnv {
fn name(&self) -> &str {
"source-env"
}
fn signature(&self) -> Signature {
Signature::build("source-env")
.required(
"filename",
SyntaxShape::String, // type is string to avoid automatically canonicalizing the path
"the filepath to the script file to source the environment from",
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"Source the environment from a source file into the current environment."
}
fn run(
&self,
engine_state: &EngineState,
caller_stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let source_filename: Spanned<String> = call.req(engine_state, caller_stack, 0)?;
// Note: this hidden positional is the block_id that corresponded to the 0th position
// it is put here by the parser
let block_id: i64 = call.req(engine_state, caller_stack, 1)?;
// Set the currently evaluated directory (file-relative PWD)
let mut parent = if let Some(path) =
find_in_dirs_env(&source_filename.item, engine_state, caller_stack)?
{
PathBuf::from(&path)
} else {
return Err(ShellError::FileNotFound(source_filename.span));
};
parent.pop();
let file_pwd = Value::String {
val: parent.to_string_lossy().to_string(),
span: call.head,
};
caller_stack.add_env_var("FILE_PWD".to_string(), file_pwd);
// Evaluate the block
let block = engine_state.get_block(block_id as usize).clone();
let mut callee_stack = caller_stack.gather_captures(&block.captures);
let result = eval_block(
engine_state,
&mut callee_stack,
&block,
input,
call.redirect_stdout,
call.redirect_stderr,
);
// Merge the block's environment to the current stack
redirect_env(engine_state, caller_stack, &callee_stack);
// Remove the file-relative PWD
caller_stack.remove_env_var(engine_state, "FILE_PWD");
result
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Sources the environment from foo.nu in the current context",
example: r#"source-env foo.nu"#,
result: None,
}]
}
}

View File

@ -13,8 +13,8 @@ use crate::To;
#[cfg(test)]
use super::{
Ansi, Date, From, If, Into, Math, Path, Random, Split, SplitColumn, SplitRow, Str, StrCollect,
StrLength, StrReplace, Url, Wrap,
Ansi, Date, From, If, Into, LetEnv, Math, Path, Random, Split, SplitColumn, SplitRow, Str,
StrCollect, StrLength, StrReplace, Url, Wrap,
};
#[cfg(test)]
@ -47,6 +47,7 @@ pub fn test_examples(cmd: impl Command + 'static) {
working_set.add_decl(Box::new(Url));
working_set.add_decl(Box::new(Ansi));
working_set.add_decl(Box::new(Wrap));
working_set.add_decl(Box::new(LetEnv));
use super::Echo;
working_set.add_decl(Box::new(Echo));

View File

@ -20,7 +20,7 @@ impl Command for IsAdmin {
}
fn search_terms(&self) -> Vec<&str> {
vec!["root", "admin", "administrator", "superuser", "supervisor"]
vec!["root", "administrator", "superuser", "supervisor"]
}
fn run(

View File

@ -20,7 +20,7 @@ impl Command for Cd {
}
fn search_terms(&self) -> Vec<&str> {
vec!["cd", "change", "directory", "dir", "folder", "switch"]
vec!["change", "directory", "dir", "folder", "switch"]
}
fn signature(&self) -> nu_protocol::Signature {

View File

@ -36,7 +36,7 @@ impl Command for Cp {
}
fn search_terms(&self) -> Vec<&str> {
vec!["cp", "copy", "file", "files"]
vec!["copy", "file", "files"]
}
fn signature(&self) -> Signature {
@ -50,7 +50,7 @@ impl Command for Cp {
)
.switch(
"verbose",
"do copy in verbose mode (default:false)",
"show successful copies in addition to failed copies (default:false)",
Some('v'),
)
// TODO: add back in additional features
@ -285,7 +285,11 @@ impl Command for Cp {
if verbose {
Ok(result.into_iter().into_pipeline_data(ctrlc))
} else {
Ok(PipelineData::new(span))
// filter to only errors
Ok(result
.into_iter()
.filter(|v| matches!(v, Value::Error { .. }))
.into_pipeline_data(ctrlc))
}
}
@ -348,9 +352,22 @@ fn copy_file(src: PathBuf, dst: PathBuf, span: Span) -> Value {
let msg = format!("copied {:} to {:}", src.display(), dst.display());
Value::String { val: msg, span }
}
Err(e) => Value::Error {
error: ShellError::FileNotFoundCustom(format!("copy file {src:?} failed: {e}"), span),
},
Err(e) => {
let message = format!("copy file {src:?} failed: {e}");
use std::io::ErrorKind;
let shell_error = match e.kind() {
ErrorKind::NotFound => ShellError::FileNotFoundCustom(message, span),
ErrorKind::PermissionDenied => ShellError::PermissionDeniedError(message, span),
ErrorKind::Interrupted => ShellError::IOInterrupted(message, span),
ErrorKind::OutOfMemory => ShellError::OutOfMemoryError(message, span),
// TODO: handle ExecutableFileBusy etc. when io_error_more is stabilized
// https://github.com/rust-lang/rust/issues/86442
_ => ShellError::IOErrorSpanned(message, span),
};
Value::Error { error: shell_error }
}
}
}

View File

@ -33,7 +33,7 @@ impl Command for Glob {
}
fn search_terms(&self) -> Vec<&str> {
vec!["glob", "files", "folders", "list", "ls"]
vec!["pattern", "files", "folders", "list", "ls"]
}
fn examples(&self) -> Vec<Example> {

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