Compare commits

..

373 Commits

Author SHA1 Message Date
JT
6cc4ef6c70 bump to 0.71, use 1.63 toolchain (#7061) 2022-11-09 06:54:00 +13:00
JT
517173bb8c Update rust-toolchain.toml 2022-11-09 06:06:33 +13:00
JT
e415be6c0e Revert back to 1.63 because of macOS build issues 2022-11-09 06:05:53 +13:00
Jakub Žádník
59332562bb Update contributing guide and PR template (#7008)
* Update contributing guide

* Refactor pull request template

* Reword PR template a bit

* Update CONTRIBUTING.md

Co-authored-by: Reilly Wood <26268125+rgwood@users.noreply.github.com>

* Reformulate

* Make "Before Submitting" a top-level header

* Add review requirement to After Submitting

* Reformulate

* Update .github/pull_request_template.md

Co-authored-by: Dan Davison <dandavison7@gmail.com>

* Reformulate contributing guide

Co-authored-by: Reilly Wood <26268125+rgwood@users.noreply.github.com>
Co-authored-by: Dan Davison <dandavison7@gmail.com>
2022-11-07 22:57:29 +01:00
Stefan Holderbach
3d8d7787de Pin reedline to 0.14.0 release (#7050)
See release notes:
https://github.com/nushell/reedline/releases/tag/v0.14.0
2022-11-07 21:31:15 +01:00
Stefan Holderbach
611fe41788 Make the example names unique across workspace (#7046)
Avoids name collision in the target directory when running test
compilation.

cc @rgwood
2022-11-07 09:00:21 +01:00
Darren Schroeder
a6118eed8d Revert "Fix for escaping backslashes in interpolated strings (fixes #6737) (#7020)" (#7038)
This reverts commit d4798d6ee1.
2022-11-06 16:17:00 -06:00
Gavin Foley
d4798d6ee1 Fix for escaping backslashes in interpolated strings (fixes #6737) (#7020)
Co-authored-by: Gavin Foley <gavinmfoley@gmail.com>
2022-11-07 08:57:28 +13:00
Stefan Holderbach
5ea245badf Make example binaries proper cargo examples (#7019)
Should not be built by default with `cargo build`
Instead are compiled with `cargo test` to avoid bitrot
Run with `cargo run -p ... --example ...`
2022-11-06 11:39:27 -08:00
Dan Davison
5ee7847035 Add accidentally missing tests of some command examples (#7035)
* Add missing tests of examples

* Fix broken tests due to externals not being in working set

* Comment out `where` test due to bug
2022-11-06 09:53:25 -08:00
mike
8a812cf03c added some search-terms to the platform category (#7021)
* added some search-terms to the `platform` category

* removed the redundant `sleep` search-term

* removed the redundant `gradients` search-term
2022-11-06 18:11:04 +01:00
Dan Davison
9a1cedfd08 Add expected result to test (#7031) 2022-11-06 18:09:56 +01:00
Stefan Holderbach
766d1ef374 Update reedline (#7023)
Fixes #6991
2022-11-06 09:37:36 +01:00
Per Bothner
beec658872 New "display_output" hook. (#6915)
* New "display_output" hook.

* Fix unrelated "clippy" complaint in nu-tables crate.

* Fix code-formattng and style issues in "display_output" hook

* Enhance eval_hook to return PipelineData.

This allows a hook (including display_output) to return a value.

Co-authored-by: JT <547158+jntrnr@users.noreply.github.com>
2022-11-06 13:46:40 +13:00
David Matos
b90d701f89 Rename column name from command to name for consistency (#7007) 2022-11-05 10:46:30 +13:00
Alex Saveau
be5d71ea47 Run a round of clippy --fix to fix a ton of lints (#7006)
Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>

Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>
2022-11-04 15:11:17 -05:00
Ian Manske
f1bde69131 Fix panic when encountering ENOTTY. (#7001) 2022-11-05 09:06:04 +13:00
Alex Saveau
36ae384fb3 Improve do command docs (#6975) 2022-11-05 07:50:56 +13:00
Stefan Holderbach
2c4048eb43 Refactor ansi stripping into nu-utils functions (#6966)
Allows use of slightly optimized variants that check if they have to use
the heavier vte parser. Tries to avoid unnnecessary allocations. Initial
performance characteristics proven out in #4378.

Also reduces boilerplate with right-ward drift.
2022-11-05 07:49:45 +13:00
Access
b9195c2668 fix: fixcd (#6799)
* fix: fixcd

try to fix

Log: try to fix the bug with can enter a permisson error fold

* change wording

* fat

* fmt

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-11-05 07:38:39 +13:00
Darren Schroeder
bb968304da bump rust-toolchain to 1.64 (#7005)
* bump rust-toolchain to 1.64

* 1.64 clippy
2022-11-04 10:27:23 -05:00
David Matos
ca9bf19041 highlight term on PipelineData::Value() (#6997) 2022-11-04 08:42:16 -05:00
JT
ecfee4c542 Remove unnecessary clone in par-each (#6995)
* Remove unnecessary clone in par-each

* clippy
2022-11-04 18:07:28 +13:00
Darren Schroeder
acb34561eb category tweak (#6982) 2022-11-02 12:17:17 -05:00
Leon
43aec8cdbe Fix $in in blocks given to any and all (#6951)
* Fix $in in blocks given to `any` and `all` (closes #6917)

* Fix help message typos

* Fix tests ($in doesn't work in examples?!)

* Fix formatting
2022-11-01 11:36:54 -07:00
WindSoilder
e46d610f77 Refactor: finish refactor on commands which take optional cell paths. (#6961)
* refactor on conversions module

* finish refactor on strings command

* simplify code

* rename from ArgumentsCp to CellPathOnlyArgs

* fmt code

* refactor on hash relative commands
2022-11-01 12:40:11 +01:00
Stefan Holderbach
1d95861a09 Remove inadvertent dep on original ansi_term (#6965)
Is default feature in `lscolors`. Not needed for function as we use
crossterm backend in this case.
2022-11-01 12:27:20 +13:00
Darren Schroeder
f48de73236 change str distance to output value::int (#6963)
* change str distance to output value::int

* update the test
2022-10-31 10:28:04 -05:00
Darren Schroeder
0b4daa66b0 tweak upsert help text (#6962)
* tweak upsert help text

* more tweaks
2022-10-31 08:18:11 -05:00
Stefan Holderbach
412952182f Update reedline to latest dev (#6953)
- Reedline now properly supports the `sqlite-dynlib` feature flag
- Reorganized examples allow removal of `gethostname` as reedline
dev-dependency
2022-10-30 22:08:07 +01:00
Jens Hedegaard Nielsen
014d36b17a Use nt-api 4 on Windows (#6949)
* Bump nushell-sytem dep to ntapi 0.4

0.3.7 trigger a warning about code being incompatible
with future rust versions. This is resolved in 0.4
https://github.com/MSxDOS/ntapi/issues/11

* Upgrade Cargo.lock for ntapi 0.4
2022-10-30 14:29:41 +01:00
Reilly Wood
f44f3a8af1 Fix double cache read in CI (#6948) 2022-10-30 08:24:10 +01:00
WindSoilder
457514590d Refactor: introduce general operate commands to reduce duplicate code (#6879)
* make format filesize more flexible

* make code simpler

* finish refactor on bytes commands

* finish refactor on str commands

* fimplify code

* rename from column_paths to cell_paths
2022-10-29 16:29:46 -05:00
Mel Massadian
843d8c2242 Make default for mv safer, require -f to overwrite (#6904)
* fix:  "saner" default for mv

fixes #6747

As highlighted in the issue, the default behavior of nu currently
is to overwrite the destination file without notice.
This is not a "standard" expectation users that want this behavior
can create a dedicated alias.

* fix: 📝 edit the comment

* fix:  updated the tests

* fix: 🚧 use --force for case test
2022-10-29 22:16:29 +02:00
Stefan Holderbach
ce4ae00a6f Remove unused dependencies (#6945)
* Remove unused dependencies

Inspired by #6938 ran `cargo +nightly udeps --features extra`.
Removes 2 crates and should remove an unnecessary intra-workspace
dependency which might open up further opportunities for compilation.

* Make windows-only dependency conditional in toml

`omnipath` is only used on Windows and already behind a `#[cfg]` block
in the code. Made the dependency in `Cargo.toml` conditional as well.

* Make `nu-pretty-hex` example a proper example

This allows making `rand` a dev-dependency in this crate.
2022-10-29 21:19:12 +02:00
WindSoilder
4f7f6a2932 Friendly error message for access beyond end (#6944)
Adds `ShellError::AccessEmptyContent`
2022-10-29 19:47:50 +02:00
Stefan Holderbach
7039602e4d Move nu-test-support into dev deps on nu-command (#6940)
This reduces the number of dependencies to build for `cargo build` or
`cargo run` by around 19.

Will not speed up CI as we need to `cargo test` but should help for
`cargo install` or users just building from source.
Time saved on my machine ~0.8 secs so likely unnoticable in noise.

Larger future goal is reducing longer dependency chains to allow more
parallel compilation.
2022-10-29 19:39:27 +02:00
Reilly Wood
acb7aff6fb Fix feature flag for open test (#6935) 2022-10-28 20:25:19 -07:00
Darren Schroeder
8940ee6c3f enable ability to upsert into a list like update (#6932) 2022-10-28 18:13:14 -05:00
Max Zhuravsky
3b26b4355e Fix bug with alias handling when searching for matching brackets (#6931)
* [4325] - fix slicing

* [4325] - fix style
2022-10-28 16:21:24 -05:00
Reilly Wood
b56e603c58 Update PR template to mention user-facing changes (#6923)
* Update PR template to mention user-facing changes

* remove checkboxes
2022-10-28 09:14:08 -07:00
Maxim Zhiburt
66c2a36123 table: Show truncated record differently (#6884)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-28 14:00:10 +02:00
Stefan Holderbach
8838815737 Update nix crate to 0.25 and narrow features (#6924)
Avoids compiling the crate twice due to incompatible versions from
dependencies. This avoids binary bloat before linking as well.
Narrow our feature selection to the used modules/functions to save
compile time. On my machine reduces `nix` crate compile time from 
around 9 secs to 0.9 secs.
2022-10-28 01:07:13 +02:00
Stefan Holderbach
834522d002 Fix each while behavior when printing. (#6897)
Fixes #6895

Warning: `Iterator::map_while` does not return a `FusedIterator`!
Depending on the consuming adaptor or code (e.g. for loop) the iteration
may be stopped but this is not guaranteed.

Adds a test example but Examples handle iterators like
`FusedIterator` and thus don't catch the regression

Cleanup the message on another test
2022-10-27 23:45:45 +02:00
Leon
f281cd5aa3 Update merge to also take single records (#6919) 2022-10-27 09:00:26 -07:00
Leon
5add5cbd12 Further edits to help messages (#6913) 2022-10-26 09:36:42 -07:00
Andrey Filipenkov
902aad6016 fix description of build-string's second example (#6912) 2022-10-26 09:36:31 -05:00
Mel Massadian
13f87857cf docs: 📝 add "map" to each's search terms (#6903) 2022-10-25 21:24:27 +02:00
Reilly Wood
e0cc2c9112 Make get 1 error message better (#6892) 2022-10-24 18:22:57 -07:00
Stefan Holderbach
92ab8b831b Reduce required dependencies for diagnostics (#6648)
Disable backtrace on miette
- gimli crate requires several seconds
Disable diagnostics on wax
- depends on an outdated miette version

Builds fine, no observable loss in diagnostics quality of life

Removes 10 crates that have to be compiled.
2022-10-24 21:42:32 +02:00
Stefan Holderbach
6a7a60429f Remove unnecessary #[allow(...)] annotations (#6870)
* Remove unnecessary `#[allow]` annots

Reduce the number of lint exceptions that are not necessary with the
current state of the code (or more recent toolchain)

* Remove dead code from `FileStructure` in nu-command

* Replace `allow(unused)` with relevant feature switch

* Deal with `needless_collect` with annotations

* Change hack for needless_collect in `from json`

This change obviates the need for `allow(needless_collect)`
Removes a pessimistic allocation for empty strings, but increases
allocation size to `Value`

Probably not really worth it.

* Revert "Deal with `needless_collect` with annotations"

This reverts commit 05aca98445.

The previous state seems to better from a performance perspective as a
`Vec<String>` is lighter weight than `Vec<Value>`
2022-10-24 20:12:16 +02:00
Stefan Holderbach
79fd7d54b2 Wrap open parse errors from from commands (#6877)
* Wrap `open` parse errors from `from` commands

Minimal fix for #6843

This propagates the underlying errors from the called `from` commands
and adds a top-level error with the full path and the understood file
extension and resulting called command.

* Repoint inner span for `from ...` to `open`

* Add actionable message: refer to help or use --raw
2022-10-24 20:09:19 +02:00
nibon7
ebca840d91 Add support to render right prompt on last line of the prompt (#6781)
* Add support to render right prompt on last line of the prompt

* reset reedline to main branch

* update reedline to fix right prompt to be rendered correctly

* reset reedline to main branch again
2022-10-23 16:18:26 +02:00
Reilly Wood
17b2bcc125 Support range in str substring (#6867) 2022-10-23 11:42:17 +02:00
Leon
24a98f8999 Mildly edited a small handful of help messages (#6868)
* Edited a handful of help messages

* Remove line break as instructed by clippy
2022-10-23 02:02:52 -04:00
Filip Andersson
e49b359848 Bumps Windows 0.37 -> 0.42. (#6865) 2022-10-22 17:59:44 -07:00
Access
c9fb381d69 feat: coredump (#6791)
fix issue in https://github.com/nushell/nushell/issues/5903
return Error to pipeline_data, if match error, then return error
directly
2022-10-22 20:24:58 +02:00
Max Zhuravsky
8224ec49bc Highlight matching brackets / parentheses (#6655)
* [4325] - wip

* [4325] - hightlight only matched symbol

* [4325] - cleanup

* [4325] - match bracket while typing

* [4325] - fix clippy

* [4325] - add bracket highlight configuration

* [4325] - fix working with non-ascii
2022-10-22 11:55:45 -05:00
Reilly Wood
fe7e87ee02 Fixes for ps on Linux (#6858)
* Fix ps returning nothing when process dies

* Fix ps CPU calcs on Linux, remove unused thread code
2022-10-22 11:54:46 -05:00
Maxim Zhiburt
a3dce8ff19 table -e Fix stackoverflow (cause endless empty list) (#6847)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-22 11:52:32 -05:00
Chris Denton
89f3cbf318 Try not to use verbatim paths for UNC shares (#6824) 2022-10-22 11:51:52 -05:00
pwygab
3f555a6836 add more helpful error for calling a decl that exists in a module (#6752)
* add more helpful error for calling a decl that exists in a module

* accord to suggestions

* make error more helpful
2022-10-22 11:41:31 -05:00
nibon7
4fdf5c663c Prepend directory to the binary tarball (#6701)
* Prepend directory to the binary tarball

According to https://www.gnu.org/software/tar/manual/html_section/transform.html, use
`--transform` parameter to prepend directory to each file name.

Closes #6676

* Don't depend on GNU tar
2022-10-22 11:39:11 -05:00
Dan Davison
1ec41a0ab4 Expose reedline EditCommand::Complete command (#6863)
This should have been done in 5eee33c7e4
2022-10-22 11:32:07 -05:00
nibon7
ab0a6b6ca6 path: fix error message (#6860)
Closes #6819
2022-10-22 06:42:46 -05:00
Reilly Wood
e3bf6fdfc0 Print command help in base from+to commands (#6856) 2022-10-21 10:08:57 -07:00
Maxim Zhiburt
46c0d29c08 table/ Fix paging indexing (#6850)
* table/ Fix paging indexing

close #6840

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add test for pagging with row_overlapping

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-21 18:02:25 +02:00
Reilly Wood
76ccd5668a Remove perf flag to streamline logging configuration (#6834) 2022-10-21 10:20:21 -05:00
nibon7
c6436eb32f rm: don't update target_exists every time in the loop (#6837) 2022-10-21 07:42:29 -07:00
Maxim Zhiburt
b2c29117d9 table -e align key to 2nd line (#6842)
Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-21 06:29:55 -05:00
Justin Ma
ffb1dfb012 Update ci workflow actions, fix #6713 (#6841)
* Update ci workflow actions, fix #6713

* Upgrade actions/setup-python to v4
2022-10-21 15:25:02 +08:00
Dan Davison
88c6fa9933 Update reedline to fix history search filtering (#6835)
https://github.com/nushell/nushell/pull/6802#issuecomment-1285826499
2022-10-21 09:04:54 +02:00
nibon7
60df45a390 Add missing shape_directory to default_config.nu (#6836)
Closes #6832
2022-10-20 21:25:09 -07:00
WindSoilder
10aa86272b Allow captured stderr saving to file (#6793)
* support redirect stderr to file

* fix test

* fix test

* fix test
2022-10-20 07:56:44 -05:00
Maxim Zhiburt
d37e6ba3b5 nu-table: Check perf improvements (#6710)
* nu-table: Checkout to test vte parsing

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Bump tabled version

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-20 07:52:15 -05:00
Dan Davison
5eee33c7e4 Tab inline completion (#6802)
* Make Tab insert (partial) completion instead of select next menu item

* Use reedline feature branch

Co-authored-by: JT <547158+jntrnr@users.noreply.github.com>
2022-10-20 23:39:48 +13:00
pwygab
5e748ae8fc make ++ append lists (#6766)
* make `++` append lists

* fmt

* fix for database
2022-10-20 23:28:18 +13:00
Dan Davison
50e53e788a Simplify and reduce allocations in pipeline data loop (#6790) 2022-10-20 23:22:07 +13:00
nibon7
7336e1df1a rm: fix error span when targets doesn't exists (#6815)
Closes #6810
2022-10-20 10:00:25 +02:00
Darren Schroeder
a724a8fe7d bump to dev version 0.70.1 (#6814) 2022-10-20 18:04:10 +13:00
JT
c731a4e275 Update Cargo.toml 2022-10-19 11:44:04 +13:00
JT
9ef65dcd69 Bump to 0.70 (#6800) 2022-10-19 07:13:36 +13:00
JT
f99c002426 Fix let-env in banner (#6795) 2022-10-18 22:42:00 +13:00
Stefan Holderbach
f0420c5a6c Pin reedline to the 0.13.0 release (#6789)
See the release notes:

https://github.com/nushell/reedline/releases/tag/v0.13.0
2022-10-17 23:45:28 +02:00
Val Packett
46eec5e3a2 Tolerate more tty acquisition failures in non-interactive mode, fixes #6719 (#6779) 2022-10-17 21:08:25 +02:00
Jakob Endler
378248341e Update README.md (#6782)
Fixed a very small inconsistency.
2022-10-17 06:23:11 -05:00
Stefan Holderbach
803f9d4daf Upgrade reedline to latest dev version (#6778)
* Reorder conditional deps for readability

* Pull reedline from the most recent main branch

* Map 'Submit' and 'SubmitOrNewline' events
  Introduced by nushell/reedline#490
2022-10-16 23:51:15 +02:00
Dan Davison
ce809881eb Rename query dfr -> query df (#6777) 2022-10-16 21:04:48 +02:00
Dan Davison
1a99893e2d Add documentation requirement to PR template (#6749) 2022-10-16 18:19:54 +03:00
Russell Chan
ec8e57cde9 Add search terms to roll commands (#6761) 2022-10-16 13:04:22 +02:00
WindSoilder
a498234f1d fix stdout hangged on (#6715) 2022-10-15 14:29:29 -05:00
Darren Schroeder
de77cb0cc4 add filesize_metric comment (#6760) 2022-10-15 14:26:18 -05:00
HannahZhuSWE
7d5d53cf85 Add search terms to arg dataframe commands (#6724)
* added search terms for arg prefixed dataframe commands

* remove search terms that already produce results
2022-10-15 12:49:09 -05:00
Kyle Anderson
1572808adb Filter out empty glob patterns to "glob" command (#6707)
* Filter out empty glob patterns

An empty argument to the "glob" command will now produce an empty result.
Working towards nushell/nushell#6653.

* Run `cargo fmt --all`

Just autoformatted the repo so that CI passes and we have a consistent code
format across modules.

* Treat empty glob argument as error

The glob command will now report an empty string argument as an error instead
of silently ignoring it.

See https://github.com/nushell/nushell/pull/6707#discussion_r993345013.

* Add tests for glob command

Two small tests for the glob command, one to check that the empty string errors
it, and another to sanity check the '*' glob, have been added.

* Rename glob sanity check star test

Co-authored-by: Kyle Anderson <kyle.anderson@uwaterloo.ca>
2022-10-15 18:00:38 +02:00
pwygab
9d77e3fc7c Improve erroring of config nu and config env (#6730)
* improve errors for `config nu` and `config env`

* fix tests
2022-10-15 08:28:54 -05:00
Doru
e22f2e9f13 window --remainder (#6738)
* Implement window remainder, and save allocation

* Fallible memory reservation
2022-10-15 08:06:54 -05:00
John Howard
4ffa4ac42a Delete out.log (#6731) 2022-10-15 07:37:57 -05:00
Doru
da6f548dfd Remove unnecessary clone (#6729) 2022-10-14 17:13:24 -05:00
JT
7532991544 Allow auto-cd to work with backticks (#6728) 2022-10-15 10:37:31 +13:00
Doru
b7f47317c2 Fix quadratic time complexity with large strides (#6727) 2022-10-14 15:17:47 -05:00
pwygab
5849e4f6e3 let alias list aliases (#6717)
* let `alias` list aliases

* fix highlighting

* Update parse_keywords.rs
2022-10-14 21:51:44 +02:00
Dylan Snelgrove
868d94f573 Add search terms for export commands (#6722)
Contributes to https://github.com/nushell/nushell/issues/5093
2022-10-14 12:02:22 -05:00
Darren Schroeder
1344ae3a65 add the ability to convert durations (#6723)
* add the ability to convert durations

* add conversion to floats if necessary
2022-10-14 11:46:48 -05:00
Zephaniah Ong
804b155035 Add search terms for uppercase (#6720)
* Add search terms for uppercase

* Add search terms

* Add search terms

* Change to parse

* Add search terms for from

* Add search terms for to

* Remove duplicate function

* Remove duplication of search terms

* Remove search term
2022-10-14 05:36:31 -05:00
HoLLy
9446e3960b Fix invalid variable name in input command docs (#6716) 2022-10-13 12:42:24 -05:00
pwygab
90ba39184a allow for $in to affect environment (#6649)
* allow for `$in` to affect environment

* fix for vars, overlays, env_hidden

* fmt

* carry over variables properly

* add test

* modify name, remove redundant

* fmt
2022-10-13 12:04:34 +03:00
Stefan Holderbach
d40a73aafe Backport fixes from nushell/nushell.github.io#633 (#6712)
Co-authored-by: Eric Hodel <drbrain@segment7.net>

Co-authored-by: Eric Hodel <drbrain@segment7.net>
2022-10-12 19:14:16 +02:00
WindSoilder
5815f122ed avoid freeze when capturing external stderr (#6700)
* avoid freeze when capturing external stderr

* try replace from sh to bash

* change description

* fmt code
2022-10-12 08:41:20 -05:00
Stefan Holderbach
0bbb3a20df Fix ex. completion git push --force-with-lease (#6702)
The `--force-with-lease` flag was given as requiring an additional string which is not true.

Fixes #6644 

for this to take effect you need to update your `config.nu`
2022-10-11 12:41:50 +02:00
WindSoilder
1998bce19f avoid freeze for table print (#6688)
* avoid freeze for table print

* make failed_with_proper_exit_code work again

* add test case for table

* fix un-used import on windows
2022-10-10 07:32:55 -05:00
pwygab
2f1711f783 return gid and uid in numbers if name not found (#6684)
* return git and uid in numbers if name not found

* fmt
2022-10-10 14:29:16 +02:00
Jake Albert
34c8b276ab Return Error on str replace RegEx parse fail (#6695) 2022-10-10 07:27:01 -05:00
Brian Myers
fde56cfe99 upgrade num-format (#6694) 2022-10-10 06:25:57 -05:00
pwygab
118033e4a5 don't attempt to eval and record down if the repl line is empty (#6674) 2022-10-08 16:38:35 -05:00
Darren Schroeder
7910d20e50 add a new command to query the registry on windows (#6670)
* add a new command to query the registry on windows

* cross platform tweaks

* return nushell datatype

* change visibility of exec and registry commands
2022-10-07 13:54:36 -05:00
Justin Ma
e1d5180e6d Update nushell version for release workflow (#6666) 2022-10-05 22:10:25 +08:00
Artemiy
79ce13abef To nuon escapes (#6660)
* Add tests for "to nuon" escaping handling

* Fix "to nuon" not escaping double quotations

* Fix "to nuon" double backslash

Fix value_to_string_without_quotes leaving escaped backslash in
non-quoted strings
2022-10-04 06:25:21 -05:00
Maxim Zhiburt
5921c19bc0 WIP/ Checkout to new tabled (#6286)
* nu-table/ Use latest tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table/ Fix first column alignment

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Fix cargo clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Fix color issue

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Fix footer row

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Bump tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Bump tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Bump tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Update

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table/ Update

* Use latest tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add optional -e, -c argument to `table` command for different view

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Update

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix cargo clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* nu-table: Add footer into -e/c mode

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Publish new expand mode

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add width ctrl for Expand mode

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Refactorings

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Refactorings

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Merge with main

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix clippy

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Fix tests

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Bump tabled

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* Add record expand and fix empty list issue

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>

* refactoring

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
2022-10-03 11:40:16 -05:00
rsteube
e629ef203a nu-cli: external completer precedence before file (#6652) 2022-10-01 07:24:22 -05:00
Dan Davison
5959d1366a Remove unnecessary flags from term size (#6651)
The columns and rows can be obtained individually using

(term size).columns
(term size).rows
2022-10-01 07:00:54 -05:00
WindSoilder
530ff3893e Eval external command result immediately when using do command with -c (#6645)
* make capture error works better in do command

* remove into string test because we have no way to generate Value::Error for now
2022-09-30 07:14:02 -05:00
WindSoilder
6f59167960 Make semicolon works better for internal commands (#6643)
* make semicolon works with some internal command like do

* refactor, make consume external result logic out of eval_external

* update comment
2022-09-30 07:13:46 -05:00
Darren Schroeder
ca715bb929 tweak the banner message and make the time more accurate (#6641) 2022-09-29 14:07:32 -05:00
unrelentingtech
4af0a6a3fa Foreground process group management, again (#6584)
* Revert "Revert "Try again: in unix like system, set foreground process while running external command (#6273)" (#6542)"

This reverts commit 2bb367f570.

* Make foreground job control hopefully work correctly

These changes are mostly inspired by the glibc manual.

* Fix typo in external command description

* Only restore tty control to shell when no fg procs are left; reuse pgrp

* Rework terminal acquirement code to be like fish

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-09-29 13:37:48 -05:00
Darren Schroeder
6486364610 changed the way durations and filesizes are parsed (#6640) 2022-09-29 13:24:17 -05:00
Darren Schroeder
6aa8a0073b add better description to table_index_mode (#6637) 2022-09-29 06:26:01 -05:00
pwygab
f5e1b08e6a ensure Operator::And errors out with incompatible types (#6638) 2022-09-29 06:17:21 -05:00
Kangaxx-0
7b9ad9d2e5 Fix issue 6596 (#6603)
* Fix issue 6596

* add two unit tests

* fix pipe

* add cp test

* fix test on windows
2022-09-29 10:43:58 +02:00
pwygab
1a3762b905 prevent alias name from being filesize or number (#6595)
* prevent alias name from being filesize or number

* add test

* fmt
2022-09-28 17:08:38 -05:00
pwygab
32fbcf39cc make first behave same way as last: always return list when with number argument (#6616)
* make `first` behave same way as `last`

* better behaviour

* fix tests

* add tests
2022-09-28 17:08:17 -05:00
Darren Schroeder
dd578926c3 add some float operations with filesize (#6618)
* add some float operations with filesize

* more changes

* update return values on filesize-filesize, duration-duration

* missed filesize in floordiv

* missed float * duration
2022-09-28 17:07:50 -05:00
Nano
5c99921e15 Table indexes (#6620)
* Table indexes

* Renamed to `show_table_indexes`

* Renamed to `table_index_mode`
2022-09-28 17:07:33 -05:00
WindSoilder
d2e4f03d19 update and fix python plugin example (#6633)
* update and fix python plugin example

* update comment
2022-09-28 17:06:43 -05:00
Darren Schroeder
23bba9935f bump to dev version 0.69.2 (#6635) 2022-09-28 17:06:21 -05:00
JT
8a5abc7afc bump to 0.69.1 (#6631) 2022-09-28 15:48:01 +13:00
JT
ec711cb79d remove -d and -t from touch (#6629)
* remove -d and -t from touch

* remove unused test import
2022-09-28 13:48:34 +13:00
JT
f2ad7fae1f bump to updated reedline (#6626) 2022-09-28 12:08:42 +13:00
JT
13a4474512 Update Cargo.toml 2022-09-28 12:02:39 +13:00
JT
b746d8427c Revert to released syntax for release-pkg 2022-09-28 07:42:25 +13:00
JT
3beaca0d06 bump to 0.69 (#6623) 2022-09-28 07:14:31 +13:00
Stefan Holderbach
f7647584a3 Clippy with the current stable toolchain (#6615)
Fix lints that are coming with rust 1.64

Passes with the earlier toolchain from `rust-toolchain.toml` as well.
2022-09-26 19:29:25 +02:00
Stefan Holderbach
f44473d510 Update reedline to better vi behavior (#6614)
See nushell/reedline#484
2022-09-26 00:22:54 +02:00
JT
d66a5398d1 Remove month and year duration constants (#6613)
remove month/year/decade durations for parsing and units, but leave the humanized output for viewing
2022-09-26 09:55:13 +13:00
JT
43905caa46 touchup some clippy warnings in tests (#6612) 2022-09-26 09:06:13 +13:00
WindSoilder
b47bd22b37 Removes export env command (#6468)
* remove export_env command

* remove several export env usage in test code

* adjust hiding relative test case

* fix clippy

* adjust tests

* update tests

* unignore these tests to expose ut failed

* using `use` instead of `overlay use` in some tests

* Revert "using `use` instead of `overlay use` in some tests"

This reverts commit 2ae24b24c3.

* Revert "adjust hiding relative test case"

This reverts commit 4369af6d05.

* Bring back module example

* Revert "update tests"

This reverts commit 6ae94ef513.

* Fix tests

* "Fix" a test

* Remove remaining deprecated env functionality

* Re-enable environment hiding for `hide`

To not break virtualenv since the overlay update is not merged yet

* Fix hiding env in `hide` and ignore some tests

Co-authored-by: kubouch <kubouch@gmail.com>
2022-09-25 19:52:43 +03:00
MichelMunoz
7f21b7fd7e 6582 - Incorrect documentation for some string operations (#6610)
* 6582 - Incorrect documentation for some string operations

* Update crates/nu-command/src/strings/str_/contains.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

* Update crates/nu-command/src/strings/str_/ends_with.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

* Update crates/nu-command/src/strings/str_/index_of.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

* Update crates/nu-command/src/strings/str_/starts_with.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

* Run rustfmt

Co-authored-by: MichelMunoz <>
Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2022-09-25 18:09:09 +02:00
nibon7
0ab4e5af2a version: show built time git branch (#6609) 2022-09-24 07:10:36 -05:00
Reilly Wood
848550771a Fix mv data loss when changing folder case (step 1) (#6599)
* Fix mv data loss when changing folder case (step 1)

* Use same-file to detect when changing case on Windows
2022-09-23 11:09:31 -07:00
WindSoilder
d323ac3edc fix sys info mem usage (#6607) 2022-09-23 11:47:52 -05:00
Tobias
2e23d4d734 fix issue 6602 (broken highlighting in find) (#6604)
* fix issue 6602 (broken highlighting in find)

Find uses highlight_search_string to see where the string was found. The problem was that the style would just "append" to the existing haystack (including the ANSI escape codes of LS_COLORS).

This first strips the ANSI escape codes out of the haystack before formatting the
output string.

* update formatting
2022-09-23 07:11:33 -05:00
Kangaxx-0
d9d14b38de [Cleanup]Nu completion unit tests (#6601)
* clean up completion unit tests

* update
2022-09-22 21:50:16 +03:00
Darren Schroeder
03b7dd2725 bump pinned rust version (#6600)
since rust 1.64 was just released, let's bump the pinned version but only be 1 version of rust behind instead of 2. 1 version should hopefully be enough to allow pkg repositories to get updated... i hope
2022-09-22 12:37:52 -05:00
Dan Davison
ad0c6bf7d5 Improve "Did you mean?" suggestions (#6579)
* Copy lev_distance.rs from the rust compiler

* Minor changes to code from rust compiler

* "Did you mean" suggestions: test instrumented to generate markdown report

* Did you mean suggestions: delete test instrumentation

* Fix tests

* Fix test

`foo` has a genuine match: `for`

* Improve tests
2022-09-20 19:46:01 -05:00
Stefan Holderbach
9aed95408d Add "space" key to bind in vi normal mode (#6590)
* Add "space" key to bind in vi normal mode

Implements #6586

No special logic to prevent you from binding it in other modes!
Needs a separate change to reedline to make it available in the default
listing of `keybindings list`.

* Update reedline to report the available `space`

Pulls in nushell/reedline#486
2022-09-20 13:04:35 +02:00
Darren Schroeder
71844755e5 add history session command (#6587) 2022-09-19 14:30:04 -05:00
Darren Schroeder
0b9dd87ca8 add history session id to $nu (#6585)
* add history session id to $nu

* get nushell to compile

* update test
2022-09-19 09:28:36 -05:00
Dan Davison
d704b05b7a Improve uniq documentation (#6580) 2022-09-18 08:24:27 -07:00
nibon7
15ebf45f46 Apply clippy fix for rust 1.63.0 (#6576)
* Apply clippy fix to avoid extra allocation

error: `format!(..)` appended to existing `String`
  --> crates/nu-engine/src/documentation.rs:82:9
   |
82 |         long_desc.push_str(&format!("\n{G}Subcommands{RESET}:\n"));
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `-D clippy::format-push-string` implied by `-D warnings`
   = help: consider using `write!` to avoid the extra allocation
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string

error: `format!(..)` appended to existing `String`
  --> crates/nu-engine/src/documentation.rs:96:9
   |
96 |         long_desc.push_str(&format!("\n{G}Parameters{RESET}:\n"));
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = help: consider using `write!` to avoid the extra allocation
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string

error: `format!(..)` appended to existing `String`
   --> crates/nu-engine/src/documentation.rs:128:9
    |
128 |         long_desc.push_str(&format!("\n{}Examples{}:", G, RESET));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider using `write!` to avoid the extra allocation
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string

error: `format!(..)` appended to existing `String`
   --> crates/nu-engine/src/documentation.rs:202:5
    |
202 |     long_desc.push_str(&format!("\n{}Flags{}:\n", G, RESET));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider using `write!` to avoid the extra allocation
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#format_push_string

error: could not compile `nu-engine` due to 4 previous errors

* Apply clippy fix to avoid deref on an immutable reference

error: deref on an immutable reference
   --> crates/nu-command/src/dataframe/eager/sql_context.rs:188:77
    |
188 |                         SetExpr::Select(select_stmt) => self.execute_select(&*select_stmt)?,
    |                                                                             ^^^^^^^^^^^^^
    |
    = note: `-D clippy::borrow-deref-ref` implied by `-D warnings`
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref
help: if you would like to reborrow, try removing `&*`
    |
188 |                         SetExpr::Select(select_stmt) => self.execute_select(select_stmt)?,
    |                                                                             ~~~~~~~~~~~
help: if you would like to deref, try using `&**`
    |
188 |                         SetExpr::Select(select_stmt) => self.execute_select(&**select_stmt)?,
    |                                                                             ~~~~~~~~~~~~~~

error: deref on an immutable reference
   --> crates/nu-command/src/database/values/dsl/expression.rs:252:15
    |
252 |         match &*expr {
    |               ^^^^^^ help: if you would like to reborrow, try removing `&*`: `expr`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#borrow_deref_ref

error: could not compile `nu-command` due to 2 previous errors
2022-09-17 12:10:32 -05:00
Dan Davison
b086f34fa2 Reinstate -a short form of save --append (#6575)
Present before engine-q merge (e.g.
265ee1281d) but not included when
--append was re-introduced at
https://github.com/nushell/nushell/pull/4744.
2022-09-17 07:02:17 -05:00
WindSoilder
5491634dda escape external args (#6560) 2022-09-17 06:07:45 -05:00
Jakub Žádník
e7bf89b311 Add export-env eval to use command (#6572) 2022-09-17 02:36:17 +03:00
Darren Schroeder
4fdfd3d15e rename with_sql to query dfr (#6568)
* rename with_sql to query dfr

* add search terms

* update example command
2022-09-16 08:34:58 -05:00
Kangaxx-0
35a521d762 Fix 6529 - Trim overlay name (#6555)
* trim overlay name

* format

* Update tests/overlays/mod.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

* cleanup

* new tests

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2022-09-16 10:27:12 +03:00
Darren Schroeder
f0ae6ffe12 update sql-parser crate and all the files it touches (#6566)
* update sql-parser crate and all the files it touches

* undo adding extra as a default feature
2022-09-15 18:03:43 -05:00
Darren Schroeder
10b9c65cb7 synchronize the db commands with file names (#6565) 2022-09-15 14:39:36 -05:00
Darren Schroeder
02e3f49bce provide a way to use sql to query dataframes (#6537) 2022-09-15 09:22:00 -05:00
nibon7
f6c791f199 Use style from lscolors to render the rest of the filename (#6564)
* Use style from lscolors to render the rest of the filename

Related: https://github.com/nushell/nushell/pull/6556#issuecomment-1246681644

* Make cargo-clippy happy
2022-09-15 06:12:20 -05:00
Darren Schroeder
cc62e4db26 update to the latest sysinfo crate (#6563) 2022-09-15 05:47:40 -05:00
nibon7
56bb9e92cb Use stripped path for lscolors to get style (#6561) 2022-09-15 05:34:47 -05:00
Darren Schroeder
2791251268 update text in readme file (#6557) 2022-09-14 15:25:55 +02:00
nibon7
b159bf2c28 Make clickable links smarter (#6556)
* Disable clickable links when we can't get metadata of files

Fixes #6498

* Refactor path name rendering related code

* Make clickable links smarter

* Remove unneeded clone

* Return early if `use_ls_colors` is disabled
2022-09-14 05:55:41 -05:00
pwygab
12a0fe39f7 default to $nothing if cellpath not found (#6535)
* default to  if cellpath not found

* fmt

* add test

* fix test

* fix clippy

* move behaviour behind `-i` flag

* prevent any possibility of an unspanned error

* ignore all errors

* seperate testes

* fmt
2022-09-13 16:17:16 +03:00
unrelentingtech
df6a7b6f5c shell_integration: Report current working directory as OSC 7 (#6481)
This is a de-facto standard supported by many terminals, originally
added to macOS Terminal.app, now also supported by VTE (GNOME),
Konsole (KDE), WezTerm, and more.
2022-09-13 07:36:53 -05:00
Kangaxx-0
8564c5371f Add more overlay use usage (#6551) 2022-09-13 10:38:21 +03:00
Aron Nopanen
d08212409f Support Arrow IPC file format with dataframes (#6548)
* Add support for Arrow IPC file format

Add support for Arrow IPC file format to dataframes commands. Support
opening of Arrow IPC-format files with extension '.arrow' or '.ipc' in
the open-df command. Add a 'to arrow' command to write a dataframe to
Arrow IPC format.

* Add unit test for open-df on Arrow

* Add -t flag to open-df command

Add a `--type`/`-t` flag to the `open-df` command, to explicitly specify
the type of file being used. Allowed values are the same at the set of
allowed file extensions.
2022-09-12 18:30:20 -05:00
Justin Ma
4490e97a13 Bump dev version to v0.68.2 (#6538) 2022-09-12 08:29:39 +12:00
Dan Davison
2bb367f570 Revert "Try again: in unix like system, set foreground process while running external command (#6273)" (#6542)
This reverts commit 1d18f6947e.
2022-09-11 15:14:58 -05:00
Dan Davison
367f79cb4f Don't compute 'did you mean' suggestions unless showing them to user (#6540) 2022-09-11 09:58:19 -07:00
Dan Davison
4926865c4e str collect => str join (#6531)
* Initialize join.rs as a copy of collect.rs

* Evolve StrCollect into StrJoin

* Replace 'str collect' with 'str join' everywhere

git ls-files | lines | par-each { |it| sed -i 's,str collect,str join,g' $it }

* Deprecate 'str collect'

* Revert "Deprecate 'str collect'"

This reverts commit 959d14203e.

* Change `str collect` help message to say that it is deprecated

We cannot remove `str collect` currently (i.e. via
`nu_protocol::ShellError::DeprecatedCommand` since a prominent project
uses the API:

b85542c31c/src/virtualenv/activation/nushell/activate.nu (L43)
2022-09-11 11:48:27 +03:00
unrelentingtech
9ee4086dfa Add a 'commandline' command for manipulating the current buffer (#6492)
* Add a 'commandline' command for manipulating the current buffer

from `executehostcommand` keybindings. Inspired by fish:
https://fishshell.com/docs/current/cmds/commandline.html

* Update to development reedline

Includes nushell/reedline#472

Co-authored-by: sholderbach <sholderbach@users.noreply.github.com>
2022-09-09 15:31:32 -05:00
Benoît Cortier
3e0655cdba build: update cpufeatures crate (#6527)
Version registered in Cargo.lock was yanked, suggesting a significant
issue with the older version.
2022-09-09 13:10:04 +02:00
Jakub Žádník
e76b3d61de Require static path for source-env (#6526) 2022-09-08 23:41:49 +03:00
Stefan Holderbach
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
pwygab
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
pwygab
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
Kangaxx-0
02f92fa527 remove tests (#6515) 2022-09-07 20:19:29 -05:00
Darren Schroeder
773d167449 update regsiter-plugins script to not use encoding (#6512) 2022-09-07 11:54:28 -05:00
WindSoilder
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
nibon7
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
JayceFayne
2030e25ddc fix typo (#6508) 2022-09-07 16:16:55 +08:00
Justin Ma
247fff424d update to nu v0.68 for release workflow (#6505) 2022-09-07 15:36:42 +12:00
Justin Ma
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
Stefan Holderbach
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
adamijak
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
Stefan Holderbach
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
unrelentingtech
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
pwygab
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
WindSoilder
a6ba58ec41 restrict plugin file name (#6479) 2022-09-04 18:00:20 -05:00
Jakub Žádník
65327e0e7e Disable cyclical module imports (#6477) 2022-09-04 23:19:20 +03:00
Jakub Žádník
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
Jakub Žádník
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
WindSoilder
aa4778ff07 remove useless file (#6472) 2022-09-04 06:39:29 -05:00
pwygab
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
dependabot[bot]
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
pwygab
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
WindSoilder
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
Ondrej Baudys
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
unrelentingtech
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
Darren Schroeder
4858a9a817 Revert "Add support for optional list stream output formatting (#6325)" (#6454)
This reverts commit ec4e3a6d5c.
2022-08-31 18:09:40 -05:00
WindSoilder
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
Reilly Wood
11531b7630 Upgrade which dependency to fix case on Windows (#6453) 2022-08-31 09:50:18 -07:00
dependabot[bot]
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
Darren Schroeder
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
Reilly Wood
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
Scott Boggs
f1d72e2670 better error handling for nu_command::env::conig::utils::get_editor (#6430) 2022-08-28 12:56:55 +03:00
WindSoilder
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
WindSoilder
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
Jakub Žádník
34d7c17e78 Bring module's environment when activating overlay (#6425) 2022-08-27 01:32:19 +03:00
Darren Schroeder
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
WindSoilder
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
nibon7
9850424251 Make run_external parameter required (#6418) 2022-08-26 06:31:33 -05:00
Herlon Aguiar
918ec9daa8 nu-command/filters: drop column check positive value (#6412) 2022-08-25 19:03:18 +03:00
nibon7
7b502a4c7f register-plugin.nu: refactor register plugin (#6409) 2022-08-25 06:57:48 -05:00
nibon7
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
pwygab
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
Darren Schroeder
5ebfa10495 convert string duration to named duration (#6406) 2022-08-24 14:45:51 -05:00
Reilly Wood
3f93dc2f1d Always report errors in cp (#6404) 2022-08-24 10:39:28 -07:00
nibon7
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
Stefan Holderbach
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
Stefan Holderbach
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
Reilly Wood
ef26d539a7 Make cp errors more specific (#6396) 2022-08-23 21:32:41 -07:00
Darren Schroeder
fce8581321 add a plugin registration script (#6395) 2022-08-23 19:38:02 -05:00
Darren Schroeder
ba6abd77c9 add another split words example (#6394) 2022-08-23 13:27:06 -05:00
WindSoilder
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
Stefan Holderbach
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
Herlon Aguiar
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
Darren Schroeder
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
pwygab
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
Jakub Žádník
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
nibon7
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
Jakub Žádník
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
Herlon Aguiar
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
nibon7
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
WindSoilder
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
Reilly Wood
c5ca839294 Add pause and cls to cmd.exe exceptions (#6371) 2022-08-21 07:21:27 -07:00
Darren Schroeder
5337a6dffa add MessagePack as a plugin protocol (#6370) 2022-08-21 06:13:38 -05:00
pwygab
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
Darren Schroeder
37bc90c62a fix the way lists are rendered in markdown (#6369) 2022-08-20 21:04:30 -05:00
nibon7
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
Justin Ma
bbcf374886 Update nu version for release workflow (#6361) 2022-08-20 08:05:58 +08:00
Darren Schroeder
99c42582fe add a split words command (#6363)
* add a split words command

* changed regex
2022-08-20 05:55:54 +12:00
Jakub Žádník
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
nibon7
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
Yura Shatunov
2b955f82b7 Fix #6330 (#6332) 2022-08-18 10:53:46 -05:00
Darren Schroeder
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
panicbit
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
Daniel Buch Hansen
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
WindSoilder
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
Ariel Cohen
df3b6d9d26 Add --execute option (#6302) 2022-08-18 12:25:52 +03:00
Justin Ma
4bbdb73668 Bump dev version (#6350) 2022-08-18 21:14:17 +12:00
Antoine Stevan
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
Justin Ma
e614970c08 Use an older version of wingetcreate to do the msi package submission (#6347) 2022-08-18 00:11:19 +08:00
Justin Ma
d931331b57 Add a manual run workflow for winget submission (#6345) 2022-08-17 23:01:34 +08:00
Darren Schroeder
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
JT
33674d3a98 bump to 0.67 (#6336) 2022-08-17 05:47:47 +12:00
Kamil
0167649e6f Bug issue template (#6329)
* auto label bug issue

* nu config is obligatory

* bug report defaults to question

* feature request defaults to question

* applied suggestions
2022-08-15 09:32:38 -05:00
Stefan Holderbach
cc263ee15d Update to reedline 0.10.0 (#6327)
Release notes:

https://github.com/nushell/reedline/releases/tag/v0.10.0
2022-08-15 13:00:00 +02:00
Stefan Holderbach
a4809f2e68 Update reedline to improved undo-system (#6326)
* Update after Reedline API update

* Remove references to deleted `ReedlineEvent::ActionHandler`
* Update `DescriptionMenu` implementation for the new `Menu` trait
  API changes that work on `Editor` rather than `LineBuffer` objects

* Update reedline

Includes nushell/reedline#460

Co-authored-by: Ben Parks <bnprks+git@gmail.com>
2022-08-15 00:35:37 +02:00
WindSoilder
21770367e2 make date format supports locale (#6306)
* add --locale flag to make output support locale

* implement again based on nu-utils get_system_locale_string

* add comment
2022-08-14 08:07:04 -05:00
Richard Braakman
6145f734b7 Add repository info to all workspace crates (#6320)
This helps people who find these crates on crates.io
2022-08-14 07:21:20 -05:00
Fernando Herrera
9d8d305e9d lazy dataframe reader (#6321)
* lazy dataframe reader

* correct space for polars dependencies
2022-08-14 13:06:31 +01:00
Andrés N. Robalino
eb55fd2383 cmd(df/first): returns the first row by default. (#6312) 2022-08-13 14:08:00 -05:00
Reilly Wood
613d2fb8df Bump chrono dependency to fix panic (#6317) 2022-08-13 11:21:28 -07:00
Jakub Žádník
8783742060 Add 'as' keyword to 'overlay add' (#6314) 2022-08-13 17:28:18 +03:00
Jakub Žádník
20528e96c7 Add hide-env to hide environment variables (#6313)
* Add hide-env to hide env vars; Cleanup tests

Also, there were some old unalias tests that I converted to hide.

* Add missing file

* Re-enable hide for env vars

* Fix test

* Rename did you mean error back

It was causing random tests to break
2022-08-13 12:55:06 +03:00
Reilly Wood
3b6c4c1bb5 run_external: only suggest alternative commands when file not found (#6311) 2022-08-13 00:27:50 -04:00
Björn Richter
cb18dd5200 Add decimals to int when using into string --decimals (#6085)
* Add decimals to int when using `into string --decimals`

* Add tests for `into string` when converting int with `--decimals`

* Apply formatting

* Merge `into_str` test files

* Comment out unused code and add TODOs

* Use decimal separator depending on system locale

* Add test helper to run closure in different locale

* Add tests for int-to-string conversion using different locales

* Add utils function to get system locale

* Add panic message when locking mutex fails

* Catch and resume panic later to prevent Mutex poisoning when test fails

* Move test to `nu-test-support` to keep `nu-utils` free of `nu-*` dependencies

See https://github.com/nushell/nushell/pull/6085#issuecomment-1193131694

* Rename test support fn `with_fake_locale` to `with_locale_override`

* Move `get_system_locale()` to `locale` module

* Allow overriding locale with special env variable (when not in release)

* Use special env var to override locale during testing

* Allow callback to return a value in `with_locale_override()`

* Allow multiple options in `nu!` macro

* Allow to set locale as `nu!` macro option

* Use new `locale` option of `nu!` macro instead of `with_locale_override`

Using the `locale` options does not lock the `LOCALE_OVERRIDE_MUTEX`
mutex in `nu-test-support::locale_override` but instead calls the `nu`
command directly with the `NU_LOCALE_OVERRIDE` environment variable.
This allows for parallel test excecution.

* Fix: Add option identifier for `cwd` in usage of `nu!` macro

* Rely on `Display` trait for formatting `nu!` macro command

- Removed the `DisplayPath` trait
- Implement `Display` for `AbsolutePath`, `RelativePath` and
  `AbsoluteFile`

* Default to locale `en_US.UTF-8` for tests when using `nu!` macro

* Add doc comment to `nu!` macro

* Format code using `cargo fmt --all`

* Pass function directly instead of wrapping the call in a closure

https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure

* Pass function to `or_else()` instead of calling it inside `or()`

https://rust-lang.github.io/rust-clippy/master/index.html#or_fun_call

* Fix: Add option identifier for `cwd` in usage of `nu!` macro
2022-08-12 21:13:50 -05:00
Jakub Žádník
ccebdd7a7f Fix environment merging in hooks (#6309) 2022-08-13 01:13:28 +03:00
Jakub Žádník
c3efb12733 Allow overlays to import prefixed definitions (#6301)
* WIP

* Fix overlay prefix not preserving correctly

* Work around failing REPL tests

* Remove wrong code when removing with --keep-custom
2022-08-12 21:06:51 +03:00
Reilly Wood
d885258dc7 Clarify external command error (#6308) 2022-08-13 05:34:10 +12:00
Darren Schroeder
2da915d0c7 make ci use rust-toolchain.toml (#6305)
* make ci use rust-toolchain.toml

* update ci to use actions-rust-lang/setup-rust-toolchain@v1
2022-08-12 09:14:14 -05:00
Fernando Herrera
ae64c58f59 Polars upgrade 0.23 (#6303)
* more lazy expressions

* upgrade polars and correct functions

* arg-where example

* cargo clippy

* restore modified filter files

* correct string addition with str

* correct string addition with str

* correct message in test
2022-08-12 13:10:36 +01:00
WindSoilder
ff6868b329 not resolve symlink (#6304) 2022-08-12 06:17:31 -05:00
Darren Schroeder
47ef193600 add rust toolchain file to pin rust version (#6298)
* add rust toolchain file to pin rust version

* rust 1.63 release, bump toolchain

* linux clippy

* pin to 1.63

* pin to 1.61
2022-08-11 15:45:01 -05:00
Stefan Holderbach
c2f4969d4f Clippy fix for Rust 1.63 (#6299)
Take more sensitive lints into account

Somewhat ugly in some cases is the replacement of `.get(0)` with
`.first()`
2022-08-11 11:54:54 -05:00
Darren Schroeder
08c98967e0 update build scripts (#6296) 2022-08-11 09:40:35 -05:00
nibon7
8d091f6f83 Add custom log target to debugging tips (#6293)
Signed-off-by: nibon7 <nibon7@163.com>

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-10 23:10:27 -07:00
Darren Schroeder
58094987ff update a few nushell dependencies (#6291)
* update a few nushell dependencies

* update a test
2022-08-10 14:56:15 -05:00
Darren Schroeder
ce26ef97e4 Revert "Allow using the system's copy of zstd for Polars (#6232)" (#6292)
This reverts commit 9f131d998d.
2022-08-10 13:26:04 -05:00
Darren Schroeder
8f9bd4a299 remove sharkdp's lscolors repo again (#6290)
somehow this got reverted accidentally by someone
2022-08-10 11:13:27 -05:00
nibon7
45dd7d8770 Fix panic when building without git (#6289)
Signed-off-by: nibon7 <nibon7@163.com>

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-10 10:31:12 -05:00
WindSoilder
0f10d984c3 add -n for path expand, so it doesn't follow symlink (#6255)
* add -p for path expand, so it doesn't follow symlink

* fix arg name

* rename from no-dereferenct to no-follow-link

* rename from no-follow-link to no-symlink, and change short -p to -n

* follow strict first

* fix

* simplify test

* fix clippy

* fix test on windows
2022-08-10 08:43:56 -05:00
Darren Schroeder
2e5d981a09 add search terms to ignore command (#6288) 2022-08-10 08:42:21 -05:00
nibon7
c74254c2cb Fix color settings for logger (#6285)
Signed-off-by: nibon7 <nibon7@163.com>
2022-08-10 06:52:11 -05:00
nibon7
271fda7c91 Return error when moving a source directory to a target directory which contains a subdirectory with the same name as the source (#6284)
Fixes #6275

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-10 06:51:11 -05:00
Reilly Wood
0e5886ace1 Fix unused import warning on Linux+Mac (#6281) 2022-08-10 00:28:03 -04:00
Reilly Wood
4b89c5f900 ignore tests that fail on local machines (#6279) 2022-08-09 23:30:40 -04:00
Reilly Wood
dcab255d59 Support running batch files without typing their extension (#6278)
* Support running batch files without typing their extension

* suppress warning
2022-08-09 19:24:08 -04:00
nibon7
fc8512be39 Replace pretty_env_logger with simplelog (#6274)
Fixes #5963

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-09 11:44:37 -05:00
Darren Schroeder
e10ef4aaae bump lscolors to v12.0 (#6272) 2022-08-09 09:32:30 -05:00
Darren Schroeder
0b70ca8451 escape single quotes and remove ansi escape sequences (#6271)
* escape single quotes and remove ansi escape sequences prior to storing strings in db

* clippy
2022-08-09 07:58:36 -05:00
JT
555d9ee763 Fix intermittent test crash (#6268)
* Fix intermittent test crash

* fix windows build
2022-08-09 14:06:46 +12:00
JT
121b801baa bump dev version ahead of language changes (#6267) 2022-08-09 08:15:41 +12:00
Darren Schroeder
9adcecbbf1 new command into sqlite allows you to take lists and create a sqlite db (#6266) 2022-08-08 14:12:42 -05:00
Winter
9f131d998d Allow using the system's copy of zstd for Polars (#6232) 2022-08-08 10:58:40 -05:00
Reilly Wood
cd0a04f02a Delete most deprecated commands (#6260) 2022-08-08 07:46:59 -07:00
WindSoilder
aaf5684f9c when spawned process during register plugin, pass env to child process (#6261)
* when spawned process during register plugin, pass env to child process

* tweak comment

* tweak comment

* remove trailing whitespace

Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
2022-08-08 07:26:49 -05:00
nibon7
2f0cb044a5 Refactor shell listing related code (#6262)
* Refactor shell listing related code

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

* add test

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-08 06:31:24 -05:00
Darren Schroeder
8b55757a0b add more verbose error messages to mv (#6259)
* add more verbose error messages to mv

* tweak output

* clippy

* yet another tweak
2022-08-07 15:25:05 -05:00
Reilly Wood
84fae6e07e Suggest alternative when command not found (#6256)
* Suggest alternative when command not found

* Add tests for command-not-found suggestions

* Put suggestion in label

* Fix tests
2022-08-07 14:40:41 -04:00
nibon7
63e220a763 Refactor shell switching related code (#6258)
* Refactor shell switching related code

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

* add tests

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

* fix tests

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-07 13:30:40 -05:00
Reilly Wood
a96fc21f88 Windows: only shell out to cmd for specific commands (#6253) 2022-08-06 13:03:06 -07:00
nibon7
1ba5b25b29 Make g - switch to the last used shell (#6249)
* Make `g -` switch to the last used shell

Related #6223

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

* simplify error handling

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

* update NUSHELL_LAST_SHELL environment

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

* add test

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

* fix description

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-06 10:11:03 -05:00
pwygab
a871f2344a fix examples and let into decimal convert bools too (#6246) 2022-08-06 07:10:33 -05:00
Kangaxx-0
a217bc0715 Fix issue 6223 (#6241)
* Fix6223

* clippy fix

Co-authored-by: Frank <v-frankz@microsoft.com>
2022-08-06 07:09:14 -05:00
WindSoilder
34ab4d8360 fix python plugin example (#6242) 2022-08-05 23:57:31 -04:00
Justin Ma
48f1c3a49e add bits ror and bits rol commands (#6224) 2022-08-05 15:40:01 +02:00
nibon7
692376e830 export get_shells and get_current_shell (#6236)
Signed-off-by: nibon7 <nibon7@163.com>
2022-08-05 07:58:40 -05:00
Justin Ma
cc99df5ef1 upgrade chrono to v0.4.20 (#6235) 2022-08-05 06:53:01 -05:00
nibon7
d255a2a050 Fix color parsing (#6234)
Signed-off-by: nibon7 <nibon7@163.com>
2022-08-05 06:30:44 -05:00
Darren Schroeder
c07835f3ad point to the latest main branch for lscolors (#6230) 2022-08-04 17:53:40 -05:00
Darren Schroeder
78a5067434 remove the nana filename string, add some exclusions to gitignore (#6228) 2022-08-04 15:26:34 -05:00
Darren Schroeder
cdeb8de75d replace the regex crate with the fancy-regex crate (#6227) 2022-08-04 14:51:02 -05:00
Justin Ma
606547ecb4 Some code refactor for shells related commands (#6226) 2022-08-04 12:55:49 -05:00
pwygab
3b809b38e8 make cd, cp, ls, mv, open and rm automatically strip ansi codes (#6220)
* make `cd`, `cp`, `ls`, `mv`, `open` and `rm` automatically strip ansi escape code

* fix nu-cli test

* fix nu-cli test 2

* fix nu-cli test 3

* remove `include-ansi` arg

* fix test
2022-08-04 06:59:20 -05:00
nibon7
7c49a42b68 Fix path_contains_hidden_folder (#6173)
* Fix path_contains_hidden_folder

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

* add test

Signed-off-by: nibon7 <nibon7@163.com>
2022-08-03 20:59:57 -05:00
Stefan Holderbach
87823b0cb5 Reduce dev-deps by narrowing rstest features (#6215)
`rstest = 0.12` added support for asynchronous timeouts during testing
thus requiring a larger set of dependencies. Since `rstest = 0.14` this
can be disabled if not used.

Should keep build times for local or CI tests in check.
2022-08-03 11:55:58 +02:00
Kangaxx-0
ebf845f431 Change how to identify custom comamnd (#6187)
Co-authored-by: Frank <v-frankz@microsoft.com>
2022-08-02 18:40:07 -05:00
Justin Ma
ce6df93d05 Add bits shl and bits shr command (#6202)
* Add `bits shift-left` and `bits shift-right` command

* update bits shift error tips

* some code refactor

* update shift right

* some code refactor for bits shift commands

* rename bits shift commands align with bits operators

* update search term

* Update crates/nu-command/src/bits/not.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

* Update crates/nu-command/src/bits/shift_left.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

* Update crates/nu-command/src/bits/shift_right.rs

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>

* ci skip

* change default number-bytes for bits shift

* fix bits not tests

* fix bits tests

Co-authored-by: Stefan Holderbach <sholderbach@users.noreply.github.com>
2022-08-02 15:52:04 -05:00
Fernando Herrera
e7958bebac sqlite query without collect (#6217) 2022-08-02 21:29:02 +01:00
pwygab
233afebdf0 allow -h flags for export subcommands (#6189)
* allow `-h` flags for `export` subcommands

* remove unnecessary check

* add tests

* fmt
2022-08-02 10:26:16 -05:00
Stefan Holderbach
56069af42d Make open test independent of locale (#6211)
The test was reading the operating system error message which is
dependent on the system locale.

Just test for the `(os error 2)` errorcode instead.
This should support both
unixoid systems and Windows in more locales.
2022-08-02 16:54:26 +02:00
WindSoilder
376d22e331 In unix like system, set foreground process while running external command (#6206)
* while executing external command, make it as foreground

* remove useless file

* add comment, make var more readable

* add comment

* fmt code

* fix windows

* fix func name

* fix clippy

* fix windows clippy

* add comments, introduce `ForegroundProcess and ForegroundChild

* fix windows clippy

* fix on windows

* no need fg_process_setup module

* Revert "no need fg_process_setup module"

This reverts commit 21ee4ffbf6.

* restrict visibility for helper functions
2022-08-02 16:53:50 +02:00
Stefan Holderbach
7fc8ff60fd Patch lscolors to not blink (#6210)
Unreleased patch for the `lscolors` -> `crossterm` translation, where I
introduced a bug/incompatibility with several terminal emulators causing
blinking

https://github.com/nushell/nushell/pull/6172#issuecomment-1201856518

Refers to patch, can be replaced once a new `lscolors` version is
released:

https://github.com/sharkdp/lscolors/pull/51
2022-08-02 15:15:26 +02:00
Fernando Herrera
1f4791a191 use from table to remove into-db command (#6205)
* use from table to remove into-db command

* correct tests for db expressions
2022-08-01 21:27:55 +01:00
Reilly Wood
2ac7a4d48d performance improvements for SQLite reads (#6204) 2022-07-31 23:09:03 -07:00
pwygab
01386f4d58 adds a config reset command (#6149)
* moves config files to nu_utils

* fmt

* fix dockerfile

* fix docs
2022-07-31 20:44:33 -05:00
Reilly Wood
1086fbe9b5 Revert query command to query db (#6200) 2022-07-31 15:36:14 -04:00
Darren Schroeder
a83bd4ab20 allow uppercase chars to be captured during suppressed input (#6199) 2022-07-31 08:12:13 -05:00
nibon7
26caf7e1b2 Return error early if seconds part of timestamp is invalid (#6193)
Signed-off-by: nibon7 <nibon7@163.com>
2022-07-31 07:32:16 -05:00
Reilly Wood
dd2a0e35f4 Add $OLDPWD example for cd (#6194)
* add example for cd

* Fix format_conversions tests
2022-07-30 23:29:52 -04:00
Justin Ma
6a4eabf5c7 Add bits or and bits xor command (#6190) 2022-07-30 13:26:37 -05:00
Justin Ma
0e2c888f73 Add bits root command and bits and command (#6188) 2022-07-30 07:34:11 -05:00
Stefan Holderbach
c140da5740 Update crossterm to version 0.24 (#6172)
- Includes version bump for `lscolors = 0.11` and `reedline` as git
patch
2022-07-30 11:41:15 +02:00
WindSoilder
586c0ea3d8 Add bits not command (#6143)
Add `bits not`

Options: `--number-bytes` and `--sized`
2022-07-30 11:25:44 +02:00
Jakub Žádník
d6f4189c7b Fix file lookup in parser keywords; Refactor nu_repl (#6185)
* Fix file lookup in parser keywords

* Make nu_repl a testbin; Fix wrong cwd test error
2022-07-29 23:42:00 +03:00
Darren Schroeder
7a820b1304 add a new welcome banner to nushell (#6163)
* add a new welcome banner to nushell

* remove top line

* tweaked colors and wording

* changed to dimmed white

* removed a comment

* make config nu stand out a little

* fix type-o
2022-07-30 05:50:12 +12:00
Justin Ma
767201c40d bump to 0.66.3 dev version (#6183) 2022-07-30 05:48:10 +12:00
Darren Schroeder
3c3614a120 move application reset mode ansi sequence after cmdline execute (#6153) 2022-07-29 08:47:31 -05:00
nibon7
9e24e452a5 Fix touch panics when using invalid timestamp (#6181)
Signed-off-by: nibon7 <nibon7@163.com>
2022-07-29 06:41:28 -05:00
Jakub Žádník
2cffff0c1b Allow modules to use other modules (#6162)
* Allow private imports inside modules

Can call `use ...` inside modules now.

* Add more tests

* Add a leak test

* Refactor exportables; Prepare for 'export use'

* Fix description

* Implement 'export use' command

This allows re-exporting module's commands and aliases from another
module.

* Add more tests; Fix import pattern list strings

The import pattern strings didn't trim the surrounding quotes.

* Add ignored test
2022-07-29 11:57:10 +03:00
nibon7
cf2e9cf481 Prevent mv panic again (#6171)
Closes #6170

Signed-off-by: nibon7 <nibon7@163.com>
2022-07-29 10:00:52 +03:00
WindSoilder
6b2c7a4c86 update defaut_env (#6176) 2022-07-29 09:57:56 +03:00
JT
98e199f7b5 Move ls back to last-known-good state (#6175)
* revert the recent ls changes

* cargo fmt
2022-07-29 11:00:54 +12:00
JT
10e463180e Revert cp and mv back to last-known-good state (#6169) 2022-07-29 07:49:20 +12:00
Jakub Žádník
c9d0003818 Quickly patch wrong 'export' command name (#6168)
It was looked up as `alias` which explains issue #6167. Proper fix is still needed to enable the -h flag for `export` subcommands.
2022-07-28 21:06:50 +03:00
Fernando Herrera
e2a21afca8 maintain quotes for arguments (#6161) 2022-07-28 16:35:55 +01:00
nibon7
2ea209bcc0 Prevent mv panic (#6158)
Signed-off-by: nibon7 <nibon7@163.com>
2022-07-28 01:15:02 -04:00
JT
e049ca8ebf bump to 0.66.2 dev version (#6157) 2022-07-28 11:38:52 +12:00
Darren Schroeder
9037a9467b winget wants this to match (#6152)
See the link below for more information
https://github.com/microsoft/winget-pkgs/pull/67598#issuecomment-1196952191
2022-07-27 11:43:17 -05:00
nibon7
4c6cf36aa5 Fix ls panics when a file or directory not exists (#6148)
* Fix ls panics when a file or directory not exists

Fixes #6146

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

* add test

Signed-off-by: nibon7 <nibon7@163.com>
2022-07-27 18:53:00 +03:00
Jakub Žádník
c92211c016 Use relative paths as file-relative when parsing a file (#6150)
* Make function local (not used anywhere else)

* Use path relative to the parsed file

* Do not use real cwd at all
2022-07-27 18:36:56 +03:00
Darren Schroeder
8bd6b5b913 clean up some comments (#6147) 2022-07-27 07:44:05 -05:00
nibon7
b67fe31544 Make path::canonicalize::canonicalize_dot test case more reliable (#6141)
Fixes #6139

Signed-off-by: nibon7 <nibon7@163.com>
2022-07-27 11:30:03 +03:00
JT
c8adb06ca7 fix var names coming from long/short flags (#6142) 2022-07-27 19:27:28 +12:00
JT
9695331eed require variable names to follow additional restrictions (#6125) 2022-07-27 14:08:54 +12:00
596 changed files with 22967 additions and 17322 deletions

View File

@@ -53,7 +53,7 @@ body:
| features | clipboard-cli, ctrlc, dataframe, default, rustyline, term, trash, uuid, which, zip |
| installed_plugins | binaryview, chart bar, chart line, fetch, from bson, from sqlite, inc, match, post, ps, query json, s3, selector, start, sys, textview, to bson, to sqlite, tree, xpath |
validations:
required: false
required: true
- type: textarea
id: context
attributes:

View File

@@ -1,5 +1,6 @@
name: Feature Request
description: "When you want a new feature for something that doesn't already exist"
labels: "enhancement"
body:
- type: textarea
id: problem

21
.github/ISSUE_TEMPLATE/question.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: Question
description: "When you have a question to ask"
labels: "question"
body:
- type: textarea
id: problem
attributes:
label: Question
description: Leave your question here
placeholder: |
A clear and concise question
Example: Is there any equivalent of bash's $CDPATH in Nu?
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional context and details
description: Add any other context, screenshots or other media that will help us understand your question here, if needed.
validations:
required: false

View File

@@ -1,17 +1,26 @@
# Description
(description of your pull request here)
(Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.)
# Tests
# Major Changes
Make sure you've done the following:
If you're considering making any major change to nushell, before starting work on it, seek feedback from regular contributors and get approval for the idea from the core team either on [Discord](https://discordapp.com/invite/NtAbbGn) or [GitHub issue](https://github.com/nushell/nushell/issues/new/choose).
Making sure we're all on board with the change saves everybody's time.
Thanks!
- [ ] Add tests that cover your changes, either in the command examples, the crate/tests folder, or in the /tests folder.
- [ ] Try to think about corner cases and various ways how your changes could break. Cover them with tests.
- [ ] If adding tests is not possible, please document in the PR body a minimal example with steps on how to reproduce so one can verify your change works.
# Tests + Formatting
Make sure you've done the following, if applicable:
- Add tests that cover your changes (either in the command examples, the crate/tests folder, or in the /tests folder)
- Try to think about corner cases and various ways how your changes could break. Cover those in the tests
Make sure you've run and fixed any issues with these commands:
- [ ] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
- [ ] `cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
- [ ] `cargo test --workspace --features=extra` to check that all the tests pass
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
- `cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
- `cargo test --workspace --features=extra` to check that all tests pass
# After Submitting
* Help us keep the docs up to date: If your PR affects the user experience of Nushell (adding/removing a command, changing an input/output type, etc.), make sure the changes are reflected in the documentation (https://github.com/nushell/nushell.github.io) after the PR is merged.

View File

@@ -20,28 +20,19 @@ jobs:
NUSHELL_CARGO_TARGET: ci
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v1
with:
key: "v2" # increment this to bust the cache if needed
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
- name: Rustfmt
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: fmt
args: --all -- --check
- name: Clippy
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: clippy
args: --features=extra --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
@@ -71,23 +62,13 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
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
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
- name: Tests
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: test
args: --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
@@ -108,34 +89,26 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- uses: Swatinem/rust-cache@v1
with:
key: "2" # increment this to bust the cache if needed
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
- name: Install Nushell
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: install
args: --path=. --profile ci --no-default-features
- name: Setup Python
uses: actions/setup-python@v2
uses: actions/setup-python@v4
with:
python-version: "3.10"
- run: python -m pip install tox
- name: Install virtualenv
run: git clone https://github.com/pypa/virtualenv.git
run: git clone https://github.com/pypa/virtualenv.git
shell: bash
- name: Test Nushell in virtualenv
@@ -158,23 +131,19 @@ jobs:
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Setup Rust toolchain
uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: ${{ matrix.rust }}
override: true
- name: Setup Rust toolchain and cache
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
- name: Clippy
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: clippy
args: --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
- name: Tests
uses: actions-rs/cargo@v1
uses: actions-rs/cargo@v1.0.1
with:
command: test
args: --profile ci --package nu_plugin_*

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
@@ -76,11 +76,11 @@ cp -v README.release.txt $'($dist)/README.txt'
$'(char nl)Check binary release version detail:'; hr-line
let ver = if $os == 'windows-latest' {
(do -i { ./output/nu.exe -c 'version' }) | str collect
(do -i { ./output/nu.exe -c 'version' }) | str join
} else {
(do -i { ./output/nu -c 'version' }) | str collect
(do -i { ./output/nu -c 'version' }) | str join
}
if ($ver | str trim | empty?) {
if ($ver | str trim | is-empty) {
$'(ansi r)Incompatible nu binary...(ansi reset)'
} else { $ver }
@@ -90,10 +90,16 @@ if ($ver | str trim | empty?) {
cd $dist; $'(char nl)Creating release archive...'; hr-line
if $os in ['ubuntu-latest', 'macos-latest'] {
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
let files = (ls | get name)
let dest = $'($bin)-($version)-($target)'
let archive = $'($dist)/($dest).tar.gz'
let archive = $'($dist)/($bin)-($version)-($target).tar.gz'
tar czf $archive *
mkdir $dest
$files | each {|it| mv $it $dest } | ignore
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
tar -czf $archive $dest
print $'archive: ---> ($archive)'; ls $archive
echo $'::set-output name=archive::($archive)'
@@ -113,7 +119,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 +130,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 +149,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.69.1
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 }}

10
.gitignore vendored
View File

@@ -22,6 +22,10 @@ debian/nu/
# VSCode's IDE items
.vscode/*
# Visual Studio Extension SourceGear Rust items
VSWorkspaceSettings.json
unstable_cargo_features.txt
# Helix configuration folder
.helix/*
.helix
@@ -29,3 +33,9 @@ debian/nu/
# Coverage tools
lcov.info
tarpaulin-report.html
# Visual Studio
.vs/*
*.rsproj
*.rsproj.user
*.sln

View File

@@ -1,8 +1,23 @@
# Contributing
Welcome to Nushell!
Welcome to Nushell and thank you for considering contributing!
To get live support from the community see our [Discord](https://discordapp.com/invite/NtAbbGn), [Twitter](https://twitter.com/nu_shell) or file an issue or feature request here on [GitHub](https://github.com/nushell/nushell/issues/new/choose)!
## Review Process
First of all, before diving into the code, if you want to create a new feature, change something significantly, and especially if the change is user-facing, it is a good practice to first get an approval from the core team before starting to work on it.
This saves both your and our time if we realize the change needs to go another direction before spending time on it.
So, please, reach out and tell us what you want to do.
This will significantly increase the chance of your PR being accepted.
The review process can be summarized as follows:
1. You want to make some change to Nushell that is more involved than simple bug-fixing.
2. Go to [Discord](https://discordapp.com/invite/NtAbbGn) or a [GitHub issue](https://github.com/nushell/nushell/issues/new/choose) and chat with some core team members and/or other contributors about it.
3. After getting a green light from the core team, implement the feature, open a pull request (PR) and write a concise but comprehensive description of the change.
4. If your PR includes any use-facing features (such as adding a flag to a command), clearly list them in the PR description.
5. Then, core team members and other regular contributors will review the PR and suggest changes.
6. When we all agree, the PR will be merged.
7. If your PR includes any user-facing features, make sure the changes are also reflected in [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged.
8. Congratulate yourself, you just improved Nushell! :-)
## Developing
@@ -66,3 +81,9 @@ cargo build
```shell
cargo run --release --features=extra -- --log-level trace
```
- 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
open $"($nu.temp-path)/nu-($nu.pid).log"
```

1406
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,10 +8,9 @@ exclude = ["images"]
homepage = "https://www.nushell.sh"
license = "MIT"
name = "nu"
readme = "README.md"
repository = "https://github.com/nushell/nushell"
rust-version = "1.60"
version = "0.66.2"
version = "0.71.0"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@@ -33,54 +32,61 @@ members = [
]
[dependencies]
chrono = { version = "0.4.19", features = ["serde"] }
crossterm = "0.23.0"
chrono = { version = "0.4.21", features = ["serde"] }
crossterm = "0.24.0"
ctrlc = "3.2.1"
log = "0.4"
miette = "5.1.0"
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
nu-ansi-term = "0.46.0"
nu-cli = { path="./crates/nu-cli", version = "0.66.2" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.66.2" }
nu-command = { path="./crates/nu-command", version = "0.66.3" }
nu-engine = { path="./crates/nu-engine", version = "0.66.2" }
nu-json = { path="./crates/nu-json", version = "0.66.2" }
nu-parser = { path="./crates/nu-parser", version = "0.66.2" }
nu-path = { path="./crates/nu-path", version = "0.66.2" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.66.2" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.66.2" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.66.2" }
nu-system = { path = "./crates/nu-system", version = "0.66.2" }
nu-table = { path = "./crates/nu-table", version = "0.66.2" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.66.2" }
nu-utils = { path = "./crates/nu-utils", version = "0.66.2" }
reedline = { version = "0.9.0", features = ["bashisms", "sqlite"]}
pretty_env_logger = "0.4.0"
nu-cli = { path="./crates/nu-cli", version = "0.71.0" }
nu-color-config = { path = "./crates/nu-color-config", version = "0.71.0" }
nu-command = { path="./crates/nu-command", version = "0.71.0" }
nu-engine = { path="./crates/nu-engine", version = "0.71.0" }
nu-json = { path="./crates/nu-json", version = "0.71.0" }
nu-parser = { path="./crates/nu-parser", version = "0.71.0" }
nu-path = { path="./crates/nu-path", version = "0.71.0" }
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.71.0" }
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.71.0" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.71.0" }
nu-system = { path = "./crates/nu-system", version = "0.71.0" }
nu-table = { path = "./crates/nu-table", version = "0.71.0" }
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.71.0" }
nu-utils = { path = "./crates/nu-utils", version = "0.71.0" }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
rayon = "1.5.1"
is_executable = "1.0.1"
simplelog = "0.12.0"
time = "0.3.12"
[target.'cfg(not(target_os = "windows"))'.dependencies]
# Our dependencies don't use OpenSSL on Windows
openssl = { version = "0.10.38", features = ["vendored"], optional = true }
signal-hook = { version = "0.3.14", default-features = false }
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
[target.'cfg(target_family = "unix")'.dependencies]
nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"]}
atty = "0.2"
[dev-dependencies]
nu-test-support = { path="./crates/nu-test-support", version = "0.66.2" }
nu-test-support = { path="./crates/nu-test-support", version = "0.71.0" }
tempfile = "3.2.0"
assert_cmd = "2.0.2"
pretty_assertions = "1.0.0"
serial_test = "0.8.0"
hamcrest2 = "0.3.0"
rstest = "0.15.0"
rstest = {version = "0.15.0", default-features = false}
itertools = "0.10.3"
[target.'cfg(windows)'.build-dependencies]
winres = "0.1"
[features]
plugin = ["nu-plugin", "nu-cli/plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"]
extra = ["default", "dataframe", "database"]
default = ["plugin", "which-support", "trash-support"]
stable = ["default"]
extra = ["default", "dataframe", "database"]
wasi = []
# Enable to statically link OpenSSL; otherwise the system version will be used. Not enabled by default because it takes a while to build
static-link-openssl = ["dep:openssl"]
@@ -120,3 +126,8 @@ debug = false
[[bin]]
name = "nu"
path = "src/main.rs"
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }

View File

@@ -71,7 +71,7 @@ Additionally, commands can output structured data (you can think of this as a th
Commands that work in the pipeline fit into one of three categories:
- Commands that produce a stream (e.g., `ls`)
- Commands that filter a stream (eg, `where type == "dir"`)
- Commands that filter a stream (e.g., `where type == "dir"`)
- Commands that consume the output of the pipeline (e.g., `table`)
Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right.

View File

@@ -1,3 +1,3 @@
To use Nu plugins, use the register command to tell Nu where to find the plugin. For example:
> register -e json ./nu_plugin_query
> register ./nu_plugin_query

View File

@@ -10,6 +10,7 @@ NU_PLUGINS=(
'nu_plugin_gstat'
'nu_plugin_inc'
'nu_plugin_query'
'nu_plugin_custom_values'
)
echo "Building nushell"

View File

@@ -24,9 +24,13 @@ cargo build
@echo.
@cd ..\..\crates\nu_plugin_query
echo Building nu_plugin_query.exe
cargo build
@echo.
@cd ..\..\crates\nu_plugin_custom_values
echo Building nu_plugin_custom_values.exe
cargo build
@echo.
@cd ..\..

View File

@@ -11,6 +11,7 @@ let plugins = [
nu_plugin_gstat,
nu_plugin_query,
nu_plugin_example,
nu_plugin_custom_values,
]
for plugin in $plugins {

View File

@@ -1,36 +1,39 @@
[package]
authors = ["The Nushell Project Developers"]
description = "CLI-related functionality for Nushell"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
edition = "2021"
license = "MIT"
name = "nu-cli"
version = "0.66.2"
version = "0.71.0"
[dev-dependencies]
nu-test-support = { path="../nu-test-support", version = "0.66.2" }
nu-command = { path = "../nu-command", version = "0.66.2" }
rstest = "0.15.0"
nu-test-support = { path="../nu-test-support", version = "0.71.0" }
nu-command = { path = "../nu-command", version = "0.71.0" }
rstest = {version = "0.15.0", default-features = false}
[dependencies]
nu-engine = { path = "../nu-engine", version = "0.66.2" }
nu-path = { path = "../nu-path", version = "0.66.2" }
nu-parser = { path = "../nu-parser", version = "0.66.2" }
nu-protocol = { path = "../nu-protocol", version = "0.66.2" }
nu-utils = { path = "../nu-utils", version = "0.66.2" }
nu-engine = { path = "../nu-engine", version = "0.71.0" }
nu-path = { path = "../nu-path", version = "0.71.0" }
nu-parser = { path = "../nu-parser", version = "0.71.0" }
nu-protocol = { path = "../nu-protocol", version = "0.71.0" }
nu-utils = { path = "../nu-utils", version = "0.71.0" }
nu-ansi-term = "0.46.0"
nu-color-config = { path = "../nu-color-config", version = "0.66.2" }
reedline = { version = "0.9.0", features = ["bashisms", "sqlite"]}
crossterm = "0.23.0"
miette = { version = "5.1.0", features = ["fancy"] }
thiserror = "1.0.31"
fuzzy-matcher = "0.3.7"
nu-color-config = { path = "../nu-color-config", version = "0.71.0" }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
chrono = "0.4.19"
atty = "0.2.14"
chrono = "0.4.21"
crossterm = "0.24.0"
fancy-regex = "0.10.0"
fuzzy-matcher = "0.3.7"
is_executable = "1.0.1"
lazy_static = "1.4.0"
log = "0.4"
regex = "1.5.4"
sysinfo = "0.24.1"
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
percent-encoding = "2"
sysinfo = "0.26.2"
thiserror = "1.0.31"
[features]
plugin = []

View File

@@ -15,7 +15,6 @@ pub fn evaluate_commands(
engine_state: &mut EngineState,
stack: &mut Stack,
input: PipelineData,
is_perf_true: bool,
table_mode: Option<Value>,
) -> Result<Option<i64>> {
// Translate environment variables from Strings to Values
@@ -68,9 +67,7 @@ pub fn evaluate_commands(
}
};
if is_perf_true {
info!("evaluate {}:{}:{}", file!(), line!(), column!());
}
info!("evaluate {}:{}:{}", file!(), line!(), column!());
Ok(exit_code)
}

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,
}
}
@@ -57,7 +60,7 @@ impl CommandCompletion {
.matches_str(&x.to_string_lossy(), prefix)),
Some(true)
)
&& is_executable::is_executable(&item.path())
&& is_executable::is_executable(item.path())
{
if let Ok(name) = item.file_name().into_string() {
executables.push(name);
@@ -116,7 +119,8 @@ impl CommandCompletion {
let partial = working_set.get_span_contents(span);
let partial = String::from_utf8_lossy(partial).to_string();
let results = if find_externals {
if find_externals {
let results_external = self
.external_command_completion(&partial, match_algorithm)
.into_iter()
@@ -148,9 +152,7 @@ impl CommandCompletion {
results
} else {
results
};
results
}
}
}
@@ -207,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,21 +57,101 @@ impl NuCompleter {
suggestions
}
fn external_completion(
&self,
block_id: BlockId,
spans: &[String],
offset: usize,
span: Span,
) -> Option<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
.iter()
.map(|it| Value::String {
val: it.to_string(),
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 Some(result);
}
}
Err(err) => println!("failed to eval completer block: {}", err),
}
None
}
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();
let (mut new_line, alias_offset) = try_find_alias(line.as_bytes(), &working_set);
let initial_line = line.to_string();
new_line.push(b'a');
let alias_total_offset: usize = alias_offset.iter().sum();
new_line.insert(alias_total_offset + pos, 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 =
@@ -89,9 +170,10 @@ impl NuCompleter {
}
};
// Parses the prefix
// Parses the prefix. Completion should look up to the cursor position, not after.
let mut prefix = working_set.get_span_contents(flat.0).to_vec();
prefix.remove(pos - (flat.0.start - span_offset));
let index = pos - (flat.0.start - span_offset);
prefix.drain(index..);
// Variables completion
if prefix.starts_with(b"$") || most_left_var.is_some() {
@@ -113,8 +195,42 @@ 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 {
if let Some(external_result) =
self.external_completion(block_id, &spans, offset, new_span)
{
return external_result;
}
}
}
// 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 +241,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 +249,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 +328,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 +340,33 @@ 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;
// Try to complete using an external completer (if set)
if let Some(block_id) = config.external_completer {
if let Some(external_result) =
self.external_completion(block_id, &spans, offset, new_span)
{
return external_result;
}
}
// 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;
}
}
};
}
@@ -245,7 +374,7 @@ impl NuCompleter {
}
}
return vec![];
vec![]
}
}
@@ -302,7 +431,7 @@ fn search_alias(input: &[u8], working_set: &StateWorkingSet) -> Option<MatchedAl
}
// Push the rest to names vector.
if pos < input.len() {
vec_names.push((&input[pos..]).to_owned());
vec_names.push(input[pos..].to_owned());
}
for name in &vec_names {
@@ -383,3 +512,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

@@ -23,7 +23,6 @@ pub fn read_plugin_file(
stack: &mut Stack,
plugin_file: Option<Spanned<String>>,
storage_path: &str,
is_perf_true: bool,
) {
// Reading signatures from signature file
// The plugin.nu file stores the parsed signature collected from each registered plugin
@@ -31,7 +30,7 @@ pub fn read_plugin_file(
let plugin_path = engine_state.plugin_signatures.clone();
if let Some(plugin_path) = plugin_path {
let plugin_filename = plugin_path.to_string_lossy().to_owned();
let plugin_filename = plugin_path.to_string_lossy();
if let Ok(contents) = std::fs::read(&plugin_path) {
eval_source(
@@ -44,9 +43,7 @@ pub fn read_plugin_file(
}
}
if is_perf_true {
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
}
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
}
#[cfg(feature = "plugin")]
@@ -80,7 +77,7 @@ pub fn eval_config_contents(
stack: &mut Stack,
) {
if config_path.exists() & config_path.is_file() {
let config_filename = config_path.to_string_lossy().to_owned();
let config_filename = config_path.to_string_lossy();
if let Ok(contents) = std::fs::read(&config_path) {
eval_source(

View File

@@ -19,7 +19,6 @@ pub fn evaluate_file(
engine_state: &mut EngineState,
stack: &mut Stack,
input: PipelineData,
is_perf_true: bool,
) -> Result<()> {
// Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) {
@@ -54,9 +53,7 @@ pub fn evaluate_file(
std::process::exit(1);
}
if is_perf_true {
info!("evaluate {}:{}:{}", file!(), line!(), column!());
}
info!("evaluate {}:{}:{}", file!(), line!(), column!());
Ok(())
}

View File

@@ -1,8 +1,8 @@
use {
nu_ansi_term::{ansi::RESET, Style},
reedline::{
menu_functions::string_difference, Completer, LineBuffer, Menu, MenuEvent, MenuTextStyle,
Painter, Suggestion,
menu_functions::string_difference, Completer, Editor, Menu, MenuEvent, MenuTextStyle,
Painter, Suggestion, UndoBehavior,
},
};
@@ -372,7 +372,7 @@ impl DescriptionMenu {
let description = self
.get_value()
.and_then(|suggestion| suggestion.description)
.unwrap_or_else(|| "".to_string())
.unwrap_or_default()
.lines()
.skip(self.skipped_rows)
.take(self.working_details.description_rows)
@@ -459,7 +459,7 @@ impl Menu for DescriptionMenu {
fn can_partially_complete(
&mut self,
_values_updated: bool,
_line_buffer: &mut LineBuffer,
_editor: &mut Editor,
_completer: &mut dyn Completer,
) -> bool {
false
@@ -481,19 +481,21 @@ impl Menu for DescriptionMenu {
}
/// Updates menu values
fn update_values(&mut self, line_buffer: &mut LineBuffer, completer: &mut dyn Completer) {
fn update_values(&mut self, editor: &mut Editor, completer: &mut dyn Completer) {
if self.only_buffer_difference {
if let Some(old_string) = &self.input {
let (start, input) = string_difference(line_buffer.get_buffer(), old_string);
let (start, input) = string_difference(editor.get_buffer(), old_string);
if !input.is_empty() {
self.reset_position();
self.values = completer.complete(input, start);
}
}
} else {
let trimmed_buffer = line_buffer.get_buffer().replace('\n', " ");
self.values =
completer.complete(trimmed_buffer.as_str(), line_buffer.insertion_point());
let trimmed_buffer = editor.get_buffer().replace('\n', " ");
self.values = completer.complete(
trimmed_buffer.as_str(),
editor.line_buffer().insertion_point(),
);
self.reset_position();
}
}
@@ -502,7 +504,7 @@ impl Menu for DescriptionMenu {
/// collected from the completer
fn update_working_details(
&mut self,
line_buffer: &mut LineBuffer,
editor: &mut Editor,
completer: &mut dyn Completer,
painter: &Painter,
) {
@@ -560,13 +562,13 @@ impl Menu for DescriptionMenu {
match event {
MenuEvent::Activate(_) => {
self.reset_position();
self.input = Some(line_buffer.get_buffer().to_string());
self.update_values(line_buffer, completer);
self.input = Some(editor.get_buffer().to_string());
self.update_values(editor, completer);
}
MenuEvent::Deactivate => self.active = false,
MenuEvent::Edit(_) => {
self.reset_position();
self.update_values(line_buffer, completer);
self.update_values(editor, completer);
self.update_examples()
}
MenuEvent::NextElement => {
@@ -608,7 +610,7 @@ impl Menu for DescriptionMenu {
let description_rows = self
.get_value()
.and_then(|suggestion| suggestion.description)
.unwrap_or_else(|| "".to_string())
.unwrap_or_default()
.lines()
.count();
@@ -627,27 +629,28 @@ impl Menu for DescriptionMenu {
}
/// The buffer gets replaced in the Span location
fn replace_in_buffer(&self, line_buffer: &mut LineBuffer) {
fn replace_in_buffer(&self, editor: &mut Editor) {
if let Some(Suggestion { value, span, .. }) = self.get_value() {
let start = span.start.min(line_buffer.len());
let end = span.end.min(line_buffer.len());
let start = span.start.min(editor.line_buffer().len());
let end = span.end.min(editor.line_buffer().len());
let string_len = if let Some(example_index) = self.example_index {
let example = self
.examples
let replacement = if let Some(example_index) = self.example_index {
self.examples
.get(example_index)
.expect("the example index is always checked");
line_buffer.replace(start..end, example);
example.len()
.expect("the example index is always checked")
} else {
line_buffer.replace(start..end, &value);
value.len()
&value
};
let mut offset = line_buffer.insertion_point();
offset += string_len.saturating_sub(end.saturating_sub(start));
line_buffer.set_insertion_point(offset);
editor.edit_buffer(
|lb| {
lb.replace_range(start..end, replacement);
let mut offset = lb.insertion_point();
offset += lb.len().saturating_sub(end.saturating_sub(start));
lb.set_insertion_point(offset);
},
UndoBehavior::CreateUndoPoint,
);
}
}

View File

@@ -17,6 +17,7 @@ pub struct NushellPrompt {
default_vi_insert_prompt_indicator: Option<String>,
default_vi_normal_prompt_indicator: Option<String>,
default_multiline_indicator: Option<String>,
render_right_prompt_on_last_line: bool,
}
impl Default for NushellPrompt {
@@ -34,6 +35,7 @@ impl NushellPrompt {
default_vi_insert_prompt_indicator: None,
default_vi_normal_prompt_indicator: None,
default_multiline_indicator: None,
render_right_prompt_on_last_line: false,
}
}
@@ -41,8 +43,13 @@ impl NushellPrompt {
self.left_prompt_string = prompt_string;
}
pub fn update_prompt_right(&mut self, prompt_string: Option<String>) {
pub fn update_prompt_right(
&mut self,
prompt_string: Option<String>,
render_right_prompt_on_last_line: bool,
) {
self.right_prompt_string = prompt_string;
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
}
pub fn update_prompt_indicator(&mut self, prompt_indicator_string: Option<String>) {
@@ -68,6 +75,7 @@ impl NushellPrompt {
prompt_indicator_string: Option<String>,
prompt_multiline_indicator_string: Option<String>,
prompt_vi: (Option<String>, Option<String>),
render_right_prompt_on_last_line: bool,
) {
let (prompt_vi_insert_string, prompt_vi_normal_string) = prompt_vi;
@@ -78,6 +86,8 @@ impl NushellPrompt {
self.default_vi_insert_prompt_indicator = prompt_vi_insert_string;
self.default_vi_normal_prompt_indicator = prompt_vi_normal_string;
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
}
fn default_wrapped_custom_string(&self, str: String) -> String {
@@ -162,4 +172,8 @@ impl Prompt for NushellPrompt {
prefix, history_search.term
))
}
fn right_prompt_on_last_line(&self) -> bool {
self.render_right_prompt_on_last_line
}
}

View File

@@ -25,7 +25,6 @@ fn get_prompt_string(
config: &Config,
engine_state: &EngineState,
stack: &mut Stack,
is_perf_true: bool,
) -> Option<String> {
stack
.get_env_var(engine_state, prompt)
@@ -44,14 +43,12 @@ fn get_prompt_string(
block,
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
);
if is_perf_true {
info!(
"get_prompt_string (block) {}:{}:{}",
file!(),
line!(),
column!()
);
}
info!(
"get_prompt_string (block) {}:{}:{}",
file!(),
line!(),
column!()
);
match ret_val {
Ok(ret_val) => Some(ret_val),
@@ -90,17 +87,10 @@ pub(crate) fn update_prompt<'prompt>(
engine_state: &EngineState,
stack: &Stack,
nu_prompt: &'prompt mut NushellPrompt,
is_perf_true: bool,
) -> &'prompt dyn Prompt {
let mut stack = stack.clone();
let left_prompt_string = get_prompt_string(
PROMPT_COMMAND,
config,
engine_state,
&mut stack,
is_perf_true,
);
let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, &mut stack);
// Now that we have the prompt string lets ansify it.
// <133 A><prompt><133 B><command><133 C><command output>
@@ -116,45 +106,20 @@ pub(crate) fn update_prompt<'prompt>(
left_prompt_string
};
let right_prompt_string = get_prompt_string(
PROMPT_COMMAND_RIGHT,
config,
engine_state,
&mut stack,
is_perf_true,
);
let right_prompt_string =
get_prompt_string(PROMPT_COMMAND_RIGHT, config, engine_state, &mut stack);
let prompt_indicator_string = get_prompt_string(
PROMPT_INDICATOR,
config,
engine_state,
&mut stack,
is_perf_true,
);
let prompt_indicator_string =
get_prompt_string(PROMPT_INDICATOR, config, engine_state, &mut stack);
let prompt_multiline_string = get_prompt_string(
PROMPT_MULTILINE_INDICATOR,
config,
engine_state,
&mut stack,
is_perf_true,
);
let prompt_multiline_string =
get_prompt_string(PROMPT_MULTILINE_INDICATOR, config, engine_state, &mut stack);
let prompt_vi_insert_string = get_prompt_string(
PROMPT_INDICATOR_VI_INSERT,
config,
engine_state,
&mut stack,
is_perf_true,
);
let prompt_vi_insert_string =
get_prompt_string(PROMPT_INDICATOR_VI_INSERT, config, engine_state, &mut stack);
let prompt_vi_normal_string = get_prompt_string(
PROMPT_INDICATOR_VI_NORMAL,
config,
engine_state,
&mut stack,
is_perf_true,
);
let prompt_vi_normal_string =
get_prompt_string(PROMPT_INDICATOR_VI_NORMAL, config, engine_state, &mut stack);
// apply the other indicators
nu_prompt.update_all_prompt_strings(
@@ -163,12 +128,11 @@ pub(crate) fn update_prompt<'prompt>(
prompt_indicator_string,
prompt_multiline_string,
(prompt_vi_insert_string, prompt_vi_normal_string),
config.render_right_prompt_on_last_line,
);
let ret_val = nu_prompt as &dyn Prompt;
if is_perf_true {
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
}
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
ret_val
}

View File

@@ -114,7 +114,7 @@ pub(crate) fn add_menus(
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
if let PipelineData::Value(value, None) = res {
for menu in create_menus(&value, config)? {
for menu in create_menus(&value)? {
line_editor =
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?;
}
@@ -491,7 +491,7 @@ fn add_menu_keybindings(keybindings: &mut Keybindings) {
KeyCode::Tab,
ReedlineEvent::UntilFound(vec![
ReedlineEvent::Menu("completion_menu".to_string()),
ReedlineEvent::MenuNext,
ReedlineEvent::Edit(vec![EditCommand::Complete]),
]),
);
@@ -666,6 +666,7 @@ fn add_parsed_keybinding(
KeyCode::Char(char)
}
"space" => KeyCode::Char(' '),
"down" => KeyCode::Down,
"up" => KeyCode::Up,
"left" => KeyCode::Left,
@@ -814,7 +815,6 @@ fn event_from_record(
) -> Result<ReedlineEvent, ShellError> {
let event = match name {
"none" => ReedlineEvent::None,
"actionhandler" => ReedlineEvent::ActionHandler,
"clearscreen" => ReedlineEvent::ClearScreen,
"clearscrollback" => ReedlineEvent::ClearScrollback,
"historyhintcomplete" => ReedlineEvent::HistoryHintComplete,
@@ -822,6 +822,8 @@ fn event_from_record(
"ctrld" => ReedlineEvent::CtrlD,
"ctrlc" => ReedlineEvent::CtrlC,
"enter" => ReedlineEvent::Enter,
"submit" => ReedlineEvent::Submit,
"submitornewline" => ReedlineEvent::SubmitOrNewline,
"esc" | "escape" => ReedlineEvent::Esc,
"up" => ReedlineEvent::Up,
"down" => ReedlineEvent::Down,
@@ -962,6 +964,7 @@ fn edit_from_record(
let char = extract_char(value, config)?;
EditCommand::MoveLeftBefore(char)
}
"complete" => EditCommand::Complete,
e => {
return Err(ShellError::UnsupportedConfigValue(
"reedline EditCommand".to_string(),

View File

@@ -5,21 +5,25 @@ use crate::{
util::{eval_source, get_guaranteed_cwd, report_error, report_error_new},
NuHighlighter, NuValidator, NushellPrompt,
};
use fancy_regex::Regex;
use lazy_static::lazy_static;
use log::{info, trace, warn};
use miette::{IntoDiagnostic, Result};
use nu_color_config::get_color_config;
use nu_engine::{convert_env_values, eval_block};
use nu_parser::{lex, parse};
use nu_parser::{lex, parse, trim_quotes_str};
use nu_protocol::{
ast::PathMember,
engine::{EngineState, Stack, StateWorkingSet},
BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId,
engine::{EngineState, ReplOperation, Stack, StateWorkingSet},
format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span,
Spanned, Type, Value, VarId,
};
use reedline::{DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi};
use std::{
io::{self, Write},
sync::atomic::Ordering,
time::Instant,
};
use reedline::{DefaultHinter, Emacs, SqliteBackedHistory, Vi};
use regex::Regex;
use std::io::{self, Write};
use std::{sync::atomic::Ordering, time::Instant};
use sysinfo::SystemExt;
// According to Daniel Imms @Tyriar, we need to do these this way:
@@ -36,22 +40,30 @@ pub fn evaluate_repl(
engine_state: &mut EngineState,
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 a 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();
if is_perf_true {
info!(
"translate environment vars {}:{}:{}",
file!(),
line!(),
column!()
);
}
info!(
"translate environment vars {}:{}:{}",
file!(),
line!(),
column!()
);
// Translate environment variables from Strings to Values
if let Some(e) = convert_env_values(engine_state, stack) {
@@ -76,31 +88,32 @@ pub fn evaluate_repl(
},
);
if is_perf_true {
info!(
"load config initially {}:{}:{}",
file!(),
line!(),
column!()
);
}
info!(
"load config initially {}:{}:{}",
file!(),
line!(),
column!()
);
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
let mut line_editor = Reedline::create();
// Now that reedline is created, get the history session id and store it in engine_state
let hist_sesh = match line_editor.get_history_session_id() {
Some(id) => i64::from(id),
None => 0,
};
engine_state.history_session_id = hist_sesh;
// Get the config once for the history `max_history_size`
// Updating that will not be possible in one session
let config = engine_state.get_config();
if is_perf_true {
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
}
let mut line_editor = Reedline::create();
let history_path = crate::config_files::get_history_path(
nushell_path,
engine_state.config.history_file_format,
);
if let Some(history_path) = history_path.as_deref() {
if is_perf_true {
info!("setup history {}:{}:{}", file!(), line!(), column!());
}
info!("setup history {}:{}:{}", file!(), line!(), column!());
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
HistoryFileFormat::PlainText => Box::new(
@@ -119,15 +132,35 @@ pub fn evaluate_repl(
let sys = sysinfo::System::new();
loop {
if is_perf_true {
info!(
"load config each loop {}:{}:{}",
file!(),
line!(),
column!()
);
let show_banner = config.show_banner;
let use_ansi = config.use_ansi_coloring;
if show_banner {
let banner = get_banner(engine_state, stack);
if use_ansi {
println!("{}", banner);
} else {
println!("{}", nu_utils::strip_ansi_string_likely(banner));
}
}
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 {
info!(
"load config each loop {}:{}:{}",
file!(),
line!(),
column!()
);
let cwd = get_guaranteed_cwd(engine_state, stack);
@@ -148,15 +181,11 @@ pub fn evaluate_repl(
let config = engine_state.get_config();
if is_perf_true {
info!("setup colors {}:{}:{}", file!(), line!(), column!());
}
info!("setup colors {}:{}:{}", file!(), line!(), column!());
let color_hm = get_color_config(config);
if is_perf_true {
info!("update reedline {}:{}:{}", file!(), line!(), column!());
}
info!("update reedline {}:{}:{}", file!(), line!(), column!());
let engine_reference = std::sync::Arc::new(engine_state.clone());
line_editor = line_editor
.with_highlighter(Box::new(NuHighlighter {
@@ -213,18 +242,14 @@ pub fn evaluate_repl(
};
if config.sync_history_on_enter {
if is_perf_true {
info!("sync history {}:{}:{}", file!(), line!(), column!());
}
info!("sync history {}:{}:{}", file!(), line!(), column!());
if let Err(e) = line_editor.sync_history() {
warn!("Failed to sync history: {}", e);
}
}
if is_perf_true {
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
}
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
// Changing the line editor based on the found keybindings
line_editor = match create_keybindings(config) {
@@ -248,14 +273,12 @@ pub fn evaluate_repl(
}
};
if is_perf_true {
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
}
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
// Right before we start our prompt and take input from the user,
// fire the "pre_prompt" hook
if let Some(hook) = config.hooks.pre_prompt.clone() {
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
report_error_new(engine_state, &err);
}
}
@@ -270,32 +293,31 @@ pub fn evaluate_repl(
}
let config = engine_state.get_config();
let prompt =
prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt, is_perf_true);
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
entry_num += 1;
if is_perf_true {
info!(
"finished setup, starting repl {}:{}:{}",
file!(),
line!(),
column!()
);
}
info!(
"finished setup, starting repl {}:{}:{}",
file!(),
line!(),
column!()
);
let input = line_editor.read_line(prompt);
let shell_integration = config.shell_integration;
match input {
Ok(Signal::Success(s)) => {
let hostname = sys.host_name();
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());
c.hostname = sys.host_name();
c.hostname = hostname.clone();
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
c
@@ -303,42 +325,35 @@ pub fn evaluate_repl(
.into_diagnostic()?; // todo: don't stop repl if error here?
}
engine_state
.repl_buffer_state
.lock()
.expect("repl buffer state mutex")
.replace(line_editor.current_buffer_contents().to_string());
// Right before we start running the code the user gave us,
// fire the "pre_execution" hook
if let Some(hook) = config.hooks.pre_execution.clone() {
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
report_error_new(engine_state, &err);
}
}
if shell_integration {
run_ansi_sequence(RESET_APPLICATION_MODE)?;
run_ansi_sequence(PRE_EXECUTE_MARKER)?;
// if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
// let path = cwd.as_string()?;
// // Try to abbreviate string for windows title
// let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
// path.replace(&p.as_path().display().to_string(), "~")
// } else {
// path
// };
// // Set window title too
// // https://tldp.org/HOWTO/Xterm-Title-3.html
// // ESC]0;stringBEL -- Set icon name and window title to string
// // ESC]1;stringBEL -- Set icon name to string
// // ESC]2;stringBEL -- Set window title to string
// run_ansi_sequence(&format!("\x1b]2;{}\x07", maybe_abbrev_path))?;
// }
}
let start_time = Instant::now();
let tokens = lex(s.as_bytes(), 0, &[], &[], false);
// Check if this is a single call to a directory, if so auto-cd
let cwd = nu_engine::env::current_dir_str(engine_state, stack)?;
let path = nu_path::expand_path_with(&s, &cwd);
let orig = s.clone();
let mut orig = s.clone();
if orig.starts_with('`') {
orig = trim_quotes_str(&orig).to_string()
}
let path = nu_path::expand_path_with(&orig, &cwd);
if looks_like_path(&orig) && path.is_dir() && tokens.0.len() == 1 {
// We have an auto-cd
@@ -391,10 +406,24 @@ pub fn evaluate_repl(
0
};
let last_shell = stack.get_env_var(engine_state, "NUSHELL_LAST_SHELL");
let last_shell = if let Some(v) = last_shell {
v.as_integer().unwrap_or_default() as usize
} else {
0
};
shells[current_shell] = Value::String { val: path, span };
stack.add_env_var("NUSHELL_SHELLS".into(), Value::List { vals: shells, span });
} else {
stack.add_env_var(
"NUSHELL_LAST_SHELL".into(),
Value::Int {
val: last_shell as i64,
span,
},
);
} else if !s.trim().is_empty() {
trace!("eval source: {}", s);
eval_source(
@@ -415,7 +444,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);
@@ -431,6 +461,21 @@ pub fn evaluate_repl(
run_ansi_sequence(&get_command_finished_marker(stack, engine_state))?;
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
let path = cwd.as_string()?;
// Communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
run_ansi_sequence(&format!(
"\x1b]7;file://{}{}{}\x1b\\",
percent_encoding::utf8_percent_encode(
&hostname.unwrap_or_else(|| "localhost".to_string()),
percent_encoding::CONTROLS
),
if path.starts_with('/') { "" } else { "/" },
percent_encoding::utf8_percent_encode(
&path,
percent_encoding::CONTROLS
)
))?;
// Try to abbreviate string for windows title
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
path.replace(&p.as_path().display().to_string(), "~")
@@ -445,6 +490,25 @@ pub fn evaluate_repl(
// ESC]2;stringBEL -- Set window title to string
run_ansi_sequence(&format!("\x1b]2;{}\x07", maybe_abbrev_path))?;
}
run_ansi_sequence(RESET_APPLICATION_MODE)?;
}
let mut ops = engine_state
.repl_operation_queue
.lock()
.expect("repl op queue mutex");
while let Some(op) = ops.pop_front() {
match op {
ReplOperation::Append(s) => line_editor.run_edit_commands(&[
EditCommand::MoveToEnd,
EditCommand::InsertString(s),
]),
ReplOperation::Insert(s) => {
line_editor.run_edit_commands(&[EditCommand::InsertString(s)])
}
ReplOperation::Replace(s) => line_editor
.run_edit_commands(&[EditCommand::Clear, EditCommand::InsertString(s)]),
}
}
}
Ok(Signal::CtrlC) => {
@@ -465,6 +529,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))?;
@@ -476,6 +544,113 @@ pub fn evaluate_repl(
Ok(())
}
fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String {
let age = match eval_string_with_input(
engine_state,
stack,
None,
"(date now) - ('2019-05-10 09:59:12-0700' | into datetime)",
) {
Ok(Value::Duration { val, .. }) => format_duration(val),
_ => "".to_string(),
};
let banner = format!(
r#"{} __ ,
{} .--()°'.' {}Welcome to {}Nushell{},
{}'|, . ,' {}based on the {}nu{} language,
{} !_-(_\ {}where all data is structured!
Please join our {}Discord{} community at {}https://discord.gg/NtAbbGn{}
Our {}GitHub{} repository is at {}https://github.com/nushell/nushell{}
Our {}Documentation{} is located at {}http://nushell.sh{}
{}Tweet{} us at {}@nu_shell{}
It's been this long since {}Nushell{}'s first commit:
{}
{}You can disable this banner using the {}config nu{}{} command
to modify the config.nu file and setting show_banner to false.
let-env config = {{
show_banner: false
...
}}{}
"#,
"\x1b[32m", //start line 1 green
"\x1b[32m", //start line 2
"\x1b[0m", //before welcome
"\x1b[32m", //before nushell
"\x1b[0m", //after nushell
"\x1b[32m", //start line 3
"\x1b[0m", //before based
"\x1b[32m", //before nu
"\x1b[0m", //after nu
"\x1b[32m", //start line 4
"\x1b[0m", //before where
"\x1b[35m", //before Discord purple
"\x1b[0m", //after Discord
"\x1b[35m", //before Discord URL
"\x1b[0m", //after Discord URL
"\x1b[1;32m", //before GitHub green_bold
"\x1b[0m", //after GitHub
"\x1b[1;32m", //before GitHub URL
"\x1b[0m", //after GitHub URL
"\x1b[32m", //before Documentation
"\x1b[0m", //after Documentation
"\x1b[32m", //before Documentation URL
"\x1b[0m", //after Documentation URL
"\x1b[36m", //before Tweet blue
"\x1b[0m", //after Tweet
"\x1b[1;36m", //before @nu_shell cyan_bold
"\x1b[0m", //after @nu_shell
"\x1b[32m", //before Nushell
"\x1b[0m", //after Nushell
age,
"\x1b[2;37m", //before banner disable dim white
"\x1b[2;36m", //before config nu dim cyan
"\x1b[0m", //after config nu
"\x1b[2;37m", //after config nu dim white
"\x1b[0m", //after banner disable
);
banner
}
// Taken from Nana's simple_eval
/// Evaluate a block of Nu code, optionally with input.
/// For example, source="$in * 2" will multiply the value in input by 2.
pub fn eval_string_with_input(
engine_state: &mut EngineState,
stack: &mut Stack,
input: Option<Value>,
source: &str,
) -> Result<Value, ShellError> {
let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);
let (output, _) = parse(&mut working_set, None, source.as_bytes(), false, &[]);
(output, working_set.render())
};
engine_state.merge_delta(delta)?;
let input_as_pipeline_data = match input {
Some(input) => PipelineData::Value(input, None),
None => PipelineData::new(Span::test_data()),
};
eval_block(
engine_state,
stack,
&block,
input_as_pipeline_data,
false,
true,
)
.map(|x| x.into_value(Span::test_data()))
}
pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
let exit_code = stack
.get_env_var(engine_state, "LAST_EXIT_CODE")
@@ -511,6 +686,7 @@ pub fn eval_env_change_hook(
eval_hook(
engine_state,
stack,
None,
vec![("$before".into(), before), ("$after".into(), after.clone())],
hook_value,
)?;
@@ -536,15 +712,17 @@ pub fn eval_env_change_hook(
pub fn eval_hook(
engine_state: &mut EngineState,
stack: &mut Stack,
input: Option<PipelineData>,
arguments: Vec<(String, Value)>,
value: &Value,
) -> Result<(), ShellError> {
) -> Result<PipelineData, ShellError> {
let value_span = value.span()?;
let condition_path = PathMember::String {
val: "condition".to_string(),
span: value_span,
};
let mut output = PipelineData::new(Span::new(0, 0));
let code_path = PathMember::String {
val: "code".to_string(),
@@ -554,7 +732,7 @@ pub fn eval_hook(
match value {
Value::List { vals, .. } => {
for val in vals {
eval_hook(engine_state, stack, arguments.clone(), val)?
eval_hook(engine_state, stack, None, arguments.clone(), val)?;
}
}
Value::Record { .. } => {
@@ -570,6 +748,7 @@ pub fn eval_hook(
engine_state,
stack,
block_id,
None,
arguments.clone(),
block_span,
) {
@@ -649,7 +828,9 @@ pub fn eval_hook(
.collect();
match eval_block(engine_state, stack, &block, input, false, false) {
Ok(_) => {}
Ok(pipeline_data) => {
output = pipeline_data;
}
Err(err) => {
report_error_new(engine_state, &err);
}
@@ -658,18 +839,20 @@ pub fn eval_hook(
for var_id in var_ids.iter() {
stack.vars.remove(var_id);
}
let cwd = get_guaranteed_cwd(engine_state, stack);
engine_state.merge_env(stack, cwd)?;
}
Value::Block {
val: block_id,
span: block_span,
..
} => {
run_hook_block(engine_state, stack, block_id, arguments, block_span)?;
let cwd = get_guaranteed_cwd(engine_state, stack);
engine_state.merge_env(stack, cwd)?;
run_hook_block(
engine_state,
stack,
block_id,
input,
arguments,
block_span,
)?;
}
other => {
return Err(ShellError::UnsupportedConfigValue(
@@ -686,7 +869,17 @@ pub fn eval_hook(
span: block_span,
..
} => {
run_hook_block(engine_state, stack, *block_id, arguments, *block_span)?;
output = PipelineData::Value(
run_hook_block(
engine_state,
stack,
*block_id,
input,
arguments,
*block_span,
)?,
None,
);
}
other => {
return Err(ShellError::UnsupportedConfigValue(
@@ -697,19 +890,23 @@ pub fn eval_hook(
}
}
Ok(())
let cwd = get_guaranteed_cwd(engine_state, stack);
engine_state.merge_env(stack, cwd)?;
Ok(output)
}
pub fn run_hook_block(
engine_state: &EngineState,
stack: &mut Stack,
block_id: BlockId,
optional_input: Option<PipelineData>,
arguments: Vec<(String, Value)>,
span: Span,
) -> Result<Value, ShellError> {
let block = engine_state.get_block(block_id);
let input = PipelineData::new(span);
let input = optional_input.unwrap_or_else(|| PipelineData::new(span));
let mut callee_stack = stack.gather_captures(&block.captures);
@@ -789,7 +986,7 @@ lazy_static! {
fn looks_like_path(orig: &str) -> bool {
#[cfg(windows)]
{
if DRIVE_PATH_REGEX.is_match(orig) {
if DRIVE_PATH_REGEX.is_match(orig).unwrap_or(false) {
return true;
}
}

View File

@@ -1,9 +1,10 @@
use log::trace;
use nu_ansi_term::Style;
use nu_color_config::get_shape_color;
use nu_color_config::{get_matching_brackets_style, get_shape_color};
use nu_parser::{flatten_block, parse, FlatShape};
use nu_protocol::ast::{Argument, Block, Expr, Expression};
use nu_protocol::engine::{EngineState, StateWorkingSet};
use nu_protocol::Config;
use nu_protocol::{Config, Span};
use reedline::{Highlighter, StyledText};
pub struct NuHighlighter {
@@ -15,10 +16,12 @@ impl Highlighter for NuHighlighter {
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
trace!("highlighting: {}", line);
let (shapes, global_span_offset) = {
let mut working_set = StateWorkingSet::new(&self.engine_state);
let mut working_set = StateWorkingSet::new(&self.engine_state);
let block = {
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
block
};
let (shapes, global_span_offset) = {
let shapes = flatten_block(&working_set, &block);
(shapes, self.engine_state.next_span_start())
};
@@ -26,6 +29,15 @@ impl Highlighter for NuHighlighter {
let mut output = StyledText::default();
let mut last_seen_span = global_span_offset;
let global_cursor_offset = _cursor + global_span_offset;
let matching_brackets_pos = find_matching_brackets(
line,
&working_set,
&block,
global_span_offset,
global_cursor_offset,
);
for shape in &shapes {
if shape.0.end <= last_seen_span
|| last_seen_span < global_span_offset
@@ -44,166 +56,71 @@ impl Highlighter for NuHighlighter {
let next_token = line
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
.to_string();
macro_rules! add_colored_token_with_bracket_highlight {
($shape:expr, $span:expr, $text:expr) => {{
let spans = split_span_by_highlight_positions(
line,
&$span,
&matching_brackets_pos,
global_span_offset,
);
spans.iter().for_each(|(part, highlight)| {
let start = part.start - $span.start;
let end = part.end - $span.start;
let text = (&next_token[start..end]).to_string();
let mut style = get_shape_color($shape.to_string(), &self.config);
if *highlight {
style = get_matching_brackets_style(style, &self.config);
}
output.push((style, text));
});
}};
}
macro_rules! add_colored_token {
($shape:expr, $text:expr) => {
output.push((get_shape_color($shape.to_string(), &self.config), $text))
};
}
match shape.1 {
FlatShape::Garbage => output.push((
// nushell Garbage
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Nothing => output.push((
// nushell Nothing
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Binary => {
// nushell ?
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Bool => {
// nushell ?
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Int => {
// nushell Int
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Float => {
// nushell Decimal
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Range => output.push((
// nushell DotDot ?
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::InternalCall => output.push((
// nushell InternalCommand
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::External => {
// nushell ExternalCommand
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::ExternalArg => {
// nushell ExternalWord
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Literal => {
// nushell ?
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Operator => output.push((
// nushell Operator
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Signature => output.push((
// nushell ?
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::String => {
// nushell String
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::StringInterpolation => {
// nushell ???
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::DateTime => {
// nushell ???
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Garbage => add_colored_token!(shape.1, next_token),
FlatShape::Nothing => add_colored_token!(shape.1, next_token),
FlatShape::Binary => add_colored_token!(shape.1, next_token),
FlatShape::Bool => add_colored_token!(shape.1, next_token),
FlatShape::Int => add_colored_token!(shape.1, next_token),
FlatShape::Float => add_colored_token!(shape.1, next_token),
FlatShape::Range => add_colored_token!(shape.1, next_token),
FlatShape::InternalCall => add_colored_token!(shape.1, next_token),
FlatShape::External => add_colored_token!(shape.1, next_token),
FlatShape::ExternalArg => add_colored_token!(shape.1, next_token),
FlatShape::Literal => add_colored_token!(shape.1, next_token),
FlatShape::Operator => add_colored_token!(shape.1, next_token),
FlatShape::Signature => add_colored_token!(shape.1, next_token),
FlatShape::String => add_colored_token!(shape.1, next_token),
FlatShape::StringInterpolation => add_colored_token!(shape.1, next_token),
FlatShape::DateTime => add_colored_token!(shape.1, next_token),
FlatShape::List => {
// nushell ???
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Table => {
// nushell ???
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Record => {
// nushell ???
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Block => {
// nushell ???
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Filepath => output.push((
// nushell Path
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Directory => output.push((
// nushell Directory
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::GlobPattern => output.push((
// nushell GlobPattern
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Variable => output.push((
// nushell Variable
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Flag => {
// nushell Flag
output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
))
}
FlatShape::Custom(..) => output.push((
get_shape_color(shape.1.to_string(), &self.config),
next_token,
)),
FlatShape::Filepath => add_colored_token!(shape.1, next_token),
FlatShape::Directory => add_colored_token!(shape.1, next_token),
FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
FlatShape::Variable => add_colored_token!(shape.1, next_token),
FlatShape::Flag => add_colored_token!(shape.1, next_token),
FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
}
last_seen_span = shape.0.end;
}
@@ -216,3 +133,297 @@ impl Highlighter for NuHighlighter {
output
}
}
fn split_span_by_highlight_positions(
line: &str,
span: &Span,
highlight_positions: &Vec<usize>,
global_span_offset: usize,
) -> Vec<(Span, bool)> {
let mut start = span.start;
let mut result: Vec<(Span, bool)> = Vec::new();
for pos in highlight_positions {
if start <= *pos && pos < &span.end {
if start < *pos {
result.push((Span { start, end: *pos }, false));
}
let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
let end = span_str
.chars()
.next()
.map(|c| pos + get_char_length(c))
.unwrap_or(pos + 1);
result.push((Span { start: *pos, end }, true));
start = end;
}
}
if start < span.end {
result.push((
Span {
start,
end: span.end,
},
false,
));
}
result
}
fn find_matching_brackets(
line: &str,
working_set: &StateWorkingSet,
block: &Block,
global_span_offset: usize,
global_cursor_offset: usize,
) -> Vec<usize> {
const BRACKETS: &str = "{}[]()";
// calculate first bracket position
let global_end_offset = line.len() + global_span_offset;
let global_bracket_pos =
if global_cursor_offset == global_end_offset && global_end_offset > global_span_offset {
// cursor is at the end of a non-empty string -- find block end at the previous position
if let Some(last_char) = line.chars().last() {
global_cursor_offset - get_char_length(last_char)
} else {
global_cursor_offset
}
} else {
// cursor is in the middle of a string -- find block end at the current position
global_cursor_offset
};
// check that position contains bracket
let match_idx = global_bracket_pos - global_span_offset;
if match_idx >= line.len()
|| !BRACKETS.contains(get_char_at_index(line, match_idx).unwrap_or_default())
{
return Vec::new();
}
// find matching bracket by finding matching block end
let matching_block_end = find_matching_block_end_in_block(
line,
working_set,
block,
global_span_offset,
global_bracket_pos,
);
if let Some(pos) = matching_block_end {
let matching_idx = pos - global_span_offset;
if BRACKETS.contains(get_char_at_index(line, matching_idx).unwrap_or_default()) {
return if global_bracket_pos < pos {
vec![global_bracket_pos, pos]
} else {
vec![pos, global_bracket_pos]
};
}
}
Vec::new()
}
fn find_matching_block_end_in_block(
line: &str,
working_set: &StateWorkingSet,
block: &Block,
global_span_offset: usize,
global_cursor_offset: usize,
) -> Option<usize> {
for p in &block.pipelines {
for e in &p.expressions {
if e.span.contains(global_cursor_offset) {
if let Some(pos) = find_matching_block_end_in_expr(
line,
working_set,
e,
global_span_offset,
global_cursor_offset,
) {
return Some(pos);
}
}
}
}
None
}
fn find_matching_block_end_in_expr(
line: &str,
working_set: &StateWorkingSet,
expression: &Expression,
global_span_offset: usize,
global_cursor_offset: usize,
) -> Option<usize> {
macro_rules! find_in_expr_or_continue {
($inner_expr:ident) => {
if let Some(pos) = find_matching_block_end_in_expr(
line,
working_set,
$inner_expr,
global_span_offset,
global_cursor_offset,
) {
return Some(pos);
}
};
}
if expression.span.contains(global_cursor_offset) && expression.span.start >= global_span_offset
{
let expr_first = expression.span.start;
let span_str = &line
[expression.span.start - global_span_offset..expression.span.end - global_span_offset];
let expr_last = span_str
.chars()
.last()
.map(|c| expression.span.end - get_char_length(c))
.unwrap_or(expression.span.start);
return match &expression.expr {
Expr::Bool(_) => None,
Expr::Int(_) => None,
Expr::Float(_) => None,
Expr::Binary(_) => None,
Expr::Range(..) => None,
Expr::Var(_) => None,
Expr::VarDecl(_) => None,
Expr::ExternalCall(..) => None,
Expr::Operator(_) => None,
Expr::UnaryNot(_) => None,
Expr::Keyword(..) => None,
Expr::ValueWithUnit(..) => None,
Expr::DateTime(_) => None,
Expr::Filepath(_) => None,
Expr::Directory(_) => None,
Expr::GlobPattern(_) => None,
Expr::String(_) => None,
Expr::CellPath(_) => None,
Expr::ImportPattern(_) => None,
Expr::Overlay(_) => None,
Expr::Signature(_) => None,
Expr::Nothing => None,
Expr::Garbage => None,
Expr::Table(hdr, rows) => {
if expr_last == global_cursor_offset {
// cursor is at table end
Some(expr_first)
} else if expr_first == global_cursor_offset {
// cursor is at table start
Some(expr_last)
} else {
// cursor is inside table
for inner_expr in hdr {
find_in_expr_or_continue!(inner_expr);
}
for row in rows {
for inner_expr in row {
find_in_expr_or_continue!(inner_expr);
}
}
None
}
}
Expr::Record(exprs) => {
if expr_last == global_cursor_offset {
// cursor is at record end
Some(expr_first)
} else if expr_first == global_cursor_offset {
// cursor is at record start
Some(expr_last)
} else {
// cursor is inside record
for (k, v) in exprs {
find_in_expr_or_continue!(k);
find_in_expr_or_continue!(v);
}
None
}
}
Expr::Call(call) => {
for arg in &call.arguments {
let opt_expr = match arg {
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
Argument::Positional(inner_expr) => Some(inner_expr),
};
if let Some(inner_expr) = opt_expr {
find_in_expr_or_continue!(inner_expr);
}
}
None
}
Expr::FullCellPath(b) => find_matching_block_end_in_expr(
line,
working_set,
&b.head,
global_span_offset,
global_cursor_offset,
),
Expr::BinaryOp(lhs, op, rhs) => {
find_in_expr_or_continue!(lhs);
find_in_expr_or_continue!(op);
find_in_expr_or_continue!(rhs);
None
}
Expr::Block(block_id)
| Expr::RowCondition(block_id)
| Expr::Subexpression(block_id) => {
if expr_last == global_cursor_offset {
// cursor is at block end
Some(expr_first)
} else if expr_first == global_cursor_offset {
// cursor is at block start
Some(expr_last)
} else {
// cursor is inside block
let nested_block = working_set.get_block(*block_id);
find_matching_block_end_in_block(
line,
working_set,
nested_block,
global_span_offset,
global_cursor_offset,
)
}
}
Expr::StringInterpolation(inner_expr) => {
for inner_expr in inner_expr {
find_in_expr_or_continue!(inner_expr);
}
None
}
Expr::List(inner_expr) => {
if expr_last == global_cursor_offset {
// cursor is at list end
Some(expr_first)
} else if expr_first == global_cursor_offset {
// cursor is at list start
Some(expr_last)
} else {
// cursor is inside list
for inner_expr in inner_expr {
find_in_expr_or_continue!(inner_expr);
}
None
}
}
};
}
None
}
fn get_char_at_index(s: &str, index: usize) -> Option<char> {
s[index..].chars().next()
}
fn get_char_length(c: char) -> usize {
c.to_string().len()
}

View File

@@ -1,11 +1,11 @@
use log::trace;
use crate::repl::eval_hook;
use nu_engine::eval_block;
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
use nu_protocol::engine::StateWorkingSet;
use nu_protocol::CliError;
use nu_protocol::{
engine::{EngineState, Stack},
PipelineData, ShellError, Span, Value,
print_if_stream, PipelineData, ShellError, Span, Value,
};
#[cfg(windows)]
use nu_utils::enable_vt_processing;
@@ -204,8 +204,6 @@ pub fn eval_source(
fname: &str,
input: PipelineData,
) -> bool {
trace!("eval_source");
let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state);
let (output, err) = parse(
@@ -231,23 +229,41 @@ pub fn eval_source(
}
match eval_block(engine_state, stack, &block, input, false, false) {
Ok(mut pipeline_data) => {
if let PipelineData::ExternalStream { exit_code, .. } = &mut pipeline_data {
if let Some(exit_code) = exit_code.take().and_then(|it| it.last()) {
stack.add_env_var("LAST_EXIT_CODE".to_string(), exit_code);
} else {
set_last_exit_code(stack, 0);
Ok(pipeline_data) => {
let config = engine_state.get_config();
let result;
if let PipelineData::ExternalStream {
stdout: stream,
stderr: stderr_stream,
exit_code,
..
} = pipeline_data
{
result = print_if_stream(stream, stderr_stream, false, exit_code);
} else if let Some(hook) = config.hooks.display_output.clone() {
match eval_hook(engine_state, stack, Some(pipeline_data), vec![], &hook) {
Err(err) => {
result = Err(err);
}
Ok(val) => {
result = val.print(engine_state, stack, false, false);
}
}
} else {
set_last_exit_code(stack, 0);
result = pipeline_data.print(engine_state, stack, false, false);
}
if let Err(err) = pipeline_data.print(engine_state, stack, false, false) {
let working_set = StateWorkingSet::new(engine_state);
match result {
Err(err) => {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &err);
report_error(&working_set, &err);
return false;
return false;
}
Ok(exit_code) => {
set_last_exit_code(stack, exit_code);
}
}
// reset vt processing, aka ansi because illbehaved externals can break it

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,734 @@
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)
}
#[test]
fn variables_dollar_sign_with_varialblecompletion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let target_dir = "$ ";
let suggestions = completer.complete(target_dir, target_dir.len());
assert_eq!(7, suggestions.len());
}
#[rstest]
fn variables_double_dash_argument_with_flagcompletion(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_single_dash_argument_with_flagcompletion(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_command_with_commandcompletion(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-c ", 4);
let expected: Vec<String> = vec!["my-command".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn variables_subcommands_with_customcompletion(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_customcompletion_subcommands_with_customcompletion_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_with_filecompletion() {
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_with_filecompletion() {
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_with_globcompletion() {
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_with_globcompletion() {
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_with_filecompletion() {
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_with_filecompletion() {
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_with_filecompletion() {
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_with_directorycompletions() {
// 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)
}
#[rstest]
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -h", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command c", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
let suggestions = completer_strings.complete("my-command c | ls", 11);
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
match_suggestions(expected, suggestions);
}
#[rstest]
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
let suggestions = completer.complete("tst -h | ls", 5);
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
match_suggestions(expected, suggestions);
}
#[test]
fn filecompletions_triggers_after_cursor() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
let suggestions = completer.complete("cp test_c", 3);
#[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

@@ -104,9 +104,7 @@ pub fn merge_input(
(block, working_set.render())
};
if let Err(err) = engine_state.merge_delta(delta) {
return Err(err);
}
engine_state.merge_delta(delta)?;
assert!(eval_block(
engine_state,

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

@@ -1,14 +1,15 @@
[package]
authors = ["The Nushell Project Developers"]
description = "Color configuration code used by Nushell"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-config"
edition = "2021"
license = "MIT"
name = "nu-color-config"
version = "0.66.2"
version = "0.71.0"
[dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.66.2" }
nu-protocol = { path = "../nu-protocol", version = "0.71.0" }
nu-ansi-term = "0.46.0"
nu-json = { path = "../nu-json", version = "0.66.2" }
nu-table = { path = "../nu-table", version = "0.66.2" }
nu-json = { path = "../nu-json", version = "0.71.0" }
nu-table = { path = "../nu-table", version = "0.71.0" }
serde = { version="1.0.123", features=["derive"] }

View File

@@ -1,7 +1,9 @@
mod color_config;
mod matching_brackets_style;
mod nu_style;
mod shape_color;
pub use color_config::*;
pub use matching_brackets_style::*;
pub use nu_style::*;
pub use shape_color::*;

View File

@@ -0,0 +1,30 @@
use crate::color_config::lookup_ansi_color_style;
use nu_ansi_term::Style;
use nu_protocol::Config;
pub fn get_matching_brackets_style(default_style: Style, conf: &Config) -> Style {
const MATCHING_BRACKETS_CONFIG_KEY: &str = "shape_matching_brackets";
match conf.color_config.get(MATCHING_BRACKETS_CONFIG_KEY) {
Some(int_color) => match int_color.as_string() {
Ok(int_color) => merge_styles(default_style, lookup_ansi_color_style(&int_color)),
Err(_) => default_style,
},
None => default_style,
}
}
fn merge_styles(base: Style, extra: Style) -> Style {
Style {
foreground: extra.foreground.or(base.foreground),
background: extra.background.or(base.background),
is_bold: extra.is_bold || base.is_bold,
is_dimmed: extra.is_dimmed || base.is_dimmed,
is_italic: extra.is_italic || base.is_italic,
is_underline: extra.is_underline || base.is_underline,
is_blink: extra.is_blink || base.is_blink,
is_reverse: extra.is_reverse || base.is_reverse,
is_hidden: extra.is_hidden || base.is_hidden,
is_strikethrough: extra.is_strikethrough || base.is_strikethrough,
}
}

View File

@@ -11,12 +11,12 @@ pub struct NuStyle {
pub fn parse_nustyle(nu_style: NuStyle) -> Style {
// get the nu_ansi_term::Color foreground color
let fg_color = match nu_style.fg {
Some(fg) => color_from_hex(&fg).expect("error with foreground color"),
Some(fg) => color_from_hex(&fg).unwrap_or_default(),
_ => None,
};
// get the nu_ansi_term::Color background color
let bg_color = match nu_style.bg {
Some(bg) => color_from_hex(&bg).expect("error with background color"),
Some(bg) => color_from_hex(&bg).unwrap_or_default(),
_ => None,
};
// get the attributes

View File

@@ -1,29 +1,30 @@
[package]
authors = ["The Nushell Project Developers"]
description = "Nushell's built-in commands"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
edition = "2021"
license = "MIT"
name = "nu-command"
version = "0.66.3"
version = "0.71.0"
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.66.2" }
nu-engine = { path = "../nu-engine", version = "0.66.2" }
nu-glob = { path = "../nu-glob", version = "0.66.2" }
nu-json = { path = "../nu-json", version = "0.66.2" }
nu-parser = { path = "../nu-parser", version = "0.66.2" }
nu-path = { path = "../nu-path", version = "0.66.2" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.66.2" }
nu-protocol = { path = "../nu-protocol", version = "0.66.2" }
nu-system = { path = "../nu-system", version = "0.66.2" }
nu-table = { path = "../nu-table", version = "0.66.2" }
nu-term-grid = { path = "../nu-term-grid", version = "0.66.2" }
nu-test-support = { path = "../nu-test-support", version = "0.66.2" }
nu-utils = { path = "../nu-utils", version = "0.66.2" }
nu-color-config = { path = "../nu-color-config", version = "0.71.0" }
nu-engine = { path = "../nu-engine", version = "0.71.0" }
nu-glob = { path = "../nu-glob", version = "0.71.0" }
nu-json = { path = "../nu-json", version = "0.71.0" }
nu-parser = { path = "../nu-parser", version = "0.71.0" }
nu-path = { path = "../nu-path", version = "0.71.0" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.71.0" }
nu-protocol = { path = "../nu-protocol", version = "0.71.0" }
nu-system = { path = "../nu-system", version = "0.71.0" }
nu-table = { path = "../nu-table", version = "0.71.0" }
nu-term-grid = { path = "../nu-term-grid", version = "0.71.0" }
nu-utils = { path = "../nu-utils", version = "0.71.0" }
nu-ansi-term = "0.46.0"
num-format = { version = "0.4.3" }
# Potential dependencies for extras
alphanumeric-sort = "1.4.4"
@@ -31,16 +32,17 @@ base64 = "0.13.0"
byteorder = "1.4.3"
bytesize = "1.1.0"
calamine = "0.18.0"
chrono = { version = "0.4.19", features = ["serde"] }
chrono = { version = "0.4.21", features = ["serde", "unstable-locales"] }
chrono-humanize = "0.2.1"
chrono-tz = "0.6.1"
crossterm = "0.23.0"
chrono-tz = "0.6.3"
crossterm = "0.24.0"
csv = "1.1.6"
dialoguer = "0.9.0"
digest = "0.10.0"
dtparse = "1.2.0"
eml-parser = "0.1.0"
encoding_rs = "0.8.30"
fancy-regex = "0.10.0"
filesize = "0.2.0"
filetime = "0.2.15"
fs_extra = "1.2.0"
@@ -52,30 +54,30 @@ is-root = "0.1.2"
itertools = "0.10.0"
lazy_static = "1.4.0"
log = "0.4.14"
lscolors = { version = "0.10.0", features = ["crossterm"]}
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
md5 = { package = "md-5", version = "0.10.0" }
meval = "0.2.0"
mime = "0.3.16"
notify = "4.0.17"
num = { version = "0.4.0", optional = true }
num-traits = "0.2.14"
pathdiff = "0.2.1"
powierza-coefficient = "1.0.1"
quick-xml = "0.23.0"
rand = "0.8"
rayon = "1.5.1"
regex = "1.5.4"
reqwest = {version = "0.11", features = ["blocking", "json"] }
roxmltree = "0.14.0"
rust-embed = "6.3.0"
same-file = "1.0.6"
serde = { version="1.0.123", features=["derive"] }
serde_ini = "0.2.0"
serde_urlencoded = "0.7.0"
serde_yaml = "0.8.16"
serde_yaml = "0.9.4"
sha2 = "0.10.0"
# Disable default features b/c the default features build Git (very slow to compile)
shadow-rs = { version = "0.16.1", default-features = false }
strip-ansi-escapes = "0.1.1"
sysinfo = "0.24.6"
sysinfo = "0.26.2"
terminal_size = "0.2.1"
thiserror = "1.0.31"
titlecase = "2.0.0"
@@ -83,36 +85,58 @@ 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.9.0", features = ["bashisms", "sqlite"]}
wax = { version = "0.5.0", features = ["diagnostics"] }
which = { version = "4.3.0", optional = true }
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
wax = { version = "0.5.0" }
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
sqlparser = { version = "0.16.0", features = ["serde"], optional = true }
sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
[target.'cfg(windows)'.dependencies]
winreg = "0.10.1"
[target.'cfg(unix)'.dependencies]
umask = "2.0.0"
users = "0.11.0"
libc = "0.2"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
version = "2.1.3"
optional = true
[dependencies.polars]
version = "0.22.8"
# path = "../../../../polars/polars"
version = "0.23.2"
optional = true
features = [
"default", "to_dummies", "parquet", "json", "serde", "serde-lazy",
"object", "checked_arithmetic", "strings", "cum_agg", "is_in",
"rolling_window", "strings", "rows", "random",
"dtype-datetime", "dtype-struct", "lazy", "cross_join",
"dynamic_groupby", "dtype-categorical", "concat_str"
"arg_where",
"checked_arithmetic",
"concat_str",
"cross_join",
"csv-file",
"cum_agg",
"default",
"dtype-datetime",
"dtype-struct",
"dtype-categorical",
"dynamic_groupby",
"ipc",
"is_in",
"json",
"lazy",
"object",
"parquet",
"random",
"rolling_window",
"rows",
"serde",
"serde-lazy",
"strings",
"strings",
"to_dummies",
]
[target.'cfg(windows)'.dependencies.windows]
version = "0.37.0"
version = "0.42.0"
features = [
"alloc",
"Win32_Foundation",
"Win32_Storage_FileSystem",
"Win32_System_SystemServices",
@@ -129,8 +153,11 @@ database = ["sqlparser", "rusqlite"]
shadow-rs = { version = "0.16.1", default-features = false }
[dev-dependencies]
nu-test-support = { path = "../nu-test-support", version = "0.71.0" }
hamcrest2 = "0.3.0"
dirs-next = "2.0.0"
proptest = "1.0.0"
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
rstest = "0.15.0"
rstest = {version = "0.15.0", default-features = false}

View File

@@ -3,16 +3,18 @@ use std::process::Command;
fn main() -> shadow_rs::SdResult<()> {
// Look up the current Git commit ourselves instead of relying on shadow_rs,
// because shadow_rs does it in a really slow-to-compile way (it builds libgit2)
let hash = get_git_hash().expect("failed to get latest git commit hash");
let hash = get_git_hash().unwrap_or_default();
println!("cargo:rustc-env=NU_COMMIT_HASH={}", hash);
shadow_rs::new()
}
fn get_git_hash() -> Result<String, std::io::Error> {
let out = Command::new("git").args(["rev-parse", "HEAD"]).output()?;
Ok(String::from_utf8(out.stdout)
.expect("could not convert stdout to string")
.trim()
.to_string())
fn get_git_hash() -> Option<String> {
Command::new("git")
.args(["rev-parse", "HEAD"])
.output()
.ok()
.filter(|output| output.status.success())
.and_then(|output| String::from_utf8(output.stdout).ok())
.map(|hash| hash.trim().to_string())
}

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

@@ -0,0 +1,100 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"bits and"
}
fn signature(&self) -> Signature {
Signature::build("bits and")
.required(
"target",
SyntaxShape::Int,
"target integer to perform bit and",
)
.category(Category::Bits)
}
fn usage(&self) -> &str {
"Performs bitwise and for integers"
}
fn search_terms(&self) -> Vec<&str> {
vec!["logic and"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let target: i64 = call.req(engine_state, stack, 0)?;
input.map(
move |value| operate(value, target, head),
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Apply bits and to two numbers",
example: "2 | bits and 2",
result: Some(Value::Int {
val: 2,
span: Span::test_data(),
}),
},
Example {
description: "Apply logical and to a list of numbers",
example: "[4 3 2] | bits and 2",
result: Some(Value::List {
vals: vec![Value::test_int(0), Value::test_int(2), Value::test_int(2)],
span: Span::test_data(),
}),
},
]
}
}
fn operate(value: Value, target: i64, head: Span) -> Value {
match value {
Value::Int { val, span } => Value::Int {
val: val & target,
span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
),
},
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@@ -0,0 +1,49 @@
use nu_engine::get_full_help;
use nu_protocol::{
ast::Call,
engine::{Command, EngineState, Stack},
Category, IntoPipelineData, PipelineData, Signature, Value,
};
#[derive(Clone)]
pub struct Bits;
impl Command for Bits {
fn name(&self) -> &str {
"bits"
}
fn signature(&self) -> Signature {
Signature::build("bits").category(Category::Bits)
}
fn usage(&self) -> &str {
"Various commands for working with bits"
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
Ok(Value::String {
val: get_full_help(&Bits.signature(), &Bits.examples(), engine_state, stack),
span: call.head,
}
.into_pipeline_data())
}
}
#[cfg(test)]
mod test {
use crate::Bits;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(Bits {})
}
}

View File

@@ -0,0 +1,99 @@
mod and;
mod bits_;
mod not;
mod or;
mod rotate_left;
mod rotate_right;
mod shift_left;
mod shift_right;
mod xor;
use nu_protocol::Spanned;
pub use and::SubCommand as BitsAnd;
pub use bits_::Bits;
pub use not::SubCommand as BitsNot;
pub use or::SubCommand as BitsOr;
pub use rotate_left::SubCommand as BitsRotateLeft;
pub use rotate_right::SubCommand as BitsRotateRight;
pub use shift_left::SubCommand as BitsShiftLeft;
pub use shift_right::SubCommand as BitsShiftRight;
pub use xor::SubCommand as BitsXor;
#[derive(Clone, Copy)]
enum NumberBytes {
One,
Two,
Four,
Eight,
Auto,
Invalid,
}
#[derive(Clone, Copy)]
enum InputNumType {
One,
Two,
Four,
Eight,
SignedOne,
SignedTwo,
SignedFour,
SignedEight,
}
fn get_number_bytes(number_bytes: &Option<Spanned<String>>) -> NumberBytes {
match number_bytes.as_ref() {
None => NumberBytes::Eight,
Some(size) => match size.item.as_str() {
"1" => NumberBytes::One,
"2" => NumberBytes::Two,
"4" => NumberBytes::Four,
"8" => NumberBytes::Eight,
"auto" => NumberBytes::Auto,
_ => NumberBytes::Invalid,
},
}
}
fn get_input_num_type(val: i64, signed: bool, number_size: NumberBytes) -> InputNumType {
if signed || val < 0 {
match number_size {
NumberBytes::One => InputNumType::SignedOne,
NumberBytes::Two => InputNumType::SignedTwo,
NumberBytes::Four => InputNumType::SignedFour,
NumberBytes::Eight => InputNumType::SignedEight,
NumberBytes::Auto => {
if val <= 0x7F && val >= -(2i64.pow(7)) {
InputNumType::SignedOne
} else if val <= 0x7FFF && val >= -(2i64.pow(15)) {
InputNumType::SignedTwo
} else if val <= 0x7FFFFFFF && val >= -(2i64.pow(31)) {
InputNumType::SignedFour
} else {
InputNumType::SignedEight
}
}
NumberBytes::Invalid => InputNumType::SignedFour,
}
} else {
match number_size {
NumberBytes::One => InputNumType::One,
NumberBytes::Two => InputNumType::Two,
NumberBytes::Four => InputNumType::Four,
NumberBytes::Eight => InputNumType::Eight,
NumberBytes::Auto => {
if val <= 0xFF {
InputNumType::One
} else if val <= 0xFFFF {
InputNumType::Two
} else if val <= 0xFFFFFFFF {
InputNumType::Four
} else {
InputNumType::Eight
}
}
NumberBytes::Invalid => InputNumType::Four,
}
}
}

View File

@@ -0,0 +1,163 @@
use super::{get_number_bytes, NumberBytes};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"bits not"
}
fn signature(&self) -> Signature {
Signature::build("bits not")
.switch(
"signed",
"always treat input number as a signed number",
Some('s'),
)
.named(
"number-bytes",
SyntaxShape::String,
"the size of unsigned number in bytes, it can be 1, 2, 4, 8, auto",
Some('n'),
)
.category(Category::Bits)
}
fn usage(&self) -> &str {
"Performs logical negation on each bit"
}
fn search_terms(&self) -> Vec<&str> {
vec!["negation"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let signed = call.has_flag("signed");
let number_bytes: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "number-bytes")?;
let bytes_len = get_number_bytes(&number_bytes);
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
val.span,
));
}
}
input.map(
move |value| operate(value, head, signed, bytes_len),
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Apply logical negation to a list of numbers",
example: "[4 3 2] | bits not",
result: Some(Value::List {
vals: vec![
Value::test_int(140737488355323),
Value::test_int(140737488355324),
Value::test_int(140737488355325),
],
span: Span::test_data(),
}),
},
Example {
description:
"Apply logical negation to a list of numbers, treat input as 2 bytes number",
example: "[4 3 2] | bits not -n 2",
result: Some(Value::List {
vals: vec![
Value::test_int(65531),
Value::test_int(65532),
Value::test_int(65533),
],
span: Span::test_data(),
}),
},
Example {
description:
"Apply logical negation to a list of numbers, treat input as signed number",
example: "[4 3 2] | bits not -s",
result: Some(Value::List {
vals: vec![
Value::test_int(-5),
Value::test_int(-4),
Value::test_int(-3),
],
span: Span::test_data(),
}),
},
]
}
}
fn operate(value: Value, head: Span, signed: bool, number_size: NumberBytes) -> Value {
match value {
Value::Int { val, span } => {
if signed || val < 0 {
Value::Int { val: !val, span }
} else {
use NumberBytes::*;
let out_val = match number_size {
One => !val & 0x00_00_00_00_00_FF,
Two => !val & 0x00_00_00_00_FF_FF,
Four => !val & 0x00_00_FF_FF_FF_FF,
Eight => !val & 0x7F_FF_FF_FF_FF_FF,
Auto => {
if val <= 0xFF {
!val & 0x00_00_00_00_00_FF
} else if val <= 0xFF_FF {
!val & 0x00_00_00_00_FF_FF
} else if val <= 0xFF_FF_FF_FF {
!val & 0x00_00_FF_FF_FF_FF
} else {
!val & 0x7F_FF_FF_FF_FF_FF
}
}
// This case shouldn't happen here, as it's handled before
Invalid => 0,
};
Value::Int { val: out_val, span }
}
}
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only numerical values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
),
},
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@@ -0,0 +1,100 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"bits or"
}
fn signature(&self) -> Signature {
Signature::build("bits or")
.required(
"target",
SyntaxShape::Int,
"target integer to perform bit or",
)
.category(Category::Bits)
}
fn usage(&self) -> &str {
"Performs bitwise or for integers"
}
fn search_terms(&self) -> Vec<&str> {
vec!["logic or"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let target: i64 = call.req(engine_state, stack, 0)?;
input.map(
move |value| operate(value, target, head),
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Apply bits or to two numbers",
example: "2 | bits or 6",
result: Some(Value::Int {
val: 6,
span: Span::test_data(),
}),
},
Example {
description: "Apply logical or to a list of numbers",
example: "[8 3 2] | bits or 2",
result: Some(Value::List {
vals: vec![Value::test_int(10), Value::test_int(3), Value::test_int(2)],
span: Span::test_data(),
}),
},
]
}
}
fn operate(value: Value, target: i64, head: Span) -> Value {
match value {
Value::Int { val, span } => Value::Int {
val: val | target,
span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
),
},
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@@ -0,0 +1,156 @@
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
};
use num_traits::int::PrimInt;
use std::fmt::Display;
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"bits rol"
}
fn signature(&self) -> Signature {
Signature::build("bits rol")
.required("bits", SyntaxShape::Int, "number of bits to rotate left")
.switch(
"signed",
"always treat input number as a signed number",
Some('s'),
)
.named(
"number-bytes",
SyntaxShape::String,
"the word size in number of bytes, it can be 1, 2, 4, 8, auto, default value `8`",
Some('n'),
)
.category(Category::Bits)
}
fn usage(&self) -> &str {
"Bitwise rotate left for integers"
}
fn search_terms(&self) -> Vec<&str> {
vec!["rotate left"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let bits: usize = call.req(engine_state, stack, 0)?;
let signed = call.has_flag("signed");
let number_bytes: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "number-bytes")?;
let bytes_len = get_number_bytes(&number_bytes);
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
val.span,
));
}
}
input.map(
move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Rotate left a number with 2 bits",
example: "17 | bits rol 2",
result: Some(Value::Int {
val: 68,
span: Span::test_data(),
}),
},
Example {
description: "Rotate left a list of numbers with 2 bits",
example: "[5 3 2] | bits rol 2",
result: Some(Value::List {
vals: vec![Value::test_int(20), Value::test_int(12), Value::test_int(8)],
span: Span::test_data(),
}),
},
]
}
}
fn get_rotate_left<T: Display + PrimInt>(val: T, bits: u32, span: Span) -> Value
where
i64: std::convert::TryFrom<T>,
{
let rotate_result = i64::try_from(val.rotate_left(bits));
match rotate_result {
Ok(val) => Value::Int { val, span },
Err(_) => Value::Error {
error: ShellError::GenericError(
"Rotate left result beyond the range of 64 bit signed number".to_string(),
format!(
"{} of the specified number of bytes rotate left {} bits exceed limit",
val, bits
),
Some(span),
None,
Vec::new(),
),
},
}
}
fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: NumberBytes) -> Value {
match value {
Value::Int { val, span } => {
use InputNumType::*;
// let bits = (((bits % 64) + 64) % 64) as u32;
let bits = bits as u32;
let input_type = get_input_num_type(val, signed, number_size);
match input_type {
One => get_rotate_left(val as u8, bits, span),
Two => get_rotate_left(val as u16, bits, span),
Four => get_rotate_left(val as u32, bits, span),
Eight => get_rotate_left(val as u64, bits, span),
SignedOne => get_rotate_left(val as i8, bits, span),
SignedTwo => get_rotate_left(val as i16, bits, span),
SignedFour => get_rotate_left(val as i32, bits, span),
SignedEight => get_rotate_left(val as i64, bits, span),
}
}
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
),
},
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@@ -0,0 +1,160 @@
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
};
use num_traits::int::PrimInt;
use std::fmt::Display;
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"bits ror"
}
fn signature(&self) -> Signature {
Signature::build("bits ror")
.required("bits", SyntaxShape::Int, "number of bits to rotate right")
.switch(
"signed",
"always treat input number as a signed number",
Some('s'),
)
.named(
"number-bytes",
SyntaxShape::String,
"the word size in number of bytes, it can be 1, 2, 4, 8, auto, default value `8`",
Some('n'),
)
.category(Category::Bits)
}
fn usage(&self) -> &str {
"Bitwise rotate right for integers"
}
fn search_terms(&self) -> Vec<&str> {
vec!["rotate right"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let bits: usize = call.req(engine_state, stack, 0)?;
let signed = call.has_flag("signed");
let number_bytes: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "number-bytes")?;
let bytes_len = get_number_bytes(&number_bytes);
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
val.span,
));
}
}
input.map(
move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Rotate right a number with 60 bits",
example: "17 | bits ror 60",
result: Some(Value::Int {
val: 272,
span: Span::test_data(),
}),
},
Example {
description: "Rotate right a list of numbers of one byte",
example: "[15 33 92] | bits ror 2 -n 1",
result: Some(Value::List {
vals: vec![
Value::test_int(195),
Value::test_int(72),
Value::test_int(23),
],
span: Span::test_data(),
}),
},
]
}
}
fn get_rotate_right<T: Display + PrimInt>(val: T, bits: u32, span: Span) -> Value
where
i64: std::convert::TryFrom<T>,
{
let rotate_result = i64::try_from(val.rotate_right(bits));
match rotate_result {
Ok(val) => Value::Int { val, span },
Err(_) => Value::Error {
error: ShellError::GenericError(
"Rotate right result beyond the range of 64 bit signed number".to_string(),
format!(
"{} of the specified number of bytes rotate right {} bits exceed limit",
val, bits
),
Some(span),
None,
Vec::new(),
),
},
}
}
fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: NumberBytes) -> Value {
match value {
Value::Int { val, span } => {
use InputNumType::*;
// let bits = (((bits % 64) + 64) % 64) as u32;
let bits = bits as u32;
let input_type = get_input_num_type(val, signed, number_size);
match input_type {
One => get_rotate_right(val as u8, bits, span),
Two => get_rotate_right(val as u16, bits, span),
Four => get_rotate_right(val as u32, bits, span),
Eight => get_rotate_right(val as u64, bits, span),
SignedOne => get_rotate_right(val as i8, bits, span),
SignedTwo => get_rotate_right(val as i16, bits, span),
SignedFour => get_rotate_right(val as i32, bits, span),
SignedEight => get_rotate_right(val as i64, bits, span),
}
}
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
),
},
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@@ -0,0 +1,188 @@
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
};
use num_traits::CheckedShl;
use std::fmt::Display;
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"bits shl"
}
fn signature(&self) -> Signature {
Signature::build("bits shl")
.required("bits", SyntaxShape::Int, "number of bits to shift left")
.switch(
"signed",
"always treat input number as a signed number",
Some('s'),
)
.named(
"number-bytes",
SyntaxShape::String,
"the word size in number of bytes, it can be 1, 2, 4, 8, auto, default value `8`",
Some('n'),
)
.category(Category::Bits)
}
fn usage(&self) -> &str {
"Bitwise shift left for integers"
}
fn search_terms(&self) -> Vec<&str> {
vec!["shift left"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let bits: usize = call.req(engine_state, stack, 0)?;
let signed = call.has_flag("signed");
let number_bytes: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "number-bytes")?;
let bytes_len = get_number_bytes(&number_bytes);
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
val.span,
));
}
}
input.map(
move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Shift left a number by 7 bits",
example: "2 | bits shl 7",
result: Some(Value::Int {
val: 256,
span: Span::test_data(),
}),
},
Example {
description: "Shift left a number with 1 byte by 7 bits",
example: "2 | bits shl 7 -n 1",
result: Some(Value::Int {
val: 0,
span: Span::test_data(),
}),
},
Example {
description: "Shift left a signed number by 1 bit",
example: "0x7F | bits shl 1 -s",
result: Some(Value::Int {
val: 254,
span: Span::test_data(),
}),
},
Example {
description: "Shift left a list of numbers",
example: "[5 3 2] | bits shl 2",
result: Some(Value::List {
vals: vec![Value::test_int(20), Value::test_int(12), Value::test_int(8)],
span: Span::test_data(),
}),
},
]
}
}
fn get_shift_left<T: CheckedShl + Display + Copy>(val: T, bits: u32, span: Span) -> Value
where
i64: std::convert::TryFrom<T>,
{
match val.checked_shl(bits) {
Some(val) => {
let shift_result = i64::try_from(val);
match shift_result {
Ok(val) => Value::Int { val, span },
Err(_) => Value::Error {
error: ShellError::GenericError(
"Shift left result beyond the range of 64 bit signed number".to_string(),
format!(
"{} of the specified number of bytes shift left {} bits exceed limit",
val, bits
),
Some(span),
None,
Vec::new(),
),
},
}
}
None => Value::Error {
error: ShellError::GenericError(
"Shift left failed".to_string(),
format!(
"{} shift left {} bits failed, you may shift too many bits",
val, bits
),
Some(span),
None,
Vec::new(),
),
},
}
}
fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: NumberBytes) -> Value {
match value {
Value::Int { val, span } => {
use InputNumType::*;
// let bits = (((bits % 64) + 64) % 64) as u32;
let bits = bits as u32;
let input_type = get_input_num_type(val, signed, number_size);
match input_type {
One => get_shift_left(val as u8, bits, span),
Two => get_shift_left(val as u16, bits, span),
Four => get_shift_left(val as u32, bits, span),
Eight => get_shift_left(val as u64, bits, span),
SignedOne => get_shift_left(val as i8, bits, span),
SignedTwo => get_shift_left(val as i16, bits, span),
SignedFour => get_shift_left(val as i32, bits, span),
SignedEight => get_shift_left(val as i64, bits, span),
}
}
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
),
},
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@@ -0,0 +1,172 @@
use super::{get_input_num_type, get_number_bytes, InputNumType, NumberBytes};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
};
use num_traits::CheckedShr;
use std::fmt::Display;
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"bits shr"
}
fn signature(&self) -> Signature {
Signature::build("bits shr")
.required("bits", SyntaxShape::Int, "number of bits to shift right")
.switch(
"signed",
"always treat input number as a signed number",
Some('s'),
)
.named(
"number-bytes",
SyntaxShape::String,
"the word size in number of bytes, it can be 1, 2, 4, 8, auto, default value `8`",
Some('n'),
)
.category(Category::Bits)
}
fn usage(&self) -> &str {
"Bitwise shift right for integers"
}
fn search_terms(&self) -> Vec<&str> {
vec!["shift right"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let bits: usize = call.req(engine_state, stack, 0)?;
let signed = call.has_flag("signed");
let number_bytes: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "number-bytes")?;
let bytes_len = get_number_bytes(&number_bytes);
if let NumberBytes::Invalid = bytes_len {
if let Some(val) = number_bytes {
return Err(ShellError::UnsupportedInput(
"Only 1, 2, 4, 8, or 'auto' bytes are supported as word sizes".to_string(),
val.span,
));
}
}
input.map(
move |value| operate(value, bits, head, signed, bytes_len),
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Shift right a number with 2 bits",
example: "8 | bits shr 2",
result: Some(Value::Int {
val: 2,
span: Span::test_data(),
}),
},
Example {
description: "Shift right a list of numbers",
example: "[15 35 2] | bits shr 2",
result: Some(Value::List {
vals: vec![Value::test_int(3), Value::test_int(8), Value::test_int(0)],
span: Span::test_data(),
}),
},
]
}
}
fn get_shift_right<T: CheckedShr + Display + Copy>(val: T, bits: u32, span: Span) -> Value
where
i64: std::convert::TryFrom<T>,
{
match val.checked_shr(bits) {
Some(val) => {
let shift_result = i64::try_from(val);
match shift_result {
Ok(val) => Value::Int { val, span },
Err(_) => Value::Error {
error: ShellError::GenericError(
"Shift right result beyond the range of 64 bit signed number".to_string(),
format!(
"{} of the specified number of bytes shift right {} bits exceed limit",
val, bits
),
Some(span),
None,
Vec::new(),
),
},
}
}
None => Value::Error {
error: ShellError::GenericError(
"Shift right failed".to_string(),
format!(
"{} shift right {} bits failed, you may shift too many bits",
val, bits
),
Some(span),
None,
Vec::new(),
),
},
}
}
fn operate(value: Value, bits: usize, head: Span, signed: bool, number_size: NumberBytes) -> Value {
match value {
Value::Int { val, span } => {
use InputNumType::*;
// let bits = (((bits % 64) + 64) % 64) as u32;
let bits = bits as u32;
let input_type = get_input_num_type(val, signed, number_size);
match input_type {
One => get_shift_right(val as u8, bits, span),
Two => get_shift_right(val as u16, bits, span),
Four => get_shift_right(val as u32, bits, span),
Eight => get_shift_right(val as u64, bits, span),
SignedOne => get_shift_right(val as i8, bits, span),
SignedTwo => get_shift_right(val as i16, bits, span),
SignedFour => get_shift_right(val as i32, bits, span),
SignedEight => get_shift_right(val as i64, bits, span),
}
}
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
),
},
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@@ -0,0 +1,100 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
};
#[derive(Clone)]
pub struct SubCommand;
impl Command for SubCommand {
fn name(&self) -> &str {
"bits xor"
}
fn signature(&self) -> Signature {
Signature::build("bits xor")
.required(
"target",
SyntaxShape::Int,
"target integer to perform bit xor",
)
.category(Category::Bits)
}
fn usage(&self) -> &str {
"Performs bitwise xor for integers"
}
fn search_terms(&self) -> Vec<&str> {
vec!["logic xor"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let target: i64 = call.req(engine_state, stack, 0)?;
input.map(
move |value| operate(value, target, head),
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Apply bits xor to two numbers",
example: "2 | bits xor 2",
result: Some(Value::Int {
val: 0,
span: Span::test_data(),
}),
},
Example {
description: "Apply logical xor to a list of numbers",
example: "[8 3 2] | bits xor 2",
result: Some(Value::List {
vals: vec![Value::test_int(10), Value::test_int(1), Value::test_int(0)],
span: Span::test_data(),
}),
},
]
}
}
fn operate(value: Value, target: i64, head: Span) -> Value {
match value {
Value::Int { val, span } => Value::Int {
val: val ^ target,
span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Only integer values are supported, input type: {:?}",
other.get_type()
),
other.span().unwrap_or(head),
),
},
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_examples() {
use crate::test_examples;
test_examples(SubCommand {})
}
}

View File

@@ -1,4 +1,4 @@
use super::{operate, BytesArgument};
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@@ -10,12 +10,12 @@ struct Arguments {
added_data: Vec<u8>,
index: Option<usize>,
end: bool,
column_paths: Option<Vec<CellPath>>,
cell_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
@@ -47,7 +47,7 @@ impl Command for BytesAdd {
}
fn usage(&self) -> &str {
"add specified bytes to the input"
"Add specified bytes to the input"
}
fn search_terms(&self) -> Vec<&str> {
@@ -62,12 +62,8 @@ impl Command for BytesAdd {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let added_data: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let index: Option<usize> = call.get_flag(engine_state, stack, "index")?;
let end = call.has_flag("end");
@@ -75,7 +71,7 @@ impl Command for BytesAdd {
added_data,
index,
end,
column_paths,
cell_paths,
};
operate(add, arg, input, call.head, engine_state.ctrlc.clone())
}
@@ -118,7 +114,25 @@ impl Command for BytesAdd {
}
}
fn add(input: &[u8], args: &Arguments, span: Span) -> Value {
fn add(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => add_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn add_impl(input: &[u8], args: &Arguments, span: Span) -> Value {
match args.index {
None => {
if args.end {

View File

@@ -1,4 +1,4 @@
use super::{operate, BytesArgument};
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@@ -15,12 +15,12 @@ struct Arguments {
start: isize,
end: isize,
arg_span: Span,
column_paths: Option<Vec<CellPath>>,
cell_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
@@ -141,17 +141,13 @@ impl Command for BytesAt {
) -> Result<PipelineData, ShellError> {
let range: Value = call.req(engine_state, stack, 0)?;
let (start, end, arg_span) = parse_range(range, call.head)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let arg = Arguments {
start,
end,
arg_span,
column_paths,
cell_paths,
};
operate(at, arg, input, call.head, engine_state.ctrlc.clone())
}
@@ -228,7 +224,25 @@ impl Command for BytesAt {
}
}
fn at(input: &[u8], arg: &Arguments, span: Span) -> Value {
fn at(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => at_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let len: isize = input.len() as isize;
let start: isize = if arg.start < 0 {

View File

@@ -1,4 +1,4 @@
use super::{operate, BytesArgument};
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@@ -8,12 +8,12 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
struct Arguments {
pattern: Vec<u8>,
column_paths: Option<Vec<CellPath>>,
cell_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
@@ -53,15 +53,11 @@ impl Command for BytesEndsWith {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let arg = Arguments {
pattern,
column_paths,
cell_paths,
};
operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone())
}
@@ -96,10 +92,24 @@ impl Command for BytesEndsWith {
}
}
fn ends_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
Value::Bool {
val: input.ends_with(pattern),
span,
fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => Value::Bool {
val: val.ends_with(&args.pattern),
span: *val_span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}

View File

@@ -1,4 +1,4 @@
use super::{operate, BytesArgument};
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::ast::{Call, CellPath};
use nu_protocol::engine::{Command, EngineState, Stack};
@@ -10,12 +10,12 @@ struct Arguments {
pattern: Vec<u8>,
end: bool,
all: bool,
column_paths: Option<Vec<CellPath>>,
cell_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
@@ -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(
@@ -60,17 +60,13 @@ impl Command for BytesIndexOf {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let arg = Arguments {
pattern,
end: call.has_flag("end"),
all: call.has_flag("all"),
column_paths,
cell_paths,
};
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
}
@@ -126,7 +122,25 @@ impl Command for BytesIndexOf {
}
}
fn index_of(input: &[u8], arg: &Arguments, span: Span) -> Value {
fn index_of(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => index_of_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn index_of_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
if arg.all {
search_all_index(input, &arg.pattern, arg.end, span)
} else {

View File

@@ -1,4 +1,4 @@
use super::{operate, BytesArgument};
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@@ -9,16 +9,6 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
#[derive(Clone)]
pub struct BytesLen;
struct Arguments {
column_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
}
}
impl Command for BytesLen {
fn name(&self) -> &str {
"bytes length"
@@ -39,7 +29,7 @@ impl Command for BytesLen {
}
fn search_terms(&self) -> Vec<&str> {
vec!["len", "size", "count"]
vec!["size", "count"]
}
fn run(
@@ -49,13 +39,8 @@ impl Command for BytesLen {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let arg = Arguments { column_paths };
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let arg = CellPathOnlyArgs::from(cell_paths);
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
}
@@ -78,10 +63,24 @@ impl Command for BytesLen {
}
}
fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value {
Value::Int {
val: input.len() as i64,
span,
fn length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => Value::Int {
val: val.len() as i64,
span: *val_span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}

View File

@@ -11,11 +11,6 @@ mod replace;
mod reverse;
mod starts_with;
use nu_protocol::ast::CellPath;
use nu_protocol::{PipelineData, ShellError, Span, Value};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
pub use add::BytesAdd;
pub use at::BytesAt;
pub use build_::BytesBuild;
@@ -28,71 +23,3 @@ pub use remove::BytesRemove;
pub use replace::BytesReplace;
pub use reverse::BytesReverse;
pub use starts_with::BytesStartsWith;
trait BytesArgument {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>>;
}
/// map input pipeline data, for each elements, if it's Binary, invoke relative `cmd` with `arg`.
fn operate<C, A>(
cmd: C,
mut arg: A,
input: PipelineData,
span: Span,
ctrlc: Option<Arc<AtomicBool>>,
) -> Result<PipelineData, ShellError>
where
A: BytesArgument + Send + Sync + 'static,
C: Fn(&[u8], &A, Span) -> Value + Send + Sync + 'static + Clone + Copy,
{
match arg.take_column_paths() {
None => input.map(
move |v| match v {
Value::Binary {
val,
span: val_span,
} => cmd(&val, &arg, val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
},
ctrlc,
),
Some(column_paths) => {
let arg = Arc::new(arg);
input.map(
move |mut v| {
for path in &column_paths {
let opt = arg.clone();
let r = v.update_cell_path(
&path.members,
Box::new(move |old| {
match old {
Value::Binary {val, span: val_span} => cmd(val, &opt, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
}}}),
);
if let Err(error) = r {
return Value::Error { error };
}
}
v
},
ctrlc,
)
}
}
}

View File

@@ -1,4 +1,4 @@
use super::{operate, BytesArgument};
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@@ -9,13 +9,13 @@ use nu_protocol::{
struct Arguments {
pattern: Vec<u8>,
end: bool,
column_paths: Option<Vec<CellPath>>,
cell_paths: Option<Vec<CellPath>>,
all: bool,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
@@ -41,7 +41,7 @@ impl Command for BytesRemove {
}
fn usage(&self) -> &str {
"remove bytes"
"Remove bytes"
}
fn search_terms(&self) -> Vec<&str> {
@@ -55,12 +55,8 @@ impl Command for BytesRemove {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if pattern_to_remove.item.is_empty() {
return Err(ShellError::UnsupportedInput(
@@ -73,7 +69,7 @@ impl Command for BytesRemove {
let arg = Arguments {
pattern: pattern_to_remove,
end: call.has_flag("end"),
column_paths,
cell_paths,
all: call.has_flag("all"),
};
@@ -135,7 +131,25 @@ impl Command for BytesRemove {
}
}
fn remove(input: &[u8], arg: &Arguments, span: Span) -> Value {
fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => remove_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let mut result = vec![];
let remove_all = arg.all;
let input_len = input.len();

View File

@@ -1,4 +1,4 @@
use super::{operate, BytesArgument};
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@@ -9,13 +9,13 @@ use nu_protocol::{
struct Arguments {
find: Vec<u8>,
replace: Vec<u8>,
column_paths: Option<Vec<CellPath>>,
cell_paths: Option<Vec<CellPath>>,
all: bool,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
@@ -55,12 +55,8 @@ impl Command for BytesReplace {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
if find.item.is_empty() {
return Err(ShellError::UnsupportedInput(
@@ -72,7 +68,7 @@ impl Command for BytesReplace {
let arg = Arguments {
find: find.item,
replace: call.req::<Vec<u8>>(engine_state, stack, 1)?,
column_paths,
cell_paths,
all: call.has_flag("all"),
};
@@ -126,7 +122,25 @@ impl Command for BytesReplace {
}
}
fn replace(input: &[u8], arg: &Arguments, span: Span) -> Value {
fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => replace_impl(val, args, *val_span),
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}
fn replace_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
let mut replaced = vec![];
let replace_all = arg.all;

View File

@@ -1,4 +1,4 @@
use super::{operate, BytesArgument};
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@@ -6,16 +6,6 @@ use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
struct Arguments {
column_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
}
}
#[derive(Clone)]
pub struct BytesReverse;
@@ -50,13 +40,8 @@ impl Command for BytesReverse {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let arg = Arguments { column_paths };
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let arg = CellPathOnlyArgs::from(cell_paths);
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
}
@@ -82,12 +67,28 @@ impl Command for BytesReverse {
}
}
fn reverse(input: &[u8], _args: &Arguments, span: Span) -> Value {
let mut reversed_input = input.to_vec();
reversed_input.reverse();
Value::Binary {
val: reversed_input,
span,
fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => {
let mut reversed_input = val.to_vec();
reversed_input.reverse();
Value::Binary {
val: reversed_input,
span: *val_span,
}
}
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}

View File

@@ -1,4 +1,4 @@
use super::{operate, BytesArgument};
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
@@ -8,12 +8,12 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
struct Arguments {
pattern: Vec<u8>,
column_paths: Option<Vec<CellPath>>,
cell_paths: Option<Vec<CellPath>>,
}
impl BytesArgument for Arguments {
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
self.column_paths.take()
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
@@ -53,15 +53,11 @@ impl Command for BytesStartsWith {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let column_paths = if column_paths.is_empty() {
None
} else {
Some(column_paths)
};
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let arg = Arguments {
pattern,
column_paths,
cell_paths,
};
operate(
starts_with,
@@ -102,10 +98,24 @@ impl Command for BytesStartsWith {
}
}
fn starts_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
Value::Bool {
val: input.starts_with(pattern),
span,
fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
match val {
Value::Binary {
val,
span: val_span,
} => Value::Bool {
val: val.starts_with(&args.pattern),
span: *val_span,
},
other => Value::Error {
error: ShellError::UnsupportedInput(
format!(
"Input's type is {}. This command only works with bytes.",
other.get_type()
),
span,
),
},
}
}

View File

@@ -215,8 +215,8 @@ fn histogram_impl(
const MAX_FREQ_COUNT: f64 = 100.0;
for (val, count) in counter.into_iter() {
let quantile = match calc_method {
PercentageCalcMethod::Normalize => (count as f64 / total_cnt as f64),
PercentageCalcMethod::Relative => (count as f64 / max_cnt as f64),
PercentageCalcMethod::Normalize => count as f64 / total_cnt as f64,
PercentageCalcMethod::Relative => count as f64 / max_cnt as f64,
};
let percentage = format!("{:.2}%", quantile * 100_f64);

View File

@@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@@ -96,31 +97,12 @@ fn fmt(
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, 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)));
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
pub fn action(input: &Value, span: Span) -> Value {
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input {
Value::Int { val, .. } => fmt_it(*val, span),
Value::Filesize { val, .. } => fmt_it(*val, span),

View File

@@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@@ -29,7 +30,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["convert", "binary", "bytes", "bin"]
vec!["convert", "bytes"]
}
fn run(
@@ -100,7 +101,7 @@ fn into_binary(
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
match input {
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
@@ -120,27 +121,10 @@ fn into_binary(
}
.into_pipeline_data())
}
_ => input.map(
move |v| {
if column_paths.is_empty() {
action(&v, 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)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
),
_ => {
let arg = CellPathOnlyArgs::from(cell_paths);
operate(action, arg, input, call.head, engine_state.ctrlc.clone())
}
}
}
@@ -160,7 +144,7 @@ fn float_to_endian(n: f64) -> Vec<u8> {
}
}
pub fn action(input: &Value, span: Span) -> Value {
pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input {
Value::Binary { .. } => input.clone(),
Value::Int { val, .. } => Value::Binary {
@@ -180,7 +164,7 @@ pub fn action(input: &Value, span: Span) -> Value {
span,
},
Value::Bool { val, .. } => Value::Binary {
val: int_to_endian(if *val { 1i64 } else { 0 }),
val: int_to_endian(i64::from(*val)),
span,
},
Value::Date { val, .. } => Value::Binary {

View File

@@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@@ -108,28 +109,9 @@ fn into_bool(
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, 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)));
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
@@ -154,7 +136,7 @@ fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
}
}
fn action(input: &Value, span: Span) -> Value {
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
match input {
Value::Bool { .. } => input.clone(),
Value::Int { val, .. } => Value::Bool {

View File

@@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use crate::{generate_strftime_list, parse_date_from_string};
use chrono::{DateTime, FixedOffset, Local, TimeZone, Utc};
use nu_engine::CallExt;
@@ -5,14 +6,20 @@ use nu_protocol::ast::Call;
use nu_protocol::ast::CellPath;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Value,
};
struct Arguments {
timezone: Option<Spanned<String>>,
offset: Option<Spanned<i64>>,
format: Option<String>,
column_paths: Vec<CellPath>,
zone_options: Option<Spanned<Zone>>,
format_options: Option<DatetimeFormat>,
cell_paths: Option<Vec<CellPath>>,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
// In case it may be confused with chrono::TimeZone
@@ -95,7 +102,36 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input)
if call.has_flag("list") {
Ok(generate_strftime_list(call.head, true).into_pipeline_data())
} else {
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
// if zone-offset is specified, then zone will be neglected
let timezone = call.get_flag::<Spanned<String>>(engine_state, stack, "timezone")?;
let zone_options =
match &call.get_flag::<Spanned<i64>>(engine_state, stack, "offset")? {
Some(zone_offset) => Some(Spanned {
item: Zone::new(zone_offset.item),
span: zone_offset.span,
}),
None => timezone.as_ref().map(|zone| Spanned {
item: Zone::from_string(zone.item.clone()),
span: zone.span,
}),
};
let format_options = call
.get_flag::<String>(engine_state, stack, "format")?
.as_ref()
.map(|fmt| DatetimeFormat(fmt.to_string()));
let args = Arguments {
format_options,
zone_options,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
}
fn usage(&self) -> &str {
@@ -103,7 +139,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> {
@@ -162,72 +198,9 @@ impl Command for SubCommand {
#[derive(Clone)]
struct DatetimeFormat(String);
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
let options = Arguments {
timezone: call.get_flag(engine_state, stack, "timezone")?,
offset: call.get_flag(engine_state, stack, "offset")?,
format: call.get_flag(engine_state, stack, "format")?,
column_paths: call.rest(engine_state, stack, 0)?,
};
// if zone-offset is specified, then zone will be neglected
let zone_options = match &options.offset {
Some(zone_offset) => Some(Spanned {
item: Zone::new(zone_offset.item),
span: zone_offset.span,
}),
None => options.timezone.as_ref().map(|zone| Spanned {
item: Zone::from_string(zone.item.clone()),
span: zone.span,
}),
};
let list_flag = call.has_flag("list");
let format_options = options
.format
.as_ref()
.map(|fmt| DatetimeFormat(fmt.to_string()));
input.map(
move |v| {
if options.column_paths.is_empty() && !list_flag {
action(&v, &zone_options, &format_options, head)
} else if list_flag {
generate_strftime_list(head, true)
} else {
let mut ret = v;
for path in &options.column_paths {
let zone_options = zone_options.clone();
let format_options = format_options.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, &zone_options, &format_options, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action(
input: &Value,
timezone: &Option<Spanned<Zone>>,
dateformat: &Option<DatetimeFormat>,
head: Span,
) -> Value {
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
let timezone = &args.zone_options;
let dateformat = &args.format_options;
// Check to see if input looks like a Unix timestamp (i.e. can it be parsed to an int?)
let timestamp = match input {
Value::Int { val, .. } => Ok(*val),
@@ -311,7 +284,7 @@ fn action(
Some(dt) => match DateTime::parse_from_str(val, &dt.0) {
Ok(d) => Value::Date { val: d, span: head },
Err(reason) => {
return Value::Error {
Value::Error {
error: ShellError::CantConvert(
format!("could not parse as datetime using format '{}'", dt.0),
reason.to_string(),
@@ -359,7 +332,12 @@ mod tests {
fn takes_a_date_format() {
let date_str = Value::test_string("16.11.1984 8:00 am +0000");
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
let actual = action(&date_str, &None, &fmt_options, Span::test_data());
let args = Arguments {
zone_options: None,
format_options: fmt_options,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: DateTime::parse_from_str("16.11.1984 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")
.unwrap(),
@@ -371,7 +349,12 @@ mod tests {
#[test]
fn takes_iso8601_date_format() {
let date_str = Value::test_string("2020-08-04T16:39:18+00:00");
let actual = action(&date_str, &None, &None, Span::test_data());
let args = Arguments {
zone_options: None,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: DateTime::parse_from_str("2020-08-04T16:39:18+00:00", "%Y-%m-%dT%H:%M:%S%z")
.unwrap(),
@@ -387,7 +370,12 @@ mod tests {
item: Zone::East(8),
span: Span::test_data(),
});
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
.unwrap(),
@@ -404,7 +392,12 @@ mod tests {
item: Zone::East(8),
span: Span::test_data(),
});
let actual = action(&date_int, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_int, &args, Span::test_data());
let expected = Value::Date {
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
.unwrap(),
@@ -421,7 +414,12 @@ mod tests {
item: Zone::Local,
span: Span::test_data(),
});
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: Local.timestamp(1614434140, 0).into(),
span: Span::test_data(),
@@ -433,8 +431,12 @@ mod tests {
#[test]
fn takes_timestamp_without_timezone() {
let date_str = Value::test_string("1614434140");
let timezone_option = None;
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: None,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::Date {
val: Utc.timestamp(1614434140, 0).into(),
@@ -451,7 +453,12 @@ mod tests {
item: Zone::Utc,
span: Span::test_data(),
});
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
let args = Arguments {
zone_options: timezone_option,
format_options: None,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
assert_eq!(actual.get_type(), Error);
}
@@ -460,7 +467,12 @@ mod tests {
fn communicates_parsing_error_given_an_invalid_datetimelike_string() {
let date_str = Value::test_string("16.11.1984 8:00 am Oops0000");
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
let actual = action(&date_str, &None, &fmt_options, Span::test_data());
let args = Arguments {
zone_options: None,
format_options: fmt_options,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
assert_eq!(actual.get_type(), Error);
}

View File

@@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@@ -36,13 +37,15 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
operate(engine_state, stack, call, input)
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Convert string to integer in table",
description: "Convert string to decimal in table",
example: "[[num]; ['5.01']] | into decimal num",
result: Some(Value::List {
vals: vec![Value::Record {
@@ -54,50 +57,25 @@ impl Command for SubCommand {
}),
},
Example {
description: "Convert string to integer",
description: "Convert string to decimal",
example: "'1.345' | into decimal",
result: Some(Value::test_float(1.345)),
},
Example {
description: "Convert decimal to integer",
description: "Convert decimal to decimal",
example: "'-5.9' | into decimal",
result: Some(Value::test_float(-5.9)),
},
Example {
description: "Convert boolean to decimal",
example: "true | into decimal",
result: Some(Value::test_float(1.0)),
},
]
}
}
fn operate(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, 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)));
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
fn action(input: &Value, head: Span) -> Value {
fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
match input {
Value::String { val: s, span } => {
let other = s.trim();
@@ -118,6 +96,13 @@ fn action(input: &Value, head: Span) -> Value {
val: *v as f64,
span: *span,
},
Value::Bool { val: b, span } => Value::Float {
val: match b {
true => 1.0,
false => 0.0,
},
span: *span,
},
other => {
let span = other.span();
match span {
@@ -151,7 +136,7 @@ mod tests {
let word = Value::test_string("3.1415");
let expected = Value::test_float(3.1415);
let actual = action(&word, Span::test_data());
let actual = action(&word, &CellPathOnlyArgs::from(vec![]), Span::test_data());
assert_eq!(actual, expected);
}
@@ -159,7 +144,11 @@ mod tests {
fn communicates_parsing_error_given_an_invalid_decimallike_string() {
let decimal_str = Value::test_string("11.6anra");
let actual = action(&decimal_str, Span::test_data());
let actual = action(
&decimal_str,
&CellPathOnlyArgs::from(vec![]),
Span::test_data(),
);
assert_eq!(actual.get_type(), Error);
}
@@ -168,7 +157,11 @@ mod tests {
fn int_to_decimal() {
let decimal_str = Value::test_int(10);
let expected = Value::test_float(10.0);
let actual = action(&decimal_str, Span::test_data());
let actual = action(
&decimal_str,
&CellPathOnlyArgs::from(vec![]),
Span::test_data(),
);
assert_eq!(actual, expected);
}

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,22 @@ impl Command for SubCommand {
span,
}),
},
Example {
description: "Convert string to the requested duration as a string",
example: "'7min' | into duration --convert sec",
result: Some(Value::String {
val: "420 sec".to_string(),
span,
}),
},
Example {
description: "Convert duration to the requested duration as a string",
example: "420sec | into duration --convert min",
result: Some(Value::String {
val: "7 min".to_string(),
span,
}),
},
]
}
}
@@ -117,17 +140,23 @@ 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)?;
let config = engine_state.get_config();
let float_precision = config.float_precision as usize;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, head)
action(&v, &convert_to_unit, float_precision, 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, float_precision, head)),
);
if let Err(error) = r {
return Value::Error { error };
}
@@ -140,6 +169,150 @@ fn into_duration(
)
}
fn convert_str_from_unit_to_unit(
val: i64,
from_unit: &str,
to_unit: &str,
span: Span,
value_span: Span,
) -> Result<f64, ShellError> {
match (from_unit, to_unit) {
("ns", "ns") => Ok(val as f64),
("ns", "us") => Ok(val as f64 / 1000.0),
("ns", "ms") => Ok(val as f64 / 1000.0 / 1000.0),
("ns", "sec") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0),
("ns", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0),
("ns", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0),
("ns", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
("ns", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("ns", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("ns", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ns", "dec") => {
Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0)
}
("us", "ns") => Ok(val as f64 * 1000.0),
("us", "us") => Ok(val as f64),
("us", "ms") => Ok(val as f64 / 1000.0),
("us", "sec") => Ok(val as f64 / 1000.0 / 1000.0),
("us", "min") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0),
("us", "hr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0),
("us", "day") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0),
("us", "wk") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("us", "month") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("us", "yr") => Ok(val as f64 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("us", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ms", "ns") => Ok(val as f64 * 1000.0 * 1000.0),
("ms", "us") => Ok(val as f64 * 1000.0),
("ms", "ms") => Ok(val as f64),
("ms", "sec") => Ok(val as f64 / 1000.0),
("ms", "min") => Ok(val as f64 / 1000.0 / 60.0),
("ms", "hr") => Ok(val as f64 / 1000.0 / 60.0 / 60.0),
("ms", "day") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0),
("ms", "wk") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 7.0),
("ms", "month") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 30.0),
("ms", "yr") => Ok(val as f64 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("ms", "dec") => Ok(val as f64 / 10.0 / 1000.0 / 60.0 / 60.0 / 24.0 / 365.0),
("sec", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0),
("sec", "us") => Ok(val as f64 * 1000.0 * 1000.0),
("sec", "ms") => Ok(val as f64 * 1000.0),
("sec", "sec") => Ok(val as f64),
("sec", "min") => Ok(val as f64 / 60.0),
("sec", "hr") => Ok(val as f64 / 60.0 / 60.0),
("sec", "day") => Ok(val as f64 / 60.0 / 60.0 / 24.0),
("sec", "wk") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 7.0),
("sec", "month") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 30.0),
("sec", "yr") => Ok(val as f64 / 60.0 / 60.0 / 24.0 / 365.0),
("sec", "dec") => Ok(val as f64 / 10.0 / 60.0 / 60.0 / 24.0 / 365.0),
("min", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0),
("min", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0),
("min", "ms") => Ok(val as f64 * 1000.0 * 60.0),
("min", "sec") => Ok(val as f64 * 60.0),
("min", "min") => Ok(val as f64),
("min", "hr") => Ok(val as f64 / 60.0),
("min", "day") => Ok(val as f64 / 60.0 / 24.0),
("min", "wk") => Ok(val as f64 / 60.0 / 24.0 / 7.0),
("min", "month") => Ok(val as f64 / 60.0 / 24.0 / 30.0),
("min", "yr") => Ok(val as f64 / 60.0 / 24.0 / 365.0),
("min", "dec") => Ok(val as f64 / 10.0 / 60.0 / 24.0 / 365.0),
("hr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0),
("hr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0),
("hr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0),
("hr", "sec") => Ok(val as f64 * 60.0 * 60.0),
("hr", "min") => Ok(val as f64 * 60.0),
("hr", "hr") => Ok(val as f64),
("hr", "day") => Ok(val as f64 / 24.0),
("hr", "wk") => Ok(val as f64 / 24.0 / 7.0),
("hr", "month") => Ok(val as f64 / 24.0 / 30.0),
("hr", "yr") => Ok(val as f64 / 24.0 / 365.0),
("hr", "dec") => Ok(val as f64 / 10.0 / 24.0 / 365.0),
("day", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0),
("day", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0),
("day", "min") => Ok(val as f64 * 60.0 * 24.0),
("day", "hr") => Ok(val as f64 * 24.0),
("day", "day") => Ok(val as f64),
("day", "wk") => Ok(val as f64 / 7.0),
("day", "month") => Ok(val as f64 / 30.0),
("day", "yr") => Ok(val as f64 / 365.0),
("day", "dec") => Ok(val as f64 / 10.0 / 365.0),
("wk", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 7.0),
("wk", "min") => Ok(val as f64 * 60.0 * 24.0 * 7.0),
("wk", "hr") => Ok(val as f64 * 24.0 * 7.0),
("wk", "day") => Ok(val as f64 * 7.0),
("wk", "wk") => Ok(val as f64),
("wk", "month") => Ok(val as f64 / 4.0),
("wk", "yr") => Ok(val as f64 / 52.0),
("wk", "dec") => Ok(val as f64 / 10.0 / 52.0),
("month", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 30.0),
("month", "min") => Ok(val as f64 * 60.0 * 24.0 * 30.0),
("month", "hr") => Ok(val as f64 * 24.0 * 30.0),
("month", "day") => Ok(val as f64 * 30.0),
("month", "wk") => Ok(val as f64 * 4.0),
("month", "month") => Ok(val as f64),
("month", "yr") => Ok(val as f64 / 12.0),
("month", "dec") => Ok(val as f64 / 10.0 / 12.0),
("yr", "ns") => Ok(val as f64 * 1000.0 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "us") => Ok(val as f64 * 1000.0 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "ms") => Ok(val as f64 * 1000.0 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "sec") => Ok(val as f64 * 60.0 * 60.0 * 24.0 * 365.0),
("yr", "min") => Ok(val as f64 * 60.0 * 24.0 * 365.0),
("yr", "hr") => Ok(val as f64 * 24.0 * 365.0),
("yr", "day") => Ok(val as f64 * 365.0),
("yr", "wk") => Ok(val as f64 * 52.0),
("yr", "month") => Ok(val as f64 * 12.0),
("yr", "yr") => Ok(val as f64),
("yr", "dec") => Ok(val as f64 / 10.0),
_ => 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 {
@@ -153,9 +326,6 @@ fn string_to_duration(s: &str, span: Span, value_span: Span) -> Result<i64, Shel
Unit::Hour => return Ok(x * 60 * 60 * 1000 * 1000 * 1000),
Unit::Day => return Ok(x * 24 * 60 * 60 * 1000 * 1000 * 1000),
Unit::Week => return Ok(x * 7 * 24 * 60 * 60 * 1000 * 1000 * 1000),
Unit::Month => return Ok(x * 30 * 24 * 60 * 60 * 1000 * 1000 * 1000), //30 days to a month
Unit::Year => return Ok(x * 365 * 24 * 60 * 60 * 1000 * 1000 * 1000), //365 days to a year
Unit::Decade => return Ok(x * 10 * 365 * 24 * 60 * 60 * 1000 * 1000 * 1000), //365 days to a year
_ => {}
}
}
@@ -168,20 +338,133 @@ 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)),
_ => 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>>,
float_precision: usize,
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 {
let from_unit = "ns";
let duration = *val_num;
match convert_str_from_unit_to_unit(
duration,
from_unit,
&to_unit.item,
span,
*value_span,
) {
Ok(d) => {
if d.fract() == 0.0 {
Value::String {
val: format!("{} {}", d, &to_unit.item),
span: *value_span,
}
} else {
Value::String {
val: format!("{:.float_precision$} {}", d, &to_unit.item),
span: *value_span,
}
}
}
Err(e) => Value::Error { error: e },
}
} 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) => {
if d.fract() == 0.0 {
Value::String {
val: format!("{} {}", d, &to_unit.item),
span: *value_span,
}
} else {
Value::String {
val: format!("{:.float_precision$} {}", 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 +490,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, 2, span);
assert_eq!(actual, expected);
}
@@ -220,8 +504,9 @@ mod test {
val: 4 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected);
}
@@ -233,8 +518,9 @@ mod test {
val: 5 * 1000 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected);
}
@@ -246,8 +532,9 @@ mod test {
val: 1000 * 1000 * 1000,
span,
};
let convert_duration = None;
let actual = action(&word, span);
let actual = action(&word, &convert_duration, 2, span);
assert_eq!(actual, expected);
}
@@ -259,8 +546,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, 2, span);
assert_eq!(actual, expected);
}
@@ -272,8 +560,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, 2, span);
assert_eq!(actual, expected);
}
@@ -285,8 +574,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, 2, span);
assert_eq!(actual, expected);
}
@@ -298,8 +588,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, 2, span);
assert_eq!(actual, expected);
}
}

View File

@@ -1,3 +1,4 @@
use crate::input_handler::{operate, CellPathOnlyArgs};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@@ -28,7 +29,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["convert", "number", "size", "bytes"]
vec!["convert", "number", "bytes"]
}
fn run(
@@ -38,7 +39,9 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
into_filesize(engine_state, stack, call, input)
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn examples(&self) -> Vec<Example> {
@@ -84,37 +87,7 @@ impl Command for SubCommand {
}
}
fn into_filesize(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map(
move |v| {
if column_paths.is_empty() {
action(&v, 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)));
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
pub fn action(input: &Value, span: Span) -> Value {
pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
if let Ok(value_span) = input.span() {
match input {
Value::Filesize { .. } => input.clone(),

View File

@@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@@ -6,11 +7,17 @@ use nu_protocol::{
};
struct Arguments {
radix: Option<Value>,
column_paths: Vec<CellPath>,
radix: u32,
cell_paths: Option<Vec<CellPath>>,
little_endian: bool,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)]
pub struct SubCommand;
@@ -46,7 +53,29 @@ impl Command for SubCommand {
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
into_int(engine_state, stack, call, input)
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let radix = call.get_flag::<Value>(engine_state, stack, "radix")?;
let radix: u32 = match radix {
Some(Value::Int { val, span }) => {
if !(2..=36).contains(&val) {
return Err(ShellError::UnsupportedInput(
"Radix must lie in the range [2, 36]".to_string(),
span,
));
}
val as u32
}
Some(_) => 10,
None => 10,
};
let args = Arguments {
radix,
little_endian: call.has_flag("little-endian"),
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
fn examples(&self) -> Vec<Example> {
@@ -121,59 +150,9 @@ impl Command for SubCommand {
}
}
fn into_int(
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let head = call.head;
let options = Arguments {
radix: call.get_flag(engine_state, stack, "radix")?,
little_endian: call.has_flag("little-endian"),
column_paths: call.rest(engine_state, stack, 0)?,
};
let radix: u32 = match options.radix {
Some(Value::Int { val, .. }) => val as u32,
Some(_) => 10,
None => 10,
};
if let Some(val) = &options.radix {
if !(2..=36).contains(&radix) {
return Err(ShellError::UnsupportedInput(
"Radix must lie in the range [2, 36]".to_string(),
val.span()?,
));
}
}
input.map(
move |v| {
if options.column_paths.is_empty() {
action(&v, head, radix, options.little_endian)
} else {
let mut ret = v;
for path in &options.column_paths {
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| action(old, head, radix, options.little_endian)),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
)
}
pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Value {
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
let radix = args.radix;
let little_endian = args.little_endian;
match input {
Value::Int { val: _, .. } => {
if radix == 10 {
@@ -401,21 +380,45 @@ mod test {
let word = Value::test_string("10");
let expected = Value::test_int(10);
let actual = action(&word, Span::test_data(), 10, false);
let actual = action(
&word,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, expected);
}
#[test]
fn turns_binary_to_integer() {
let s = Value::test_string("0b101");
let actual = action(&s, Span::test_data(), 10, false);
let actual = action(
&s,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, Value::test_int(5));
}
#[test]
fn turns_hex_to_integer() {
let s = Value::test_string("0xFF");
let actual = action(&s, Span::test_data(), 16, false);
let actual = action(
&s,
&Arguments {
radix: 16,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual, Value::test_int(255));
}
@@ -423,7 +426,15 @@ mod test {
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
let integer_str = Value::test_string("36anra");
let actual = action(&integer_str, Span::test_data(), 10, false);
let actual = action(
&integer_str,
&Arguments {
radix: 10,
cell_paths: None,
little_endian: false,
},
Span::test_data(),
);
assert_eq!(actual.get_type(), Error)
}

View File

@@ -1,3 +1,4 @@
use crate::input_handler::{operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::{
ast::{Call, CellPath},
@@ -5,8 +6,21 @@ use nu_protocol::{
into_code, Category, Config, Example, IntoPipelineData, PipelineData, ShellError, Signature,
Span, SyntaxShape, Value,
};
use nu_utils::get_system_locale;
use num_format::ToFormattedString;
// TODO num_format::SystemLocale once platform-specific dependencies are stable (see Cargo.toml)
struct Arguments {
decimals_value: Option<i64>,
decimals: bool,
cell_paths: Option<Vec<CellPath>>,
config: Config,
}
impl CmdArgument for Arguments {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
self.cell_paths.take()
}
}
#[derive(Clone)]
pub struct SubCommand;
@@ -38,7 +52,7 @@ impl Command for SubCommand {
}
fn search_terms(&self) -> Vec<&str> {
vec!["convert", "str", "text"]
vec!["convert", "text"]
}
fn run(
@@ -149,9 +163,6 @@ fn string_helper(
let decimals = call.has_flag("decimals");
let head = call.head;
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let config = engine_state.get_config().clone();
if let Some(decimal_val) = decimals_value {
if decimals && decimal_val.is_negative() {
return Err(ShellError::UnsupportedInput(
@@ -160,6 +171,15 @@ fn string_helper(
));
}
}
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let config = engine_state.get_config().clone();
let args = Arguments {
decimals_value,
decimals,
cell_paths,
config,
};
match input {
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::String {
@@ -179,58 +199,18 @@ fn string_helper(
}
.into_pipeline_data())
}
_ => input.map(
move |v| {
if column_paths.is_empty() {
action(&v, head, decimals, decimals_value, false, &config)
} else {
let mut ret = v;
for path in &column_paths {
let config = config.clone();
let r = ret.update_cell_path(
&path.members,
Box::new(move |old| {
action(old, head, decimals, decimals_value, false, &config)
}),
);
if let Err(error) = r {
return Value::Error { error };
}
}
ret
}
},
engine_state.ctrlc.clone(),
),
_ => operate(action, args, input, head, engine_state.ctrlc.clone()),
}
}
pub fn action(
input: &Value,
span: Span,
decimals: bool,
digits: Option<i64>,
group_digits: bool,
config: &Config,
) -> Value {
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
let decimals = args.decimals;
let digits = args.decimals_value;
let config = &args.config;
match input {
Value::Int { val, .. } => {
let res = if group_digits {
format_int(*val) // int.to_formatted_string(*locale)
} else if let Some(dig) = digits {
let mut val_with_trailing_zeroes = val.to_string();
if dig != 0 {
val_with_trailing_zeroes.push('.');
}
for _ in 0..dig {
val_with_trailing_zeroes.push('0');
}
val_with_trailing_zeroes
} else {
val.to_string()
};
let decimal_value = digits.unwrap_or(0) as usize;
let res = format_int(*val, false, decimal_value);
Value::String { val: res, span }
}
Value::Float { val, .. } => {
@@ -305,21 +285,29 @@ pub fn action(
},
}
}
fn format_int(int: i64) -> String {
int.to_string()
// TODO once platform-specific dependencies are stable (see Cargo.toml)
// #[cfg(windows)]
// {
// int.to_formatted_string(&Locale::en)
// }
// #[cfg(not(windows))]
// {
// match SystemLocale::default() {
// Ok(locale) => int.to_formatted_string(&locale),
// Err(_) => int.to_formatted_string(&Locale::en),
// }
// }
fn format_int(int: i64, group_digits: bool, decimals: usize) -> String {
let locale = get_system_locale();
let str = if group_digits {
int.to_formatted_string(&locale)
} else {
int.to_string()
};
if decimals > 0 {
let decimal_point = locale.decimal();
format!(
"{}{decimal_point}{dummy:0<decimals$}",
str,
decimal_point = decimal_point,
dummy = "",
decimals = decimals
)
} else {
str
}
}
#[cfg(test)]

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 {
@@ -49,10 +49,17 @@ impl Command for Alias {
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Alias ll to ls -l",
example: "alias ll = ls -l",
result: None,
}]
vec![
Example {
description: "Alias ll to ls -l",
example: "alias ll = ls -l",
result: None,
},
Example {
description: "Make an alias that makes a list of all custom commands",
example: "alias customs = ($nu.scope.commands | where is_custom | get command)",
result: None,
},
]
}
}

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

@@ -0,0 +1,84 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::ReplOperation;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::Category;
use nu_protocol::IntoPipelineData;
use nu_protocol::{PipelineData, ShellError, Signature, SyntaxShape, Value};
#[derive(Clone)]
pub struct Commandline;
impl Command for Commandline {
fn name(&self) -> &str {
"commandline"
}
fn signature(&self) -> Signature {
Signature::build("commandline")
.switch(
"append",
"appends the string to the end of the buffer",
Some('a'),
)
.switch(
"insert",
"inserts the string into the buffer at the cursor position",
Some('i'),
)
.switch(
"replace",
"replaces the current contents of the buffer (default)",
Some('r'),
)
.optional(
"cmd",
SyntaxShape::String,
"the string to perform the operation with",
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"View or modify the current command line input buffer"
}
fn search_terms(&self) -> Vec<&str> {
vec!["repl", "interactive"]
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
if let Some(cmd) = call.opt::<Value>(engine_state, stack, 0)? {
let mut ops = engine_state
.repl_operation_queue
.lock()
.expect("repl op queue mutex");
ops.push_back(if call.has_flag("append") {
ReplOperation::Append(cmd.as_string()?)
} else if call.has_flag("insert") {
ReplOperation::Insert(cmd.as_string()?)
} else {
ReplOperation::Replace(cmd.as_string()?)
});
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
} else if let Some(ref cmd) = *engine_state
.repl_buffer_state
.lock()
.expect("repl buffer state mutex")
{
Ok(Value::String {
val: cmd.clone(),
span: call.head,
}
.into_pipeline_data())
} else {
Ok(Value::Nothing { span: call.head }.into_pipeline_data())
}
}
}

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

@@ -2,7 +2,8 @@ use nu_engine::{eval_block, CallExt};
use nu_protocol::ast::Call;
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
use nu_protocol::{
Category, Example, IntoPipelineData, PipelineData, Signature, SyntaxShape, Value,
Category, Example, ListStream, PipelineData, RawStream, ShellError, Signature, SyntaxShape,
Value,
};
#[derive(Clone)]
@@ -22,7 +23,7 @@ impl Command for Do {
.required("block", SyntaxShape::Any, "the block to run")
.switch(
"ignore-errors",
"ignore errors as the block runs",
"ignore shell errors as the block runs",
Some('i'),
)
.switch(
@@ -102,9 +103,75 @@ impl Command for Do {
Err(_) => Ok(PipelineData::new(call.head)),
}
} else if capture_errors {
// collect stdout and stderr and check exit code.
// if exit code is not 0, return back ShellError.
match result {
Ok(x) => Ok(x),
Err(err) => Ok((Value::Error { error: err }).into_pipeline_data()),
Ok(PipelineData::ExternalStream {
stdout,
stderr,
exit_code,
span,
metadata,
}) => {
// collect all output first.
let mut stderr_ctrlc = None;
let stderr_msg = match stderr {
None => "".to_string(),
Some(stderr_stream) => {
stderr_ctrlc = stderr_stream.ctrlc.clone();
stderr_stream.into_string().map(|s| s.item)?
}
};
let mut stdout_ctrlc = None;
let stdout_msg = match stdout {
None => "".to_string(),
Some(stdout_stream) => {
stdout_ctrlc = stdout_stream.ctrlc.clone();
stdout_stream.into_string().map(|s| s.item)?
}
};
let mut exit_code_ctrlc = None;
let exit_code: Vec<Value> = match exit_code {
None => vec![],
Some(exit_code_stream) => {
exit_code_ctrlc = exit_code_stream.ctrlc.clone();
exit_code_stream.into_iter().collect()
}
};
if let Some(Value::Int { val: code, .. }) = exit_code.last() {
// if exit_code is not 0, it indicates error occured, return back Err.
if *code != 0 {
return Err(ShellError::ExternalCommand(
"External command runs to failed".to_string(),
stderr_msg,
span,
));
}
}
// construct pipeline data to our caller
Ok(PipelineData::ExternalStream {
stdout: Some(RawStream::new(
Box::new(vec![Ok(stdout_msg.into_bytes())].into_iter()),
stdout_ctrlc,
span,
)),
stderr: Some(RawStream::new(
Box::new(vec![Ok(stderr_msg.into_bytes())].into_iter()),
stderr_ctrlc,
span,
)),
exit_code: Some(ListStream::from_stream(
exit_code.into_iter(),
exit_code_ctrlc,
)),
span,
metadata,
})
}
Ok(other) => Ok(other),
Err(e) => Err(e),
}
} else {
result
@@ -119,10 +186,15 @@ impl Command for Do {
result: Some(Value::test_string("hello")),
},
Example {
description: "Run the block and ignore errors",
description: "Run the block and ignore shell errors",
example: r#"do -i { thisisnotarealcommand }"#,
result: None,
},
Example {
description: "Abort the pipeline if a program returns a non-zero exit code",
example: r#"do -c { nu -c 'exit 1' } | myscarycommand"#,
result: None,
},
Example {
description: "Run the block, with a positional parameter",
example: r#"do {|x| 100 + $x } 50"#,

View File

@@ -2,7 +2,7 @@ use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Value,
Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
};
#[derive(Clone)]
@@ -14,7 +14,7 @@ impl Command for Echo {
}
fn usage(&self) -> &str {
"Echo the arguments back to the user."
"Returns its arguments, ignoring the piped-in value."
}
fn signature(&self) -> Signature {
@@ -24,7 +24,9 @@ impl Command for Echo {
}
fn extra_usage(&self) -> &str {
"Unlike `print`, this command returns an actual value that will be passed to the next command of the pipeline."
r#"When given no arguments, it returns an empty string. When given one argument,
it returns it. Otherwise, it returns a list of the arguments. There is usually
little reason to use this over just writing the values as-is."#
}
fn run(
@@ -61,13 +63,17 @@ impl Command for Echo {
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Put a hello message in the pipeline",
example: "echo 'hello'",
result: Some(Value::test_string("hello")),
description: "Put a list of numbers in the pipeline. This is the same as [1 2 3].",
example: "echo 1 2 3",
result: Some(Value::List {
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
span: Span::test_data(),
}),
},
Example {
description: "Print the value of the special '$nu' variable",
example: "echo $nu",
description:
"Returns the piped-in value, by using the special $in variable to obtain it.",
example: "echo $in",
result: None,
},
]

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

@@ -18,12 +18,12 @@ impl Command for ExportCommand {
}
fn usage(&self) -> &str {
"Export custom commands or environment variables from a module."
"Export definitions or environment variables from a 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 {
@@ -59,4 +59,8 @@ impl Command for ExportCommand {
}),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["module"]
}
}

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 {
@@ -51,4 +51,8 @@ impl Command for ExportAlias {
result: None,
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["aka", "abbr", "module"]
}
}

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 {
@@ -55,4 +55,8 @@ impl Command for ExportDef {
}),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["module"]
}
}

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.
@@ -81,4 +81,8 @@ export def-env cd_with_fallback [arg = ""] {
}),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["module"]
}
}

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 {
@@ -47,4 +47,8 @@ impl Command for ExportExtern {
result: None,
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["signature", "module", "declare"]
}
}

View File

@@ -3,35 +3,26 @@ use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{Category, Example, PipelineData, Signature, Span, SyntaxShape, Value};
#[derive(Clone)]
pub struct ExportEnv;
pub struct ExportUse;
impl Command for ExportEnv {
impl Command for ExportUse {
fn name(&self) -> &str {
"export env"
"export use"
}
fn usage(&self) -> &str {
"Export a block from a module that will be evaluated as an environment variable when imported."
"Use definitions from a module and export them from this module"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("export env")
.required(
"name",
SyntaxShape::String,
"name of the environment variable",
)
.required(
"block",
SyntaxShape::Block(Some(vec![])),
"body of the environment variable definition",
)
Signature::build("export use")
.required("pattern", SyntaxShape::ImportPattern, "import pattern")
.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"#
https://www.nushell.sh/book/thinking_in_nu.html"#
}
fn is_parser_keyword(&self) -> bool {
@@ -45,18 +36,25 @@ impl Command for ExportEnv {
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
//TODO: Add the env to stack
Ok(PipelineData::new(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Import and evaluate environment variable from a module",
example: r#"module foo { export env FOO_ENV { "BAZ" } }; use foo FOO_ENV; $env.FOO_ENV"#,
description: "Re-export a command from another module",
example: r#"module spam { export def foo [] { "foo" } }
module eggs { export use spam foo }
use eggs foo
foo
"#,
result: Some(Value::String {
val: "BAZ".to_string(),
val: "foo".to_string(),
span: Span::test_data(),
}),
}]
}
fn search_terms(&self) -> Vec<&str> {
vec!["reexport", "import", "module"]
}
}

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

@@ -1,3 +1,4 @@
use fancy_regex::Regex;
use nu_ansi_term::{
Color::{Default, Red, White},
Style,
@@ -11,7 +12,6 @@ use nu_protocol::{
ShellError, Signature, Span, Spanned, SyntaxShape, Value,
};
use std::borrow::Borrow;
#[derive(Clone)]
pub struct Help;
@@ -142,7 +142,7 @@ fn help(
cols.push("is_custom".into());
vals.push(Value::Bool {
val: decl.get_block_id().is_some(),
val: decl.is_custom_command(),
span: head,
});
@@ -243,7 +243,7 @@ fn help(
cols.push("is_custom".into());
vals.push(Value::Bool {
val: decl.get_block_id().is_some(),
val: decl.is_custom_command(),
span: head,
});
@@ -350,7 +350,7 @@ pub fn highlight_search_string(
string_style: &Style,
) -> Result<String, ShellError> {
let regex_string = format!("(?i){}", needle);
let regex = match regex::Regex::new(&regex_string) {
let regex = match Regex::new(&regex_string) {
Ok(regex) => regex,
Err(err) => {
return Err(ShellError::GenericError(
@@ -362,28 +362,47 @@ pub fn highlight_search_string(
));
}
};
// strip haystack to remove existing ansi style
let stripped_haystack = nu_utils::strip_ansi_likely(haystack);
let mut last_match_end = 0;
let style = Style::new().fg(White).on(Red);
let mut highlighted = String::new();
for cap in regex.captures_iter(haystack) {
let start = match cap.get(0) {
Some(cap) => cap.start(),
None => 0,
};
let end = match cap.get(0) {
Some(cap) => cap.end(),
None => 0,
};
highlighted.push_str(
&string_style
.paint(&haystack[last_match_end..start])
.to_string(),
);
highlighted.push_str(&style.paint(&haystack[start..end]).to_string());
last_match_end = end;
for cap in regex.captures_iter(stripped_haystack.as_ref()) {
match cap {
Ok(capture) => {
let start = match capture.get(0) {
Some(acap) => acap.start(),
None => 0,
};
let end = match capture.get(0) {
Some(acap) => acap.end(),
None => 0,
};
highlighted.push_str(
&string_style
.paint(&stripped_haystack[last_match_end..start])
.to_string(),
);
highlighted.push_str(&style.paint(&stripped_haystack[start..end]).to_string());
last_match_end = end;
}
Err(e) => {
return Err(ShellError::GenericError(
"Error with regular expression capture".into(),
e.to_string(),
None,
None,
Vec::new(),
));
}
}
}
highlighted.push_str(&string_style.paint(&haystack[last_match_end..]).to_string());
highlighted.push_str(
&string_style
.paint(&stripped_haystack[last_match_end..])
.to_string(),
);
Ok(highlighted)
}

View File

@@ -1,7 +1,7 @@
use nu_protocol::ast::{Call, Expr, Expression, ImportPatternMember};
use nu_protocol::ast::{Call, Expr, Expression};
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
Category, Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
};
#[derive(Clone)]
@@ -19,14 +19,14 @@ impl Command for Hide {
}
fn usage(&self) -> &str {
"Hide symbols in the current scope"
"Hide definitions in the current scope"
}
fn extra_usage(&self) -> &str {
r#"Symbols are hidden by priority: First aliases, then custom commands, then environment variables.
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 {
@@ -40,12 +40,15 @@ This command is a parser keyword. For details, check:
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let import_pattern = if let Some(Expression {
let env_var_name = if let Some(Expression {
expr: Expr::ImportPattern(pat),
..
}) = call.positional_nth(0)
{
pat
Spanned {
item: String::from_utf8_lossy(&pat.head.name).to_string(),
span: pat.head.span,
}
} else {
return Err(ShellError::GenericError(
"Unexpected import".into(),
@@ -56,78 +59,7 @@ This command is a parser keyword. For details, check:
));
};
let head_name_str = if let Ok(s) = String::from_utf8(import_pattern.head.name.clone()) {
s
} else {
return Err(ShellError::NonUtf8(import_pattern.head.span));
};
if let Some(module_id) = engine_state.find_module(&import_pattern.head.name, &[]) {
// The first word is a module
let module = engine_state.get_module(module_id);
let env_vars_to_hide = if import_pattern.members.is_empty() {
module.env_vars_with_head(&import_pattern.head.name)
} else {
match &import_pattern.members[0] {
ImportPatternMember::Glob { .. } => module.env_vars(),
ImportPatternMember::Name { name, span } => {
let mut output = vec![];
if let Some((name, id)) =
module.env_var_with_head(name, &import_pattern.head.name)
{
output.push((name, id));
} else if !(module.has_alias(name) || module.has_decl(name)) {
return Err(ShellError::EnvVarNotFoundAtRuntime(
String::from_utf8_lossy(name).into(),
*span,
));
}
output
}
ImportPatternMember::List { names } => {
let mut output = vec![];
for (name, span) in names {
if let Some((name, id)) =
module.env_var_with_head(name, &import_pattern.head.name)
{
output.push((name, id));
} else if !(module.has_alias(name) || module.has_decl(name)) {
return Err(ShellError::EnvVarNotFoundAtRuntime(
String::from_utf8_lossy(name).into(),
*span,
));
}
}
output
}
}
};
for (name, _) in env_vars_to_hide {
let name = if let Ok(s) = String::from_utf8(name.clone()) {
s
} else {
return Err(ShellError::NonUtf8(import_pattern.span()));
};
if stack.remove_env_var(engine_state, &name).is_none() {
return Err(ShellError::NotFound(
call.positional_nth(0)
.expect("already checked for present positional")
.span,
));
}
}
} else if !import_pattern.hidden.contains(&import_pattern.head.name)
&& stack.remove_env_var(engine_state, &head_name_str).is_none()
{
// TODO: we may want to error in the future
}
stack.remove_env_var(engine_state, &env_var_name.item);
Ok(PipelineData::new(call.head))
}

View File

@@ -0,0 +1,75 @@
use nu_engine::CallExt;
use nu_protocol::ast::Call;
use nu_protocol::engine::{Command, EngineState, Stack};
use nu_protocol::{
did_you_mean, Category, Example, PipelineData, ShellError, Signature, Span, Spanned,
SyntaxShape, Value,
};
#[derive(Clone)]
pub struct HideEnv;
impl Command for HideEnv {
fn name(&self) -> &str {
"hide-env"
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("hide-env")
.rest(
"name",
SyntaxShape::String,
"environment variable names to hide",
)
.switch(
"ignore-errors",
"do not throw an error if an environment variable was not found",
Some('i'),
)
.category(Category::Core)
}
fn usage(&self) -> &str {
"Hide environment variables in the current scope"
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
_input: PipelineData,
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
let env_var_names: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
let ignore_errors = call.has_flag("ignore-errors");
for name in env_var_names {
if stack.remove_env_var(engine_state, &name.item).is_none() && !ignore_errors {
let all_names: Vec<String> = stack
.get_env_var_names(engine_state)
.iter()
.cloned()
.collect();
if let Some(closest_match) = did_you_mean(&all_names, &name.item) {
return Err(ShellError::DidYouMeanCustom(
format!("Environment variable '{}' not found", name.item),
closest_match,
name.span,
));
} else {
return Err(ShellError::EnvVarNotFoundAtRuntime(name.item, name.span));
}
}
}
Ok(PipelineData::new(call.head))
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Hide an environment variable",
example: r#"let-env HZ_ENV_ABC = 1; hide-env HZ_ENV_ABC; 'HZ_ENV_ABC' in (env).name"#,
result: Some(Value::boolean(false, Span::test_data())),
}]
}
}

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

@@ -18,6 +18,10 @@ impl Command for Ignore {
Signature::build("ignore").category(Category::Core)
}
fn search_terms(&self) -> Vec<&str> {
vec!["silent", "quiet", "out-null"]
}
fn run(
&self,
_engine_state: &EngineState,

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 {

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