Compare commits

...

227 Commits

Author SHA1 Message Date
7dc1d6a350 Extract .nu-env tests and more granularity (#3078)
The autoenv logic mutates environment variables in the running session as
it operates and decides what to do for trusted directories containing `.nu-env`
files. Few of the ways to interact with it were all in a single test function.

We separate out all the ways that were done in the single test function to document
 it better. This will greatly help once we start refactoring our way out from setting
 environment variables this way to just setting them to `Scope`.

This is part of an on-going effort to keep variables (`PATH` and `ENV`)
in our `Scope` and rely on it for everything related to variables.

We expect to move away from setting (`std::*`) envrironment variables in the current
running process. This is non-trivial since we need to handle cases from vars
coming in from the outside world, prioritize, and also compare to the ones
we have both stored in memory and in configuration files.

Also to send out our in-memory (in `Scope`) variables properly to external
programs once we no longer rely on `std::env` vars from the running process.
2021-02-18 20:24:27 -05:00
deff1aa63b Bump to 0.27.1 (#3073) 2021-02-18 18:54:48 +13:00
08e7d0dfb6 Keep the environment properly set. (#3072)
* Revert "fix prompts on startup (#3056)"

This reverts commit b202951c1d.

* Ensure environment variables synced with global internal Nu Scope.
2021-02-18 15:56:14 +13:00
892aae267d add height method to Host trait, and implementors (#3064) 2021-02-17 09:02:13 +13:00
11a9144e84 update mock table for easier table testing (#3065) 2021-02-17 04:47:47 +13:00
039f223b53 Bump to 0.27 (#3063) 2021-02-16 19:20:05 +13:00
e1cb026184 Add back in column truncation (#3061) 2021-02-16 07:15:16 +13:00
2a96152a43 fix sample_config: date --format no longer supported (#3060)
Co-authored-by: alexhk <alexhk@protonmail.com>
2021-02-15 10:24:55 -06:00
0795d56c1c Source path including tilda (#3059)
* Use expand_path to handle the path including tilda

* Publish path::expand_path for using in nu-command

* cargo fmt

Co-authored-by: Wataru Yamaguchi <nagisamark2@gmail.com>
2021-02-15 21:41:49 +13:00
48a90fea70 Fix let-env (#3057) 2021-02-15 20:58:51 +13:00
b202951c1d fix prompts on startup (#3056)
* fix prompts on startup

* Try again
2021-02-15 20:14:16 +13:00
c3d2c61729 nu-parser: fix parsing comments with unclosed ' " [] {} in functions (#3053)
* nu-parser: fix parsing comments with unclosed ' " [] {} in functions

* add tests for last commit
2021-02-14 21:40:28 +13:00
d7b707939f Fix quick command reference link in README (#3052) 2021-02-14 18:38:01 +13:00
991ac6eb77 change help text (#3054) 2021-02-13 13:20:34 -06:00
011b7c4a07 refactor parser: rename method pub fn block to parse_block (#3047)
* refactor parser: rename method block to parse_block

* nu-cli/src/completion/engine.rs block to parse_block
2021-02-13 09:31:11 +13:00
617341f8d5 nu-engine: deserialize_struct: fix missing conversion from string to column path (#3048) 2021-02-13 09:29:38 +13:00
abd2632977 Remove accidental debug symbols in release (#3050) 2021-02-13 09:28:41 +13:00
5481db4079 Fix latest clippy warnings (#3049) 2021-02-12 23:13:14 +13:00
041086d22a add config "filesize_metric = true" for default formatting of filesize (#3045) 2021-02-11 21:52:34 +13:00
aa564f5072 display boolean config options as true/false instead of Yes/No (#3043) 2021-02-11 21:50:33 +13:00
8367f2001c update nuver (#3044) 2021-02-10 08:58:38 -06:00
1cfb228924 New termsize command (#3038)
* add term size command

* update w & h, add examples

* changed default to output table
2021-02-10 08:58:22 -06:00
b403fb1275 nu-parser + nu-protocol: switch to metric for KB, MB, GB, add KiB, MiB, GiB units (#3035)
fixes inconsistency with formatting/rendering which uses standard Rust byte_unit
https://en.wikipedia.org/wiki/Byte#Multiple-byte_units
2021-02-10 15:31:12 +13:00
3443ca40c5 updated a few items (#3040) 2021-02-09 17:11:36 -06:00
96f95653a6 added comment for table_mode (#3036) 2021-02-09 09:06:30 -06:00
7f7e8465da Fix fmt and small cleaning in nu-parser (#3033)
* parse_unit: reduce indentation in loop

* fix fmt: crates/nu-parser/src/lex/tests.rs
2021-02-09 17:46:10 +13:00
e3a273cf73 Update config (#3031) 2021-02-09 17:44:42 +13:00
233161d56e sort_by: support -r flag for reverse (#3025)
* sort_by: support -r flag for reverse

* Update sort_by.rs

Fix reverse test

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-02-08 11:10:06 +13:00
d883ab250a which: accept several applications/commands (#3024)
* which: accept several applications

* fix fmt: which_.rs
2021-02-08 08:17:06 +13:00
ef4e3f907c parser/refactor def (#2986)
* Move tests into own file

* Move data structs to own file

* Move functions parsing 1 Token (primitives) into own file

* Rename param_flag_list to signature

* Add tests

* Fix clippy lint

* Change imports to new lexer structure
2021-02-08 08:10:14 +13:00
debeadbf3f Soft rest arguments column path cohersions. (#3016) 2021-02-06 20:05:47 -05:00
d66baaceb9 Fix 'ps -l' output when a process doesn't have a parent process. (#3015)
Before, ps would not insert a value if the process didn't have a parent.
Now, ps will insert an empty cell. This caused broken tables as some
rows didn't have all the columns.
2021-02-06 22:41:08 +13:00
85329f9a81 Fix readme (#3013) 2021-02-06 14:18:38 +13:00
a5fefaf78b Ensure selection of columns are done once per column (#3012) 2021-02-05 19:34:26 -05:00
6f17662a4e Update some deps (#3011) 2021-02-06 09:54:54 +13:00
c83aea3c89 Bump to 0.26.1 (#3008) 2021-02-05 19:38:04 +13:00
67aaf4cb2d fix the ps command's virtual mem (#3007) 2021-02-05 18:57:49 +13:00
3083346884 update sysinfo to v16 (#3006) 2021-02-05 06:59:24 +13:00
d07789677f Clean up lexer (#2956)
* Document the lexer and lightly improve its names

The bulk of this pull request adds a substantial amount of new inline
documentation for the lexer. Along the way, I made a few minor changes
to the names in the lexer, most of which were internal.

The main change that affects other files is renaming `group` to `block`,
since the function is actually parsing a block (a list of groups).

* Further clean up the lexer

- Consolidate the logic of the various token builders into a single type
- Improve and clean up the event-driven BlockParser
- Clean up comment parsing. Comments now contain their original leading
  whitespace as well as trailing whitespace, and know how to move some
  leading whitespace back into the body based on how the lexer decides
  to dedent the comments. This preserves the original whitespace
  information while still making it straight-forward to eliminate leading
  whitespace in help comments.

* Update meta.rs

* WIP

* fix clippy

* remove unwraps

* remove unwraps

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
Co-authored-by: Jonathan Turner <jonathan.d.turner@gmail.com>
2021-02-04 20:20:21 +13:00
fb1846120d standardize on how to get file size (#2992)
* standardize on how to get file size

* forgot to remove comment

* make specified size lowercase

* fix the test due to precision

* added another test

* Update README.md

add contributors graphic

* clippy - test adjustment

* tweaked matching
2021-02-03 07:19:38 -06:00
da1e1295ea Update README.md 2021-02-03 15:08:18 +13:00
ecaea57263 Value helpers (#3000)
* Update README.md

add contributors graphic

* just a couple of helpers

* separated some helpers out to individual fns
2021-02-03 15:06:11 +13:00
fa928bd25d Minimal markdown syntax per element support. (#2997) 2021-02-02 12:09:19 -05:00
c1981dfc26 Fix README formatting (#2996) 2021-02-02 19:23:38 +13:00
fd41fa31d5 add $nothing and tests (#2995) 2021-02-02 19:23:12 +13:00
2c52144f41 Update README.md (#2993)
add contributors graphic
2021-02-02 07:05:37 +13:00
87c7898b65 update sysinfo due to breaking change with get_version (#2988) 2021-01-30 12:21:32 -06:00
44e088c6fe Move filesize to use bigint (#2984)
* Move filesize to be bigint-sized

* Add tests and fix filesize display

* clippy
2021-01-30 11:35:18 +13:00
7b4cbd7ce9 Few fixes for WASI build (#2983)
- Disable shadow-rs (libgit2-sys compilation on WASI fails for various strange reasons, so seems easier to disable altogether for now).
 - Disable directories-support (WASI doesn't have concept of user directory and such calls fail at runtime).
2021-01-30 09:11:07 +13:00
b052d524da added pow operator, and filesize math (#2976)
* added pow operator, and filesize math

* removed + and - arms, removed some pow, pow higher precedence

* Update value.rs

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-01-29 07:44:02 -06:00
47c4b8e88a allow str from to convert more things to string (#2977)
* allow str from to convert more things to string

* fixed FileSize so it reports with units configured

* added tests
2021-01-29 07:43:35 -06:00
d0a2a02eea Add possibility to declare optional parameters and switch flags (#2966)
* Add possibility to declare optional parameters and switch flags

With this commit applied it is now possible to specify optional parameters and flags
as switches. This PR **only** makes guarantees about **parsing** optional flags and
switches correctly. This PR **does not guarantee flawless functionality** of
optional parameters / switches within scripts.
functionality within scripts. Example:

test.nu
```shell
def my_command [
    opt_param?
    opt_param2?: int
    --switch
] {echo hi nushell}
```

```shell
> source test.nu
> my_command -h
───┬─────────
 0 │ hi
 1 │ nushell
───┴─────────
Usage:
  > my_command <mandatory_param> (opt_param) (opt_param2) {flags}

Parameters:
  <mandatory_param>
  (opt_param)
  (opt_param2)

Flags:
  -h, --help: Display this help message
  --switch
  --opt_flag <any>
```

* Update def docs
2021-01-28 06:31:29 +13:00
b1e1dab4cb add % operator for modulus, work with decimals (#2975)
* add % operator, work with decimals

* removed the % operator to reserve for something else
2021-01-26 12:42:34 -06:00
388973e9ab Bump to 0.26.0 (#2974) 2021-01-26 23:07:08 +13:00
2129ec7558 allow pad to use multi-byte chars (#2973) 2021-01-26 22:09:38 +13:00
82f122525c Delete nushell-demo.svg 2021-01-25 20:35:10 +13:00
7c4c00f1e6 Update README.md 2021-01-25 20:34:05 +13:00
fe6c7dc10a Add files via upload 2021-01-25 20:33:06 +13:00
9bc24e3b12 Remove unnecessary clone() (#2970) 2021-01-25 20:13:05 +13:00
833baca66e Add a new animated demo (#2971) 2021-01-25 20:12:44 +13:00
9fd92512a2 Use equality assert macros (#2969) 2021-01-25 18:16:10 +13:00
b692ca7896 Fix ps sys units (#2967)
* Fix the units for sys and ps

* Better conversion
2021-01-25 08:34:43 +13:00
52dc04a35a Error on bad row in column path (#2964)
* Error on bad row in column path

* Add more pathing tests
2021-01-22 18:14:13 -05:00
42b1287759 Parity and anchor carrying for str command suite. (#2965)
Bring the majority of str sub commands to parity supporting their actions
by column paths. Ensuring they carry over anchor meta data as well.
2021-01-22 18:13:30 -05:00
5a471aa1d0 fixed char signature (#2963) 2021-01-22 15:48:31 -06:00
a4b8d4a098 Add rest support to blocks (#2962) 2021-01-23 10:28:32 +13:00
a3be6affa4 fix some misalignment errors (#2959) 2021-01-23 07:39:09 +13:00
71b99edd48 parser/add rest args to def (#2961)
* Add rest arg to def

This commit applied adds the ability to define the rest parameter of a def
command. It does not implement the functionality to expand the rest argument in
a user defined def function.

The rest argument has to be exactly worded "...rest".

Example after this PR is applied:

file test.nu
```shell
def my_command [
    ...rest:int # My rest arg
] {
    echo 1 2 3
}
```

```shell
> source test.nu
> my_command -h
Usage:
  > my_command ...args {flags}

Parameters:
  ...args: My rest arg

Flags:
  -h, --help: Display this help message
```

* Fix space in help on wrong side
2021-01-23 07:13:29 +13:00
64553ddcb7 upgrade shadow-rs 0.5.23 (#2960)
* update to shadow-rs 0.4. use easy

* update shadow-rs to 0.5

* fix version not used

* update

* update Cargo.lock

* update Cargo.lock

* fix wasm build error when use dependence git2
fix error link:https://dev.azure.com/nushell/nushell/_build/results?buildId=4858&view=logs&j=1a745d4c-b027-5f34-06d8-d6f256bfe9f9&t=a0a335cb-fa1f-5bbf-be01-1a90d6899e54

* remove code not used; fix warning by RUSTFLAGS="-D warnings" build error

* upgrade shadow-rs 0.5.2

* upgrade shadow-rs 0.5.7

make nushell reduce dependence crates smaller and  build fast.

* upgrade shadow-rs 0.5.8

fix when use api 'strip_prefix()' method in less than rust1.45.0 build failed

* fix https://github.com/baoyachi/shadow-rs/issues?q=is%3Aissue+is%3Aclosed
2021-01-23 07:09:57 +13:00
2a42482ae9 Clean up and refactoring examples tests. (#2957) 2021-01-20 21:07:16 -05:00
11f345a8ae added more char escapes (#2955)
* added more char escapes

* move commands with \x1b over from char.rs to ansi.rs
2021-01-21 13:15:58 +13:00
fec50d8cfe Fix bug #2921 (#2945)
* Fix bug #2921

Moving whether a range should be parsed further back, giving e.G. parsing of
invocations precedence fixes the bug

* Add test
2021-01-21 07:58:37 +13:00
05e42381df Add --skip flag to nth command (#2953)
clippy & rustfmt included
2021-01-21 06:37:30 +13:00
b435075e09 Temporarily(?) switch from heim+uom to sysinfo (#2954)
* Switch from heim to sysinfo

* WIP

* more cleanup

* fmt

* lint
2021-01-20 20:18:38 +13:00
430da53f0b Replace dirs and directories with maintained (#2949) 2021-01-19 14:24:27 -06:00
2e6d836dd1 Flush out! lines, helps autoview (#2952) 2021-01-20 07:23:37 +13:00
899d324a9c fix: error Variable not in scope for a def parameter #2901 (#2951)
adding tests to notice regressions on this issue

Co-authored-by: hk <alexhaka10@protonmail.com>
2021-01-20 07:21:11 +13:00
576ed6a906 parser/split long short flags (#2944)
* Remove wrong test case

* Parse long and shortflags without space correctly

* Update param_flag_list.rs

* Update param_flag_list.rs

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-01-20 07:19:53 +13:00
d744cf8437 [Gitpod] Don't test removed feature 'test-bins' (#2948)
Fixes nushell/nushell#2947
2021-01-19 22:43:42 +13:00
088e662285 Replace git current_branch to shadow-rs branch (#2935)
* update to shadow-rs 0.4. use easy

* update shadow-rs to 0.5

* fix version not used

* update

* update Cargo.lock

* update Cargo.lock

* fix wasm build error when use dependence git2
fix error link:https://dev.azure.com/nushell/nushell/_build/results?buildId=4858&view=logs&j=1a745d4c-b027-5f34-06d8-d6f256bfe9f9&t=a0a335cb-fa1f-5bbf-be01-1a90d6899e54

* remove code not used; fix warning by RUSTFLAGS="-D warnings" build error

* upgrade shadow-rs 0.5.2

* upgrade shadow-rs 0.5.7

make nushell reduce dependence crates smaller and  build fast.

* upgrade shadow-rs 0.5.8

fix when use api 'strip_prefix()' method in less than rust1.45.0 build failed

* use shadow-rs branch replace with current_branch method;
remove and reduce git dependencies.

* upgrade shadow-rs 0.5.12-pre,test build error with wasm

* upgrade Cargo.lock

* upgarde shadow-rs depencdence

* fix build error in wasm

* add clippy warning
2021-01-16 07:06:29 +13:00
f9b0b81eb2 Add def documentation (#2939) 2021-01-15 20:21:18 +13:00
c5485c6501 a small regex optimization (#2937)
* a small regex optimization

* removed comments
2021-01-15 20:20:28 +13:00
d8ed01400f str set sub command removal. (#2940) 2021-01-14 18:55:37 -05:00
ebc4694e05 move keybinding_path to nu-data (#2927) 2021-01-14 06:31:47 +13:00
a9441d670e Revert tab completion changes (#2929)
* Undo tab completion changes

* Remove extra newline
2021-01-14 06:29:18 +13:00
495d2ebd70 Improve tab completion behaviour (#2916)
* Improve tab completion behaviour

* Fix clippy issue

* Add test cases
2021-01-13 17:04:29 +13:00
ad26adc3e3 remove set from windows cmd_builtins (#2924) 2021-01-13 14:46:58 +13:00
63a62e19f9 Update alias docs (#2925) 2021-01-13 14:46:15 +13:00
4f2ae34df9 Don't throw err on typename as parameter name (#2926)
Before this was an error:
`def e [path:path] {echo $path}`
Now its not.
2021-01-13 14:44:55 +13:00
a636f161a4 Add dirs dependency to nu-engine (#2922)
* Add dirs dependency to nu-engine

* Dir feature should be added to root features
2021-01-13 10:18:13 +13:00
dfb1e22559 Update alias docs to new syntax (#2917)
This confused me today after upgrading Nu. I believe this is now correct.
2021-01-13 08:30:27 +13:00
dff85a7f70 RangeIterator can also go down (#2913) 2021-01-13 08:27:54 +13:00
3be198d2f5 Don't print description in help if none exists (#2915) 2021-01-13 07:27:48 +13:00
d19314fe3a Fix the wasm build (#2919) 2021-01-13 07:14:35 +13:00
d06f457b2a nu-cli refactor moving commands into their own crate nu-command (#2910)
* move commands, futures.rs, script.rs, utils

* move over maybe_print_errors

* add nu_command crate references to nu_cli

* in commands.rs open up to pub mod from pub(crate)

* nu-cli, nu-command, and nu tests are now passing

* cargo fmt

* clean up nu-cli/src/prelude.rs

* code cleanup

* for some reason lex.rs was not formatted, may be causing my error

* remove mod completion from lib.rs which was not being used along with quickcheck macros

* add in allow unused imports

* comment out one failing external test; comment out one failing internal test

* revert commenting out failing tests; something else might be going on; someone with a windows machine should check and see what is going on with these failing windows tests

* Update Cargo.toml

Extend the optional features to nu-command

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-01-12 17:59:53 +13:00
7d07881d96 Bump to 0.25.2 (#2908) 2021-01-12 07:50:53 +13:00
3e6e3a207c Feature/def signature with comments (#2905)
* Put parse_definition related funcs into own module

* Add failing lexer test

* Implement Parsing of definition signature

This commit applied changes how the signature of a function is parsed. Before
there was a little bit of "quick-and-dirty" string-matching/parsing involved.
Now, a signature is a little bit more properly parsed.
The grammar of a definition signature understood by these parsing-functions is
as follows:
 `[ (parameter | flag | <eol>)* ]`
where
parameter is:
    `name (<:> type)? (<,> | <eol> | (#Comment <eol>))?`
flag is:
    `--name (-shortform)? (<:> type)? (<,> | <eol> | (#Comment <eol>))?`
(Note: After the last item no <,> has to come.)
Note: It is now possible to pass comments to flags and parameters
Example:
[
  d:int          # The required d parameter
  --x (-x):string # The all powerful x flag
  --y (-y):int    # The accompanying y flag
]

(Sadly there seems to be a bug (Or is this expected behaviour?) in the lexer, because of which `--x(-x)` would
be treated as one baseline token and is therefore not correctly recognized as 2. For
now a space has to be inserted)

During the implementation of the module, 2 question arose:
Should flag/parameter names be allowed to be type names?
Example case:
```shell
def f [ string ] { echo $string }
```
Currently an error is thrown

* Fix clippy lints

* Remove wrong comment

* Add spacing

* Add Cargo.lock
2021-01-12 06:53:58 +13:00
481c6d4511 nu_cli refactor in preparation for a crate called nu_command (#2907)
* move basic_shell_manager to nu-engine

* move basic_evaluation_context to nu-engine

* fix failing test in feature which commands/classified/external.rs
2021-01-11 17:58:15 +13:00
231a445809 working for comparing filepath to string (#2906)
* working for comparing filepath to string

* added tests
2021-01-11 16:41:19 +13:00
93e8f6c05e Split nu-cli into nu-cli/nu-engine (#2898)
We split off the evaluation engine part of nu-cli into its own crate. This helps improve build times for nu-cli by 17% in my tests. It also helps us see a bit better what's the core engine portion vs the part specific to the interactive CLI piece.

There's more than can be done here, but I think it's a good start in the right direction.
2021-01-10 15:50:49 +13:00
9de2144fc4 compare filepath and string (#2897) 2021-01-09 14:09:49 -06:00
363dc51ba0 Add aliased command to which output (#2894)
* Add aliased command to which output

* Fix alias arguments not being displayed
2021-01-10 06:19:46 +13:00
99117ff2ef Fix reading/writing bigint and bigdecimal (#2893) 2021-01-09 12:53:59 +13:00
5356cb9fbd Obey precedence rules in which; Fix #2875 (#2885)
* Obay precedence rules in which; Fix #2875

Before which did not obay the precedence of alias before def commands.
Furthermore, `which -a echo` would only report either an alias or a def command or an
internal command with the provided name. Not all.

With this commit applied its fixed :)

Example:
```shell
/home/leo/repos/nushell(fix/which_reports_wrong_usage)> def echo [] {^echo hi}
/home/leo/repos/nushell(fix/which_reports_wrong_usage)> echo
hi
/home/leo/repos/nushell(fix/which_reports_wrong_usage)> which -a echo
───┬──────┬──────────────────────────┬─────────
 # │ arg  │           path           │ builtin
───┼──────┼──────────────────────────┼─────────
 0 │ echo │ Nushell custom command   │ No
 1 │ echo │ Nushell built-in command │ Yes
 2 │ echo │ /usr/bin/echo            │ No
───┴──────┴──────────────────────────┴─────────
/home/leo/repos/nushell(fix/which_reports_wrong_usage)> alias echo = ^echo hi there
/home/leo/repos/nushell(fix/which_reports_wrong_usage)> echo
hi there
/home/leo/repos/nushell(fix/which_reports_wrong_usage)> which -a echo
───┬──────┬──────────────────────────┬─────────
 # │ arg  │           path           │ builtin
───┼──────┼──────────────────────────┼─────────
 0 │ echo │ Nushell alias            │ No
 1 │ echo │ Nushell custom command   │ No
 2 │ echo │ Nushell built-in command │ Yes
 3 │ echo │ /usr/bin/echo            │ No
───┴──────┴──────────────────────────┴─────────
```

* Fix clippy lint

* Fix vec always Some even if empty
2021-01-09 06:44:31 +13:00
0e13d9fbaa Rename the Path and Pattern primitives (#2889)
* Rename the Path primitive to FilePath

* Rename glob pattern also

* more fun

* Fix the Windows path methods
2021-01-08 20:30:41 +13:00
2dcb16870b Treat all the startup commands as a single script file (#2890) 2021-01-08 19:36:31 +13:00
ac9909112f Remove the line primitive (#2887) 2021-01-08 14:45:25 +13:00
eb3c2c9e76 Add comments to next LiteCommand (#2846)
This commit applied adds comments preceding a command to the LiteCommands new
field `comments`.

This can be usefull for example when defining a function with `def`. Nushell
could pick up the comments and display them when the user types `help my_def_func`.

Example
```shell
def my_echo [arg] { echo $arg }
```
The LiteCommand def will now contain the comments `My echo` and `It's much
better :)`.

The comment is not associated with the next command if there is a (or multiple) newline
between them.
Example
```shell

echo 42
```

This new functionality is similar to DocStrings. One might introduce a special
notation for such DocStrings, so that the parser can differentiate better
between discardable comments and usefull documentation.
2021-01-08 06:14:51 +13:00
3d29e3efbf Update README.md 2021-01-07 18:12:04 +13:00
f410fb6689 Document lexer (#2865)
* Update dependencies

* Document the lexer and lightly improve its names

The bulk of this pull request adds a substantial amount of new inline
documentation for the lexer. Along the way, I made a few minor changes
to the names in the lexer, most of which were internal.

The main change that affects other files is renaming `group` to `block`,
since the function is actually parsing a block (a list of groups).

* Fix rustfmt

* Update lock

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
Co-authored-by: Jonathan Turner <jonathan.d.turner@gmail.com>
2021-01-07 16:03:00 +13:00
eb62fd466e Adding coerce filesize functionality to math avg median (#2848)
* Adding coerce filesize functionality to math avg median

* Updating initial value creating in Math Summation Reducer

* Update reducers.rs

Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-01-07 16:01:52 +13:00
b50cdd6de8 Update dependency rust-embed now that issue with its use of syn has been fixed. (#2880)
* update the rust-embed dependency of nu-cli to 5.8.0 and undo the version pin of syn now that rust-embed-impl has been fixed

* unpin syn version in chart plugin
2021-01-07 14:33:39 +13:00
f38e2b5c6d updated dependencies (#2857)
Same as #2786

Co-authored-by: sousajo <sousajo@pop-os.localdomain>
Co-authored-by: Jonathan Turner <jonathandturner@users.noreply.github.com>
2021-01-07 13:38:22 +13:00
455915ec9e upgrade shadow-rs 0.5.8 (#2861)
* update to shadow-rs 0.4. use easy

* update shadow-rs to 0.5

* fix version not used

* update

* update Cargo.lock

* update Cargo.lock

* fix wasm build error when use dependence git2
fix error link:https://dev.azure.com/nushell/nushell/_build/results?buildId=4858&view=logs&j=1a745d4c-b027-5f34-06d8-d6f256bfe9f9&t=a0a335cb-fa1f-5bbf-be01-1a90d6899e54

* remove code not used; fix warning by RUSTFLAGS="-D warnings" build error

* upgrade shadow-rs 0.5.2

* upgrade shadow-rs 0.5.7

make nushell reduce dependence crates smaller and  build fast.

* upgrade shadow-rs 0.5.8

fix when use api 'strip_prefix()' method in less than rust1.45.0 build failed
2021-01-07 06:51:28 +13:00
2333158256 nucli refactor: move rustyline and ctrlc features in cli.rs to line_editor.rs (#2854)
* move rustyline and ctrlc features in cli.rs to feature.rs

* rename feature.rs to line_editor.rs
2021-01-07 06:47:36 +13:00
3ffa804088 Add syn to the lock file (#2871) 2021-01-06 15:58:04 +13:00
98810d22b1 Update Cargo.toml 2021-01-06 15:37:39 +13:00
5e72b2a797 Bump to 0.25.1 for the hotfix release (#2870) 2021-01-06 15:16:08 +13:00
7e4e7fa4a6 Pin the syn version to avoid breaking change (#2868)
* Pin the syn version to avoid breaking change

* pin syn in wasm also
2021-01-06 14:32:08 +13:00
d297199d7c Bump to 0.25.0 (#2860) 2021-01-05 18:10:24 +13:00
17a433996e rename set/set-env to let/let-env (#2859) 2021-01-05 12:30:55 +13:00
b9bb4692a4 Allow source during parsing. Hacky but works (#2855) 2021-01-04 19:32:17 +13:00
d05dcdda02 make nushell reduce dependence crates smaller and build fast (#2853)
* update to shadow-rs 0.4. use easy

* update shadow-rs to 0.5

* fix version not used

* update

* update Cargo.lock

* update Cargo.lock

* fix wasm build error when use dependence git2
fix error link:https://dev.azure.com/nushell/nushell/_build/results?buildId=4858&view=logs&j=1a745d4c-b027-5f34-06d8-d6f256bfe9f9&t=a0a335cb-fa1f-5bbf-be01-1a90d6899e54

* remove code not used; fix warning by RUSTFLAGS="-D warnings" build error

* upgrade shadow-rs 0.5.2

* upgrade shadow-rs 0.5.7

make nushell reduce dependence crates smaller and  build fast.
2021-01-04 06:14:03 +13:00
27fe356214 Add proper shadowing (#2851) 2021-01-03 20:48:02 +13:00
fc44df1e45 Don't leak set/set-env/source scopes via actions (#2849) 2021-01-03 19:44:21 +13:00
77f915befe Tighten how input streams handle nothing, and related (#2847) 2021-01-03 14:22:44 +13:00
a5f7600f6f Fix typos (#2842) 2021-01-02 17:24:32 +13:00
7eb8634ad7 Fix typo in sort-by error message (#2841) 2021-01-01 18:34:50 -06:00
452d8c06e9 Improve some errors, streamline internal error handling (#2839)
* Improve some errors, streamline internal error handling

* Fix lints
2021-01-02 08:52:19 +13:00
48f535f02e Display aliases and custom commands in which; fix #2810 (#2834)
* Display aliases and custom commands in which; Fix #2810

Example output of nu after the commit is applied:

```shell
/home/leo/repos/nushell(feature/which_inspect_alias)> def docker-ps [] { docker ps --format '{{json .}}' | from json -o }
/home/leo/repos/nushell(feature/which_inspect_alias)> which docker-ps
───┬───────────┬────────────────────────┬─────────
 # │    arg    │          path          │ builtin
───┼───────────┼────────────────────────┼─────────
 0 │ docker-ps │ nushell custom command │ No
───┴───────────┴────────────────────────┴─────────
/home/leo/repos/nushell(feature/which_inspect_alias)> alias d = gid pd
/home/leo/repos/nushell(feature/which_inspect_alias)> which d
───┬─────┬───────────────┬─────────
 # │ arg │     path      │ builtin
───┼─────┼───────────────┼─────────
 0 │ d   │ nushell alias │ No
───┴─────┴───────────────┴─────────
```

* Update documentation
2021-01-02 06:40:44 +13:00
43c10b0625 Properly handle commands defined inside of other commands (#2837)
* Fix function inner scopes

* tweak error
2021-01-01 19:23:54 +13:00
328b09fe04 Properly error when 'source' argument can't be found (#2836) 2021-01-01 17:33:38 +13:00
15d49e4096 Rust 1.49 Clippy Fixes (#2835) 2021-01-01 15:13:59 +13:00
3ef53fe2cd move create_default_context out of cli.rs and into its own mod (#2833) 2021-01-01 15:12:16 +13:00
7d8e759e98 Nucli refactor script mod (#2831)
* move process_script and run_script_standalone out of cli.rs

* cargo fmt

* code cleanup imports

* unused imports issue in cli.rs
2020-12-31 12:38:31 +13:00
69b3be61a4 Simplify run_block slightly (#2830)
* Simplify run_block slightly

* Add early return on C-c
2020-12-31 12:37:07 +13:00
79476a5cb2 Replace clipboard with arboard (#2832) 2020-12-31 06:16:02 +13:00
f449baf8de Change ls to output path (#2829)
* make name a path vs string

* add support for comparing path to string
2020-12-28 14:52:28 -06:00
5ff4bcfb7a Nucli refactor crate stream (#2828)
* nu-stream is building on its own, now clean up Cargo.toml

* replace the stream crate in nu-cli

* cc

* since we moved stream out of the nu-cli crate and into its own crate we need to remove pub(crate) and just make it pub

* clean up the prelude and hand merge everything together

* clean up Cargo.tom

* cargo fmt along with Cargo.lock
2020-12-28 18:34:27 +13:00
98537ce8b7 remove code not used. Fix use shadow-rs build warning (#2827)
* update to shadow-rs 0.4. use easy

* update shadow-rs to 0.5

* fix version not used

* update

* update Cargo.lock

* update Cargo.lock

* fix wasm build error when use dependence git2
fix error link:https://dev.azure.com/nushell/nushell/_build/results?buildId=4858&view=logs&j=1a745d4c-b027-5f34-06d8-d6f256bfe9f9&t=a0a335cb-fa1f-5bbf-be01-1a90d6899e54

* remove code not used; fix warning by RUSTFLAGS="-D warnings" build error

* upgrade shadow-rs 0.5.2
2020-12-28 08:00:14 +13:00
d2a00a2daa update to shadow-rs 0.5. make use easy (#2793)
* update to shadow-rs 0.4. use easy

* update shadow-rs to 0.5

* fix version not used

* update

* update Cargo.lock

* update Cargo.lock

* fix wasm build error when use dependence git2
fix error link:https://dev.azure.com/nushell/nushell/_build/results?buildId=4858&view=logs&j=1a745d4c-b027-5f34-06d8-d6f256bfe9f9&t=a0a335cb-fa1f-5bbf-be01-1a90d6899e54
2020-12-24 05:56:05 +13:00
f22938fc4a Add support for custom subcommands (#2814)
* Add support for custom subcommands

* clippy
2020-12-23 20:43:56 +13:00
1e67ae8e94 Fix broken links in the README (#2813) 2020-12-22 17:35:06 +13:00
c012d648fb Add experimental support for flags in custom commands (#2808)
* Add experimental support for flags in custom commands

* clippy
2020-12-21 20:36:59 +13:00
67acaae53c Rename cond math (#2807)
* Simplifies 'if' to work on the available scope rather than a stream

* Rename initializer/math for better readability

* Fix description

* fmt
2020-12-21 17:32:06 +13:00
e3da546e23 Simplifies 'if' to work on the available scope rather than a stream (#2805) 2020-12-21 16:02:39 +13:00
e5b136f70d Add script sourcing (#2803)
* Add script sourcing

* clippy
2020-12-19 20:47:34 +13:00
058ef69da3 Add set-env for setting environment variables (#2802) 2020-12-19 19:25:03 +13:00
2a483531a4 Bug report uses version command (#2797) 2020-12-19 18:25:32 +13:00
05202671db Improve errors on success (#2801) 2020-12-19 18:24:56 +13:00
8509873043 Parse mid-line comments (#2800) 2020-12-19 11:23:02 +13:00
57a2d695e2 Removing the defs inside of blocks for now (#2798) 2020-12-19 07:53:00 +13:00
0b5ab1ef22 Don't print a nothing value (#2796) 2020-12-19 05:48:22 +13:00
2eac79569c highlight trailing spaces in tables in darkgray (#2794)
* highlight trailing spaces in tables in darkgray

* added leading spaces highlighting

* added config point to change the color

* Trigger Build
2020-12-18 07:47:05 -06:00
ac578b8491 Multiline scripts part 2 (#2795)
* Begin allowing comments and multiline scripts.

* clippy

* Finish moving to groups. Test pass

* Keep going

* WIP

* WIP

* BROKEN WIP

* WIP

* WIP

* Fix more tests

* WIP: alias starts working

* Broken WIP

* Broken WIP

* Variables begin to work

* captures start working

* A little better but needs fixed scope

* Shorthand env setting

* Update main merge

* Broken WIP

* WIP

* custom command parsing

* Custom commands start working

* Fix coloring and parsing of block

* Almost there

* Add some tests

* Add more param types

* Bump version

* Fix benchmark

* Fix stuff
2020-12-18 20:53:49 +13:00
5183fd25bb Bump version (#2792) 2020-12-16 09:13:18 +13:00
10f5a8ef78 Update uom and heim dependencies (#2767)
v0.29.0 and earlier versions of `uom` fail to compile on nightly because
of now-ambiguous trait bounds. The issue was corrected in v0.30.0 of
`uom`. `uom` and `heim` dependencies have been updated to the
latest version to include this fix and allow nushell to compile on
nightly.

Co-authored-by: Boutin, Michael <mjboutin@ecolab.com>
2020-12-15 13:27:21 -06:00
a30837298d Bump version (#2791) 2020-12-16 06:30:50 +13:00
f377a3a7b4 Added math abs command. (#2789) 2020-12-16 05:37:12 +13:00
83c874666a Date utility commands (#2780)
* updated & added date related commands based on the new design

* added proper error handling when date format string is invalid

* fixed format issue

* fixed an issue caused due to the change in primitive Date type

* added `date list-timezone` command to list all supported time zones and updated `date to-timezone` accordingly
2020-12-12 12:18:03 -06:00
e000ed47cd Fix Gitpod dev setup by forcing a dev image rebuild (#2783) 2020-12-09 06:44:23 +13:00
af2f064f42 Add random chars cmd (#2782) 2020-12-09 06:43:46 +13:00
9c7b25134b Parsing: Explain parsing errors and show sample lines. (#2774)
This makes the errors slightly better. It took me a while to realize I was missing the `--raw` flag.

```
open "data.csv" | from csv --separator ';'
```

    error: Could not parse as CSV split by ',' (Line 1: expected 1 fields, found 14)
      ┌─ shell:1:1
      │
    1 │ open "data.csv" | from csv --separator ';'
      │ ^^^^ ------------------------------------------------- value originates from here
      │ │
      │ input cannot be parsed as CSV split by ','. Sample input:
    Name;Data
    Ugly;row
    AnotherUgly;row

I think this still needs some refinement. Maybe we don't want to show
the separator all the time, omitting the defaults or the separator
on other formats.
2020-12-07 07:19:04 +13:00
2d15df9e6c Revert "Bump Rustyline to 7.0.0 (#2776)" (#2778)
This reverts commit e73278990c.
2020-12-05 17:12:42 +13:00
d2ab287756 Tell Nu to look for hash's rest columns paths first. (#2777) 2020-12-04 13:49:58 -05:00
e73278990c Bump Rustyline to 7.0.0 (#2776)
* Bump Rustyline to 7.0.0

* Append history instead of always save

* Add associated type to Hinter

* Convert to using Rustyline KeyEvent

* Use AcceptOrInsertLine as struct

* Cargo fmt

* Make convert_keyevent pub

* Better naming for RL conversion
2020-12-05 06:29:40 +13:00
12bc92df35 Make run_block public (#2772) 2020-12-02 23:00:30 +13:00
f19a801022 enhanced version command with more info (#2773) 2020-12-01 13:57:49 -06:00
b193303aa3 Add hash command with base64 subcommand (#2769)
* WIP try testing hash command

Ensure test worked

fmt

WIP get it working for other types of base64

Use optional named arg

WIP

* rebased and refactored a little with encoding and decoding

Fix some typos

Add some more charactersets

refactor several args into the encoding config struct and fix character_set arg. It needs to match the field

Add main hash command so it can be found via help

Added tests for running the whole pipeline

* add test case to cover invalid character sets

* clippy and fmt
2020-12-01 06:47:35 +13:00
e299e76fcf Bump to 0.23 (#2766) 2020-11-25 07:22:27 +13:00
c857e18c4a Avoid subtract overflow when no ending index given. (#2764) 2020-11-24 05:50:38 -05:00
5fb3df4054 Initial implementation of the random decimal subcommand. (#2762)
Co-authored-by: Stacy Maydew <stacy.maydew@starlab.io>
2020-11-24 22:19:48 +13:00
8b597187fc Path Command Enhancement Project (#2742)
* Add string argument support for path subcommands

* Add --replace option to 'path extension' command

* Add examples of replacing for path extension

* Refactor path extension and its example

* Add replacement functionality to path basename

* Refactor path subcommands to support more args

This adds a lot of redundancy to non-relevant subcommands such as type,
exists or expand.

* Add replace and num_levels options to path dirname

* Rename num_levels option to num-levels

* Remove commented code

* Clean up path basename

* Fix path dirname description

* Add path filestem opts; Rename extension -> suffix

* Add prefix option and examples to path filestem

* Fix broken num-levels of path dirname

* Fix failing example test of path filestem

* Fix failing test of path extension

* Formatting

* Add Windows-specific path subcommand examples

`path expand` is still broken but otherwise seems to fix all examples
on Windows

* Fix weird path expand on Windows

Also disable example tests for path expand. Failed caconicalization
(e.g., due to path not existing) returns the original path so the
examples always fail.

* Formatting

* Return path datatype when appropriate

* Do not append empty remainder to path dirname

* Add tests for path subcommands

* Formatting

* Revisit path subcommand description strings

* Apply clippy suggestions; Formatting

* Remove problematic test checking '~' expansion

Wouldn't run on minimal due to useing optional dependency.
The test success was also deending on the presence of home dir on the
testing machine which might not be completely robust.

* Add missing newline to file
2020-11-24 22:18:38 +13:00
930f9f0063 Fix new clippy warnings (#2760)
* Fix new clippy warnings

* Fork serde-hjson and bring in

* Fork serde-hjson and bring in

* Fix clippy lint again
2020-11-22 13:37:16 +13:00
63d4df9810 Fix broken links to the documentation (#2755)
- fix the "installation chapter of the book" link, the current link has no "en/" for English as it does for other languages
- correct the link "learning resources in our [documentation]", missing in the new site, where the documentation is well highlighted in the top bar. Rephrased to point to the cookbook
2020-11-19 17:21:05 +13:00
13ba533fc4 helps table columns align a little bit better (#2753)
* helps table columns align a little bit better

* no change to push CI to work again.
2020-11-18 07:18:12 -06:00
6d60bab2fd fix zipped themes by adding zip feature (#2752) 2020-11-16 13:00:48 -06:00
5be774b2e5 these changes reduce size by 24mb (#2747) 2020-11-12 09:39:42 -06:00
b412ff92c0 Seq with dates (#2746)
* seq with dates - wip

* everything seems to be working, yay!

* clippy
2020-11-11 14:35:02 -06:00
5a75e11b0e Revert "Getting closer to multiline scripts (#2738)" (#2745)
This reverts commit e66bf70589.
2020-11-10 18:22:13 +13:00
e66bf70589 Getting closer to multiline scripts (#2738)
* Begin allowing comments and multiline scripts.

* clippy

* Finish moving to groups. Test pass
2020-11-10 16:52:42 +13:00
3924e9d50a added as_html switch so a selector can be passed to a selector (#2739) 2020-11-09 13:37:32 -06:00
8df748463d Getting ready for multiline scripts (#2737)
* WIP

* WIP

* WIP

* Tests are passing

* make parser more resilient

* lint
2020-11-10 05:27:07 +13:00
0113661c81 Flag to clear history file (#2720) 2020-11-10 05:23:41 +13:00
0ee054b14d Fix to md errors (#2729)
* Fix to md errors

* Fix variable name and avoid typecasts
2020-11-07 06:40:53 +13:00
80b39454ff Change Nu Shell and NuShell to Nushell (#2728) 2020-11-07 06:39:49 +13:00
97f3671e2c web scraping with css selectors (#2725)
* first step of making selector

* wip

* wip tests working

* probably good enough for a first pass

* oops, missed something.

* and something else...

* grrrr version errors
2020-11-03 15:46:42 -06:00
b674cee9d2 Remove the recursely-dep'd tests (#2727) 2020-11-04 09:26:07 +13:00
cb8491cfee Bump to 0.22 (#2726) 2020-11-04 07:31:41 +13:00
8196b031f8 Delete comments showing output of older nu version (#2717) 2020-11-03 19:29:13 +13:00
50dd56d3c4 bugfix for when pathext ends in ';' (#2723) 2020-11-02 13:00:47 -06:00
0f7e1d4d01 Support broad range of escape sequences (#2719)
* WIP

* changed to matches

* fixed a bug with osc

* changed back to if let because: clippy

* fixed example test
2020-10-30 15:06:15 -05:00
ec77c572b9 handle precision a tiny bit better than just hard coding to 4 decimal places. (#2712) 2020-10-31 06:40:28 +13:00
f97561c416 Inode added to ls -l (#2711) 2020-10-31 06:39:01 +13:00
5faa82e323 Update required rust version (#2718)
Co-authored-by: Joshua Shanks <jjshanks@stripe.com>
2020-10-30 12:17:49 -05:00
4e17292a12 Seq for nushell (#2704)
* seq command - WIP

* why, oh why

* works with parameters

* widths should've been optional

* dbg messages

* working. rest had to be first.

* updated so that it outputs a table instead of just strings

* made to work with floats, allowed separator be more than 1 char

* clippy

* fixed tests

* changed terminator help desc

* commit to get ci moving again
2020-10-29 15:51:48 -05:00
666fbbb0d1 Precision added to round cmd (#2710) 2020-10-29 16:14:08 +13:00
c6fe58467b Change alias shape inference to proposal of RFC#4 (#2685)
* Change alias shape inference to proposal of RFC#4

* Remove commented code

* Fix typo

* Change comment to be more informative

* Make match statement to lookup in table

* Remove resolved question

https://github.com/nushell/nushell/pull/2685#discussion_r509832054

* Pick ...or_insert_dependency functions into pieces

Previously there was get_shape_of_expr_or_insert dependency, now there is
get_shape_of_expr and get_shape_of_expr_or_insert_dependency

2 new functions have been added: get_result_shape_of_math_expr and
get_result_shape_of_math_expr_or_insert_dependency

* Remove flattening of deep binary expressions

Previously deep binary expressions have been flattened through the insertion of
fake vars. This logic was quite complicated. Now if a variable depends on the
result shape of a binary expression and the result shape can't be computed,
the variable simply depends on the whole binary.

* Change Expression::Variable(Variable::It(...)) to Expression::Variable(...)

* Simplify get_result_shapes_in_math_expr

* Simplify infer_shapes_in_binary_expr

* Clarify comment

* Clarify comment

* Fix clippy lint

* Move check for real var into checked_insert

* Remove comment

* Rename var
2020-10-29 06:49:38 +13:00
46d1938f5c add unicode to char command to print any unicode character (#2709)
* parsing unicode literal strings into chars

* refactored code to use -u option

* nudge ci
2020-10-28 09:08:09 -05:00
8229af7591 Improve parameter inference for blocks (#2708) 2020-10-28 07:47:11 +13:00
ee76523507 Add in parameter inference for blocks (#2706) 2020-10-27 20:37:35 +13:00
c283db373b Always escape non-literal arguments when running external command (#2697) 2020-10-27 16:33:40 +13:00
1b0ed30516 Added a bunch of extensions as helpers (#2698)
* Added a bunch of extensions as helpers

* change to restart ci
2020-10-26 09:25:06 -05:00
a6fdee4a51 bump to 0.21.1 (#2702)
* bump to 0.21.1

* bump trash version
2020-10-26 21:10:06 +13:00
6951fb440c Remove it expansion (#2701)
* Remove it-expansion, take 2

* Cleanup

* silly update to test CI
2020-10-26 19:55:52 +13:00
502c9ea706 Radix added to str decimal conversion (#2696) 2020-10-26 16:35:18 +13:00
22f67be461 added some weather symbols back and changed to emoji (#2695) 2020-10-22 15:10:19 -05:00
77ffd06715 Allow appending table literals. (#2693) 2020-10-22 03:26:30 -05:00
1d833ef972 Set weather chars as emoji only (#2691) 2020-10-22 14:36:27 +13:00
0d8064ed2d Add rounding functionalties (#2672)
* added math round

* added math floor

* added math ceil

* added math.md examples

* moved the detection of nonnumerical values in ceil/floor/round

* math round now works on streams

* math floor now works on streams

* math ceil now works on streams
2020-10-22 13:18:27 +13:00
cc06ea4d87 Add Tau constant (#2673)
Adds Tau constant using meval::Context.

Also adds a test to match pi's.

Note: Tau ends up not being more precise than 2*pi.

Resolves: #2258
2020-10-22 13:16:51 +13:00
3cf7652e86 fixed a bug where 'B' wasn't showing up (#2690)
right when get_appropriate_unit was called
2020-10-21 14:19:35 -05:00
1eb28c6cb6 add heavy & none table border options (#2686) 2020-10-21 08:53:08 -05:00
db590369a8 Fix filesize "B" regression (#2688) 2020-10-21 20:26:10 +13:00
f4d654d2a2 fix: remove duplicated "to" (#2682) 2020-10-21 05:35:43 +13:00
5725e55abb Flatten rows containing same sub-table columns with distinct column names. (#2684) 2020-10-20 05:37:40 -05:00
b6d19cc9fa Move command changes. Refactorings. (#2683)
Continuing on anchoring and improvements on Nu's overall internal commands (#2635).
`move column` sub command has been turned into the command `move` since
we use it to move exclusively columns. Examples added as well.

Fixed it to carry along any anchor locations that might be in place if
table to be moved originates from other sources.
2020-10-20 04:07:13 -05:00
bc6c884a14 added num-format to allow bytes to be formatted with commas. (#2681) 2020-10-19 12:52:11 -05:00
cb78bf8fd6 add filesize_format to config (#2676) 2020-10-19 11:34:39 -05:00
400bc97e35 Add parser improvements (#2679)
* Add parser improvements

Previously everything starting with "$" was parsed as a column path.
With this commit applied, the lite_arg starting with $ is parsed as
the most appropriate thing
- $true/$false ==> Expression::Boolean
- $(...) ==> Invocation
- $it ==> ColumnPath
- Anything with at least one '.' ==> ColumnPath
- Anything else ==> Variable

* Ignore failing tests
2020-10-19 20:03:14 +13:00
2fd464bf7b Refactor to md and Add Padding for Pretty Flag (#2678)
* refactor and cleanup to md

* Add padding around values in each row

* Add padding to test

* Update code to satisfy Clippy and pass other failing tests
2020-10-19 19:58:24 +13:00
e626522b3a LS support for other number formatting (#2650)
* make sort-by fail gracefully if mismatched types are compared

* Added a test to check if sorted-by with invalid types exists gracefully

* Linter changes

* removed redundant pattern matching

* Changed the error message

* Added a comma after every argument

* Changed the test to accomodate the new err messages

* Err message for sort-by invalid types now shows the mismatched types

* Lints problems

* Changed unwrap to expect

* Added the -f flag to rm command

Now when you a use rm -f there will be no error message, even if the
file doesnt actually exist

* Lint problems

* Fixed the wrong line

* Removed println

* Spelling mistake

* Fix problems when you mv a file into itself

* Lint mistakes

* Remove unecessary filtering in most cases

* Allow the removal of sockets

* Conditional compilations to systems without socket

* Add a size-format option to ls command

* Added kib and mib formating

* Make patterns lowercase

* New subcommand to format, filesize

* Forgot the linter once more

* Remove the ls changes since its no longer needed

* CI mistakes

* Lint stuff

* Fix lint

* Added formatting for bytes

* fix lint

* Changed the usage comment
2020-10-17 06:15:40 +13:00
791e07650d ColumnPath creation flexibility. (#2674) 2020-10-15 17:25:17 -05:00
bf2363947b Add pretty flag to to md (#2640)
* First draft for adding a `pretty` flag to `to md`

* rustfmt

* Fix Clippy warnings

* rustfmt

* Using Clippy suggestion broken code, reverting and putting in a statement to ignore clippy warning

* Add test for `to md -p`
2020-10-15 16:20:55 +13:00
a2cc2259e7 add bson and sqlite to wix (#2668)
* add bson and sqlite to wix

* add sqlite and bson from and to
2020-10-14 04:46:06 -05:00
808fe496a6 Fix typo in test support crate description (#2669) 2020-10-14 04:45:32 -05:00
2fb48bd6ac Flatten command. (#2670) 2020-10-14 04:36:11 -05:00
636 changed files with 29037 additions and 14491 deletions

View File

@ -23,8 +23,25 @@ A clear and concise description of what you expected to happen.
If applicable, add screenshots to help explain your problem.
**Configuration (please complete the following information):**
- OS (e.g. Windows):
- Nu version (you can use the `version` command to find out):
- Optional features (if any):
Add any other context about the problem here.
Run `version | pivot` and paste the output to show OS, features, etc.
```
> version | pivot
╭───┬────────────────────┬───────────────────────────────────────────────────────────────────────╮
│ # │ Column0 │ Column1 │
├───┼────────────────────┼───────────────────────────────────────────────────────────────────────┤
│ 0 │ version │ 0.24.1 │
│ 1 │ build_os │ macos-x86_64 │
│ 2 │ rust_version │ rustc 1.48.0 │
│ 3 │ cargo_version │ cargo 1.48.0 │
│ 4 │ pkg_version │ 0.24.1 │
│ 5 │ build_time │ 2020-12-18 09:54:09 │
│ 6 │ build_rust_channel │ release │
│ 7 │ features │ ctrlc, default, directories, dirs, git, ichwh, ptree, rich-benchmark, │
│ │ │ rustyline, term, uuid, which, zip │
╰───┴────────────────────┴───────────────────────────────────────────────────────────────────────╯
```
**Add any other context about the problem here.**

6
.gitpod.Dockerfile vendored
View File

@ -1,5 +1,9 @@
FROM gitpod/workspace-full
# Gitpod will not rebuild Nushell's dev image unless *some* change is made to this Dockerfile.
# To force a rebuild, simply increase this counter:
ENV TRIGGER_REBUILD 1
USER gitpod
RUN sudo apt-get update && \
@ -11,4 +15,4 @@ RUN sudo apt-get update && \
rust-lldb \
&& sudo rm -rf /var/lib/apt/lists/*
ENV RUST_LLDB=/usr/bin/lldb-8
ENV RUST_LLDB=/usr/bin/lldb-11

View File

@ -4,7 +4,7 @@ tasks:
- name: Clippy
init: cargo clippy --all --features=stable -- -D clippy::result_unwrap_used -D clippy::option_unwrap_used
- name: Testing
init: cargo test --all --features=stable,test-bins
init: cargo test --all --features=stable
- name: Build
init: cargo build --features=stable
- name: Nu

3626
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ license = "MIT"
name = "nu"
readme = "README.md"
repository = "https://github.com/nushell/nushell"
version = "0.21.0"
version = "0.27.1"
[workspace]
members = ["crates/*/"]
@ -18,63 +18,80 @@ members = ["crates/*/"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
nu-cli = {version = "0.21.0", path = "./crates/nu-cli"}
nu-data = {version = "0.21.0", path = "./crates/nu-data"}
nu-errors = {version = "0.21.0", path = "./crates/nu-errors"}
nu-parser = {version = "0.21.0", path = "./crates/nu-parser"}
nu-plugin = {version = "0.21.0", path = "./crates/nu-plugin"}
nu-protocol = {version = "0.21.0", path = "./crates/nu-protocol"}
nu-source = {version = "0.21.0", path = "./crates/nu-source"}
nu-value-ext = {version = "0.21.0", path = "./crates/nu-value-ext"}
nu-cli = { version = "0.27.1", path = "./crates/nu-cli", default-features = false }
nu-command = { version = "0.27.1", path = "./crates/nu-command" }
nu-data = { version = "0.27.1", path = "./crates/nu-data" }
nu-engine = { version = "0.27.1", path = "./crates/nu-engine" }
nu-errors = { version = "0.27.1", path = "./crates/nu-errors" }
nu-parser = { version = "0.27.1", path = "./crates/nu-parser" }
nu-plugin = { version = "0.27.1", path = "./crates/nu-plugin" }
nu-protocol = { version = "0.27.1", path = "./crates/nu-protocol" }
nu-source = { version = "0.27.1", path = "./crates/nu-source" }
nu-value-ext = { version = "0.27.1", path = "./crates/nu-value-ext" }
nu_plugin_binaryview = {version = "0.21.0", path = "./crates/nu_plugin_binaryview", optional = true}
nu_plugin_chart = {version = "0.21.0", path = "./crates/nu_plugin_chart", optional = true}
nu_plugin_fetch = {version = "0.21.0", path = "./crates/nu_plugin_fetch", optional = true}
nu_plugin_from_bson = {version = "0.21.0", path = "./crates/nu_plugin_from_bson", optional = true}
nu_plugin_from_sqlite = {version = "0.21.0", path = "./crates/nu_plugin_from_sqlite", optional = true}
nu_plugin_inc = {version = "0.21.0", path = "./crates/nu_plugin_inc", optional = true}
nu_plugin_match = {version = "0.21.0", path = "./crates/nu_plugin_match", optional = true}
nu_plugin_post = {version = "0.21.0", path = "./crates/nu_plugin_post", optional = true}
nu_plugin_ps = {version = "0.21.0", path = "./crates/nu_plugin_ps", optional = true}
nu_plugin_s3 = {version = "0.21.0", path = "./crates/nu_plugin_s3", optional = true}
nu_plugin_start = {version = "0.21.0", path = "./crates/nu_plugin_start", optional = true}
nu_plugin_sys = {version = "0.21.0", path = "./crates/nu_plugin_sys", optional = true}
nu_plugin_textview = {version = "0.21.0", path = "./crates/nu_plugin_textview", optional = true}
nu_plugin_to_bson = {version = "0.21.0", path = "./crates/nu_plugin_to_bson", optional = true}
nu_plugin_to_sqlite = {version = "0.21.0", path = "./crates/nu_plugin_to_sqlite", optional = true}
nu_plugin_tree = {version = "0.21.0", path = "./crates/nu_plugin_tree", optional = true}
nu_plugin_xpath = {version = "0.21.0", path = "./crates/nu_plugin_xpath", optional = true}
nu_plugin_binaryview = { version = "0.27.1", path = "./crates/nu_plugin_binaryview", optional = true }
nu_plugin_chart = { version = "0.27.1", path = "./crates/nu_plugin_chart", optional = true }
nu_plugin_fetch = { version = "0.27.1", path = "./crates/nu_plugin_fetch", optional = true }
nu_plugin_from_bson = { version = "0.27.1", path = "./crates/nu_plugin_from_bson", optional = true }
nu_plugin_from_sqlite = { version = "0.27.1", path = "./crates/nu_plugin_from_sqlite", optional = true }
nu_plugin_inc = { version = "0.27.1", path = "./crates/nu_plugin_inc", optional = true }
nu_plugin_match = { version = "0.27.1", path = "./crates/nu_plugin_match", optional = true }
nu_plugin_post = { version = "0.27.1", path = "./crates/nu_plugin_post", optional = true }
nu_plugin_ps = { version = "0.27.1", path = "./crates/nu_plugin_ps", optional = true }
nu_plugin_s3 = { version = "0.27.1", path = "./crates/nu_plugin_s3", optional = true }
nu_plugin_selector = { version = "0.27.1", path = "./crates/nu_plugin_selector", optional = true }
nu_plugin_start = { version = "0.27.1", path = "./crates/nu_plugin_start", optional = true }
nu_plugin_sys = { version = "0.27.1", path = "./crates/nu_plugin_sys", optional = true }
nu_plugin_textview = { version = "0.27.1", path = "./crates/nu_plugin_textview", optional = true }
nu_plugin_to_bson = { version = "0.27.1", path = "./crates/nu_plugin_to_bson", optional = true }
nu_plugin_to_sqlite = { version = "0.27.1", path = "./crates/nu_plugin_to_sqlite", optional = true }
nu_plugin_tree = { version = "0.27.1", path = "./crates/nu_plugin_tree", optional = true }
nu_plugin_xpath = { version = "0.27.1", path = "./crates/nu_plugin_xpath", optional = true }
# Required to bootstrap the main binary
clap = "2.33.3"
ctrlc = {version = "3.1.6", optional = true}
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
log = "0.4.11"
ctrlc = { version = "3.1.7", optional = true }
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
itertools = "0.10.0"
log = "0.4.14"
pretty_env_logger = "0.4.0"
[dev-dependencies]
nu-test-support = { version = "0.27.1", path = "./crates/nu-test-support" }
dunce = "1.0.1"
nu-test-support = {version = "0.21.0", path = "./crates/nu-test-support"}
serial_test = "0.5.1"
[build-dependencies]
[features]
ctrlc-support = ["nu-cli/ctrlc"]
directories-support = ["nu-cli/directories", "nu-cli/dirs", "nu-data/directories", "nu-data/dirs"]
git-support = ["nu-cli/git2"]
ptree-support = ["nu-cli/ptree"]
rich-benchmark = ["nu-cli/rich-benchmark"]
rustyline-support = ["nu-cli/rustyline-support"]
term-support = ["nu-cli/term"]
uuid-support = ["nu-cli/uuid_crate"]
which-support = ["nu-cli/ichwh", "nu-cli/which"]
ctrlc-support = ["nu-cli/ctrlc", "nu-command/ctrlc"]
directories-support = [
"nu-cli/directories",
"nu-cli/dirs",
"nu-command/directories",
"nu-command/dirs",
"nu-data/directories",
"nu-data/dirs",
"nu-engine/dirs",
]
ptree-support = ["nu-cli/ptree", "nu-command/ptree"]
rustyline-support = ["nu-cli/rustyline-support", "nu-command/rustyline-support"]
term-support = ["nu-cli/term", "nu-command/term"]
uuid-support = ["nu-cli/uuid_crate", "nu-command/uuid_crate"]
which-support = [
"nu-cli/ichwh",
"nu-cli/which",
"nu-command/ichwh",
"nu-command/which",
]
default = [
"nu-cli/shadow-rs",
"sys",
"ps",
"textview",
"inc",
"git-support",
"directories-support",
"ctrlc-support",
"which-support",
@ -85,12 +102,26 @@ default = [
"match",
"post",
"fetch",
"rich-benchmark",
"zip-support",
]
extra = ["default", "binaryview", "tree", "clipboard-cli", "trash-support", "start", "bson", "sqlite", "s3", "chart", "xpath"]
stable = ["default"]
wasi = ["inc", "match", "directories-support", "ptree-support", "match", "tree", "rustyline-support"]
stable = ["default"]
extra = [
"default",
"binaryview",
"tree",
"clipboard-cli",
"trash-support",
"start",
"bson",
"sqlite",
"s3",
"chart",
"xpath",
"selector",
]
wasi = ["inc", "match", "ptree-support", "match", "tree", "rustyline-support"]
trace = ["nu-parser/trace"]
@ -102,19 +133,27 @@ post = ["nu_plugin_post"]
ps = ["nu_plugin_ps"]
sys = ["nu_plugin_sys"]
textview = ["nu_plugin_textview"]
zip-support = ["nu-cli/zip", "nu-command/zip"]
# Extra
binaryview = ["nu_plugin_binaryview"]
bson = ["nu_plugin_from_bson", "nu_plugin_to_bson"]
chart = ["nu_plugin_chart"]
clipboard-cli = ["nu-cli/clipboard-cli"]
clipboard-cli = ["nu-cli/clipboard-cli", "nu-command/clipboard-cli"]
s3 = ["nu_plugin_s3"]
selector = ["nu_plugin_selector"]
sqlite = ["nu_plugin_from_sqlite", "nu_plugin_to_sqlite"]
start = ["nu_plugin_start"]
trash-support = ["nu-cli/trash-support"]
trash-support = ["nu-cli/trash-support", "nu-command/trash-support"]
tree = ["nu_plugin_tree"]
xpath = ["nu_plugin_xpath"]
[profile.release]
#strip = "symbols" #Couldn't get working +nightly
codegen-units = 1 #Reduce parallel codegen units
lto = true #Link Time Optimization
opt-level = 'z' #Optimize for size
# Core plugins that ship with `cargo install nu` by default
# Currently, Cargo limits us to installing only one binary
# unless we use [[bin]], so we use this as a workaround
@ -190,6 +229,31 @@ name = "nu_plugin_extra_xpath"
path = "src/plugins/nu_plugin_extra_xpath.rs"
required-features = ["xpath"]
[[bin]]
name = "nu_plugin_extra_selector"
path = "src/plugins/nu_plugin_extra_selector.rs"
required-features = ["selector"]
[[bin]]
name = "nu_plugin_extra_from_bson"
path = "src/plugins/nu_plugin_extra_from_bson.rs"
required-features = ["bson"]
[[bin]]
name = "nu_plugin_extra_to_bson"
path = "src/plugins/nu_plugin_extra_to_bson.rs"
required-features = ["bson"]
[[bin]]
name = "nu_plugin_extra_from_sqlite"
path = "src/plugins/nu_plugin_extra_from_sqlite.rs"
required-features = ["sqlite"]
[[bin]]
name = "nu_plugin_extra_to_sqlite"
path = "src/plugins/nu_plugin_extra_to_sqlite.rs"
required-features = ["sqlite"]
# Main nu binary
[[bin]]
name = "nu"

View File

@ -7,11 +7,11 @@
[![The Changelog #363](https://img.shields.io/badge/The%20Changelog-%23363-61c192.svg)](https://changelog.com/podcast/363)
[![@nu_shell](https://img.shields.io/badge/twitter-@nu_shell-1DA1F3?style=flat-square)](https://twitter.com/nu_shell)
## Nu Shell
## Nushell
A new type of shell.
![Example of nushell](images/nushell-autocomplete.gif "Example of nushell")
![Example of nushell](images/nushell-autocomplete5.gif "Example of nushell")
## Status
@ -34,7 +34,7 @@ There are also [good first issues](https://github.com/nushell/nushell/issues?q=i
We also have an active [Discord](https://discord.gg/NtAbbGn) and [Twitter](https://twitter.com/nu_shell) if you'd like to come and chat with us.
You can also find more learning resources in our [documentation](https://www.nushell.sh/documentation.html) site.
You can also find information on more specific topics in our [cookbook](https://www.nushell.sh/cookbook/).
Try it in Gitpod.
@ -44,19 +44,19 @@ Try it in Gitpod.
### Local
Up-to-date installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/en/installation.html). **Windows users**: please note that Nu works on Windows 10 and does not currently have Windows 7/8.1 support.
Up-to-date installation instructions can be found in the [installation chapter of the book](https://www.nushell.sh/book/installation.html). **Windows users**: please note that Nu works on Windows 10 and does not currently have Windows 7/8.1 support.
To build Nu, you will need to use the **latest stable (1.41 or later)** version of the compiler.
To build Nu, you will need to use the **latest stable (1.47 or later)** version of the compiler.
Required dependencies:
* pkg-config and libssl (only needed on Linux)
* On Debian/Ubuntu: `apt install pkg-config libssl-dev`
- pkg-config and libssl (only needed on Linux)
- On Debian/Ubuntu: `apt install pkg-config libssl-dev`
Optional dependencies:
* To use Nu with all possible optional features enabled, you'll also need the following:
* On Linux (on Debian/Ubuntu): `apt install libxcb-composite0-dev libx11-dev`
- To use Nu with all possible optional features enabled, you'll also need the following:
- On Linux (on Debian/Ubuntu): `apt install libxcb-composite0-dev libx11-dev`
To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs/) and the latest stable compiler via `rustup install stable`):
@ -64,7 +64,7 @@ To install Nu via cargo (make sure you have installed [rustup](https://rustup.rs
cargo install nu
```
You can also build Nu yourself with all the bells and whistles (be sure to have installed the [dependencies](https://www.nushell.sh/book/en/installation.html#dependencies) for your platform), once you have checked out this repo with git:
You can also build Nu yourself with all the bells and whistles (be sure to have installed the [dependencies](https://www.nushell.sh/book/installation.html#dependencies) for your platform), once you have checked out this repo with git:
```bash
cargo build --workspace --features=extra
@ -139,9 +139,9 @@ Just as the Unix philosophy, Nu allows commands to output from stdout and read f
Additionally, commands can output structured data (you can think of this as a third kind of stream).
Commands that work in the pipeline fit into one of three categories:
* Commands that produce a stream (eg, `ls`)
* Commands that filter a stream (eg, `where type == "Dir"`)
* Commands that consume the output of the pipeline (eg, `autoview`)
- Commands that produce a stream (eg, `ls`)
- Commands that filter a stream (eg, `where type == "Dir"`)
- Commands that consume the output of the pipeline (eg, `autoview`)
Commands are separated by the pipe symbol (`|`) to denote a pipeline flowing left to right.
@ -219,22 +219,22 @@ We can pipeline this into a command that gets the contents of one of the columns
name │ nu
readme │ README.md
repository │ https://github.com/nushell/nushell
version │ 0.15.1
version │ 0.21.0
───────────────┴────────────────────────────────────
```
Finally, we can use commands outside of Nu once we have the data we want:
```shell
> open Cargo.toml | get package.version | echo $it
0.15.1
> open Cargo.toml | get package.version
0.21.0
```
Here we use the variable `$it` to refer to the value being piped to the external command.
### Configuration
Nu has early support for configuring the shell. You can refer to the book for a list of [all supported variables](https://www.nushell.sh/book/en/configuration.html).
Nu has early support for configuring the shell. You can refer to the book for a list of [all supported variables](https://www.nushell.sh/book/configuration.html).
To set one of these variables, you can use `config set`. For example:
@ -270,49 +270,55 @@ If the plugin is a sink, it is given the full vector of final data and is given
Nu adheres closely to a set of goals that make up its design philosophy. As features are added, they are checked against these goals.
* First and foremost, Nu is cross-platform. Commands and techniques should carry between platforms and offer first-class consistent support for Windows, macOS, and Linux.
- First and foremost, Nu is cross-platform. Commands and techniques should carry between platforms and offer first-class consistent support for Windows, macOS, and Linux.
* Nu ensures direct compatibility with existing platform-specific executables that make up people's workflows.
- Nu ensures direct compatibility with existing platform-specific executables that make up people's workflows.
* Nu's workflow and tools should have the usability in day-to-day experience of using a shell in 2019 (and beyond).
- Nu's workflow and tools should have the usability in day-to-day experience of using a shell in 2019 (and beyond).
* Nu views data as both structured and unstructured. It is a structured shell like PowerShell.
- Nu views data as both structured and unstructured. It is a structured shell like PowerShell.
* Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state.
- Finally, Nu views data functionally. Rather than using mutation, pipelines act as a means to load, change, and save data without mutable state.
## Commands
You can find a list of Nu commands, complete with documentation, in [quick command references](https://www.nushell.sh/documentation.html#quick-command-references).
You can find a list of Nu commands, complete with documentation, in [quick command references](https://www.nushell.sh/book/command_reference.html).
## Progress
Nu is in heavy development, and will naturally change as it matures and people use it. The chart below isn't meant to be exhaustive, but rather helps give an idea for some of the areas of development and their relative completion:
| Features | Not started | Prototype | MVP | Preview | Mature | Notes
| -------- |:-----------:|:---------:|:---:|:-------:|:------:| -----
| Aliases | | X | | | | Initial implementation but lacks necessary features
| Notebook | | X | | | | Initial jupyter support, but it loses state and lacks features
| File ops | | | X | | | cp, mv, rm, mkdir have some support, but lacking others
| Environment | | X | | | | Temporary environment, but no session-wide env variables
| Shells | | X | | | | Basic value and file shells, but no opt-in/opt-out for commands
| Protocol | | | X | | | Streaming protocol is serviceable
| Plugins | | X | | | | Plugins work on one row at a time, lack batching and expression eval
| Errors | | | X | | | Error reporting works, but could use usability polish
| Documentation | | X | | | | Book and related are barebones and lack task-based lessons
| Paging | | X | | | | Textview has paging, but we'd like paging for tables
| Functions| X | | | | | No functions, yet, only aliases
| Variables| X | | | | | Nu doesn't yet support variables
| Completions | | X | | | | Completions are currently barebones, at best
| Type-checking | | X | | | | Commands check basic types, but input/output isn't checked
| Features | Not started | Prototype | MVP | Preview | Mature | Notes |
| ------------- | :---------: | :-------: | :-: | :-----: | :----: | -------------------------------------------------------------------- |
| Aliases | | X | | | | Initial implementation but lacks necessary features |
| Notebook | | X | | | | Initial jupyter support, but it loses state and lacks features |
| File ops | | | X | | | cp, mv, rm, mkdir have some support, but lacking others |
| Environment | | X | | | | Temporary environment, but no session-wide env variables |
| Shells | | X | | | | Basic value and file shells, but no opt-in/opt-out for commands |
| Protocol | | | X | | | Streaming protocol is serviceable |
| Plugins | | X | | | | Plugins work on one row at a time, lack batching and expression eval |
| Errors | | | X | | | Error reporting works, but could use usability polish |
| Documentation | | X | | | | Book and related are barebones and lack task-based lessons |
| Paging | | X | | | | Textview has paging, but we'd like paging for tables |
| Functions | | X | | | | No functions, yet, only aliases |
| Variables | | X | | | | Nu doesn't yet support variables |
| Completions | | X | | | | Completions are currently barebones, at best |
| Type-checking | | X | | | | Commands check basic types, but input/output isn't checked |
## Current Roadmap
We've added a `Roadmap Board` to help collaboratively capture the direction we're going for the current release as well as capture some important issues we'd like to see in NuShell. You can find the Roadmap [here](https://github.com/nushell/nushell/projects/2).
We've added a `Roadmap Board` to help collaboratively capture the direction we're going for the current release as well as capture some important issues we'd like to see in Nushell. You can find the Roadmap [here](https://github.com/nushell/nushell/projects/2).
## Contributing
See [Contributing](CONTRIBUTING.md) for details.
Thanks to all the people who already contributed!
<a href="https://github.com/nushell/nushell/graphs/contributors">
<img src="https://contributors-img.web.app/image?repo=nushell/nushell" />
</a>
## License
The project is made available under the MIT license. See the `LICENSE` file for more information.

View File

@ -1,107 +1,111 @@
[package]
authors = ["The Nu Project Contributors"]
build = "build.rs"
description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-cli"
version = "0.21.0"
version = "0.27.1"
[lib]
doctest = false
[dependencies]
nu-data = {version = "0.21.0", path = "../nu-data"}
nu-errors = {version = "0.21.0", path = "../nu-errors"}
nu-parser = {version = "0.21.0", path = "../nu-parser"}
nu-plugin = {version = "0.21.0", path = "../nu-plugin"}
nu-protocol = {version = "0.21.0", path = "../nu-protocol"}
nu-source = {version = "0.21.0", path = "../nu-source"}
nu-table = {version = "0.21.0", path = "../nu-table"}
nu-test-support = {version = "0.21.0", path = "../nu-test-support"}
nu-value-ext = {version = "0.21.0", path = "../nu-value-ext"}
nu-command = { version = "0.27.1", path = "../nu-command" }
nu-data = { version = "0.27.1", path = "../nu-data" }
nu-engine = { version = "0.27.1", path = "../nu-engine" }
nu-errors = { version = "0.27.1", path = "../nu-errors" }
nu-json = { version = "0.27.1", path = "../nu-json" }
nu-parser = { version = "0.27.1", path = "../nu-parser" }
nu-plugin = { version = "0.27.1", path = "../nu-plugin" }
nu-protocol = { version = "0.27.1", path = "../nu-protocol" }
nu-source = { version = "0.27.1", path = "../nu-source" }
nu-stream = { version = "0.27.1", path = "../nu-stream" }
nu-table = { version = "0.27.1", path = "../nu-table" }
nu-test-support = { version = "0.27.1", path = "../nu-test-support" }
nu-value-ext = { version = "0.27.1", path = "../nu-value-ext" }
Inflector = "0.11"
ansi_term = "0.12.1"
async-recursion = "0.3.1"
async-trait = "0.1.40"
base64 = "0.12.3"
arboard = { version = "1.1.0", optional = true }
async-recursion = "0.3.2"
async-trait = "0.1.42"
base64 = "0.13.0"
bigdecimal = { version = "0.2.0", features = ["serde"] }
byte-unit = "4.0.9"
bytes = "0.5.6"
calamine = "0.16.1"
chrono = {version = "0.4.15", features = ["serde"]}
bytes = "1.0.1"
calamine = "0.17.0"
chrono = { version = "0.4.19", features = ["serde"] }
chrono-tz = "0.5.3"
clap = "2.33.3"
codespan-reporting = "0.9.5"
csv = "1.1.3"
ctrlc = {version = "3.1.6", optional = true}
codespan-reporting = "0.11.0"
csv = "1.1.5"
ctrlc = { version = "3.1.7", optional = true }
derive-new = "0.5.8"
directories = {version = "3.0.1", optional = true}
dirs = {version = "3.0.1", optional = true}
directories-next = { version = "2.0.0", optional = true }
dirs-next = { version = "2.0.0", optional = true }
dtparse = "1.2.0"
dunce = "1.0.1"
eml-parser = "0.1.0"
encoding_rs = "0.8.28"
filesize = "0.2.0"
fs_extra = "1.2.0"
futures = {version = "0.3.5", features = ["compat", "io-compat"]}
futures-util = "0.3.5"
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
futures-util = "0.3.12"
futures_codec = "0.4.1"
getset = "0.1.1"
git2 = {version = "0.13.11", default_features = false, optional = true}
glob = "0.3.0"
heim = {version = "0.1.0-beta.3", optional = true}
htmlescape = "0.3.1"
ical = "0.6.0"
ical = "0.7.0"
ichwh = { version = "0.3.4", optional = true }
indexmap = {version = "1.6.0", features = ["serde-1"]}
itertools = "0.9.0"
log = "0.4.11"
indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0"
lazy_static = "1.*"
log = "0.4.14"
meval = "0.2.0"
num-bigint = {version = "0.3.0", features = ["serde"]}
num-bigint = { version = "0.3.1", features = ["serde"] }
num-format = { version = "0.4.0", features = ["with-num-bigint"] }
num-traits = "0.2.12"
parking_lot = "0.11.0"
num-traits = "0.2.14"
parking_lot = "0.11.1"
pin-utils = "0.1.0"
pretty-hex = "0.2.0"
ptree = {version = "0.3.0", optional = true}
pretty-hex = "0.2.1"
ptree = { version = "0.3.1", optional = true }
query_interface = "0.3.5"
quick-xml = "0.18.1"
rand = "0.7.3"
regex = "1.3.9"
roxmltree = "0.13.0"
rust-embed = "5.6.0"
quick-xml = "0.21.0"
rand = "0.8.3"
rayon = "1.5.0"
regex = "1.4.3"
roxmltree = "0.14.0"
rust-embed = "5.9.0"
rustyline = { version = "6.3.0", optional = true }
serde = {version = "1.0.115", features = ["derive"]}
serde-hjson = "0.9.1"
serde = { version = "1.0.123", features = ["derive"] }
serde_bytes = "0.11.5"
serde_ini = "0.2.0"
serde_json = "1.0.57"
serde_json = "1.0.61"
serde_urlencoded = "0.7.0"
serde_yaml = "0.8.13"
sha2 = "0.9.1"
shellexpand = "2.0.0"
serde_yaml = "0.8.16"
sha2 = "0.9.3"
shellexpand = "2.1.0"
strip-ansi-escapes = "0.1.0"
sxd-document = "0.3.2"
sxd-xpath = "0.4.2"
tempfile = "3.1.0"
term = {version = "0.6.1", optional = true}
tempfile = "3.2.0"
term = { version = "0.7.0", optional = true }
term_size = "0.3.2"
termcolor = "1.1.0"
toml = "0.5.6"
unicode-segmentation = "1.6.0"
uom = {version = "0.28.0", features = ["f64", "try-from"]}
uuid_crate = {package = "uuid", version = "0.8.1", features = ["v4"], optional = true}
which = {version = "4.0.2", optional = true}
zip = {version = "0.5.7", optional = true}
Inflector = "0.11"
clipboard = {version = "0.5.0", optional = true}
encoding_rs = "0.8.24"
rayon = "1.4.0"
trash = {version = "1.1.1", optional = true}
termcolor = "1.1.2"
titlecase = "1.1.0"
toml = "0.5.8"
trash = { version = "1.3.0", optional = true }
unicode-segmentation = "1.7.1"
url = "2.1.1"
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
which = { version = "4.0.2", optional = true }
zip = { version = "0.5.9", optional = true }
shadow-rs = { version = "0.5", default-features = false, optional = true }
[target.'cfg(unix)'.dependencies]
umask = "1.0.0"
users = "0.10.0"
users = "0.11.0"
# TODO this will be possible with new dependency resolver
# (currently on nightly behind -Zfeatures=itarget):
@ -112,18 +116,20 @@ users = "0.10.0"
[dependencies.rusqlite]
features = ["bundled", "blob"]
optional = true
version = "0.24.0"
version = "0.24.2"
[build-dependencies]
git2 = {version = "0.13.11", optional = true}
shadow-rs = "0.5"
[dev-dependencies]
quickcheck = "0.9.2"
quickcheck_macros = "0.9.1"
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
[features]
clipboard-cli = ["clipboard"]
rich-benchmark = ["heim"]
rustyline-support = ["rustyline"]
default = ["shadow-rs"]
clipboard-cli = ["arboard"]
rustyline-support = ["rustyline", "nu-engine/rustyline-support"]
stable = []
trash-support = ["trash"]
dirs = ["dirs-next"]
directories = ["directories-next"]

View File

@ -1,36 +1,3 @@
use std::path::Path;
use std::{env, fs, io};
fn main() -> Result<(), io::Error> {
let out_dir = env::var_os("OUT_DIR").expect(
"\
OUT_DIR environment variable not found. \
OUT_DIR is guaranteed to to exist in a build script by cargo - see \
https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-build-scripts\
");
let latest_commit_hash = latest_commit_hash(env::current_dir()?).unwrap_or_default();
let commit_hash_path = Path::new(&out_dir).join("git_commit_hash");
fs::write(commit_hash_path, latest_commit_hash)?;
Ok(())
}
#[allow(unused_variables)]
fn latest_commit_hash<P: AsRef<Path>>(dir: P) -> Result<String, Box<dyn std::error::Error>> {
#[cfg(feature = "git2")]
{
use git2::Repository;
let dir = dir.as_ref();
Ok(Repository::discover(dir)?
.head()?
.peel_to_commit()?
.id()
.to_string())
}
#[cfg(not(feature = "git2"))]
{
Ok(String::new())
}
fn main() -> shadow_rs::SdResult<()> {
shadow_rs::new()
}

File diff suppressed because it is too large Load Diff

View File

@ -1,64 +0,0 @@
use crate::commands::Command;
use indexmap::IndexMap;
use nu_errors::ShellError;
use nu_parser::SignatureRegistry;
use nu_protocol::Signature;
use parking_lot::Mutex;
use std::sync::Arc;
#[derive(Debug, Clone, Default)]
pub struct CommandRegistry {
registry: Arc<Mutex<IndexMap<String, Command>>>,
}
impl SignatureRegistry for CommandRegistry {
fn has(&self, name: &str) -> bool {
let registry = self.registry.lock();
registry.contains_key(name)
}
fn get(&self, name: &str) -> Option<Signature> {
let registry = self.registry.lock();
registry.get(name).map(|command| command.signature())
}
fn clone_box(&self) -> Box<dyn SignatureRegistry> {
Box::new(self.clone())
}
}
impl CommandRegistry {
pub fn new() -> CommandRegistry {
CommandRegistry {
registry: Arc::new(Mutex::new(IndexMap::default())),
}
}
}
impl CommandRegistry {
pub fn get_command(&self, name: &str) -> Option<Command> {
let registry = self.registry.lock();
registry.get(name).cloned()
}
pub fn expect_command(&self, name: &str) -> Result<Command, ShellError> {
self.get_command(name).ok_or_else(|| {
ShellError::untagged_runtime_error(format!("Could not load command: {}", name))
})
}
pub fn has(&self, name: &str) -> bool {
let registry = self.registry.lock();
registry.contains_key(name)
}
pub fn insert(&mut self, name: impl Into<String>, command: Command) {
let mut registry = self.registry.lock();
registry.insert(name.into(), command);
}
pub fn names(&self) -> Vec<String> {
let registry = self.registry.lock();
registry.keys().cloned().collect()
}
}

View File

@ -1,351 +0,0 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_data::config;
use nu_errors::ShellError;
use nu_parser::SignatureRegistry;
use nu_protocol::hir::{ClassifiedCommand, Expression, NamedValue, SpannedExpression, Variable};
use nu_protocol::{
hir::Block, CommandAction, NamedType, PositionalType, ReturnSuccess, Signature, SyntaxShape,
UntaggedValue, Value,
};
use nu_source::Tagged;
use std::collections::HashMap;
pub struct Alias;
#[derive(Deserialize)]
pub struct AliasArgs {
pub name: Tagged<String>,
pub args: Vec<Value>,
pub block: Block,
pub infer: Option<bool>,
pub save: Option<bool>,
}
#[async_trait]
impl WholeStreamCommand for Alias {
fn name(&self) -> &str {
"alias"
}
fn signature(&self) -> Signature {
Signature::build("alias")
.required("name", SyntaxShape::String, "the name of the alias")
.required("args", SyntaxShape::Table, "the arguments to the alias")
.required(
"block",
SyntaxShape::Block,
"the block to run as the body of the alias",
)
.switch("infer", "infer argument types (experimental)", Some('i'))
.switch("save", "save the alias to your config", Some('s'))
}
fn usage(&self) -> &str {
"Define a shortcut for another command."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
alias(args, registry).await
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "An alias without parameters",
example: "alias say-hi [] { echo 'Hello!' }",
result: None,
},
Example {
description: "An alias with a single parameter",
example: "alias l [x] { ls $x }",
result: None,
},
]
}
}
pub async fn alias(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let mut raw_input = args.raw_input.clone();
let (
AliasArgs {
name,
args: list,
block,
infer,
save,
},
_ctx,
) = args.process(&registry).await?;
let mut processed_args: Vec<String> = vec![];
if let Some(true) = save {
let mut result = nu_data::config::read(name.clone().tag, &None)?;
// process the alias to remove the --save flag
let left_brace = raw_input.find('{').unwrap_or(0);
let right_brace = raw_input.rfind('}').unwrap_or_else(|| raw_input.len());
let left = raw_input[..left_brace]
.replace("--save", "") // TODO using regex (or reconstruct string from AST?)
.replace("-si", "-i")
.replace("-s ", "")
.replace("-is", "-i");
let right = raw_input[right_brace..]
.replace("--save", "")
.replace("-si", "-i")
.replace("-s ", "")
.replace("-is", "-i");
raw_input = format!("{}{}{}", left, &raw_input[left_brace..right_brace], right);
// create a value from raw_input alias
let alias: Value = raw_input.trim().to_string().into();
let alias_start = raw_input.find('[').unwrap_or(0); // used to check if the same alias already exists
// add to startup if alias doesn't exist and replce if it does
match result.get_mut("startup") {
Some(startup) => {
if let UntaggedValue::Table(ref mut commands) = startup.value {
if let Some(command) = commands.iter_mut().find(|command| {
let cmd_str = command.as_string().unwrap_or_default();
cmd_str.starts_with(&raw_input[..alias_start])
}) {
*command = alias;
} else {
commands.push(alias);
}
}
}
None => {
let table = UntaggedValue::table(&[alias]);
result.insert("startup".to_string(), table.into_value(Tag::default()));
}
}
config::write(&result, &None)?;
}
for item in list.iter() {
if let Ok(string) = item.as_string() {
processed_args.push(format!("${}", string));
} else {
return Err(ShellError::labeled_error(
"Expected a string",
"expected a string",
item.tag(),
));
}
}
if let Some(true) = infer {
Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::AddAlias(
name.to_string(),
to_arg_shapes(processed_args, &block, &registry)?,
block,
),
)))
} else {
Ok(OutputStream::one(ReturnSuccess::action(
CommandAction::AddAlias(
name.to_string(),
processed_args
.into_iter()
.map(|arg| (arg, SyntaxShape::Any))
.collect(),
block,
),
)))
}
}
fn to_arg_shapes(
args: Vec<String>,
block: &Block,
registry: &CommandRegistry,
) -> Result<Vec<(String, SyntaxShape)>, ShellError> {
match find_block_shapes(block, registry) {
Ok(found) => Ok(args
.iter()
.map(|arg| {
(
arg.clone(),
match found.get(arg) {
None | Some((_, None)) => SyntaxShape::Any,
Some((_, Some(shape))) => *shape,
},
)
})
.collect()),
Err(err) => Err(err),
}
}
type ShapeMap = HashMap<String, (Span, Option<SyntaxShape>)>;
fn check_insert(
existing: &mut ShapeMap,
to_add: (String, (Span, Option<SyntaxShape>)),
) -> Result<(), ShellError> {
match (to_add.1).1 {
None => match existing.get(&to_add.0) {
None => {
existing.insert(to_add.0, to_add.1);
Ok(())
}
Some(_) => Ok(()),
},
Some(new) => match existing.insert(to_add.0.clone(), ((to_add.1).0, Some(new))) {
None => Ok(()),
Some(exist) => match exist.1 {
None => Ok(()),
Some(shape) => match shape {
SyntaxShape::Any => Ok(()),
shape if shape == new => Ok(()),
_ => Err(ShellError::labeled_error_with_secondary(
"Type conflict in alias variable use",
format!("{:?}", new),
(to_add.1).0,
format!("{:?}", shape),
exist.0,
)),
},
},
},
}
}
fn check_merge(existing: &mut ShapeMap, new: &ShapeMap) -> Result<(), ShellError> {
for (k, v) in new.iter() {
check_insert(existing, (k.clone(), *v))?;
}
Ok(())
}
fn find_expr_shapes(
spanned_expr: &SpannedExpression,
registry: &CommandRegistry,
) -> Result<ShapeMap, ShellError> {
match &spanned_expr.expr {
// TODO range will need similar if/when invocations can be parsed within range expression
Expression::Binary(bin) => find_expr_shapes(&bin.left, registry).and_then(|mut left| {
find_expr_shapes(&bin.right, registry)
.and_then(|right| check_merge(&mut left, &right).map(|()| left))
}),
Expression::Block(b) => find_block_shapes(&b, registry),
Expression::Path(path) => match &path.head.expr {
Expression::Invocation(b) => find_block_shapes(&b, registry),
Expression::Variable(Variable::Other(var, _)) => {
let mut result = HashMap::new();
result.insert(var.to_string(), (spanned_expr.span, None));
Ok(result)
}
_ => Ok(HashMap::new()),
},
_ => Ok(HashMap::new()),
}
}
fn find_block_shapes(block: &Block, registry: &CommandRegistry) -> Result<ShapeMap, ShellError> {
let apply_shape = |found: ShapeMap, sig_shape: SyntaxShape| -> ShapeMap {
found
.iter()
.map(|(v, sh)| match sh.1 {
None => (v.clone(), (sh.0, Some(sig_shape))),
Some(shape) => (v.clone(), (sh.0, Some(shape))),
})
.collect()
};
let mut arg_shapes = HashMap::new();
for pipeline in &block.block {
for classified in &pipeline.list {
match classified {
ClassifiedCommand::Expr(spanned_expr) => {
let found = find_expr_shapes(&spanned_expr, registry)?;
check_merge(&mut arg_shapes, &found)?
}
ClassifiedCommand::Internal(internal) => {
if let Some(signature) = registry.get(&internal.name) {
if let Some(positional) = &internal.args.positional {
for (i, spanned_expr) in positional.iter().enumerate() {
let found = find_expr_shapes(&spanned_expr, registry)?;
if i >= signature.positional.len() {
if let Some((sig_shape, _)) = &signature.rest_positional {
check_merge(
&mut arg_shapes,
&apply_shape(found, *sig_shape),
)?;
} else {
unreachable!("should have error'd in parsing");
}
} else {
let (pos_type, _) = &signature.positional[i];
match pos_type {
// TODO pass on mandatory/optional?
PositionalType::Mandatory(_, sig_shape)
| PositionalType::Optional(_, sig_shape) => {
check_merge(
&mut arg_shapes,
&apply_shape(found, *sig_shape),
)?;
}
}
}
}
}
if let Some(named) = &internal.args.named {
for (name, val) in named.iter() {
if let NamedValue::Value(_, spanned_expr) = val {
let found = find_expr_shapes(&spanned_expr, registry)?;
match signature.named.get(name) {
None => {
unreachable!("should have error'd in parsing");
}
Some((named_type, _)) => {
if let NamedType::Mandatory(_, sig_shape)
| NamedType::Optional(_, sig_shape) = named_type
{
check_merge(
&mut arg_shapes,
&apply_shape(found, *sig_shape),
)?;
}
}
}
}
}
}
} else {
unreachable!("registry has lost name it provided");
}
}
ClassifiedCommand::Dynamic(_) | ClassifiedCommand::Error(_) => (),
}
}
}
Ok(arg_shapes)
}
#[cfg(test)]
mod tests {
use super::Alias;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Alias {})?)
}
}

View File

@ -1,146 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use ansi_term::Color;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
pub struct Ansi;
#[derive(Deserialize)]
struct AnsiArgs {
color: Value,
}
#[async_trait]
impl WholeStreamCommand for Ansi {
fn name(&self) -> &str {
"ansi"
}
fn signature(&self) -> Signature {
Signature::build("ansi").required(
"color",
SyntaxShape::Any,
"the name of the color to use or 'reset' to reset the color",
)
}
fn usage(&self) -> &str {
"Output ANSI codes to change color"
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Change color to green",
example: r#"ansi green"#,
result: Some(vec![Value::from("\u{1b}[32m")]),
},
Example {
description: "Reset the color",
example: r#"ansi reset"#,
result: Some(vec![Value::from("\u{1b}[0m")]),
},
Example {
description:
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
example: r#"echo [$(ansi rb) Hello " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#,
result: Some(vec![Value::from(
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld",
)]),
},
]
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let (AnsiArgs { color }, _) = args.process(&registry).await?;
let color_string = color.as_string()?;
let ansi_code = str_to_ansi_color(color_string);
if let Some(output) = ansi_code {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(color.tag()),
)))
} else {
Err(ShellError::labeled_error(
"Unknown color",
"unknown color",
color.tag(),
))
}
}
}
pub fn str_to_ansi_color(s: String) -> Option<String> {
match s.as_str() {
"g" | "green" => Some(Color::Green.prefix().to_string()),
"gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()),
"gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()),
"gi" | "green_italic" => Some(Color::Green.italic().prefix().to_string()),
"gd" | "green_dimmed" => Some(Color::Green.dimmed().prefix().to_string()),
"gr" | "green_reverse" => Some(Color::Green.reverse().prefix().to_string()),
"r" | "red" => Some(Color::Red.prefix().to_string()),
"rb" | "red_bold" => Some(Color::Red.bold().prefix().to_string()),
"ru" | "red_underline" => Some(Color::Red.underline().prefix().to_string()),
"ri" | "red_italic" => Some(Color::Red.italic().prefix().to_string()),
"rd" | "red_dimmed" => Some(Color::Red.dimmed().prefix().to_string()),
"rr" | "red_reverse" => Some(Color::Red.reverse().prefix().to_string()),
"u" | "blue" => Some(Color::Blue.prefix().to_string()),
"ub" | "blue_bold" => Some(Color::Blue.bold().prefix().to_string()),
"uu" | "blue_underline" => Some(Color::Blue.underline().prefix().to_string()),
"ui" | "blue_italic" => Some(Color::Blue.italic().prefix().to_string()),
"ud" | "blue_dimmed" => Some(Color::Blue.dimmed().prefix().to_string()),
"ur" | "blue_reverse" => Some(Color::Blue.reverse().prefix().to_string()),
"b" | "black" => Some(Color::Black.prefix().to_string()),
"bb" | "black_bold" => Some(Color::Black.bold().prefix().to_string()),
"bu" | "black_underline" => Some(Color::Black.underline().prefix().to_string()),
"bi" | "black_italic" => Some(Color::Black.italic().prefix().to_string()),
"bd" | "black_dimmed" => Some(Color::Black.dimmed().prefix().to_string()),
"br" | "black_reverse" => Some(Color::Black.reverse().prefix().to_string()),
"y" | "yellow" => Some(Color::Yellow.prefix().to_string()),
"yb" | "yellow_bold" => Some(Color::Yellow.bold().prefix().to_string()),
"yu" | "yellow_underline" => Some(Color::Yellow.underline().prefix().to_string()),
"yi" | "yellow_italic" => Some(Color::Yellow.italic().prefix().to_string()),
"yd" | "yellow_dimmed" => Some(Color::Yellow.dimmed().prefix().to_string()),
"yr" | "yellow_reverse" => Some(Color::Yellow.reverse().prefix().to_string()),
"p" | "purple" => Some(Color::Purple.prefix().to_string()),
"pb" | "purple_bold" => Some(Color::Purple.bold().prefix().to_string()),
"pu" | "purple_underline" => Some(Color::Purple.underline().prefix().to_string()),
"pi" | "purple_italic" => Some(Color::Purple.italic().prefix().to_string()),
"pd" | "purple_dimmed" => Some(Color::Purple.dimmed().prefix().to_string()),
"pr" | "purple_reverse" => Some(Color::Purple.reverse().prefix().to_string()),
"c" | "cyan" => Some(Color::Cyan.prefix().to_string()),
"cb" | "cyan_bold" => Some(Color::Cyan.bold().prefix().to_string()),
"cu" | "cyan_underline" => Some(Color::Cyan.underline().prefix().to_string()),
"ci" | "cyan_italic" => Some(Color::Cyan.italic().prefix().to_string()),
"cd" | "cyan_dimmed" => Some(Color::Cyan.dimmed().prefix().to_string()),
"cr" | "cyan_reverse" => Some(Color::Cyan.reverse().prefix().to_string()),
"w" | "white" => Some(Color::White.prefix().to_string()),
"wb" | "white_bold" => Some(Color::White.bold().prefix().to_string()),
"wu" | "white_underline" => Some(Color::White.underline().prefix().to_string()),
"wi" | "white_italic" => Some(Color::White.italic().prefix().to_string()),
"wd" | "white_dimmed" => Some(Color::White.dimmed().prefix().to_string()),
"wr" | "white_reverse" => Some(Color::White.reverse().prefix().to_string()),
"reset" => Some("\x1b[0m".to_owned()),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::Ansi;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Ansi {})?)
}
}

View File

@ -1,63 +0,0 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)]
struct Arguments {
row: Value,
}
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"append"
}
fn signature(&self) -> Signature {
Signature::build("append").required(
"row value",
SyntaxShape::Any,
"the value of the row to append to the table",
)
}
fn usage(&self) -> &str {
"Append the given row to the table"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let (Arguments { mut row }, input) = args.process(registry).await?;
let input: Vec<Value> = input.collect().await;
if let Some(first) = input.get(0) {
row.tag = first.tag();
}
Ok(
futures::stream::iter(input.into_iter().chain(vec![row]).map(ReturnSuccess::value))
.to_output_stream(),
)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Add something to the end of a list or table",
example: "echo [1 2 3] | append 4",
result: Some(vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(2).into(),
UntaggedValue::int(3).into(),
UntaggedValue::int(4).into(),
]),
}]
}
}

View File

@ -1,141 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct Char;
#[derive(Deserialize)]
struct CharArgs {
name: Tagged<String>,
}
#[async_trait]
impl WholeStreamCommand for Char {
fn name(&self) -> &str {
"char"
}
fn signature(&self) -> Signature {
Signature::build("ansi").required(
"character",
SyntaxShape::Any,
"the name of the character to output",
)
}
fn usage(&self) -> &str {
"Output special characters (eg. 'newline')"
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Output newline",
example: r#"char newline"#,
result: Some(vec![Value::from("\n")]),
},
Example {
description: "Output prompt character, newline and a hamburger character",
example: r#"echo $(char prompt) $(char newline) $(char hamburger)"#,
result: Some(vec![
UntaggedValue::string("\u{25b6}").into(),
UntaggedValue::string("\n").into(),
UntaggedValue::string("\u{2261}").into(),
]),
},
]
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let (CharArgs { name }, _) = args.process(&registry).await?;
let special_character = str_to_character(&name.item);
if let Some(output) = special_character {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(name.tag()),
)))
} else {
Err(ShellError::labeled_error(
"Unknown character",
"unknown character",
name.tag(),
))
}
}
}
fn str_to_character(s: &str) -> Option<String> {
match s {
"newline" | "enter" | "nl" => Some("\n".into()),
"tab" => Some("\t".into()),
"sp" | "space" => Some(" ".into()),
// Unicode names came from https://www.compart.com/en/unicode
// Private Use Area (U+E000-U+F8FF)
"branch" => Some('\u{e0a0}'.to_string()), // 
"segment" => Some('\u{e0b0}'.to_string()), // 
"identical_to" | "hamburger" => Some('\u{2261}'.to_string()), // ≡
"not_identical_to" | "branch_untracked" => Some('\u{2262}'.to_string()), // ≢
"strictly_equivalent_to" | "branch_identical" => Some('\u{2263}'.to_string()), // ≣
"upwards_arrow" | "branch_ahead" => Some('\u{2191}'.to_string()), // ↑
"downwards_arrow" | "branch_behind" => Some('\u{2193}'.to_string()), // ↓
"up_down_arrow" | "branch_ahead_behind" => Some('\u{2195}'.to_string()), // ↕
"black_right_pointing_triangle" | "prompt" => Some('\u{25b6}'.to_string()), // ▶
"vector_or_cross_product" | "failed" => Some('\u{2a2f}'.to_string()), //
"high_voltage_sign" | "elevated" => Some('\u{26a1}'.to_string()), // ⚡
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
"hash" | "hashtag" | "pound_sign" | "sharp" | "root" => Some("#".into()), // #
// Weather symbols
"sun" => Some("\x1b[33;1m\u{2600}\x1b[0m".to_string()), // Yellow Bold ☀
"moon" => Some("\x1b[36m\u{263d}\x1b[0m".to_string()), // Cyan ☽
"clouds" => Some("\x1b[37;1m\u{2601}\x1b[0m".to_string()), // White Bold ☁
"rain" => Some("\x1b[37;1m\u{2614}\x1b[0m".to_string()), // White Bold ☔
"fog" => Some("\x1b[37;1m\u{2592}\x1b[0m".to_string()), // White Bold ▒
"mist" => Some("\x1b[34m\u{2591}\x1b[0m".to_string()), // Blue ░
"haze" => Some("\x1b[33m\u{2591}\x1b[0m".to_string()), // Yellow ░
"snow" => Some("\x1b[37;1m\u{2744}\x1b[0m".to_string()), // White Bold ❄
"thunderstorm" => Some("\x1b[33;1m\u{26a1}\x1b[0m".to_string()), // Yellow Bold ⚡
// Reference for ansi codes https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
// Another good reference http://ascii-table.com/ansi-escape-sequences.php
// For setting title like `echo [$(char title) $(pwd) $(char bel)] | str collect`
"title" => Some("\x1b]2;".to_string()), // ESC]2; xterm sets window title
"bel" => Some('\x07'.to_string()), // Terminal Bell
"backspace" => Some('\x08'.to_string()), // Backspace
// Ansi Erase Sequences
"clear_screen" => Some("\x1b[J".to_string()), // clears the screen
"clear_screen_from_cursor_to_end" => Some("\x1b[0J".to_string()), // clears from cursor until end of screen
"clear_screen_from_cursor_to_beginning" => Some("\x1b[1J".to_string()), // clears from cursor to beginning of screen
"cls" | "clear_entire_screen" => Some("\x1b[2J".to_string()), // clears the entire screen
"erase_line" => Some("\x1b[K".to_string()), // clears the current line
"erase_line_from_cursor_to_end" => Some("\x1b[0K".to_string()), // clears from cursor to end of line
"erase_line_from_cursor_to_beginning" => Some("\x1b[1K".to_string()), // clears from cursor to start of line
"erase_entire_line" => Some("\x1b[2K".to_string()), // clears entire line
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::Char;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Char {})?)
}
}

View File

@ -1,88 +0,0 @@
use crate::commands::classified::expr::run_expression_block;
use crate::commands::classified::internal::run_internal_command;
use crate::evaluation_context::EvaluationContext;
use crate::prelude::*;
use crate::stream::InputStream;
use futures::stream::TryStreamExt;
use nu_errors::ShellError;
use nu_protocol::hir::{Block, ClassifiedCommand, Commands};
use nu_protocol::{ReturnSuccess, Scope, UntaggedValue, Value};
use std::sync::atomic::Ordering;
pub(crate) async fn run_block(
block: &Block,
ctx: &mut EvaluationContext,
mut input: InputStream,
scope: Arc<Scope>,
) -> Result<InputStream, ShellError> {
let mut output: Result<InputStream, ShellError> = Ok(InputStream::empty());
for pipeline in &block.block {
match output {
Ok(inp) if inp.is_empty() => {}
Ok(inp) => {
let mut output_stream = inp.to_output_stream();
loop {
match output_stream.try_next().await {
Ok(Some(ReturnSuccess::Value(Value {
value: UntaggedValue::Error(e),
..
}))) => return Err(e),
Ok(Some(_item)) => {
if let Some(err) = ctx.get_errors().get(0) {
ctx.clear_errors();
return Err(err.clone());
}
if ctx.ctrl_c.load(Ordering::SeqCst) {
break;
}
}
Ok(None) => {
if let Some(err) = ctx.get_errors().get(0) {
ctx.clear_errors();
return Err(err.clone());
}
break;
}
Err(e) => return Err(e),
}
}
}
Err(e) => {
return Err(e);
}
}
output = run_pipeline(pipeline, ctx, input, scope.clone()).await;
input = InputStream::empty();
}
output
}
async fn run_pipeline(
commands: &Commands,
ctx: &mut EvaluationContext,
mut input: InputStream,
scope: Arc<Scope>,
) -> Result<InputStream, ShellError> {
for item in commands.list.clone() {
input = match item {
ClassifiedCommand::Dynamic(_) => {
return Err(ShellError::unimplemented("Dynamic commands"))
}
ClassifiedCommand::Expr(expr) => {
run_expression_block(*expr, ctx, scope.clone()).await?
}
ClassifiedCommand::Error(err) => return Err(err.into()),
ClassifiedCommand::Internal(left) => {
run_internal_command(left, ctx, input, scope.clone()).await?
}
};
}
Ok(input)
}

View File

@ -1,10 +0,0 @@
pub(crate) mod block;
mod dynamic;
pub(crate) mod expr;
pub(crate) mod external;
pub(crate) mod internal;
pub(crate) mod maybe_text_codec;
pub(crate) mod plugin;
#[allow(unused_imports)]
pub(crate) use dynamic::Command as DynamicCommand;

View File

@ -1,340 +0,0 @@
use crate::command_registry::CommandRegistry;
use crate::commands::help::get_help;
use crate::deserializer::ConfigDeserializer;
use crate::evaluate::evaluate_args::evaluate_args;
use crate::prelude::*;
use derive_new::new;
use getset::Getters;
use nu_errors::ShellError;
use nu_protocol::hir;
use nu_protocol::{CallInfo, EvaluatedArgs, ReturnSuccess, Scope, Signature, UntaggedValue, Value};
use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use std::ops::Deref;
use std::sync::atomic::AtomicBool;
#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct UnevaluatedCallInfo {
pub args: hir::Call,
pub name_tag: Tag,
pub scope: Arc<Scope>,
}
impl UnevaluatedCallInfo {
pub async fn evaluate(self, registry: &CommandRegistry) -> Result<CallInfo, ShellError> {
let args = evaluate_args(&self.args, registry, self.scope.clone()).await?;
Ok(CallInfo {
args,
name_tag: self.name_tag,
})
}
pub fn switch_present(&self, switch: &str) -> bool {
self.args.switch_preset(switch)
}
}
#[derive(Getters)]
#[get = "pub(crate)"]
pub struct CommandArgs {
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub ctrl_c: Arc<AtomicBool>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub shell_manager: ShellManager,
pub call_info: UnevaluatedCallInfo,
pub input: InputStream,
pub raw_input: String,
}
#[derive(Getters, Clone)]
#[get = "pub(crate)"]
pub struct RawCommandArgs {
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub ctrl_c: Arc<AtomicBool>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub shell_manager: ShellManager,
pub call_info: UnevaluatedCallInfo,
}
impl RawCommandArgs {
pub fn with_input(self, input: impl Into<InputStream>) -> CommandArgs {
CommandArgs {
host: self.host,
ctrl_c: self.ctrl_c,
current_errors: self.current_errors,
shell_manager: self.shell_manager,
call_info: self.call_info,
input: input.into(),
raw_input: String::default(),
}
}
}
impl std::fmt::Debug for CommandArgs {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.call_info.fmt(f)
}
}
impl CommandArgs {
pub async fn evaluate_once(
self,
registry: &CommandRegistry,
) -> Result<EvaluatedWholeStreamCommandArgs, ShellError> {
let host = self.host.clone();
let ctrl_c = self.ctrl_c.clone();
let shell_manager = self.shell_manager.clone();
let input = self.input;
let call_info = self.call_info.evaluate(registry).await?;
Ok(EvaluatedWholeStreamCommandArgs::new(
host,
ctrl_c,
shell_manager,
call_info,
input,
))
}
pub async fn evaluate_once_with_scope(
self,
registry: &CommandRegistry,
scope: Arc<Scope>,
) -> Result<EvaluatedWholeStreamCommandArgs, ShellError> {
let host = self.host.clone();
let ctrl_c = self.ctrl_c.clone();
let shell_manager = self.shell_manager.clone();
let input = self.input;
let call_info = UnevaluatedCallInfo {
name_tag: self.call_info.name_tag,
args: self.call_info.args,
scope: scope.clone(),
};
let call_info = call_info.evaluate(registry).await?;
Ok(EvaluatedWholeStreamCommandArgs::new(
host,
ctrl_c,
shell_manager,
call_info,
input,
))
}
pub async fn process<'de, T: Deserialize<'de>>(
self,
registry: &CommandRegistry,
) -> Result<(T, InputStream), ShellError> {
let args = self.evaluate_once(registry).await?;
let call_info = args.call_info.clone();
let mut deserializer = ConfigDeserializer::from_call_info(call_info);
Ok((T::deserialize(&mut deserializer)?, args.input))
}
}
pub struct RunnableContext {
pub input: InputStream,
pub shell_manager: ShellManager,
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub ctrl_c: Arc<AtomicBool>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub registry: CommandRegistry,
pub name: Tag,
pub raw_input: String,
}
impl RunnableContext {
pub fn get_command(&self, name: &str) -> Option<Command> {
self.registry.get_command(name)
}
}
pub struct EvaluatedWholeStreamCommandArgs {
pub args: EvaluatedCommandArgs,
pub input: InputStream,
}
impl Deref for EvaluatedWholeStreamCommandArgs {
type Target = EvaluatedCommandArgs;
fn deref(&self) -> &Self::Target {
&self.args
}
}
impl EvaluatedWholeStreamCommandArgs {
pub fn new(
host: Arc<parking_lot::Mutex<dyn Host>>,
ctrl_c: Arc<AtomicBool>,
shell_manager: ShellManager,
call_info: CallInfo,
input: impl Into<InputStream>,
) -> EvaluatedWholeStreamCommandArgs {
EvaluatedWholeStreamCommandArgs {
args: EvaluatedCommandArgs {
host,
ctrl_c,
shell_manager,
call_info,
},
input: input.into(),
}
}
pub fn name_tag(&self) -> Tag {
self.args.call_info.name_tag.clone()
}
pub fn parts(self) -> (InputStream, EvaluatedArgs) {
let EvaluatedWholeStreamCommandArgs { args, input } = self;
(input, args.call_info.args)
}
pub fn split(self) -> (InputStream, EvaluatedCommandArgs) {
let EvaluatedWholeStreamCommandArgs { args, input } = self;
(input, args)
}
}
#[derive(Getters, new)]
#[get = "pub(crate)"]
pub struct EvaluatedCommandArgs {
pub host: Arc<parking_lot::Mutex<dyn Host>>,
pub ctrl_c: Arc<AtomicBool>,
pub shell_manager: ShellManager,
pub call_info: CallInfo,
}
impl EvaluatedCommandArgs {
pub fn nth(&self, pos: usize) -> Option<&Value> {
self.call_info.args.nth(pos)
}
/// Get the nth positional argument, error if not possible
pub fn expect_nth(&self, pos: usize) -> Result<&Value, ShellError> {
self.call_info
.args
.nth(pos)
.ok_or_else(|| ShellError::unimplemented("Better error: expect_nth"))
}
pub fn get(&self, name: &str) -> Option<&Value> {
self.call_info.args.get(name)
}
pub fn has(&self, name: &str) -> bool {
self.call_info.args.has(name)
}
}
pub struct Example {
pub example: &'static str,
pub description: &'static str,
pub result: Option<Vec<Value>>,
}
#[async_trait]
pub trait WholeStreamCommand: Send + Sync {
fn name(&self) -> &str;
fn signature(&self) -> Signature {
Signature::new(self.name()).desc(self.usage()).filter()
}
fn usage(&self) -> &str;
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError>;
fn is_binary(&self) -> bool {
false
}
// Commands that are not meant to be run by users
fn is_internal(&self) -> bool {
false
}
fn examples(&self) -> Vec<Example> {
Vec::new()
}
}
#[derive(Clone)]
pub struct Command(Arc<dyn WholeStreamCommand>);
impl PrettyDebugWithSource for Command {
fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
b::typed(
"whole stream command",
b::description(self.name())
+ b::space()
+ b::equals()
+ b::space()
+ self.signature().pretty_debug(source),
)
}
}
impl std::fmt::Debug for Command {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Command({})", self.name())
}
}
impl Command {
pub fn name(&self) -> &str {
self.0.name()
}
pub fn signature(&self) -> Signature {
self.0.signature()
}
pub fn usage(&self) -> &str {
self.0.usage()
}
pub fn examples(&self) -> Vec<Example> {
self.0.examples()
}
pub async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
if args.call_info.switch_present("help") {
let cl = self.0.clone();
let registry = registry.clone();
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
UntaggedValue::string(get_help(&*cl, &registry)).into_value(Tag::unknown()),
))))
} else {
self.0.run(args, registry).await
}
}
pub fn is_binary(&self) -> bool {
self.0.is_binary()
}
pub fn is_internal(&self) -> bool {
self.0.is_internal()
}
pub fn stream_command(&self) -> &dyn WholeStreamCommand {
&*self.0
}
}
pub fn whole_stream_command(command: impl WholeStreamCommand + 'static) -> Command {
Command(Arc::new(command))
}

View File

@ -1,76 +0,0 @@
use crate::prelude::*;
use chrono::{DateTime, Local};
use nu_errors::ShellError;
use crate::commands::date::utils::{date_to_value, date_to_value_raw};
use crate::commands::WholeStreamCommand;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
pub struct Date;
#[derive(Deserialize)]
pub struct FormatArgs {
format: Tagged<String>,
raw: Option<bool>,
}
#[async_trait]
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date format"
}
fn signature(&self) -> Signature {
Signature::build("date format")
.required("format", SyntaxShape::String, "strftime format")
.switch("raw", "print date without tables", Some('r'))
}
fn usage(&self) -> &str {
"format the current date using the given format string."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
format(args, registry).await
}
}
pub async fn format(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let tag = args.call_info.name_tag.clone();
let (FormatArgs { format, raw }, _) = args.process(&registry).await?;
let dt_fmt = format.to_string();
let value = {
let local: DateTime<Local> = Local::now();
if let Some(true) = raw {
UntaggedValue::string(date_to_value_raw(local, dt_fmt)).into_untagged_value()
} else {
date_to_value(local, tag, dt_fmt)
}
};
Ok(OutputStream::one(value))
}
#[cfg(test)]
mod tests {
use super::Date;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Date {})?)
}
}

View File

@ -1,11 +0,0 @@
pub mod command;
pub mod format;
pub mod now;
pub mod utc;
mod utils;
pub use command::Command as Date;
pub use format::Date as DateFormat;
pub use now::Date as DateNow;
pub use utc::Date as DateUTC;

View File

@ -1,63 +0,0 @@
use crate::prelude::*;
use chrono::{DateTime, Local};
use nu_errors::ShellError;
use crate::commands::date::utils::date_to_value;
use crate::commands::WholeStreamCommand;
use nu_protocol::Signature;
pub struct Date;
#[async_trait]
impl WholeStreamCommand for Date {
fn name(&self) -> &str {
"date now"
}
fn signature(&self) -> Signature {
Signature::build("date now")
}
fn usage(&self) -> &str {
"return the current date."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
now(args, registry).await
}
}
pub async fn now(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let args = args.evaluate_once(&registry).await?;
let tag = args.call_info.name_tag.clone();
let no_fmt = "".to_string();
let value = {
let local: DateTime<Local> = Local::now();
date_to_value(local, tag, no_fmt)
};
Ok(OutputStream::one(value))
}
#[cfg(test)]
mod tests {
use super::Date;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Date {})?)
}
}

View File

@ -1,64 +0,0 @@
use crate::prelude::*;
use chrono::DateTime;
use nu_protocol::{Dictionary, Value};
use chrono::{Datelike, TimeZone, Timelike};
use core::fmt::Display;
use indexmap::IndexMap;
use nu_protocol::UntaggedValue;
pub fn date_to_value_raw<T: TimeZone>(dt: DateTime<T>, dt_format: String) -> String
where
T::Offset: Display,
{
let result = dt.format(&dt_format);
format!("{}", result)
}
pub fn date_to_value<T: TimeZone>(dt: DateTime<T>, tag: Tag, dt_format: String) -> Value
where
T::Offset: Display,
{
let mut indexmap = IndexMap::new();
if dt_format.is_empty() {
indexmap.insert(
"year".to_string(),
UntaggedValue::int(dt.year()).into_value(&tag),
);
indexmap.insert(
"month".to_string(),
UntaggedValue::int(dt.month()).into_value(&tag),
);
indexmap.insert(
"day".to_string(),
UntaggedValue::int(dt.day()).into_value(&tag),
);
indexmap.insert(
"hour".to_string(),
UntaggedValue::int(dt.hour()).into_value(&tag),
);
indexmap.insert(
"minute".to_string(),
UntaggedValue::int(dt.minute()).into_value(&tag),
);
indexmap.insert(
"second".to_string(),
UntaggedValue::int(dt.second()).into_value(&tag),
);
let tz = dt.offset();
indexmap.insert(
"timezone".to_string(),
UntaggedValue::string(format!("{}", tz)).into_value(&tag),
);
} else {
let result = dt.format(&dt_format);
indexmap.insert(
"formatted".to_string(),
UntaggedValue::string(format!("{}", result)).into_value(&tag),
);
}
UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag)
}

View File

@ -1,90 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_data::config::{Conf, NuConfig};
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::PathBuf;
const DEFAULT_LOCATION: &str = "history.txt";
pub fn history_path(config: &dyn Conf) -> PathBuf {
let default_path = nu_data::config::user_data()
.map(|mut p| {
p.push(DEFAULT_LOCATION);
p
})
.unwrap_or_else(|_| PathBuf::from(DEFAULT_LOCATION));
config
.var("history-path")
.map_or(default_path.clone(), |custom_path| {
match custom_path.as_string() {
Ok(path) => PathBuf::from(path),
Err(_) => default_path,
}
})
}
pub struct History;
#[async_trait]
impl WholeStreamCommand for History {
fn name(&self) -> &str {
"history"
}
fn signature(&self) -> Signature {
Signature::build("history")
}
fn usage(&self) -> &str {
"Display command history."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
history(args, registry)
}
}
fn history(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let config: Box<dyn Conf> = Box::new(NuConfig::new());
let tag = args.call_info.name_tag;
let path = history_path(&config);
let file = File::open(path);
if let Ok(file) = file {
let reader = BufReader::new(file);
let output = reader.lines().filter_map(move |line| match line {
Ok(line) => Some(ReturnSuccess::value(
UntaggedValue::string(line).into_value(tag.clone()),
)),
Err(_) => None,
});
Ok(futures::stream::iter(output).to_output_stream())
} else {
Err(ShellError::labeled_error(
"Could not open history",
"history file could not be opened",
tag,
))
}
}
#[cfg(test)]
mod tests {
use super::History;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(History {})?)
}
}

View File

@ -1,185 +0,0 @@
use crate::command_registry::CommandRegistry;
use crate::commands::classified::block::run_block;
use crate::commands::WholeStreamCommand;
use crate::evaluate::evaluate_baseline_expr;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{
hir::Block, hir::ClassifiedCommand, Scope, Signature, SyntaxShape, UntaggedValue,
};
pub struct If;
#[derive(Deserialize)]
pub struct IfArgs {
condition: Block,
then_case: Block,
else_case: Block,
}
#[async_trait]
impl WholeStreamCommand for If {
fn name(&self) -> &str {
"if"
}
fn signature(&self) -> Signature {
Signature::build("if")
.required(
"condition",
SyntaxShape::Math,
"the condition that must match",
)
.required(
"then_case",
SyntaxShape::Block,
"block to run if condition is true",
)
.required(
"else_case",
SyntaxShape::Block,
"block to run if condition is false",
)
}
fn usage(&self) -> &str {
"Run blocks if a condition is true or false."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
if_command(args, registry).await
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Run a block if a condition is true",
example: "echo 10 | if $it > 5 { echo 'greater than 5' } { echo 'less than or equal to 5' }",
result: Some(vec![UntaggedValue::string("greater than 5").into()]),
},
Example {
description: "Run a block if a condition is false",
example: "echo 1 | if $it > 5 { echo 'greater than 5' } { echo 'less than or equal to 5' }",
result: Some(vec![UntaggedValue::string("less than or equal to 5").into()]),
},
]
}
}
async fn if_command(
raw_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = Arc::new(registry.clone());
let scope = raw_args.call_info.scope.clone();
let tag = raw_args.call_info.name_tag.clone();
let context = Arc::new(EvaluationContext::from_raw(&raw_args, &registry));
let (
IfArgs {
condition,
then_case,
else_case,
},
input,
) = raw_args.process(&registry).await?;
let condition = {
if condition.block.len() != 1 {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
match condition.block[0].list.get(0) {
Some(item) => match item {
ClassifiedCommand::Expr(expr) => expr.clone(),
_ => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
},
None => {
return Err(ShellError::labeled_error(
"Expected a condition",
"expected a condition",
tag,
));
}
}
};
Ok(input
.then(move |input| {
let condition = condition.clone();
let then_case = then_case.clone();
let else_case = else_case.clone();
let registry = registry.clone();
let scope = Scope::append_it(scope.clone(), input);
let mut context = context.clone();
async move {
//FIXME: should we use the scope that's brought in as well?
let condition = evaluate_baseline_expr(&condition, &*registry, scope.clone()).await;
match condition {
Ok(condition) => match condition.as_bool() {
Ok(b) => {
if b {
match run_block(
&then_case,
Arc::make_mut(&mut context),
InputStream::empty(),
scope,
)
.await
{
Ok(stream) => stream.to_output_stream(),
Err(e) => futures::stream::iter(vec![Err(e)].into_iter())
.to_output_stream(),
}
} else {
match run_block(
&else_case,
Arc::make_mut(&mut context),
InputStream::empty(),
scope,
)
.await
{
Ok(stream) => stream.to_output_stream(),
Err(e) => futures::stream::iter(vec![Err(e)].into_iter())
.to_output_stream(),
}
}
}
Err(e) => {
futures::stream::iter(vec![Err(e)].into_iter()).to_output_stream()
}
},
Err(e) => futures::stream::iter(vec![Err(e)].into_iter()).to_output_stream(),
}
}
})
.flatten()
.to_output_stream())
}
#[cfg(test)]
mod tests {
use super::If;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(If {})?)
}
}

View File

@ -1,112 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct SubCommandArgs {
expression: Option<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"math eval"
}
fn usage(&self) -> &str {
"Evaluate a math expression into a number"
}
fn signature(&self) -> Signature {
Signature::build("math eval").desc(self.usage()).optional(
"math expression",
SyntaxShape::String,
"the math expression to evaluate",
)
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
eval(args, registry).await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Evalulate math in the pipeline",
example: "echo '10 / 4' | math eval",
result: Some(vec![UntaggedValue::decimal_from_float(
2.5,
Span::unknown(),
)
.into()]),
}]
}
}
pub async fn eval(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.span;
let (SubCommandArgs { expression }, input) = args.process(registry).await?;
Ok(input
.map(move |x| {
if let Some(Tagged {
tag,
item: expression,
}) = &expression
{
UntaggedValue::string(expression).into_value(tag)
} else {
x
}
})
.map(move |input| {
if let Ok(string) = input.as_string() {
match parse(&string, &input.tag) {
Ok(value) => ReturnSuccess::value(value),
Err(err) => Err(ShellError::labeled_error(
"Math evaluation error",
err,
&input.tag.span,
)),
}
} else {
Err(ShellError::labeled_error(
"Expected a string from pipeline",
"requires string input",
name,
))
}
})
.to_output_stream())
}
pub fn parse<T: Into<Tag>>(math_expression: &str, tag: T) -> Result<Value, String> {
match meval::eval_str(math_expression) {
Ok(num) if num.is_infinite() || num.is_nan() => Err("cannot represent result".to_string()),
Ok(num) => Ok(UntaggedValue::from(Primitive::from(num)).into_value(tag)),
Err(error) => Err(error.to_string().to_lowercase()),
}
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::SubCommand;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(SubCommand {})?)
}
}

View File

@ -1,47 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
#[derive(Clone)]
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"move"
}
fn signature(&self) -> Signature {
Signature::build("move")
}
fn usage(&self) -> &str {
"Moves across desired subcommand."
}
async fn run(
&self,
_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
UntaggedValue::string(crate::commands::help::get_help(&Command, &registry))
.into_value(Tag::unknown()),
))))
}
}
#[cfg(test)]
mod tests {
use super::Command;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Command {})?)
}
}

View File

@ -1,62 +0,0 @@
use super::{operate, DefaultArguments};
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use std::path::Path;
pub struct PathBasename;
#[async_trait]
impl WholeStreamCommand for PathBasename {
fn name(&self) -> &str {
"path basename"
}
fn signature(&self) -> Signature {
Signature::build("path basename")
.rest(SyntaxShape::ColumnPath, "optionally operate by path")
}
fn usage(&self) -> &str {
"gets the filename of a path"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (DefaultArguments { rest }, input) = args.process(&registry).await?;
operate(input, rest, &action, tag.span).await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Get basename of a path",
example: "echo '/home/joe/test.txt' | path basename",
result: Some(vec![Value::from("test.txt")]),
}]
}
}
fn action(path: &Path) -> UntaggedValue {
UntaggedValue::string(match path.file_name() {
Some(filename) => filename.to_string_lossy().to_string(),
_ => "".to_string(),
})
}
#[cfg(test)]
mod tests {
use super::PathBasename;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(PathBasename {})?)
}
}

View File

@ -1,61 +0,0 @@
use super::{operate, DefaultArguments};
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use std::path::Path;
pub struct PathDirname;
#[async_trait]
impl WholeStreamCommand for PathDirname {
fn name(&self) -> &str {
"path dirname"
}
fn signature(&self) -> Signature {
Signature::build("path dirname").rest(SyntaxShape::ColumnPath, "optionally operate by path")
}
fn usage(&self) -> &str {
"gets the dirname of a path"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (DefaultArguments { rest }, input) = args.process(&registry).await?;
operate(input, rest, &action, tag.span).await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Get dirname of a path",
example: "echo '/home/joe/test.txt' | path dirname",
result: Some(vec![Value::from("/home/joe")]),
}]
}
}
fn action(path: &Path) -> UntaggedValue {
UntaggedValue::string(match path.parent() {
Some(dirname) => dirname.to_string_lossy().to_string(),
_ => "".to_string(),
})
}
#[cfg(test)]
mod tests {
use super::PathDirname;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(PathDirname {})?)
}
}

View File

@ -1,58 +0,0 @@
use super::{operate, DefaultArguments};
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use std::path::Path;
pub struct PathExists;
#[async_trait]
impl WholeStreamCommand for PathExists {
fn name(&self) -> &str {
"path exists"
}
fn signature(&self) -> Signature {
Signature::build("path exists").rest(SyntaxShape::ColumnPath, "optionally operate by path")
}
fn usage(&self) -> &str {
"checks whether the path exists"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (DefaultArguments { rest }, input) = args.process(&registry).await?;
operate(input, rest, &action, tag.span).await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Check if file exists",
example: "echo '/home/joe/todo.txt' | path exists",
result: Some(vec![Value::from(UntaggedValue::boolean(false))]),
}]
}
}
fn action(path: &Path) -> UntaggedValue {
UntaggedValue::boolean(path.exists())
}
#[cfg(test)]
mod tests {
use super::PathExists;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(PathExists {})?)
}
}

View File

@ -1,65 +0,0 @@
use super::{operate, DefaultArguments};
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue};
use std::path::Path;
pub struct PathExpand;
#[async_trait]
impl WholeStreamCommand for PathExpand {
fn name(&self) -> &str {
"path expand"
}
fn signature(&self) -> Signature {
Signature::build("path expand").rest(SyntaxShape::ColumnPath, "optionally operate by path")
}
fn usage(&self) -> &str {
"expands the path to its absolute form"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (DefaultArguments { rest }, input) = args.process(&registry).await?;
operate(input, rest, &action, tag.span).await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Expand relative directories",
example: "echo '/home/joe/foo/../bar' | path expand",
result: None,
//Some(vec![Value::from("/home/joe/bar")]),
}]
}
}
fn action(path: &Path) -> UntaggedValue {
let ps = path.to_string_lossy();
let expanded = shellexpand::tilde(&ps);
let path: &Path = expanded.as_ref().as_ref();
UntaggedValue::string(match path.canonicalize() {
Ok(p) => p.to_string_lossy().to_string(),
Err(_) => ps.to_string(),
})
}
#[cfg(test)]
mod tests {
use super::PathExpand;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(PathExpand {})?)
}
}

View File

@ -1,69 +0,0 @@
use super::{operate, DefaultArguments};
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use std::path::Path;
pub struct PathExtension;
#[async_trait]
impl WholeStreamCommand for PathExtension {
fn name(&self) -> &str {
"path extension"
}
fn signature(&self) -> Signature {
Signature::build("path extension")
.rest(SyntaxShape::ColumnPath, "optionally operate by path")
}
fn usage(&self) -> &str {
"gets the extension of a path"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (DefaultArguments { rest }, input) = args.process(&registry).await?;
operate(input, rest, &action, tag.span).await
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Get extension of a path",
example: "echo 'test.txt' | path extension",
result: Some(vec![Value::from("txt")]),
},
Example {
description: "You get an empty string if there is no extension",
example: "echo 'test' | path extension",
result: Some(vec![Value::from("")]),
},
]
}
}
fn action(path: &Path) -> UntaggedValue {
UntaggedValue::string(match path.extension() {
Some(ext) => ext.to_string_lossy().to_string(),
_ => "".to_string(),
})
}
#[cfg(test)]
mod tests {
use super::PathExtension;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(PathExtension {})?)
}
}

View File

@ -1,62 +0,0 @@
use super::{operate, DefaultArguments};
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape, UntaggedValue, Value};
use std::path::Path;
pub struct PathFilestem;
#[async_trait]
impl WholeStreamCommand for PathFilestem {
fn name(&self) -> &str {
"path filestem"
}
fn signature(&self) -> Signature {
Signature::build("path filestem")
.rest(SyntaxShape::ColumnPath, "optionally operate by path")
}
fn usage(&self) -> &str {
"gets the filestem of a path"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (DefaultArguments { rest }, input) = args.process(&registry).await?;
operate(input, rest, &action, tag.span).await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Get filestem of a path",
example: "echo '/home/joe/test.txt' | path filestem",
result: Some(vec![Value::from("test")]),
}]
}
}
fn action(path: &Path) -> UntaggedValue {
UntaggedValue::string(match path.file_stem() {
Some(stem) => stem.to_string_lossy().to_string(),
_ => "".to_string(),
})
}
#[cfg(test)]
mod tests {
use super::PathFilestem;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(PathFilestem {})?)
}
}

View File

@ -1,79 +0,0 @@
use crate::commands::classified::block::run_block;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use derive_new::new;
use nu_errors::ShellError;
use nu_protocol::{hir::Block, Scope, Signature, SyntaxShape, UntaggedValue};
#[derive(new, Clone)]
pub struct AliasCommand {
name: String,
args: Vec<(String, SyntaxShape)>,
block: Block,
}
#[async_trait]
impl WholeStreamCommand for AliasCommand {
fn name(&self) -> &str {
&self.name
}
fn signature(&self) -> Signature {
let mut alias = Signature::build(&self.name);
for (arg, shape) in &self.args {
alias = alias.optional(arg, *shape, "");
}
alias
}
fn usage(&self) -> &str {
""
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let call_info = args.call_info.clone();
let registry = registry.clone();
let mut block = self.block.clone();
block.set_redirect(call_info.args.external_redirection);
let alias_command = self.clone();
let mut context = EvaluationContext::from_args(&args, &registry);
let input = args.input;
let scope = call_info.scope.clone();
let evaluated = call_info.evaluate(&registry).await?;
let mut vars = IndexMap::new();
let mut num_positionals = 0;
if let Some(positional) = &evaluated.args.positional {
num_positionals = positional.len();
for (pos, arg) in positional.iter().enumerate() {
vars.insert(alias_command.args[pos].0.to_string(), arg.clone());
}
}
if alias_command.args.len() > num_positionals {
for idx in 0..(alias_command.args.len() - num_positionals) {
vars.insert(
alias_command.args[idx + num_positionals].0.to_string(),
UntaggedValue::nothing().into_untagged_value(),
);
}
}
let scope = Scope::append_vars(scope, vars);
// FIXME: we need to patch up the spans to point at the top-level error
Ok(run_block(&block, &mut context, input, scope)
.await?
.to_output_stream())
}
}

View File

@ -1,78 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct SubCommandArgs {
separator: Option<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"str collect"
}
fn signature(&self) -> Signature {
Signature::build("str collect").desc(self.usage()).optional(
"separator",
SyntaxShape::String,
"the separator to put between the different values",
)
}
fn usage(&self) -> &str {
"collects a list of strings into a string"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
collect(args, registry).await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Collect a list of string",
example: "echo ['a' 'b' 'c'] | str collect",
result: Some(vec![Value::from("abc")]),
}]
}
}
pub async fn collect(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (SubCommandArgs { separator }, input) = args.process(registry).await?;
let separator = separator.map(|tagged| tagged.item).unwrap_or_default();
let strings: Vec<Result<String, ShellError>> =
input.map(|value| value.as_string()).collect().await;
let strings: Vec<String> = strings.into_iter().collect::<Result<_, _>>()?;
let output = strings.join(&separator);
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(tag),
)))
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::SubCommand;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(SubCommand {})?)
}
}

View File

@ -1,66 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"str length"
}
fn signature(&self) -> Signature {
Signature::build("str length")
}
fn usage(&self) -> &str {
"outputs the lengths of the strings in the pipeline"
}
async fn run(
&self,
args: CommandArgs,
_registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
Ok(args
.input
.map(move |x| match x.as_string() {
Ok(s) => ReturnSuccess::value(UntaggedValue::int(s.len()).into_untagged_value()),
Err(err) => Err(err),
})
.to_output_stream())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Return the lengths of multiple strings",
example: "echo 'hello' | str length",
result: Some(vec![UntaggedValue::int(5).into_untagged_value()]),
},
Example {
description: "Return the lengths of multiple strings",
example: "echo 'hi' 'there' | str length",
result: Some(vec![
UntaggedValue::int(2).into_untagged_value(),
UntaggedValue::int(5).into_untagged_value(),
]),
},
]
}
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::SubCommand;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(SubCommand {})?)
}
}

View File

@ -1,59 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"str reverse"
}
fn signature(&self) -> Signature {
Signature::build("str reverse")
}
fn usage(&self) -> &str {
"outputs the reversals of the strings in the pipeline"
}
async fn run(
&self,
args: CommandArgs,
_registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
Ok(args
.input
.map(move |x| match x.as_string() {
Ok(s) => ReturnSuccess::value(
UntaggedValue::string(s.chars().rev().collect::<String>())
.into_untagged_value(),
),
Err(err) => Err(err),
})
.to_output_stream())
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Return the reversals of multiple strings",
example: "echo 'Nushell' | str reverse",
result: Some(vec![UntaggedValue::string("llehsuN").into_untagged_value()]),
}]
}
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::SubCommand;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(SubCommand {})?)
}
}

View File

@ -1,124 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::{Tag, Tagged};
use nu_value_ext::ValueExt;
#[derive(Deserialize)]
struct Arguments {
replace: Tagged<String>,
rest: Vec<ColumnPath>,
}
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"str set"
}
fn signature(&self) -> Signature {
Signature::build("str set")
.required("set", SyntaxShape::String, "the new string to set")
.rest(
SyntaxShape::ColumnPath,
"optionally set text by column paths",
)
}
fn usage(&self) -> &str {
"sets text"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
operate(args, registry).await
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Set contents with preferred string",
example: "echo 'good day' | str set 'good bye'",
result: Some(vec![Value::from("good bye")]),
},
Example {
description: "Set the contents on preferred column paths",
example: "open Cargo.toml | str set '255' package.version",
result: None,
},
]
}
}
#[derive(Clone)]
struct Replace(String);
async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let (Arguments { replace, rest }, input) = args.process(&registry).await?;
let options = Replace(replace.item);
let column_paths: Vec<_> = rest;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, &options, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
let options = options.clone();
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, &options, old.tag())),
)?;
}
ReturnSuccess::value(ret)
}
})
.to_output_stream())
}
fn action(_input: &Value, options: &Replace, tag: impl Into<Tag>) -> Result<Value, ShellError> {
let replacement = &options.0;
Ok(UntaggedValue::string(replacement.as_str()).into_value(tag))
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::{action, Replace, SubCommand};
use nu_plugin::test_helpers::value::string;
use nu_source::Tag;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(SubCommand {})?)
}
#[test]
fn sets() {
let word = string("andres");
let expected = string("robalino");
let set_options = Replace(String::from("robalino"));
let actual = action(&word, &set_options, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
}

View File

@ -1,144 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_errors::ShellError;
use nu_protocol::ShellTypeName;
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::Tag;
use nu_value_ext::ValueExt;
use num_bigint::BigInt;
use std::str::FromStr;
#[derive(Deserialize)]
struct Arguments {
rest: Vec<ColumnPath>,
}
pub struct SubCommand;
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"str to-int"
}
fn signature(&self) -> Signature {
Signature::build("str to-int").rest(
SyntaxShape::ColumnPath,
"optionally convert text into integer by column paths",
)
}
fn usage(&self) -> &str {
"converts text into integer"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
operate(args, registry).await
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Convert to an integer",
example: "echo '255' | str to-int",
result: None,
}]
}
}
async fn operate(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let (Arguments { rest }, input) = args.process(&registry).await?;
let column_paths: Vec<_> = rest;
Ok(input
.map(move |v| {
if column_paths.is_empty() {
ReturnSuccess::value(action(&v, v.tag())?)
} else {
let mut ret = v;
for path in &column_paths {
ret = ret.swap_data_by_column_path(
path,
Box::new(move |old| action(old, old.tag())),
)?;
}
ReturnSuccess::value(ret)
}
})
.to_output_stream())
}
fn action(input: &Value, tag: impl Into<Tag>) -> Result<Value, ShellError> {
match &input.value {
UntaggedValue::Primitive(Primitive::Line(s))
| UntaggedValue::Primitive(Primitive::String(s)) => {
let other = s.trim();
let out = match BigInt::from_str(other) {
Ok(v) => UntaggedValue::int(v),
Err(reason) => {
return Err(ShellError::labeled_error(
"could not parse as an integer",
reason.to_string(),
tag.into().span,
))
}
};
Ok(out.into_value(tag))
}
other => {
let got = format!("got {}", other.type_name());
Err(ShellError::labeled_error(
"value is not string",
got,
tag.into().span,
))
}
}
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::{action, SubCommand};
use nu_plugin::test_helpers::value::{int, string};
use nu_source::Tag;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(SubCommand {})?)
}
#[test]
fn turns_to_integer() {
let word = string("10");
let expected = int(10);
let actual = action(&word, Tag::unknown()).unwrap();
assert_eq!(actual, expected);
}
#[test]
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
let integer_str = string("36anra");
let actual = action(&integer_str, Tag::unknown());
assert!(actual.is_err());
}
}

View File

@ -1,91 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use futures::StreamExt;
use nu_data::value::format_leaf;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue, Value};
pub struct ToMarkdown;
#[async_trait]
impl WholeStreamCommand for ToMarkdown {
fn name(&self) -> &str {
"to md"
}
fn signature(&self) -> Signature {
Signature::build("to md")
}
fn usage(&self) -> &str {
"Convert table into simple Markdown"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
to_md(args, registry).await
}
}
async fn to_md(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let args = args.evaluate_once(&registry).await?;
let name_tag = args.name_tag();
let input: Vec<Value> = args.input.collect().await;
let headers = nu_protocol::merge_descriptors(&input);
let mut output_string = String::new();
if !headers.is_empty() && (headers.len() > 1 || headers[0] != "") {
output_string.push_str("|");
for header in &headers {
output_string.push_str(&htmlescape::encode_minimal(&header));
output_string.push_str("|");
}
output_string.push_str("\n|");
for _ in &headers {
output_string.push_str("-");
output_string.push_str("|");
}
output_string.push_str("\n");
}
for row in input {
match row.value {
UntaggedValue::Row(row) => {
output_string.push_str("|");
for header in &headers {
let data = row.get_data(header);
output_string.push_str(&format_leaf(data.borrow()).plain_string(100_000));
output_string.push_str("|");
}
output_string.push_str("\n");
}
p => {
output_string.push_str(
&(htmlescape::encode_minimal(&format_leaf(&p).plain_string(100_000))),
);
output_string.push_str("\n");
}
}
}
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output_string).into_value(name_tag),
)))
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::ToMarkdown;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(ToMarkdown {})?)
}
}

View File

@ -1,96 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use indexmap::IndexMap;
use nu_data::TaggedListBuilder;
use nu_errors::ShellError;
use nu_protocol::{Dictionary, Signature, UntaggedValue};
const GIT_COMMIT_HASH: &str = include_str!(concat!(env!("OUT_DIR"), "/git_commit_hash"));
pub struct Version;
#[async_trait]
impl WholeStreamCommand for Version {
fn name(&self) -> &str {
"version"
}
fn signature(&self) -> Signature {
Signature::build("version")
}
fn usage(&self) -> &str {
"Display Nu version"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
version(args, registry)
}
fn examples(&self) -> Vec<Example> {
vec![Example {
description: "Display Nu version",
example: "version",
result: None,
}]
}
}
pub fn version(args: CommandArgs, _registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let tag = args.call_info.args.span;
let mut indexmap = IndexMap::with_capacity(4);
indexmap.insert(
"version".to_string(),
UntaggedValue::string(clap::crate_version!()).into_value(&tag),
);
let commit_hash = Some(GIT_COMMIT_HASH.trim()).filter(|x| !x.is_empty());
if let Some(commit_hash) = commit_hash {
indexmap.insert(
"commit_hash".to_string(),
UntaggedValue::string(commit_hash).into_value(&tag),
);
}
indexmap.insert("features".to_string(), features_enabled(&tag).into_value());
let value = UntaggedValue::Row(Dictionary::from(indexmap)).into_value(&tag);
Ok(OutputStream::one(value))
}
fn features_enabled(tag: impl Into<Tag>) -> TaggedListBuilder {
let mut names = TaggedListBuilder::new(tag);
names.push_untagged(UntaggedValue::string("default"));
#[cfg(feature = "clipboard-cli")]
{
names.push_untagged(UntaggedValue::string("clipboard"));
}
#[cfg(feature = "trash-support")]
{
names.push_untagged(UntaggedValue::string("trash"));
}
names
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::Version;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Version {})?)
}
}

View File

@ -1,134 +0,0 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use indexmap::map::IndexMap;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct Which;
#[async_trait]
impl WholeStreamCommand for Which {
fn name(&self) -> &str {
"which"
}
fn signature(&self) -> Signature {
Signature::build("which")
.required("application", SyntaxShape::String, "application")
.switch("all", "list all executables", Some('a'))
}
fn usage(&self) -> &str {
"Finds a program file."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
which(args, registry).await
}
}
/// Shortcuts for creating an entry to the output table
fn entry(arg: impl Into<String>, path: Value, builtin: bool, tag: Tag) -> Value {
let mut map = IndexMap::new();
map.insert(
"arg".to_string(),
UntaggedValue::Primitive(Primitive::String(arg.into())).into_value(tag.clone()),
);
map.insert("path".to_string(), path);
map.insert(
"builtin".to_string(),
UntaggedValue::boolean(builtin).into_value(tag.clone()),
);
UntaggedValue::row(map).into_value(tag)
}
macro_rules! entry_builtin {
($arg:expr, $tag:expr) => {
entry(
$arg.clone(),
UntaggedValue::Primitive(Primitive::String("nushell built-in command".to_string()))
.into_value($tag.clone()),
true,
$tag,
)
};
}
#[allow(unused)]
macro_rules! entry_path {
($arg:expr, $path:expr, $tag:expr) => {
entry(
$arg.clone(),
UntaggedValue::Primitive(Primitive::Path($path)).into_value($tag.clone()),
false,
$tag,
)
};
}
#[derive(Deserialize, Debug)]
struct WhichArgs {
application: Tagged<String>,
all: bool,
}
async fn which(args: CommandArgs, registry: &CommandRegistry) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let mut output = vec![];
let (WhichArgs { application, all }, _) = args.process(&registry).await?;
let external = application.starts_with('^');
let item = if external {
application.item[1..].to_string()
} else {
application.item.clone()
};
if !external {
let builtin = registry.has(&item);
if builtin {
output.push(ReturnSuccess::value(entry_builtin!(
item,
application.tag.clone()
)));
}
}
#[cfg(feature = "ichwh")]
{
if let Ok(paths) = ichwh::which_all(&item).await {
for path in paths {
output.push(ReturnSuccess::value(entry_path!(
item,
path.into(),
application.tag.clone()
)));
}
}
}
if all {
Ok(futures::stream::iter(output.into_iter()).to_output_stream())
} else {
Ok(futures::stream::iter(output.into_iter().take(1)).to_output_stream())
}
}
#[cfg(test)]
mod tests {
use super::ShellError;
use super::Which;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Which {})?)
}
}

View File

@ -5,7 +5,7 @@ use indexmap::set::IndexSet;
use super::matchers::Matcher;
use crate::completion::{Completer, CompletionContext, Suggestion};
use crate::evaluation_context::EvaluationContext;
use nu_engine::EvaluationContext;
pub struct CommandCompleter;
@ -17,7 +17,7 @@ impl Completer for CommandCompleter {
matcher: &dyn Matcher,
) -> Vec<Suggestion> {
let context: &EvaluationContext = ctx.as_ref();
let mut commands: IndexSet<String> = IndexSet::from_iter(context.registry.names());
let mut commands: IndexSet<String> = IndexSet::from_iter(context.scope.get_command_names());
// Command suggestions can come from three possible sets:
// 1. internal command names,
@ -38,7 +38,7 @@ impl Completer for CommandCompleter {
})
.collect();
if partial != "" {
if !partial.is_empty() {
let path_completer = crate::completion::path::PathCompleter;
let path_results = path_completer.path_suggestions(partial, matcher);
let iter = path_results.into_iter().filter_map(|path_suggestion| {
@ -65,6 +65,8 @@ fn pathext() -> Option<Vec<String>> {
std::env::var_os("PATHEXT").map(|v| {
v.to_string_lossy()
.split(';')
// Filter out empty tokens and ';' at the end
.filter(|f| f.len() > 1)
// Cut off the leading '.' character
.map(|ext| ext[1..].to_string())
.collect::<Vec<_>>()

View File

@ -38,7 +38,7 @@ impl<'s> Flatten<'s> {
.collect(),
Expression::Command => vec![LocationType::Command.spanned(e.span)],
Expression::Path(path) => self.expression(&path.head),
Expression::Variable(_) => vec![LocationType::Variable.spanned(e.span)],
Expression::Variable(_, _) => vec![LocationType::Variable.spanned(e.span)],
Expression::Boolean(_)
| Expression::FilePath(_)
@ -138,7 +138,7 @@ impl<'s> Flatten<'s> {
result
}
fn pipeline(&self, pipeline: &Commands) -> Vec<CompletionLocation> {
fn pipeline(&self, pipeline: &Pipeline) -> Vec<CompletionLocation> {
let mut result = Vec::new();
for command in &pipeline.list {
@ -158,7 +158,11 @@ impl<'s> Flatten<'s> {
/// Flattens the block into a Vec of completion locations
pub fn completion_locations(&self, block: &Block) -> Vec<CompletionLocation> {
block.block.iter().flat_map(|v| self.pipeline(v)).collect()
block
.block
.iter()
.flat_map(|g| g.pipelines.iter().flat_map(|v| self.pipeline(v)))
.collect()
}
pub fn new(line: &'s str) -> Flatten<'s> {
@ -252,7 +256,7 @@ pub fn completion_location(line: &str, block: &Block, pos: usize) -> Vec<Complet
mod tests {
use super::*;
use nu_parser::SignatureRegistry;
use nu_parser::{classify_block, lex, parse_block, ParserScope};
use nu_protocol::{Signature, SyntaxShape};
#[derive(Clone, Debug)]
@ -264,38 +268,52 @@ mod tests {
}
}
impl SignatureRegistry for VecRegistry {
fn has(&self, name: &str) -> bool {
impl ParserScope for VecRegistry {
fn has_signature(&self, name: &str) -> bool {
self.0.iter().any(|v| v.name == name)
}
fn get(&self, name: &str) -> Option<nu_protocol::Signature> {
fn get_signature(&self, name: &str) -> Option<nu_protocol::Signature> {
self.0.iter().find(|v| v.name == name).map(Clone::clone)
}
fn clone_box(&self) -> Box<dyn SignatureRegistry> {
Box::new(self.clone())
fn get_alias(&self, _name: &str) -> Option<Vec<Spanned<String>>> {
None
}
fn add_alias(&self, _name: &str, _replacement: Vec<Spanned<String>>) {
todo!()
}
fn add_definition(&self, _block: Block) {}
fn get_definitions(&self) -> Vec<Block> {
vec![]
}
fn enter_scope(&self) {}
fn exit_scope(&self) {}
}
mod completion_location {
use super::*;
use nu_parser::{classify_block, lite_parse, SignatureRegistry};
use nu_parser::ParserScope;
fn completion_location(
line: &str,
registry: &dyn SignatureRegistry,
scope: &dyn ParserScope,
pos: usize,
) -> Vec<LocationType> {
let lite_block = match lite_parse(line, 0) {
Ok(v) => v,
Err(e) => e.partial.expect("lite_parse result"),
};
let (tokens, _) = lex(line, 0);
let (lite_block, _) = parse_block(tokens);
let block = classify_block(&lite_block, registry);
scope.enter_scope();
let (block, _) = classify_block(&lite_block, scope);
scope.exit_scope();
super::completion_location(line, &block.block, pos)
super::completion_location(line, &block, pos)
.into_iter()
.map(|v| v.item)
.collect()

View File

@ -1,6 +1,6 @@
use super::matchers::Matcher;
use crate::completion::{Completer, CompletionContext, Suggestion};
use crate::evaluation_context::EvaluationContext;
use nu_engine::EvaluationContext;
pub struct FlagCompleter {
pub(crate) cmd: String,
@ -15,7 +15,7 @@ impl Completer for FlagCompleter {
) -> Vec<Suggestion> {
let context: &EvaluationContext = ctx.as_ref();
if let Some(cmd) = context.registry.get_command(&self.cmd) {
if let Some(cmd) = context.scope.get_command(&self.cmd) {
let sig = cmd.signature();
let mut suggestions = Vec::new();
for (name, (named_type, _desc)) in sig.named.iter() {

View File

@ -4,8 +4,8 @@ pub(crate) mod flag;
pub(crate) mod matchers;
pub(crate) mod path;
use crate::evaluation_context::EvaluationContext;
use matchers::Matcher;
use nu_engine::EvaluationContext;
#[derive(Debug, Eq, PartialEq)]
pub struct Suggestion {

View File

@ -22,14 +22,14 @@ impl PathCompleter {
None => ("", expanded),
};
let base_dir = if base_dir_name == "" {
let base_dir = if base_dir_name.is_empty() {
PathBuf::from(".")
} else {
#[cfg(feature = "directories")]
{
let home_prefix = format!("~{}", SEP);
if base_dir_name.starts_with(&home_prefix) {
let mut home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("~"));
let mut home_dir = dirs_next::home_dir().unwrap_or_else(|| PathBuf::from("~"));
home_dir.push(&base_dir_name[2..]);
home_dir
} else {

View File

@ -1,6 +1,3 @@
pub(crate) mod directory_specific_environment;
pub(crate) mod environment;
pub(crate) mod environment_syncer;
pub(crate) mod host;
pub(crate) use self::host::Host;

View File

@ -1,6 +1,5 @@
use crate::commands;
use commands::autoenv;
use indexmap::{IndexMap, IndexSet};
use nu_command::commands::autoenv;
use nu_errors::ShellError;
use serde::Deserialize;
use std::env::*;
@ -52,7 +51,7 @@ impl DirectorySpecificEnvironment {
}
}
fn toml_if_trusted(&mut self, nu_env_file: &PathBuf) -> Result<NuEnvDoc, ShellError> {
fn toml_if_trusted(&mut self, nu_env_file: &Path) -> Result<NuEnvDoc, ShellError> {
let content = std::fs::read(&nu_env_file)?;
if autoenv::file_is_trusted(&nu_env_file, &content)? {
@ -162,7 +161,7 @@ impl DirectorySpecificEnvironment {
pub fn maybe_add_key(
&mut self,
seen_vars: &mut IndexSet<EnvKey>,
dir: &PathBuf,
dir: &Path,
key: &str,
val: &str,
) {
@ -170,7 +169,7 @@ impl DirectorySpecificEnvironment {
if !seen_vars.contains(key) {
seen_vars.insert(key.to_string());
self.added_vars
.entry(dir.clone())
.entry(PathBuf::from(dir))
.or_insert(IndexMap::new())
.insert(key.to_string(), var_os(key));

View File

@ -1,38 +1,9 @@
use crate::env::directory_specific_environment::*;
use indexmap::{indexmap, IndexSet};
use nu_data::config::Conf;
use nu_engine::Env;
use nu_errors::ShellError;
use nu_protocol::{UntaggedValue, Value};
use std::env::*;
use std::ffi::OsString;
use std::fmt::Debug;
pub trait Env: Debug + Send {
fn env(&self) -> Option<Value>;
fn path(&self) -> Option<Value>;
fn add_env(&mut self, key: &str, value: &str);
fn add_path(&mut self, new_path: OsString);
}
impl Env for Box<dyn Env> {
fn env(&self) -> Option<Value> {
(**self).env()
}
fn path(&self) -> Option<Value> {
(**self).path()
}
fn add_env(&mut self, key: &str, value: &str) {
(**self).add_env(key, value);
}
fn add_path(&mut self, new_path: OsString) {
(**self).add_path(new_path);
}
}
#[derive(Debug, Default)]
pub struct Environment {
@ -128,7 +99,7 @@ impl Env for Environment {
{
let mut new_paths = current_paths.clone();
let new_path_candidates = split_paths(&paths).map(|path| {
let new_path_candidates = std::env::split_paths(&paths).map(|path| {
UntaggedValue::string(path.to_string_lossy()).into_value(tag.clone())
});

View File

@ -1,9 +1,10 @@
use crate::env::environment::{Env, Environment};
use crate::evaluation_context::EvaluationContext;
use crate::env::environment::Environment;
use nu_data::config::{Conf, NuConfig};
use nu_engine::Env;
use nu_engine::EvaluationContext;
use nu_errors::ShellError;
use parking_lot::Mutex;
use std::sync::Arc;
use std::sync::{atomic::Ordering, Arc};
pub struct EnvironmentSyncer {
pub env: Arc<Mutex<Box<Environment>>>,
@ -50,7 +51,7 @@ impl EnvironmentSyncer {
pub fn did_config_change(&mut self) -> bool {
let config = self.config.lock();
config.is_modified().unwrap_or_else(|_| false)
config.is_modified().unwrap_or(false)
}
pub fn reload(&mut self) {
@ -63,8 +64,12 @@ impl EnvironmentSyncer {
pub fn autoenv(&self, ctx: &mut EvaluationContext) -> Result<(), ShellError> {
let mut environment = self.env.lock();
let auto = environment.autoenv(ctx.user_recently_used_autoenv_untrust);
ctx.user_recently_used_autoenv_untrust = false;
let recently_used = ctx
.user_recently_used_autoenv_untrust
.load(Ordering::SeqCst);
let auto = environment.autoenv(recently_used);
ctx.user_recently_used_autoenv_untrust
.store(false, Ordering::SeqCst);
auto
}
@ -90,9 +95,11 @@ impl EnvironmentSyncer {
ctx.with_host(|host| {
host.env_set(
std::ffi::OsString::from(var.0),
std::ffi::OsString::from(string),
std::ffi::OsString::from(&string),
)
});
ctx.scope.add_env_var_to_base(var.0, string);
}
}
}
@ -123,8 +130,14 @@ impl EnvironmentSyncer {
if let Ok(paths_ready) = prepared {
ctx.with_host(|host| {
host.env_set(std::ffi::OsString::from("PATH"), paths_ready);
host.env_set(
std::ffi::OsString::from("PATH"),
std::ffi::OsString::from(&paths_ready),
);
});
ctx.scope
.add_env_var_to_base("PATH", paths_ready.to_string_lossy().to_string());
}
}
}
@ -148,10 +161,10 @@ impl EnvironmentSyncer {
#[cfg(test)]
mod tests {
use super::EnvironmentSyncer;
use crate::env::environment::Env;
use crate::evaluation_context::EvaluationContext;
use indexmap::IndexMap;
use nu_data::config::tests::FakeConfig;
use nu_engine::basic_evaluation_context;
use nu_engine::Env;
use nu_errors::ShellError;
use nu_test_support::fs::Stub::FileWithContent;
use nu_test_support::playground::Playground;
@ -166,8 +179,8 @@ mod tests {
#[test]
fn syncs_env_if_new_env_entry_is_added_to_an_existing_configuration() -> Result<(), ShellError>
{
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
let mut ctx = basic_evaluation_context()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let mut expected = IndexMap::new();
expected.insert(
@ -198,7 +211,7 @@ mod tests {
let new_file = dirs.test().join("updated_configuration.toml");
let fake_config = FakeConfig::new(&file);
let mut actual = EnvironmentSyncer::with_config(Box::new(fake_config.clone()));
let mut actual = EnvironmentSyncer::with_config(Box::new(fake_config));
// Here, the environment variables from the current session
// are cleared since we will load and set them from the
@ -269,8 +282,8 @@ mod tests {
#[test]
fn syncs_env_if_new_env_entry_in_session_is_not_in_configuration_file() -> Result<(), ShellError>
{
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
let mut ctx = basic_evaluation_context()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let mut expected = IndexMap::new();
expected.insert(
@ -368,8 +381,8 @@ mod tests {
#[test]
fn nu_envs_have_higher_priority_and_does_not_get_overwritten() -> Result<(), ShellError> {
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
let mut ctx = basic_evaluation_context()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let mut expected = IndexMap::new();
expected.insert(
@ -444,8 +457,8 @@ mod tests {
#[test]
fn syncs_path_if_new_path_entry_in_session_is_not_in_configuration_file(
) -> Result<(), ShellError> {
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
let mut ctx = basic_evaluation_context()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let expected = std::env::join_paths(vec![
PathBuf::from("/Users/andresrobalino/.volta/bin"),
@ -531,8 +544,8 @@ mod tests {
#[test]
fn nu_paths_have_higher_priority_and_new_paths_get_appended_to_the_end(
) -> Result<(), ShellError> {
let mut ctx = EvaluationContext::basic()?;
ctx.host = Arc::new(Mutex::new(Box::new(crate::env::host::FakeHost::new())));
let mut ctx = basic_evaluation_context()?;
ctx.host = Arc::new(Mutex::new(Box::new(nu_engine::FakeHost::new())));
let expected = std::env::join_paths(vec![
PathBuf::from("/Users/andresrobalino/.volta/bin"),

View File

@ -1,195 +0,0 @@
use crate::command_registry::CommandRegistry;
use crate::commands::{command::CommandArgs, Command, UnevaluatedCallInfo};
use crate::env::host::Host;
use crate::shell::shell_manager::ShellManager;
use crate::stream::{InputStream, OutputStream};
use indexmap::IndexMap;
use nu_errors::ShellError;
use nu_protocol::{hir, Scope};
use nu_source::{Tag, Text};
use parking_lot::Mutex;
use std::error::Error;
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
#[derive(Clone)]
pub struct EvaluationContext {
pub registry: CommandRegistry,
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub ctrl_c: Arc<AtomicBool>,
pub raw_input: String,
pub user_recently_used_autoenv_untrust: bool,
pub(crate) shell_manager: ShellManager,
/// Windows-specific: keep track of previous cwd on each drive
pub windows_drives_previous_cwd: Arc<Mutex<std::collections::HashMap<String, String>>>,
}
impl EvaluationContext {
pub(crate) fn registry(&self) -> &CommandRegistry {
&self.registry
}
pub(crate) fn from_raw(
raw_args: &CommandArgs,
registry: &CommandRegistry,
) -> EvaluationContext {
EvaluationContext {
registry: registry.clone(),
host: raw_args.host.clone(),
current_errors: raw_args.current_errors.clone(),
ctrl_c: raw_args.ctrl_c.clone(),
shell_manager: raw_args.shell_manager.clone(),
user_recently_used_autoenv_untrust: false,
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
raw_input: String::default(),
}
}
pub(crate) fn from_args(args: &CommandArgs, registry: &CommandRegistry) -> EvaluationContext {
EvaluationContext {
registry: registry.clone(),
host: args.host.clone(),
current_errors: args.current_errors.clone(),
ctrl_c: args.ctrl_c.clone(),
shell_manager: args.shell_manager.clone(),
user_recently_used_autoenv_untrust: false,
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
raw_input: String::default(),
}
}
pub fn basic() -> Result<EvaluationContext, Box<dyn Error>> {
let registry = CommandRegistry::new();
Ok(EvaluationContext {
registry,
host: Arc::new(parking_lot::Mutex::new(Box::new(
crate::env::host::BasicHost,
))),
current_errors: Arc::new(Mutex::new(vec![])),
ctrl_c: Arc::new(AtomicBool::new(false)),
user_recently_used_autoenv_untrust: false,
shell_manager: ShellManager::basic()?,
windows_drives_previous_cwd: Arc::new(Mutex::new(std::collections::HashMap::new())),
raw_input: String::default(),
})
}
pub(crate) fn error(&mut self, error: ShellError) {
self.with_errors(|errors| errors.push(error))
}
pub(crate) fn clear_errors(&mut self) {
self.current_errors.lock().clear()
}
pub(crate) fn get_errors(&self) -> Vec<ShellError> {
self.current_errors.lock().clone()
}
pub(crate) fn add_error(&self, err: ShellError) {
self.current_errors.lock().push(err);
}
pub(crate) fn maybe_print_errors(&mut self, source: Text) -> bool {
let errors = self.current_errors.clone();
let mut errors = errors.lock();
if errors.len() > 0 {
let error = errors[0].clone();
*errors = vec![];
crate::cli::print_err(error, &source);
true
} else {
false
}
}
pub(crate) fn configure<T>(
&mut self,
config: &dyn nu_data::config::Conf,
block: impl FnOnce(&dyn nu_data::config::Conf, &mut Self) -> T,
) {
block(config, &mut *self);
}
pub(crate) fn with_host<T>(&mut self, block: impl FnOnce(&mut dyn Host) -> T) -> T {
let mut host = self.host.lock();
block(&mut *host)
}
pub(crate) fn with_errors<T>(&mut self, block: impl FnOnce(&mut Vec<ShellError>) -> T) -> T {
let mut errors = self.current_errors.lock();
block(&mut *errors)
}
pub fn add_commands(&mut self, commands: Vec<Command>) {
for command in commands {
self.registry.insert(command.name().to_string(), command);
}
}
#[allow(unused)]
pub(crate) fn get_command(&self, name: &str) -> Option<Command> {
self.registry.get_command(name)
}
pub(crate) fn is_command_registered(&self, name: &str) -> bool {
self.registry.has(name)
}
pub(crate) fn expect_command(&self, name: &str) -> Result<Command, ShellError> {
self.registry.expect_command(name)
}
pub(crate) async fn run_command(
&mut self,
command: Command,
name_tag: Tag,
args: hir::Call,
scope: Arc<Scope>,
input: InputStream,
) -> Result<OutputStream, ShellError> {
let command_args = self.command_args(args, input, name_tag, scope);
command.run(command_args, self.registry()).await
}
fn call_info(&self, args: hir::Call, name_tag: Tag, scope: Arc<Scope>) -> UnevaluatedCallInfo {
UnevaluatedCallInfo {
args,
name_tag,
scope,
}
}
fn command_args(
&self,
args: hir::Call,
input: InputStream,
name_tag: Tag,
scope: Arc<Scope>,
) -> CommandArgs {
CommandArgs {
host: self.host.clone(),
ctrl_c: self.ctrl_c.clone(),
current_errors: self.current_errors.clone(),
shell_manager: self.shell_manager.clone(),
call_info: self.call_info(args, name_tag, scope),
input,
raw_input: self.raw_input.clone(),
}
}
pub fn get_env(&self) -> IndexMap<String, String> {
let mut output = IndexMap::new();
for (var, value) in self.host.lock().vars() {
output.insert(var, value);
}
output
}
}

View File

@ -1,474 +0,0 @@
use nu_errors::ShellError;
use nu_protocol::hir::ClassifiedBlock;
use nu_protocol::{
Primitive, ReturnSuccess, Scope, ShellTypeName, Signature, SyntaxShape, UntaggedValue, Value,
};
use nu_source::{AnchorLocation, TaggedItem};
use crate::prelude::*;
use indexmap::indexmap;
use num_bigint::BigInt;
use indexmap::IndexMap;
use crate::command_registry::CommandRegistry;
use crate::commands::classified::block::run_block;
use crate::commands::command::CommandArgs;
use crate::commands::{
whole_stream_command, BuildString, Command, Each, Echo, Get, Keep, StrCollect,
WholeStreamCommand, Wrap,
};
use crate::evaluation_context::EvaluationContext;
use crate::stream::{InputStream, OutputStream};
use async_trait::async_trait;
use futures::executor::block_on;
use serde::Deserialize;
pub fn test_examples(cmd: Command) -> Result<(), ShellError> {
let examples = cmd.examples();
let mut base_context = EvaluationContext::basic()?;
base_context.add_commands(vec![
// Mocks
whole_stream_command(MockLs {}),
// Minimal restricted commands to aid in testing
whole_stream_command(Echo {}),
whole_stream_command(BuildString {}),
whole_stream_command(Get {}),
whole_stream_command(Keep {}),
whole_stream_command(Each {}),
whole_stream_command(StrCollect),
whole_stream_command(Wrap),
cmd,
]);
for sample_pipeline in examples {
let mut ctx = base_context.clone();
let block = parse_line(sample_pipeline.example, &mut ctx)?;
if let Some(expected) = &sample_pipeline.result {
let result = block_on(evaluate_block(block, &mut ctx))?;
ctx.with_errors(|reasons| reasons.iter().cloned().take(1).next())
.map_or(Ok(()), Err)?;
if expected.len() != result.len() {
let rows_returned =
format!("expected: {}\nactual: {}", expected.len(), result.len());
let failed_call = format!("command: {}\n", sample_pipeline.example);
panic!(
"example command produced unexpected number of results.\n {} {}",
failed_call, rows_returned
);
}
for (e, a) in expected.iter().zip(result.iter()) {
if !values_equal(e, a) {
let row_errored = format!("expected: {:#?}\nactual: {:#?}", e, a);
let failed_call = format!("command: {}\n", sample_pipeline.example);
panic!(
"example command produced unexpected result.\n {} {}",
failed_call, row_errored
);
}
}
}
}
Ok(())
}
pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
let examples = cmd.examples();
let mut base_context = EvaluationContext::basic()?;
base_context.add_commands(vec![
whole_stream_command(Echo {}),
whole_stream_command(BuildString {}),
whole_stream_command(Get {}),
whole_stream_command(Keep {}),
whole_stream_command(Each {}),
whole_stream_command(cmd),
whole_stream_command(StrCollect),
whole_stream_command(Wrap),
]);
for sample_pipeline in examples {
let mut ctx = base_context.clone();
let block = parse_line(sample_pipeline.example, &mut ctx)?;
if let Some(expected) = &sample_pipeline.result {
let result = block_on(evaluate_block(block, &mut ctx))?;
ctx.with_errors(|reasons| reasons.iter().cloned().take(1).next())
.map_or(Ok(()), Err)?;
if expected.len() != result.len() {
let rows_returned =
format!("expected: {}\nactual: {}", expected.len(), result.len());
let failed_call = format!("command: {}\n", sample_pipeline.example);
panic!(
"example command produced unexpected number of results.\n {} {}",
failed_call, rows_returned
);
}
for (e, a) in expected.iter().zip(result.iter()) {
if !values_equal(e, a) {
let row_errored = format!("expected: {:#?}\nactual: {:#?}", e, a);
let failed_call = format!("command: {}\n", sample_pipeline.example);
panic!(
"example command produced unexpected result.\n {} {}",
failed_call, row_errored
);
}
}
}
}
Ok(())
}
pub fn test_anchors(cmd: Command) -> Result<(), ShellError> {
let examples = cmd.examples();
let mut base_context = EvaluationContext::basic()?;
base_context.add_commands(vec![
// Minimal restricted commands to aid in testing
whole_stream_command(MockCommand {}),
whole_stream_command(MockEcho {}),
whole_stream_command(MockLs {}),
whole_stream_command(BuildString {}),
whole_stream_command(Get {}),
whole_stream_command(Keep {}),
whole_stream_command(Each {}),
whole_stream_command(StrCollect),
whole_stream_command(Wrap),
cmd,
]);
for sample_pipeline in examples {
let pipeline_with_anchor = format!("mock --open --path | {}", sample_pipeline.example);
let mut ctx = base_context.clone();
let block = parse_line(&pipeline_with_anchor, &mut ctx)?;
let result = block_on(evaluate_block(block, &mut ctx))?;
ctx.with_errors(|reasons| reasons.iter().cloned().take(1).next())
.map_or(Ok(()), Err)?;
for actual in result.iter() {
if !is_anchor_carried(actual, mock_path()) {
let failed_call = format!("command: {}\n", pipeline_with_anchor);
panic!(
"example command didn't carry anchor tag correctly.\n {} {:#?} {:#?}",
failed_call,
actual,
mock_path()
);
}
}
}
Ok(())
}
/// Parse and run a nushell pipeline
fn parse_line(line: &str, ctx: &mut EvaluationContext) -> Result<ClassifiedBlock, ShellError> {
let line = if line.ends_with('\n') {
&line[..line.len() - 1]
} else {
line
};
let lite_result = nu_parser::lite_parse(&line, 0)?;
// TODO ensure the command whose examples we're testing is actually in the pipeline
let mut classified_block = nu_parser::classify_block(&lite_result, ctx.registry());
classified_block.block.expand_it_usage();
Ok(classified_block)
}
async fn evaluate_block(
block: ClassifiedBlock,
ctx: &mut EvaluationContext,
) -> Result<Vec<Value>, ShellError> {
let input_stream = InputStream::empty();
let env = ctx.get_env();
let scope = Scope::from_env(env);
Ok(run_block(&block.block, ctx, input_stream, scope)
.await?
.drain_vec()
.await)
}
// TODO probably something already available to do this
// TODO perhaps better panic messages when things don't compare
// Deep value comparisons that ignore tags
fn values_equal(expected: &Value, actual: &Value) -> bool {
use nu_protocol::UntaggedValue::*;
match (&expected.value, &actual.value) {
(Primitive(e), Primitive(a)) => e == a,
(Row(e), Row(a)) => {
if e.entries.len() != a.entries.len() {
return false;
}
e.entries
.iter()
.zip(a.entries.iter())
.all(|((ek, ev), (ak, av))| ek == ak && values_equal(ev, av))
}
(Table(e), Table(a)) => e.iter().zip(a.iter()).all(|(e, a)| values_equal(e, a)),
(e, a) => unimplemented!("{} {}", e.type_name(), a.type_name()),
}
}
fn is_anchor_carried(actual: &Value, anchor: AnchorLocation) -> bool {
actual.tag.anchor() == Some(anchor)
}
#[derive(Deserialize)]
struct Arguments {
path: Option<bool>,
open: bool,
}
struct MockCommand;
#[async_trait]
impl WholeStreamCommand for MockCommand {
fn name(&self) -> &str {
"mock"
}
fn signature(&self) -> Signature {
Signature::build("mock")
.switch("open", "fake opening sources", Some('o'))
.switch("path", "file open", Some('p'))
}
fn usage(&self) -> &str {
"Generates tables and metadata that mimics behavior of real commands in controlled ways."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (
Arguments {
path: mocked_path,
open: open_mock,
},
_input,
) = args.process(&registry).await?;
let out = UntaggedValue::string("Yehuda Katz in Ecuador");
if open_mock {
if let Some(true) = mocked_path {
return Ok(OutputStream::one(Ok(ReturnSuccess::Value(Value {
value: out,
tag: Tag {
anchor: Some(mock_path()),
span: name_tag.span,
},
}))));
}
}
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
out.into_value(name_tag),
))))
}
}
struct MockEcho;
#[derive(Deserialize)]
struct MockEchoArgs {
pub rest: Vec<Value>,
}
#[async_trait]
impl WholeStreamCommand for MockEcho {
fn name(&self) -> &str {
"echo"
}
fn signature(&self) -> Signature {
Signature::build("echo").rest(SyntaxShape::Any, "the values to echo")
}
fn usage(&self) -> &str {
"Mock echo."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (MockEchoArgs { rest }, input) = args.process(&registry).await?;
let mut base_value = UntaggedValue::string("Yehuda Katz in Ecuador").into_value(name_tag);
let input: Vec<Value> = input.collect().await;
if let Some(first) = input.get(0) {
base_value = first.clone()
}
let stream = rest.into_iter().map(move |i| {
let base_value = base_value.clone();
match i.as_string() {
Ok(s) => OutputStream::one(Ok(ReturnSuccess::Value(Value {
value: UntaggedValue::Primitive(Primitive::String(s)),
tag: base_value.tag,
}))),
_ => match i {
Value {
value: UntaggedValue::Table(table),
..
} => futures::stream::iter(
table
.into_iter()
.map(move |mut v| {
v.tag = base_value.tag();
v
})
.map(ReturnSuccess::value),
)
.to_output_stream(),
_ => OutputStream::one(Ok(ReturnSuccess::Value(Value {
value: i.value.clone(),
tag: base_value.tag,
}))),
},
}
});
Ok(futures::stream::iter(stream).flatten().to_output_stream())
}
}
struct MockLs;
#[async_trait]
impl WholeStreamCommand for MockLs {
fn name(&self) -> &str {
"ls"
}
fn signature(&self) -> Signature {
Signature::build("ls")
}
fn usage(&self) -> &str {
"Mock ls."
}
async fn run(
&self,
args: CommandArgs,
_: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let mut base_value =
UntaggedValue::string("Andrés N. Robalino in Portland").into_value(name_tag);
let input: Vec<Value> = args.input.collect().await;
if let Some(first) = input.get(0) {
base_value = first.clone()
}
Ok(futures::stream::iter(
file_listing()
.iter()
.map(|row| Value {
value: row.value.clone(),
tag: base_value.tag.clone(),
})
.collect::<Vec<_>>()
.into_iter()
.map(ReturnSuccess::value),
)
.to_output_stream())
}
}
fn int(s: impl Into<BigInt>) -> Value {
UntaggedValue::int(s).into_untagged_value()
}
fn string(input: impl Into<String>) -> Value {
UntaggedValue::string(input.into()).into_untagged_value()
}
fn row(entries: IndexMap<String, Value>) -> Value {
UntaggedValue::row(entries).into_untagged_value()
}
fn date(input: impl Into<String>) -> Value {
let key = input.into().tagged_unknown();
crate::value::Date::naive_from_str(key.borrow_tagged())
.expect("date from string failed")
.into_untagged_value()
}
fn file_listing() -> Vec<Value> {
vec![
row(indexmap! {
"modified".to_string() => date("2019-07-23"),
"name".to_string() => string("Andrés.txt"),
"type".to_string() => string("File"),
"chickens".to_string() => int(10),
}),
row(indexmap! {
"modified".to_string() => date("2019-07-23"),
"name".to_string() => string("Jonathan"),
"type".to_string() => string("Dir"),
"chickens".to_string() => int(5),
}),
row(indexmap! {
"modified".to_string() => date("2019-09-24"),
"name".to_string() => string("Andrés.txt"),
"type".to_string() => string("File"),
"chickens".to_string() => int(20),
}),
row(indexmap! {
"modified".to_string() => date("2019-09-24"),
"name".to_string() => string("Yehuda"),
"type".to_string() => string("Dir"),
"chickens".to_string() => int(4),
}),
]
}
fn mock_path() -> AnchorLocation {
let path = String::from("path/to/las_best_arepas_in_the_world.txt");
AnchorLocation::File(path)
}

View File

@ -1,26 +0,0 @@
pub fn current_branch() -> Option<String> {
#[cfg(feature = "git2")]
{
use git2::{Repository, RepositoryOpenFlags};
use std::ffi::OsString;
let v: Vec<OsString> = vec![];
match Repository::open_ext(".", RepositoryOpenFlags::empty(), v) {
Ok(repo) => {
let r = repo.head();
match r {
Ok(r) => match r.shorthand() {
Some(s) => Some(s.to_string()),
None => None,
},
_ => None,
}
}
_ => None,
}
}
#[cfg(not(feature = "git2"))]
{
None
}
}

View File

@ -405,14 +405,10 @@ pub struct Keybinding {
type Keybindings = Vec<Keybinding>;
pub(crate) fn keybinding_path() -> Result<std::path::PathBuf, nu_errors::ShellError> {
nu_data::config::default_path_for(&Some(std::path::PathBuf::from("keybindings.yml")))
}
pub(crate) fn load_keybindings(
rl: &mut rustyline::Editor<crate::shell::Helper>,
) -> Result<(), nu_errors::ShellError> {
let filename = keybinding_path()?;
let filename = nu_data::keybinding::keybinding_path()?;
let contents = std::fs::read_to_string(filename);
// Silently fail if there is no file there

View File

@ -14,51 +14,28 @@ extern crate quickcheck;
extern crate quickcheck_macros;
mod cli;
mod command_registry;
mod commands;
#[cfg(feature = "rustyline-support")]
mod completion;
mod deserializer;
mod documentation;
mod env;
mod evaluate;
mod evaluation_context;
mod format;
mod futures;
#[cfg(feature = "rustyline-support")]
mod git;
#[cfg(feature = "rustyline-support")]
mod keybinding;
mod path;
mod plugin;
mod line_editor;
mod shell;
mod stream;
pub mod utils;
#[cfg(test)]
mod examples;
pub mod types;
#[cfg(feature = "rustyline-support")]
pub use crate::cli::cli;
pub use crate::cli::{
create_default_context, parse_and_eval, process_line, register_plugins,
run_pipeline_standalone, run_vec_of_pipelines, LineResult,
};
pub use crate::command_registry::CommandRegistry;
pub use crate::commands::command::{
whole_stream_command, CommandArgs, EvaluatedWholeStreamCommandArgs, Example, WholeStreamCommand,
};
pub use crate::commands::help::get_help;
pub use crate::cli::{parse_and_eval, register_plugins, run_script_file};
pub use crate::env::environment_syncer::EnvironmentSyncer;
pub use crate::env::host::BasicHost;
pub use crate::evaluation_context::EvaluationContext;
pub use crate::prelude::ToOutputStream;
pub use crate::stream::{InputStream, InterruptibleStream, OutputStream};
pub use nu_command::commands::default_context::create_default_context;
pub use nu_data::config;
pub use nu_data::dict::TaggedListBuilder;
pub use nu_data::primitive;
pub use nu_data::value;
pub use nu_stream::{InputStream, InterruptibleStream, OutputStream};
pub use nu_value_ext::ValueExt;
pub use num_traits::cast::ToPrimitive;

View File

@ -0,0 +1,234 @@
use nu_engine::EvaluationContext;
use std::error::Error;
#[allow(unused_imports)]
use crate::prelude::*;
#[allow(unused_imports)]
use nu_command::script::LineResult;
#[cfg(feature = "rustyline-support")]
use crate::shell::Helper;
#[cfg(feature = "rustyline-support")]
use rustyline::{
self,
config::Configurer,
config::{ColorMode, CompletionType, Config},
error::ReadlineError,
At, Cmd, Editor, KeyPress, Movement, Word,
};
#[cfg(feature = "rustyline-support")]
pub fn convert_rustyline_result_to_string(input: Result<String, ReadlineError>) -> LineResult {
match input {
Ok(s) if s == "history -c" || s == "history --clear" => LineResult::ClearHistory,
Ok(s) => LineResult::Success(s),
Err(ReadlineError::Interrupted) => LineResult::CtrlC,
Err(ReadlineError::Eof) => LineResult::CtrlD,
Err(err) => {
outln!("Error: {:?}", err);
LineResult::Break
}
}
}
#[cfg(feature = "rustyline-support")]
pub fn default_rustyline_editor_configuration() -> Editor<Helper> {
#[cfg(windows)]
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
#[cfg(not(windows))]
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
let config = Config::builder().color_mode(ColorMode::Forced).build();
let mut rl: Editor<_> = Editor::with_config(config);
// add key bindings to move over a whole word with Ctrl+ArrowLeft and Ctrl+ArrowRight
rl.bind_sequence(
KeyPress::ControlLeft,
Cmd::Move(Movement::BackwardWord(1, Word::Vi)),
);
rl.bind_sequence(
KeyPress::ControlRight,
Cmd::Move(Movement::ForwardWord(1, At::AfterEnd, Word::Vi)),
);
// workaround for multiline-paste hang in rustyline (see https://github.com/kkawakam/rustyline/issues/202)
rl.bind_sequence(KeyPress::BracketedPasteStart, rustyline::Cmd::Noop);
// Let's set the defaults up front and then override them later if the user indicates
// defaults taken from here https://github.com/kkawakam/rustyline/blob/2fe886c9576c1ea13ca0e5808053ad491a6fe049/src/config.rs#L150-L167
rl.set_max_history_size(100);
rl.set_history_ignore_dups(true);
rl.set_history_ignore_space(false);
rl.set_completion_type(DEFAULT_COMPLETION_MODE);
rl.set_completion_prompt_limit(100);
rl.set_keyseq_timeout(-1);
rl.set_edit_mode(rustyline::config::EditMode::Emacs);
rl.set_auto_add_history(false);
rl.set_bell_style(rustyline::config::BellStyle::default());
rl.set_color_mode(rustyline::ColorMode::Enabled);
rl.set_tab_stop(8);
if let Err(e) = crate::keybinding::load_keybindings(&mut rl) {
println!("Error loading keybindings: {:?}", e);
}
rl
}
#[cfg(feature = "rustyline-support")]
pub fn configure_rustyline_editor(
rl: &mut Editor<Helper>,
config: &dyn nu_data::config::Conf,
) -> Result<(), ShellError> {
#[cfg(windows)]
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::Circular;
#[cfg(not(windows))]
const DEFAULT_COMPLETION_MODE: CompletionType = CompletionType::List;
if let Some(line_editor_vars) = config.var("line_editor") {
for (idx, value) in line_editor_vars.row_entries() {
match idx.as_ref() {
"max_history_size" => {
if let Ok(max_history_size) = value.as_u64() {
rl.set_max_history_size(max_history_size as usize);
}
}
"history_duplicates" => {
// history_duplicates = match value.as_string() {
// Ok(s) if s.to_lowercase() == "alwaysadd" => {
// rustyline::config::HistoryDuplicates::AlwaysAdd
// }
// Ok(s) if s.to_lowercase() == "ignoreconsecutive" => {
// rustyline::config::HistoryDuplicates::IgnoreConsecutive
// }
// _ => rustyline::config::HistoryDuplicates::AlwaysAdd,
// };
if let Ok(history_duplicates) = value.as_bool() {
rl.set_history_ignore_dups(history_duplicates);
}
}
"history_ignore_space" => {
if let Ok(history_ignore_space) = value.as_bool() {
rl.set_history_ignore_space(history_ignore_space);
}
}
"completion_type" => {
let completion_type = match value.as_string() {
Ok(s) if s.to_lowercase() == "circular" => {
rustyline::config::CompletionType::Circular
}
Ok(s) if s.to_lowercase() == "list" => {
rustyline::config::CompletionType::List
}
#[cfg(all(unix, feature = "with-fuzzy"))]
Ok(s) if s.to_lowercase() == "fuzzy" => {
rustyline::config::CompletionType::Fuzzy
}
_ => DEFAULT_COMPLETION_MODE,
};
rl.set_completion_type(completion_type);
}
"completion_prompt_limit" => {
if let Ok(completion_prompt_limit) = value.as_u64() {
rl.set_completion_prompt_limit(completion_prompt_limit as usize);
}
}
"keyseq_timeout_ms" => {
if let Ok(keyseq_timeout_ms) = value.as_u64() {
rl.set_keyseq_timeout(keyseq_timeout_ms as i32);
}
}
"edit_mode" => {
let edit_mode = match value.as_string() {
Ok(s) if s.to_lowercase() == "vi" => rustyline::config::EditMode::Vi,
Ok(s) if s.to_lowercase() == "emacs" => rustyline::config::EditMode::Emacs,
_ => rustyline::config::EditMode::Emacs,
};
rl.set_edit_mode(edit_mode);
// Note: When edit_mode is Emacs, the keyseq_timeout_ms is set to -1
// no matter what you may have configured. This is so that key chords
// can be applied without having to do them in a given timeout. So,
// it essentially turns off the keyseq timeout.
}
"auto_add_history" => {
if let Ok(auto_add_history) = value.as_bool() {
rl.set_auto_add_history(auto_add_history);
}
}
"bell_style" => {
let bell_style = match value.as_string() {
Ok(s) if s.to_lowercase() == "audible" => {
rustyline::config::BellStyle::Audible
}
Ok(s) if s.to_lowercase() == "none" => rustyline::config::BellStyle::None,
Ok(s) if s.to_lowercase() == "visible" => {
rustyline::config::BellStyle::Visible
}
_ => rustyline::config::BellStyle::default(),
};
rl.set_bell_style(bell_style);
}
"color_mode" => {
let color_mode = match value.as_string() {
Ok(s) if s.to_lowercase() == "enabled" => rustyline::ColorMode::Enabled,
Ok(s) if s.to_lowercase() == "forced" => rustyline::ColorMode::Forced,
Ok(s) if s.to_lowercase() == "disabled" => rustyline::ColorMode::Disabled,
_ => rustyline::ColorMode::Enabled,
};
rl.set_color_mode(color_mode);
}
"tab_stop" => {
if let Ok(tab_stop) = value.as_u64() {
rl.set_tab_stop(tab_stop as usize);
}
}
_ => (),
}
}
}
Ok(())
}
#[cfg(feature = "rustyline-support")]
pub fn nu_line_editor_helper(
context: &mut EvaluationContext,
config: &dyn nu_data::config::Conf,
) -> crate::shell::Helper {
let hinter = rustyline_hinter(config);
crate::shell::Helper::new(context.clone(), hinter)
}
#[cfg(feature = "rustyline-support")]
pub fn rustyline_hinter(
config: &dyn nu_data::config::Conf,
) -> Option<rustyline::hint::HistoryHinter> {
if let Some(line_editor_vars) = config.var("line_editor") {
for (idx, value) in line_editor_vars.row_entries() {
if idx == "show_hints" && value.expect_string() == "false" {
return None;
}
}
}
Some(rustyline::hint::HistoryHinter {})
}
pub fn configure_ctrl_c(_context: &mut EvaluationContext) -> Result<(), Box<dyn Error>> {
#[cfg(feature = "ctrlc")]
{
let cc = _context.ctrl_c.clone();
ctrlc::set_handler(move || {
cc.store(true, Ordering::SeqCst);
})?;
if _context.ctrl_c.load(Ordering::SeqCst) {
_context.ctrl_c.store(false, Ordering::SeqCst);
}
}
Ok(())
}

View File

@ -21,28 +21,6 @@ macro_rules! stream {
}}
}
#[macro_export]
macro_rules! trace_stream {
(target: $target:tt, $desc:tt = $expr:expr) => {{
if log::log_enabled!(target: $target, log::Level::Trace) {
use futures::stream::StreamExt;
let objects = $expr.inspect(move |o| {
trace!(
target: $target,
"{} = {}",
$desc,
nu_source::PrettyDebug::plain_string(o, 70)
);
});
$crate::stream::InputStream::from_stream(objects.boxed())
} else {
$expr
}
}};
}
#[macro_export]
macro_rules! trace_out_stream {
(target: $target:tt, $desc:tt = $expr:expr) => {{
@ -61,49 +39,26 @@ macro_rules! trace_out_stream {
);
});
$crate::stream::OutputStream::new(objects)
nu_stream::OutputStream::new(objects)
} else {
$expr
}
}};
}
pub(crate) use nu_protocol::{errln, out, outln};
use nu_source::HasFallibleSpan;
pub(crate) use crate::command_registry::CommandRegistry;
pub(crate) use crate::commands::command::{CommandArgs, RawCommandArgs, RunnableContext};
pub(crate) use crate::commands::Example;
pub(crate) use crate::evaluation_context::EvaluationContext;
pub(crate) use nu_data::config;
pub(crate) use nu_data::value;
// pub(crate) use crate::env::host::handle_unexpected;
pub(crate) use crate::env::Host;
pub(crate) use crate::shell::filesystem_shell::FilesystemShell;
pub(crate) use crate::shell::help_shell::HelpShell;
pub(crate) use crate::shell::shell_manager::ShellManager;
pub(crate) use crate::shell::value_shell::ValueShell;
pub(crate) use crate::stream::{InputStream, InterruptibleStream, OutputStream};
pub(crate) use bigdecimal::BigDecimal;
pub(crate) use futures::stream::BoxStream;
pub(crate) use futures::{Stream, StreamExt};
pub(crate) use nu_source::{
b, AnchorLocation, DebugDocBuilder, PrettyDebug, PrettyDebugWithSource, Span, SpannedItem, Tag,
TaggedItem, Text,
};
pub(crate) use nu_engine::Host;
#[allow(unused_imports)]
pub(crate) use nu_errors::ShellError;
#[allow(unused_imports)]
pub(crate) use nu_protocol::outln;
pub(crate) use nu_stream::OutputStream;
#[allow(unused_imports)]
pub(crate) use nu_value_ext::ValueExt;
pub(crate) use num_bigint::BigInt;
pub(crate) use num_traits::cast::ToPrimitive;
pub(crate) use serde::Deserialize;
pub(crate) use std::collections::VecDeque;
pub(crate) use std::future::Future;
pub(crate) use std::sync::atomic::AtomicBool;
pub(crate) use std::sync::Arc;
pub(crate) use async_trait::async_trait;
pub(crate) use indexmap::IndexMap;
pub(crate) use itertools::Itertools;
#[allow(unused_imports)]
pub(crate) use std::sync::atomic::Ordering;
#[allow(clippy::clippy::wrong_self_convention)]
pub trait FromInputStream {
fn from_input_stream(self) -> OutputStream;
}
@ -119,26 +74,7 @@ where
}
}
pub trait ToInputStream {
fn to_input_stream(self) -> InputStream;
}
impl<T, U> ToInputStream for T
where
T: Stream<Item = U> + Send + 'static,
U: Into<Result<nu_protocol::Value, nu_errors::ShellError>>,
{
fn to_input_stream(self) -> InputStream {
InputStream::from_stream(self.map(|item| match item.into() {
Ok(result) => result,
Err(err) => match HasFallibleSpan::maybe_span(&err) {
Some(span) => nu_protocol::UntaggedValue::Error(err).into_value(span),
None => nu_protocol::UntaggedValue::Error(err).into_untagged_value(),
},
}))
}
}
#[allow(clippy::clippy::wrong_self_convention)]
pub trait ToOutputStream {
fn to_output_stream(self) -> OutputStream;
}
@ -154,16 +90,3 @@ where
}
}
}
pub trait Interruptible<V> {
fn interruptible(self, ctrl_c: Arc<AtomicBool>) -> InterruptibleStream<V>;
}
impl<S, V> Interruptible<V> for S
where
S: Stream<Item = V> + Send + 'static,
{
fn interruptible(self, ctrl_c: Arc<AtomicBool>) -> InterruptibleStream<V> {
InterruptibleStream::new(self, ctrl_c)
}
}

View File

@ -2,15 +2,8 @@
#[cfg(feature = "rustyline-support")]
pub(crate) mod completer;
pub(crate) mod filesystem_shell;
pub(crate) mod help_shell;
#[cfg(feature = "rustyline-support")]
pub(crate) mod helper;
pub(crate) mod painter;
pub(crate) mod palette;
pub(crate) mod shell;
pub(crate) mod shell_manager;
pub(crate) mod value_shell;
#[cfg(feature = "rustyline-support")]
pub(crate) use helper::Helper;

View File

@ -4,7 +4,8 @@ use crate::completion::matchers;
use crate::completion::matchers::Matcher;
use crate::completion::path::{PathCompleter, PathSuggestion};
use crate::completion::{self, Completer, Suggestion};
use crate::evaluation_context::EvaluationContext;
use nu_engine::EvaluationContext;
use nu_parser::ParserScope;
use nu_source::Tag;
use std::borrow::Cow;
@ -23,15 +24,12 @@ impl NuCompleter {
use completion::engine::LocationType;
let nu_context: &EvaluationContext = context.as_ref();
let lite_block = match nu_parser::lite_parse(line, 0) {
Ok(block) => Some(block),
Err(result) => result.partial,
};
let locations = lite_block
.map(|block| nu_parser::classify_block(&block, &nu_context.registry))
.map(|block| completion::engine::completion_location(line, &block.block, pos))
.unwrap_or_default();
nu_context.scope.enter_scope();
let (block, _) = nu_parser::parse(line, 0, &nu_context.scope);
nu_context.scope.exit_scope();
let locations = completion::engine::completion_location(line, &block, pos);
let matcher = nu_data::config::config(Tag::unknown())
.ok()

View File

@ -1,13 +1,8 @@
use std::borrow::Cow::{self, Owned};
use nu_parser::SignatureRegistry;
use nu_source::{Tag, Tagged};
use crate::completion;
use crate::evaluation_context::EvaluationContext;
use crate::shell::completer::NuCompleter;
use crate::shell::painter::Painter;
use crate::shell::palette::DefaultPalette;
use nu_engine::{DefaultPalette, EvaluationContext, Painter};
use nu_source::{Tag, Tagged};
use std::borrow::Cow::{self, Owned};
pub struct Helper {
completer: NuCompleter,
@ -87,11 +82,7 @@ impl rustyline::highlight::Highlighter for Helper {
}
fn highlight<'l>(&self, line: &'l str, _pos: usize) -> Cow<'l, str> {
Painter::paint_string(
line,
&self.context.registry().clone_box(),
&DefaultPalette {},
)
Painter::paint_string(line, &self.context.scope, &DefaultPalette {})
}
fn highlight_char(&self, _line: &str, _pos: usize) -> bool {
@ -121,10 +112,17 @@ impl rustyline::validate::Validator for NuValidator {
) -> rustyline::Result<rustyline::validate::ValidationResult> {
let src = ctx.input();
let lite_result = nu_parser::lite_parse(src, 0);
let (tokens, err) = nu_parser::lex(src, 0);
if let Some(err) = err {
if let nu_errors::ParseErrorReason::Eof { .. } = err.reason() {
return Ok(rustyline::validate::ValidationResult::Incomplete);
}
}
if let Err(err) = lite_result {
if let nu_errors::ParseErrorReason::Eof { .. } = err.cause.reason() {
let (_, err) = nu_parser::parse_block(tokens);
if let Some(err) = err {
if let nu_errors::ParseErrorReason::Eof { .. } = err.reason() {
return Ok(rustyline::validate::ValidationResult::Incomplete);
}
}
@ -146,3 +144,51 @@ fn vec_tag<T>(input: Vec<Tagged<T>>) -> Option<Tag> {
}
impl rustyline::Helper for Helper {}
#[cfg(test)]
mod tests {
use super::*;
use nu_engine::basic_evaluation_context;
use rustyline::completion::Completer;
use rustyline::line_buffer::LineBuffer;
#[ignore]
#[test]
fn closing_quote_should_replaced() {
let text = "cd \"folder with spaces\\subdirectory\\\"";
let replacement = "\"folder with spaces\\subdirectory\\subsubdirectory\\\"";
let mut buffer = LineBuffer::with_capacity(256);
buffer.insert_str(0, text);
buffer.set_pos(text.len() - 1);
let helper = Helper::new(basic_evaluation_context().unwrap(), None);
helper.update(&mut buffer, "cd ".len(), &replacement);
assert_eq!(
buffer.as_str(),
"cd \"folder with spaces\\subdirectory\\subsubdirectory\\\""
);
}
#[ignore]
#[test]
fn replacement_with_cursor_in_text() {
let text = "cd \"folder with spaces\\subdirectory\\\"";
let replacement = "\"folder with spaces\\subdirectory\\subsubdirectory\\\"";
let mut buffer = LineBuffer::with_capacity(256);
buffer.insert_str(0, text);
buffer.set_pos(text.len() - 30);
let helper = Helper::new(basic_evaluation_context().unwrap(), None);
helper.update(&mut buffer, "cd ".len(), &replacement);
assert_eq!(
buffer.as_str(),
"cd \"folder with spaces\\subdirectory\\subsubdirectory\\\""
);
}
}

View File

@ -0,0 +1 @@
pub(crate) mod deduction;

File diff suppressed because it is too large Load Diff

View File

@ -1,114 +0,0 @@
use nu_test_support::nu;
use nu_test_support::playground::Playground;
#[test]
fn alias_args_work() {
Playground::setup("append_test_1", |dirs, _| {
let actual = nu!(
cwd: dirs.root(),
r#"
alias double_echo [a b] {echo $a $b}
double_echo 1 2 | to json
"#
);
assert_eq!(actual.out, "[1,2]");
})
}
#[test]
fn alias_missing_args_work() {
Playground::setup("append_test_1", |dirs, _| {
let actual = nu!(
cwd: dirs.root(),
r#"
alias double_echo [a b] {^echo $a $b}
double_echo bob
"#
);
assert_eq!(actual.out, "bob");
})
}
#[test]
#[cfg(not(windows))]
fn alias_parses_path_tilde() {
let actual = nu!(
cwd: "tests/fixtures/formats",
r#"
alias -i new-cd [dir] { cd $dir }
new-cd ~
pwd
"#
);
#[cfg(target_os = "linux")]
assert!(actual.out.contains("home"));
#[cfg(target_os = "macos")]
assert!(actual.out.contains("Users"));
}
#[test]
fn error_alias_wrong_shape_shallow() {
let actual = nu!(
cwd: ".",
r#"
alias -i round-to [num digits] { echo $num | str from -d $digits }
round-to 3.45 a
"#
);
assert!(actual.err.contains("Type"));
}
#[test]
fn error_alias_wrong_shape_deep_invocation() {
let actual = nu!(
cwd: ".",
r#"
alias -i round-to [nums digits] { echo $nums | each {= $(str from -d $digits)}}
round-to 3.45 a
"#
);
assert!(actual.err.contains("Type"));
}
#[test]
fn error_alias_wrong_shape_deep_binary() {
let actual = nu!(
cwd: ".",
r#"
alias -i round-plus-one [nums digits] { echo $nums | each {= $(str from -d $digits | str to-decimal) + 1}}
round-plus-one 3.45 a
"#
);
assert!(actual.err.contains("Type"));
}
#[test]
fn error_alias_wrong_shape_deeper_binary() {
let actual = nu!(
cwd: ".",
r#"
alias -i round-one-more [num digits] { echo $num | str from -d $(= $digits + 1) }
round-one-more 3.45 a
"#
);
assert!(actual.err.contains("Type"));
}
#[test]
fn error_alias_syntax_shape_clash() {
let actual = nu!(
cwd: ".",
r#"
alias -i clash [a] { echo 1.1 2 3 | each { str from -d $a } | range $a } }
"#
);
assert!(actual.err.contains("alias"));
}

View File

@ -1,13 +0,0 @@
use nu_test_support::{nu, pipeline};
#[test]
fn echo_range_is_lazy() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
echo 1..10000000000 | first 3 | echo $it | to json
"#
));
assert_eq!(actual.out, "[1,2,3]");
}

View File

@ -1,44 +0,0 @@
use nu_test_support::{nu, pipeline};
#[test]
fn creates_the_resulting_string_from_the_given_fields() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
open cargo_sample.toml
| get package
| format "{name} has license {license}"
| echo $it
"#
));
assert_eq!(actual.out, "nu has license ISC");
}
#[test]
fn given_fields_can_be_column_paths() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
open cargo_sample.toml
| format "{package.name} is {package.description}"
| echo $it
"#
));
assert_eq!(actual.out, "nu is a new type of shell");
}
#[test]
fn can_use_variables() {
let actual = nu!(
cwd: "tests/fixtures/formats", pipeline(
r#"
open cargo_sample.toml
| format "{$it.package.name} is {$it.package.description}"
| echo $it
"#
));
assert_eq!(actual.out, "nu is a new type of shell");
}

View File

@ -1,127 +0,0 @@
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
#[test]
fn regular_columns() {
Playground::setup("select_test_1", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContentToBeTrimmed(
"los_tres_caballeros.csv",
r#"
first_name,last_name,rusty_at,type
Andrés,Robalino,10/11/2013,A
Jonathan,Turner,10/12/2013,B
Yehuda,Katz,10/11/2013,A
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.csv
| select rusty_at last_name
| nth 0
| get last_name
| echo $it
"#
));
assert_eq!(actual.out, "Robalino");
})
}
#[test]
fn complex_nested_columns() {
Playground::setup("select_test_2", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContentToBeTrimmed(
"los_tres_caballeros.json",
r#"
{
"nu": {
"committers": [
{"name": "Andrés N. Robalino"},
{"name": "Jonathan Turner"},
{"name": "Yehuda Katz"}
],
"releases": [
{"version": "0.2"}
{"version": "0.8"},
{"version": "0.9999999"}
],
"0xATYKARNU": [
["Th", "e", " "],
["BIG", " ", "UnO"],
["punto", "cero"]
]
}
}
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.json
| select nu."0xATYKARNU" nu.committers.name nu.releases.version
| where "nu.releases.version" > "0.8"
| get "nu.releases.version"
| echo $it
"#
));
assert_eq!(actual.out, "0.9999999");
})
}
#[test]
fn allows_if_given_unknown_column_name_is_missing() {
Playground::setup("select_test_3", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContentToBeTrimmed(
"los_tres_caballeros.csv",
r#"
first_name,last_name,rusty_at,type
Andrés,Robalino,10/11/2013,A
Jonathan,Turner,10/12/2013,B
Yehuda,Katz,10/11/2013,A
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.csv
| select rrusty_at first_name
| count
| echo $it
"#
));
assert_eq!(actual.out, "3");
})
}
#[test]
fn column_names_with_spaces() {
Playground::setup("select_test_4", |dirs, sandbox| {
sandbox.with_files(vec![FileWithContentToBeTrimmed(
"test_data.csv",
r#"
first name,last name
Jonathan,Turner
Jonathan,Arns
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open test_data.csv
| select "last name"
| to json
"#
));
let expected_output = r#"[{"last name":"Turner"},{"last name":"Arns"}]"#;
assert_eq!(actual.out, expected_output);
})
}

View File

@ -1,25 +0,0 @@
use nu_test_support::{nu, pipeline};
#[test]
fn out_md_simple() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo 3 | to md
"#
));
assert_eq!(actual.out, "3");
}
#[test]
fn out_md_table() {
let actual = nu!(
cwd: ".", pipeline(
r#"
echo '{"name": "jason"}' | from json | to md
"#
));
assert_eq!(actual.out, "|name||-||jason|");
}

View File

@ -0,0 +1,132 @@
[package]
authors = ["The Nu Project Contributors"]
build = "build.rs"
description = "CLI for nushell"
edition = "2018"
license = "MIT"
name = "nu-command"
version = "0.27.1"
[lib]
doctest = false
[dependencies]
nu-data = { version = "0.27.1", path = "../nu-data" }
nu-engine = { version = "0.27.1", path = "../nu-engine" }
nu-errors = { version = "0.27.1", path = "../nu-errors" }
nu-json = { version = "0.27.1", path = "../nu-json" }
nu-parser = { version = "0.27.1", path = "../nu-parser" }
nu-plugin = { version = "0.27.1", path = "../nu-plugin" }
nu-protocol = { version = "0.27.1", path = "../nu-protocol" }
nu-source = { version = "0.27.1", path = "../nu-source" }
nu-stream = { version = "0.27.1", path = "../nu-stream" }
nu-table = { version = "0.27.1", path = "../nu-table" }
nu-test-support = { version = "0.27.1", path = "../nu-test-support" }
nu-value-ext = { version = "0.27.1", path = "../nu-value-ext" }
Inflector = "0.11"
ansi_term = "0.12.1"
arboard = { version = "1.1.0", optional = true }
async-recursion = "0.3.2"
async-trait = "0.1.42"
base64 = "0.13.0"
bigdecimal = { version = "0.2.0", features = ["serde"] }
byte-unit = "4.0.9"
bytes = "1.0.1"
calamine = "0.17.0"
chrono = { version = "0.4.19", features = ["serde"] }
chrono-tz = "0.5.3"
clap = "2.33.3"
codespan-reporting = "0.11.0"
csv = "1.1.3"
ctrlc = { version = "3.1.7", optional = true }
derive-new = "0.5.8"
directories-next = { version = "2.0.0", optional = true }
dirs-next = { version = "2.0.0", optional = true }
dtparse = "1.2.0"
dunce = "1.0.1"
eml-parser = "0.1.0"
encoding_rs = "0.8.28"
filesize = "0.2.0"
fs_extra = "1.2.0"
futures = { version = "0.3.12", features = ["compat", "io-compat"] }
futures-util = "0.3.12"
futures_codec = "0.4.1"
getset = "0.1.1"
glob = "0.3.0"
htmlescape = "0.3.1"
ical = "0.7.0"
ichwh = { version = "0.3.4", optional = true }
indexmap = { version = "1.6.1", features = ["serde-1"] }
itertools = "0.10.0"
lazy_static = "1.*"
log = "0.4.14"
meval = "0.2.0"
num-bigint = { version = "0.3.1", features = ["serde"] }
num-format = { version = "0.4.0", features = ["with-num-bigint"] }
num-traits = "0.2.14"
parking_lot = "0.11.1"
pin-utils = "0.1.0"
pretty-hex = "0.2.1"
ptree = { version = "0.3.1", optional = true }
query_interface = "0.3.5"
quick-xml = "0.21.0"
rand = "0.7.3"
rayon = "1.5.0"
regex = "1.4.3"
roxmltree = "0.14.0"
rust-embed = "5.9.0"
rustyline = { version = "7.1.0", optional = true }
serde = { version = "1.0.123", features = ["derive"] }
serde_bytes = "0.11.5"
serde_ini = "0.2.0"
serde_json = "1.0.61"
serde_urlencoded = "0.7.0"
serde_yaml = "0.8.16"
sha2 = "0.9.3"
shellexpand = "2.1.0"
strip-ansi-escapes = "0.1.0"
sxd-document = "0.3.2"
sxd-xpath = "0.4.2"
tempfile = "3.2.0"
term = { version = "0.7.0", optional = true }
term_size = "0.3.2"
termcolor = "1.1.2"
titlecase = "1.1.0"
toml = "0.5.8"
trash = { version = "1.3.0", optional = true }
unicode-segmentation = "1.7.1"
url = "2.2.0"
uuid_crate = { package = "uuid", version = "0.8.2", features = ["v4"], optional = true }
which = { version = "4.0.2", optional = true }
zip = { version = "0.5.9", optional = true }
[target.'cfg(unix)'.dependencies]
umask = "1.0.0"
users = "0.11.0"
# TODO this will be possible with new dependency resolver
# (currently on nightly behind -Zfeatures=itarget):
# https://github.com/rust-lang/cargo/issues/7914
#[target.'cfg(not(windows))'.dependencies]
#num-format = {version = "0.4", features = ["with-system-locale"]}
[dependencies.rusqlite]
features = ["bundled", "blob"]
optional = true
version = "0.24.2"
[build-dependencies]
shadow-rs = "0.5"
[dev-dependencies]
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
[features]
clipboard-cli = ["arboard"]
rustyline-support = ["rustyline"]
stable = []
trash-support = ["trash"]
directories = ["directories-next"]
dirs = ["dirs-next"]

Binary file not shown.

View File

@ -0,0 +1,3 @@
fn main() -> shadow_rs::SdResult<()> {
shadow_rs::new()
}

View File

@ -4,11 +4,10 @@ pub(crate) mod macros;
mod from_delimited_data;
mod to_delimited_data;
pub(crate) mod alias;
pub(crate) mod ansi;
pub(crate) mod append;
pub(crate) mod args;
pub(crate) mod autoenv;
pub mod autoenv;
pub(crate) mod autoenv_trust;
pub(crate) mod autoenv_untrust;
pub(crate) mod autoview;
@ -21,7 +20,7 @@ pub(crate) mod chart;
pub(crate) mod classified;
#[cfg(feature = "clipboard-cli")]
pub(crate) mod clip;
pub(crate) mod command;
pub mod command;
pub(crate) mod compact;
pub(crate) mod config;
pub(crate) mod constants;
@ -29,7 +28,9 @@ pub(crate) mod count;
pub(crate) mod cp;
pub(crate) mod date;
pub(crate) mod debug;
pub(crate) mod def;
pub(crate) mod default;
pub mod default_context;
pub(crate) mod describe;
pub(crate) mod do_;
pub(crate) mod drop;
@ -42,6 +43,7 @@ pub(crate) mod every;
pub(crate) mod exec;
pub(crate) mod exit;
pub(crate) mod first;
pub(crate) mod flatten;
pub(crate) mod format;
pub(crate) mod from;
pub(crate) mod from_csv;
@ -61,6 +63,7 @@ pub(crate) mod from_yaml;
pub(crate) mod get;
pub(crate) mod group_by;
pub(crate) mod group_by_date;
pub(crate) mod hash_;
pub(crate) mod headers;
pub(crate) mod help;
pub(crate) mod histogram;
@ -70,6 +73,8 @@ pub(crate) mod insert;
pub(crate) mod into_int;
pub(crate) mod keep;
pub(crate) mod last;
pub(crate) mod let_;
pub(crate) mod let_env;
pub(crate) mod lines;
pub(crate) mod ls;
pub(crate) mod math;
@ -93,21 +98,24 @@ pub(crate) mod reject;
pub(crate) mod rename;
pub(crate) mod reverse;
pub(crate) mod rm;
pub(crate) mod run_alias;
pub(crate) mod run_external;
pub(crate) mod save;
pub(crate) mod select;
pub(crate) mod seq;
pub(crate) mod seq_dates;
pub(crate) mod shells;
pub(crate) mod shuffle;
pub(crate) mod size;
pub(crate) mod skip;
pub(crate) mod sleep;
pub(crate) mod sort_by;
pub(crate) mod source;
pub(crate) mod split;
pub(crate) mod split_by;
pub(crate) mod str_;
pub(crate) mod table;
pub(crate) mod tags;
pub(crate) mod termsize;
pub(crate) mod to;
pub(crate) mod to_csv;
pub(crate) mod to_html;
@ -129,11 +137,7 @@ pub(crate) mod wrap;
pub(crate) use autoview::Autoview;
pub(crate) use cd::Cd;
pub(crate) use command::{
whole_stream_command, Command, Example, UnevaluatedCallInfo, WholeStreamCommand,
};
pub(crate) use alias::Alias;
pub(crate) use ansi::Ansi;
pub(crate) use append::Command as Append;
pub(crate) use autoenv::Autoenv;
@ -150,8 +154,9 @@ pub(crate) use config::{
};
pub(crate) use count::Count;
pub(crate) use cp::Cpy;
pub(crate) use date::{Date, DateFormat, DateNow, DateUTC};
pub(crate) use date::{Date, DateFormat, DateListTimeZone, DateNow, DateToTable, DateToTimeZone};
pub(crate) use debug::Debug;
pub(crate) use def::Def;
pub(crate) use default::Default;
pub(crate) use describe::Describe;
pub(crate) use do_::Do;
@ -175,7 +180,8 @@ pub(crate) use every::Every;
pub(crate) use exec::Exec;
pub(crate) use exit::Exit;
pub(crate) use first::First;
pub(crate) use format::Format;
pub(crate) use flatten::Command as Flatten;
pub(crate) use format::{FileSize, Format};
pub(crate) use from::From;
pub(crate) use from_csv::FromCSV;
pub(crate) use from_eml::FromEML;
@ -192,9 +198,10 @@ pub(crate) use from_xlsx::FromXLSX;
pub(crate) use from_xml::FromXML;
pub(crate) use from_yaml::FromYAML;
pub(crate) use from_yaml::FromYML;
pub(crate) use get::Get;
pub(crate) use get::Command as Get;
pub(crate) use group_by::Command as GroupBy;
pub(crate) use group_by_date::GroupByDate;
pub(crate) use hash_::{Hash, HashBase64};
pub(crate) use headers::Headers;
pub(crate) use help::Help;
pub(crate) use histogram::Histogram;
@ -203,15 +210,17 @@ pub(crate) use insert::Command as Insert;
pub(crate) use into_int::IntoInt;
pub(crate) use keep::{Keep, KeepUntil, KeepWhile};
pub(crate) use last::Last;
pub(crate) use let_::Let;
pub(crate) use let_env::LetEnv;
pub(crate) use lines::Lines;
pub(crate) use ls::Ls;
pub(crate) use math::{
Math, MathAverage, MathEval, MathMaximum, MathMedian, MathMinimum, MathMode, MathProduct,
MathStddev, MathSummation, MathVariance,
Math, MathAbs, MathAverage, MathCeil, MathEval, MathFloor, MathMaximum, MathMedian,
MathMinimum, MathMode, MathProduct, MathRound, MathStddev, MathSummation, MathVariance,
};
pub(crate) use merge::Merge;
pub(crate) use mkdir::Mkdir;
pub(crate) use move_::{Move, MoveColumn, Mv};
pub(crate) use move_::{Move, Mv};
pub(crate) use next::Next;
pub(crate) use nth::Nth;
pub(crate) use open::Open;
@ -226,7 +235,9 @@ pub(crate) use prev::Previous;
pub(crate) use pwd::Pwd;
#[cfg(feature = "uuid_crate")]
pub(crate) use random::RandomUUID;
pub(crate) use random::{Random, RandomBool, RandomDice, RandomInteger};
pub(crate) use random::{
Random, RandomBool, RandomChars, RandomDecimal, RandomDice, RandomInteger,
};
pub(crate) use range::Range;
pub(crate) use reduce::Reduce;
pub(crate) use reject::Reject;
@ -235,28 +246,32 @@ pub(crate) use reverse::Reverse;
pub(crate) use rm::Remove;
pub(crate) use run_external::RunExternalCommand;
pub(crate) use save::Save;
pub(crate) use select::Select;
pub(crate) use select::Command as Select;
pub(crate) use seq::Seq;
pub(crate) use seq_dates::SeqDates;
pub(crate) use shells::Shells;
pub(crate) use shuffle::Shuffle;
pub(crate) use size::Size;
pub(crate) use skip::{Skip, SkipUntil, SkipWhile};
pub(crate) use sleep::Sleep;
pub(crate) use sort_by::SortBy;
pub(crate) use source::Source;
pub(crate) use split::{Split, SplitChars, SplitColumn, SplitRow};
pub(crate) use split_by::SplitBy;
pub(crate) use str_::{
Str, StrCamelCase, StrCapitalize, StrCollect, StrContains, StrDowncase, StrEndsWith,
StrFindReplace, StrFrom, StrIndexOf, StrKebabCase, StrLPad, StrLength, StrPascalCase, StrRPad,
StrReverse, StrScreamingSnakeCase, StrSet, StrSnakeCase, StrStartsWith, StrSubstring,
StrToDatetime, StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
StrReverse, StrScreamingSnakeCase, StrSnakeCase, StrStartsWith, StrSubstring, StrToDatetime,
StrToDecimal, StrToInteger, StrTrim, StrTrimLeft, StrTrimRight, StrUpcase,
};
pub(crate) use table::Table;
pub(crate) use tags::Tags;
pub(crate) use termsize::TermSize;
pub(crate) use to::To;
pub(crate) use to_csv::ToCSV;
pub(crate) use to_html::ToHTML;
pub(crate) use to_json::ToJSON;
pub(crate) use to_md::ToMarkdown;
pub(crate) use to_md::Command as ToMarkdown;
pub(crate) use to_toml::ToTOML;
pub(crate) use to_tsv::ToTSV;
pub(crate) use to_url::ToURL;
@ -274,23 +289,61 @@ pub(crate) use wrap::Wrap;
#[cfg(test)]
mod tests {
use super::*;
use crate::commands::whole_stream_command;
use crate::examples::{test_anchors, test_examples};
use nu_engine::{whole_stream_command, Command};
use nu_errors::ShellError;
fn commands() -> Vec<Command> {
fn full_tests() -> Vec<Command> {
vec![
whole_stream_command(Append),
whole_stream_command(GroupBy),
whole_stream_command(Insert),
whole_stream_command(Move),
whole_stream_command(Update),
whole_stream_command(Empty),
// whole_stream_command(Select),
// whole_stream_command(Get),
// Str Command Suite
whole_stream_command(Str),
whole_stream_command(StrToDecimal),
whole_stream_command(StrToInteger),
whole_stream_command(StrDowncase),
whole_stream_command(StrUpcase),
whole_stream_command(StrCapitalize),
whole_stream_command(StrFindReplace),
whole_stream_command(StrFrom),
whole_stream_command(StrSubstring),
whole_stream_command(StrToDatetime),
whole_stream_command(StrContains),
whole_stream_command(StrIndexOf),
whole_stream_command(StrTrim),
whole_stream_command(StrTrimLeft),
whole_stream_command(StrTrimRight),
whole_stream_command(StrStartsWith),
whole_stream_command(StrEndsWith),
//whole_stream_command(StrCollect),
whole_stream_command(StrLength),
whole_stream_command(StrLPad),
whole_stream_command(StrReverse),
whole_stream_command(StrRPad),
whole_stream_command(StrCamelCase),
whole_stream_command(StrPascalCase),
whole_stream_command(StrKebabCase),
whole_stream_command(StrSnakeCase),
whole_stream_command(StrScreamingSnakeCase),
whole_stream_command(ToMarkdown),
]
}
fn only_examples() -> Vec<Command> {
let mut commands = full_tests();
commands.extend(vec![whole_stream_command(Flatten)]);
commands
}
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
for cmd in commands() {
for cmd in only_examples() {
test_examples(cmd)?;
}
@ -299,7 +352,7 @@ mod tests {
#[test]
fn tracks_metadata() -> Result<(), ShellError> {
for cmd in commands() {
for cmd in full_tests() {
test_anchors(cmd)?;
}

View File

@ -0,0 +1,291 @@
use crate::prelude::*;
use ansi_term::Color;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct Ansi;
#[derive(Deserialize)]
struct AnsiArgs {
code: Value,
escape: Option<Tagged<String>>,
osc: Option<Tagged<String>>,
}
#[async_trait]
impl WholeStreamCommand for Ansi {
fn name(&self) -> &str {
"ansi"
}
fn signature(&self) -> Signature {
Signature::build("ansi")
.optional(
"code",
SyntaxShape::Any,
"the name of the code to use like 'green' or 'reset' to reset the color",
)
.named(
"escape", // \x1b[
SyntaxShape::Any,
"escape sequence without the escape character(s)",
Some('e'),
)
.named(
"osc", // \x1b]
SyntaxShape::Any,
"operating system command (ocs) escape sequence without the escape character(s)",
Some('o'),
)
}
fn usage(&self) -> &str {
r#"Output ANSI codes to change color
For escape sequences:
Escape: '\x1b[' is not required for --escape parameter
Format: #(;#)m
Example: 1;31m for bold red or 2;37;41m for dimmed white fg with red bg
There can be multiple text formatting sequence numbers
separated by a ; and ending with an m where the # is of the
following values:
attributes
0 reset / normal display
1 bold or increased intensity
2 faint or decreased intensity
3 italic on (non-mono font)
4 underline on
5 slow blink on
6 fast blink on
7 reverse video on
8 nondisplayed (invisible) on
9 strike-through on
foreground/bright colors background/bright colors
30/90 black 40/100 black
31/91 red 41/101 red
32/92 green 42/102 green
33/93 yellow 43/103 yellow
34/94 blue 44/104 blue
35/95 magenta 45/105 magenta
36/96 cyan 46/106 cyan
37/97 white 47/107 white
https://en.wikipedia.org/wiki/ANSI_escape_code
OSC: '\x1b]' is not required for --osc parameter
Example: echo [$(ansi -o '0') 'some title' $(char bel)] | str collect
Format: #
0 Set window title and icon name
1 Set icon name
2 Set window title
4 Set/read color palette
9 iTerm2 Grown notifications
10 Set foreground color (x11 color spec)
11 Set background color (x11 color spec)
... others"#
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Change color to green",
example: r#"ansi green"#,
result: Some(vec![Value::from("\u{1b}[32m")]),
},
Example {
description: "Reset the color",
example: r#"ansi reset"#,
result: Some(vec![Value::from("\u{1b}[0m")]),
},
Example {
description:
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
example: r#"echo [$(ansi rb) Hello " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#,
result: Some(vec![Value::from(
"\u{1b}[1;31mHello \u{1b}[1;32mNu \u{1b}[1;35mWorld",
)]),
},
Example {
description:
"Use ansi to color text (rb = red bold, gb = green bold, pb = purple bold)",
example: r#"echo [$(ansi -e '3;93;41m') Hello $(ansi reset) " " $(ansi gb) Nu " " $(ansi pb) World] | str collect"#,
result: Some(vec![Value::from(
"\u{1b}[3;93;41mHello\u{1b}[0m \u{1b}[1;32mNu \u{1b}[1;35mWorld",
)]),
},
]
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (AnsiArgs { code, escape, osc }, _) = args.process().await?;
if let Some(e) = escape {
let esc_vec: Vec<char> = e.item.chars().collect();
if esc_vec[0] == '\\' {
return Err(ShellError::labeled_error(
"no need for escape characters",
"no need for escape characters",
e.tag(),
));
}
let output = format!("\x1b[{}", e.item);
return Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(e.tag()),
)));
}
if let Some(o) = osc {
let osc_vec: Vec<char> = o.item.chars().collect();
if osc_vec[0] == '\\' {
return Err(ShellError::labeled_error(
"no need for escape characters",
"no need for escape characters",
o.tag(),
));
}
//Operating system command aka osc ESC ] <- note the right brace, not left brace for osc
// OCS's need to end with a bell '\x07' char
let output = format!("\x1b]{};", o.item);
return Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(o.tag()),
)));
}
let code_string = code.as_string()?;
let ansi_code = str_to_ansi(code_string);
if let Some(output) = ansi_code {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(code.tag()),
)))
} else {
Err(ShellError::labeled_error(
"Unknown ansi code",
"unknown ansi code",
code.tag(),
))
}
}
}
pub fn str_to_ansi(s: String) -> Option<String> {
match s.as_str() {
"g" | "green" => Some(Color::Green.prefix().to_string()),
"gb" | "green_bold" => Some(Color::Green.bold().prefix().to_string()),
"gu" | "green_underline" => Some(Color::Green.underline().prefix().to_string()),
"gi" | "green_italic" => Some(Color::Green.italic().prefix().to_string()),
"gd" | "green_dimmed" => Some(Color::Green.dimmed().prefix().to_string()),
"gr" | "green_reverse" => Some(Color::Green.reverse().prefix().to_string()),
"r" | "red" => Some(Color::Red.prefix().to_string()),
"rb" | "red_bold" => Some(Color::Red.bold().prefix().to_string()),
"ru" | "red_underline" => Some(Color::Red.underline().prefix().to_string()),
"ri" | "red_italic" => Some(Color::Red.italic().prefix().to_string()),
"rd" | "red_dimmed" => Some(Color::Red.dimmed().prefix().to_string()),
"rr" | "red_reverse" => Some(Color::Red.reverse().prefix().to_string()),
"u" | "blue" => Some(Color::Blue.prefix().to_string()),
"ub" | "blue_bold" => Some(Color::Blue.bold().prefix().to_string()),
"uu" | "blue_underline" => Some(Color::Blue.underline().prefix().to_string()),
"ui" | "blue_italic" => Some(Color::Blue.italic().prefix().to_string()),
"ud" | "blue_dimmed" => Some(Color::Blue.dimmed().prefix().to_string()),
"ur" | "blue_reverse" => Some(Color::Blue.reverse().prefix().to_string()),
"b" | "black" => Some(Color::Black.prefix().to_string()),
"bb" | "black_bold" => Some(Color::Black.bold().prefix().to_string()),
"bu" | "black_underline" => Some(Color::Black.underline().prefix().to_string()),
"bi" | "black_italic" => Some(Color::Black.italic().prefix().to_string()),
"bd" | "black_dimmed" => Some(Color::Black.dimmed().prefix().to_string()),
"br" | "black_reverse" => Some(Color::Black.reverse().prefix().to_string()),
"y" | "yellow" => Some(Color::Yellow.prefix().to_string()),
"yb" | "yellow_bold" => Some(Color::Yellow.bold().prefix().to_string()),
"yu" | "yellow_underline" => Some(Color::Yellow.underline().prefix().to_string()),
"yi" | "yellow_italic" => Some(Color::Yellow.italic().prefix().to_string()),
"yd" | "yellow_dimmed" => Some(Color::Yellow.dimmed().prefix().to_string()),
"yr" | "yellow_reverse" => Some(Color::Yellow.reverse().prefix().to_string()),
"p" | "purple" => Some(Color::Purple.prefix().to_string()),
"pb" | "purple_bold" => Some(Color::Purple.bold().prefix().to_string()),
"pu" | "purple_underline" => Some(Color::Purple.underline().prefix().to_string()),
"pi" | "purple_italic" => Some(Color::Purple.italic().prefix().to_string()),
"pd" | "purple_dimmed" => Some(Color::Purple.dimmed().prefix().to_string()),
"pr" | "purple_reverse" => Some(Color::Purple.reverse().prefix().to_string()),
"c" | "cyan" => Some(Color::Cyan.prefix().to_string()),
"cb" | "cyan_bold" => Some(Color::Cyan.bold().prefix().to_string()),
"cu" | "cyan_underline" => Some(Color::Cyan.underline().prefix().to_string()),
"ci" | "cyan_italic" => Some(Color::Cyan.italic().prefix().to_string()),
"cd" | "cyan_dimmed" => Some(Color::Cyan.dimmed().prefix().to_string()),
"cr" | "cyan_reverse" => Some(Color::Cyan.reverse().prefix().to_string()),
"w" | "white" => Some(Color::White.prefix().to_string()),
"wb" | "white_bold" => Some(Color::White.bold().prefix().to_string()),
"wu" | "white_underline" => Some(Color::White.underline().prefix().to_string()),
"wi" | "white_italic" => Some(Color::White.italic().prefix().to_string()),
"wd" | "white_dimmed" => Some(Color::White.dimmed().prefix().to_string()),
"wr" | "white_reverse" => Some(Color::White.reverse().prefix().to_string()),
"reset" => Some("\x1b[0m".to_owned()),
// Reference for ansi codes https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797
// Another good reference http://ascii-table.com/ansi-escape-sequences.php
// For setting title like `echo [$(char title) $(pwd) $(char bel)] | str collect`
"title" => Some("\x1b]2;".to_string()), // ESC]2; xterm sets window title using OSC syntax escapes
"bel" => Some('\x07'.to_string()), // Terminal Bell
"backspace" => Some('\x08'.to_string()), // Backspace
// Ansi Erase Sequences
"clear_screen" => Some("\x1b[J".to_string()), // clears the screen
"clear_screen_from_cursor_to_end" => Some("\x1b[0J".to_string()), // clears from cursor until end of screen
"clear_screen_from_cursor_to_beginning" => Some("\x1b[1J".to_string()), // clears from cursor to beginning of screen
"cls" | "clear_entire_screen" => Some("\x1b[2J".to_string()), // clears the entire screen
"erase_line" => Some("\x1b[K".to_string()), // clears the current line
"erase_line_from_cursor_to_end" => Some("\x1b[0K".to_string()), // clears from cursor to end of line
"erase_line_from_cursor_to_beginning" => Some("\x1b[1K".to_string()), // clears from cursor to start of line
"erase_entire_line" => Some("\x1b[2K".to_string()), // clears entire line
// Turn on/off cursor
"cursor_off" => Some("\x1b[?25l".to_string()),
"cursor_on" => Some("\x1b[?25h".to_string()),
// Turn on/off blinking
"cursor_blink_off" => Some("\x1b[?12l".to_string()),
"cursor_blink_on" => Some("\x1b[?12h".to_string()),
// Cursor position in ESC [ <r>;<c>R where r = row and c = column
"cursor_position" => Some("\x1b[6n".to_string()),
// Report Terminal Identity
"identity" => Some("\x1b[0c".to_string()),
// Ansi escape only - CSI command
"csi" | "escape" | "escape_left" => Some("\x1b[".to_string()),
// OSC escape (Operating system command)
"osc" | "escape_right" => Some("\x1b]".to_string()),
// Ansi RGB - Needs to be 32;2;r;g;b or 48;2;r;g;b
// assuming the rgb will be passed via command and no here
"rgb_fg" => Some("\x1b[32;2;".to_string()),
"rgb_bg" => Some("\x1b[48;2;".to_string()),
// Ansi color index - Needs 38;5;idx or 48;5;idx where idx = 0 to 255
"idx_fg" | "color_idx_fg" => Some("\x1b[38;5;".to_string()),
"idx_bg" | "color_idx_bg" => Some("\x1b[48;5;".to_string()),
// Returns terminal size like "[<r>;<c>R" where r is rows and c is columns
// This should work assuming your terminal is not greater than 999x999
"size" => Some("\x1b[s\x1b[999;999H\x1b[6n\x1b[u".to_string()),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::Ansi;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Ansi {})
}
}

View File

@ -0,0 +1,85 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)]
struct Arguments {
value: Value,
}
pub struct Command;
#[async_trait]
impl WholeStreamCommand for Command {
fn name(&self) -> &str {
"append"
}
fn signature(&self) -> Signature {
Signature::build("append").required(
"row value",
SyntaxShape::Any,
"the value of the row to append to the table",
)
}
fn usage(&self) -> &str {
"Append a row to the table"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (Arguments { mut value }, input) = args.process().await?;
let input: Vec<Value> = input.collect().await;
if let Some(first) = input.get(0) {
value.tag = first.tag();
}
// Checks if we are trying to append a row literal
if let Value {
value: UntaggedValue::Table(values),
tag,
} = &value
{
if values.len() == 1 && values[0].is_row() {
value = values[0].value.clone().into_value(tag);
}
}
Ok(futures::stream::iter(
input
.into_iter()
.chain(vec![value])
.map(ReturnSuccess::value),
)
.to_output_stream())
}
fn examples(&self) -> Vec<Example> {
use nu_protocol::row;
vec![
Example {
description: "Add values to the end of the table",
example: "echo [1 2 3] | append 4",
result: Some(vec![
UntaggedValue::int(1).into(),
UntaggedValue::int(2).into(),
UntaggedValue::int(3).into(),
UntaggedValue::int(4).into(),
]),
},
Example {
description: "Add row value to the end of the table",
example: "echo [[country]; [Ecuador] ['New Zealand']] | append [[country]; [USA]]",
result: Some(vec![
row! { "country".into() => Value::from("Ecuador")},
row! { "country".into() => Value::from("New Zealand")},
row! { "country".into() => Value::from("USA")},
]),
},
]
}
}

View File

@ -1,12 +1,12 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use serde::Deserialize;
use serde::Serialize;
use sha2::{Digest, Sha256};
use std::io::Read;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
pub struct Autoenv;
#[derive(Deserialize, Serialize, Debug, Default)]
@ -20,11 +20,12 @@ impl Trusted {
}
}
}
pub fn file_is_trusted(nu_env_file: &PathBuf, content: &[u8]) -> Result<bool, ShellError> {
pub fn file_is_trusted(nu_env_file: &Path, content: &[u8]) -> Result<bool, ShellError> {
let contentdigest = Sha256::digest(&content).as_slice().to_vec();
let nufile = std::fs::canonicalize(nu_env_file)?;
let trusted = read_trusted()?;
Ok(trusted.files.get(&nufile.to_string_lossy().to_string()) == Some(&contentdigest))
}
@ -61,15 +62,9 @@ The file can contain several optional sections:
fn signature(&self) -> Signature {
Signature::build("autoenv")
}
async fn run(
&self,
_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(crate::commands::help::get_help(&Autoenv, &registry))
.into_value(Tag::unknown()),
UntaggedValue::string(get_help(&Autoenv, &args.scope)).into_value(Tag::unknown()),
)))
}

View File

@ -1,6 +1,6 @@
use super::autoenv::read_trusted;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::SyntaxShape;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
@ -22,14 +22,11 @@ impl WholeStreamCommand for AutoenvTrust {
"Trust a .nu-env file in the current or given directory"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let ctx = EvaluationContext::from_args(&args);
let file_to_trust = match args.call_info.evaluate(registry).await?.args.nth(0) {
let file_to_trust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
Some(Value {
value: UntaggedValue::Primitive(Primitive::String(ref path)),
tag: _,

View File

@ -1,6 +1,6 @@
use super::autoenv::Trusted;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::SyntaxShape;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
@ -26,13 +26,10 @@ impl WholeStreamCommand for AutoenvUnTrust {
"Untrust a .nu-env file in the current or given directory"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let file_to_untrust = match args.call_info.evaluate(registry).await?.args.nth(0) {
let ctx = EvaluationContext::from_args(&args);
let file_to_untrust = match args.call_info.evaluate(&ctx).await?.args.nth(0) {
Some(Value {
value: UntaggedValue::Primitive(Primitive::String(ref path)),
tag: _,

View File

@ -1,11 +1,11 @@
use crate::commands::autoview::options::{ConfigExtensions, NuConfig as AutoViewConfiguration};
use crate::commands::{UnevaluatedCallInfo, WholeStreamCommand};
use crate::prelude::*;
use crate::primitive::get_color_config;
use nu_data::value::format_leaf;
use nu_engine::{UnevaluatedCallInfo, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::hir::{self, Expression, ExternalRedirection, Literal, SpannedExpression};
use nu_protocol::{Primitive, Scope, Signature, UntaggedValue, Value};
use nu_protocol::{Primitive, Signature, UntaggedValue, Value};
use nu_table::TextStyle;
use parking_lot::Mutex;
use std::sync::atomic::AtomicBool;
@ -26,20 +26,15 @@ impl WholeStreamCommand for Command {
"View the contents of the pipeline as a table or list."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
autoview(RunnableContext {
input: args.input,
registry: registry.clone(),
scope: args.scope.clone(),
shell_manager: args.shell_manager,
host: args.host,
ctrl_c: args.ctrl_c,
current_errors: args.current_errors,
name: args.call_info.name_tag,
raw_input: args.raw_input,
})
.await
}
@ -65,7 +60,7 @@ pub struct RunnableContextWithoutInput {
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub ctrl_c: Arc<AtomicBool>,
pub registry: CommandRegistry,
pub scope: Scope,
pub name: Tag,
}
@ -76,7 +71,7 @@ impl RunnableContextWithoutInput {
host: context.host,
ctrl_c: context.ctrl_c,
current_errors: context.current_errors,
registry: context.registry,
scope: context.scope,
name: context.name,
};
(context.input, new_context)
@ -109,7 +104,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
if let Some(table) = table {
let command_args = create_default_command_args(&context).with_input(stream);
let result = table.run(command_args, &context.registry).await?;
let result = table.run(command_args).await?;
result.collect::<Vec<_>>().await;
}
}
@ -126,7 +121,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
);
let command_args =
create_default_command_args(&context).with_input(stream);
let result = text.run(command_args, &context.registry).await?;
let result = text.run(command_args).await?;
result.collect::<Vec<_>>().await;
} else {
out!("{}", s);
@ -139,30 +134,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
out!("{}", s);
}
Value {
value: UntaggedValue::Primitive(Primitive::Line(ref s)),
tag: Tag { anchor, span },
} if anchor.is_some() => {
if let Some(text) = text {
let mut stream = VecDeque::new();
stream.push_back(
UntaggedValue::string(s).into_value(Tag { anchor, span }),
);
let command_args =
create_default_command_args(&context).with_input(stream);
let result = text.run(command_args, &context.registry).await?;
result.collect::<Vec<_>>().await;
} else {
out!("{}\n", s);
}
}
Value {
value: UntaggedValue::Primitive(Primitive::Line(s)),
..
} => {
out!("{}\n", s);
}
Value {
value: UntaggedValue::Primitive(Primitive::Path(s)),
value: UntaggedValue::Primitive(Primitive::FilePath(s)),
..
} => {
out!("{}", s.display());
@ -201,6 +173,13 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
let output = format_leaf(&x).plain_string(100_000);
out!("{}", output);
}
Value {
value: UntaggedValue::Primitive(Primitive::Filesize(_)),
..
} => {
let output = format_leaf(&x).plain_string(100_000);
out!("{}", output);
}
Value {
value: UntaggedValue::Primitive(Primitive::Date(d)),
..
@ -224,7 +203,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
stream.push_back(x);
let command_args =
create_default_command_args(&context).with_input(stream);
let result = binary.run(command_args, &context.registry).await?;
let result = binary.run(command_args).await?;
result.collect::<Vec<_>>().await;
} else {
use pretty_hex::*;
@ -276,7 +255,12 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
nu_table::draw_table(&table, term_width, &color_hm);
}
Value {
value: UntaggedValue::Primitive(Primitive::Nothing),
..
} => {
// Do nothing
}
Value {
value: ref item, ..
} => {
@ -285,7 +269,7 @@ pub async fn autoview(context: RunnableContext) -> Result<OutputStream, ShellErr
stream.push_back(x);
let command_args =
create_default_command_args(&context).with_input(stream);
let result = table.run(command_args, &context.registry).await?;
let result = table.run(command_args).await?;
result.collect::<Vec<_>>().await;
} else {
out!("{:?}", item);
@ -318,8 +302,8 @@ fn create_default_command_args(context: &RunnableContextWithoutInput) -> RawComm
external_redirection: ExternalRedirection::Stdout,
},
name_tag: context.name.clone(),
scope: Scope::create(),
},
scope: Scope::new(),
}
}
@ -332,6 +316,6 @@ mod tests {
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Command {})?)
test_examples(Command {})
}
}

View File

@ -1,12 +1,12 @@
use crate::commands::classified::block::run_block;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
#[cfg(feature = "rich-benchmark")]
use heim::cpu::time;
// #[cfg(feature = "rich-benchmark")]
// use heim::cpu::time;
use nu_engine::run_block;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{
hir::{Block, ClassifiedCommand, Commands, InternalCommand},
Dictionary, Scope, Signature, SyntaxShape, UntaggedValue, Value,
hir::{Block, CapturedBlock, ClassifiedCommand, Group, InternalCommand, Pipeline},
Dictionary, Signature, SyntaxShape, UntaggedValue, Value,
};
use rand::{
distributions::Alphanumeric,
@ -19,8 +19,8 @@ pub struct Benchmark;
#[derive(Deserialize, Debug)]
struct BenchmarkArgs {
block: Block,
passthrough: Option<Block>,
block: CapturedBlock,
passthrough: Option<CapturedBlock>,
}
#[async_trait]
@ -48,12 +48,8 @@ impl WholeStreamCommand for Benchmark {
"Runs a block and returns the time it took to execute it"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
benchmark(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
benchmark(args).await
}
fn examples(&self) -> Vec<Example> {
@ -72,78 +68,73 @@ impl WholeStreamCommand for Benchmark {
}
}
async fn benchmark(
raw_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
async fn benchmark(raw_args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = raw_args.call_info.args.span;
let mut context = EvaluationContext::from_raw(&raw_args, &registry);
let scope = raw_args.call_info.scope.clone();
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process(&registry).await?;
let mut context = EvaluationContext::from_args(&raw_args);
let scope = raw_args.scope.clone();
let (BenchmarkArgs { block, passthrough }, input) = raw_args.process().await?;
let env = scope.env();
let env = scope.get_env_vars();
let name = generate_free_name(&env);
let mut env = IndexMap::new();
env.insert(name, generate_random_env_value());
let scope = Scope::append_env(scope, env);
scope.add_env_var(name, generate_random_env_value());
let start_time = Instant::now();
#[cfg(feature = "rich-benchmark")]
let start = time().await;
// #[cfg(feature = "rich-benchmark")]
// let start = time().await;
let result = run_block(&block, &mut context, input, scope.clone()).await;
context.scope.enter_scope();
let result = run_block(&block.block, &context, input).await;
context.scope.exit_scope();
let output = result?.into_vec().await;
#[cfg(feature = "rich-benchmark")]
let end = time().await;
// #[cfg(feature = "rich-benchmark")]
// let end = time().await;
let end_time = Instant::now();
context.clear_errors();
// return basic runtime
#[cfg(not(feature = "rich-benchmark"))]
//#[cfg(not(feature = "rich-benchmark"))]
{
let mut indexmap = IndexMap::with_capacity(1);
let real_time = into_big_int(end_time - start_time);
indexmap.insert("real time".to_string(), real_time);
benchmark_output(indexmap, output, passthrough, &tag, &mut context, scope).await
benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
}
// return advanced stats
#[cfg(feature = "rich-benchmark")]
if let (Ok(start), Ok(end)) = (start, end) {
let mut indexmap = IndexMap::with_capacity(4);
// #[cfg(feature = "rich-benchmark")]
// if let (Ok(start), Ok(end)) = (start, end) {
// let mut indexmap = IndexMap::with_capacity(4);
let real_time = into_big_int(end_time - start_time);
indexmap.insert("real time".to_string(), real_time);
// let real_time = into_big_int(end_time - start_time);
// indexmap.insert("real time".to_string(), real_time);
let user_time = into_big_int(end.user() - start.user());
indexmap.insert("user time".to_string(), user_time);
// let user_time = into_big_int(end.user() - start.user());
// indexmap.insert("user time".to_string(), user_time);
let system_time = into_big_int(end.system() - start.system());
indexmap.insert("system time".to_string(), system_time);
// let system_time = into_big_int(end.system() - start.system());
// indexmap.insert("system time".to_string(), system_time);
let idle_time = into_big_int(end.idle() - start.idle());
indexmap.insert("idle time".to_string(), idle_time);
// let idle_time = into_big_int(end.idle() - start.idle());
// indexmap.insert("idle time".to_string(), idle_time);
benchmark_output(indexmap, output, passthrough, &tag, &mut context, scope).await
} else {
Err(ShellError::untagged_runtime_error(
"Could not retreive CPU time",
))
}
// benchmark_output(indexmap, output, passthrough, &tag, &mut context).await
// } else {
// Err(ShellError::untagged_runtime_error(
// "Could not retrieve CPU time",
// ))
// }
}
async fn benchmark_output<T, Output>(
indexmap: IndexMap<String, BigInt>,
block_output: Output,
passthrough: Option<Block>,
passthrough: Option<CapturedBlock>,
tag: T,
context: &mut EvaluationContext,
scope: Arc<Scope>,
) -> Result<OutputStream, ShellError>
where
T: Into<Tag> + Copy,
@ -161,9 +152,12 @@ where
let benchmark_output = InputStream::one(value);
// add autoview for an empty block
let time_block = add_implicit_autoview(time_block);
let time_block = add_implicit_autoview(time_block.block);
let _ = run_block(&time_block, context, benchmark_output, scope).await?;
context.scope.enter_scope();
let result = run_block(&time_block, context, benchmark_output).await;
context.scope.exit_scope();
result?;
context.clear_errors();
Ok(block_output.into())
@ -175,15 +169,19 @@ where
fn add_implicit_autoview(mut block: Block) -> Block {
if block.block.is_empty() {
block.push({
let mut commands = Commands::new(block.span);
let group = Group::new(
vec![{
let mut commands = Pipeline::new(block.span);
commands.push(ClassifiedCommand::Internal(InternalCommand::new(
"autoview".to_string(),
block.span,
block.span,
)));
commands
});
}],
block.span,
);
block.push(group);
}
block
}

View File

@ -1,8 +1,8 @@
use crate::prelude::*;
use nu_errors::ShellError;
use crate::commands::WholeStreamCommand;
use nu_data::value::format_leaf;
use nu_engine::WholeStreamCommand;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
#[derive(Deserialize)]
@ -27,13 +27,9 @@ impl WholeStreamCommand for BuildString {
"Builds a string from the arguments"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let tag = args.call_info.name_tag.clone();
let (BuildStringArgs { rest }, _) = args.process(&registry).await?;
let (BuildStringArgs { rest }, _) = args.process().await?;
let mut output_string = String::new();

View File

@ -1,7 +1,7 @@
use crate::commands::{command::EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
use crate::prelude::*;
use chrono::{Datelike, Local, NaiveDate};
use indexmap::IndexMap;
use nu_engine::{EvaluatedWholeStreamCommandArgs, WholeStreamCommand};
use nu_errors::ShellError;
use nu_protocol::{Dictionary, Signature, SyntaxShape, UntaggedValue, Value};
@ -41,12 +41,8 @@ impl WholeStreamCommand for Cal {
"Display a calendar."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
cal(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
cal(args).await
}
fn examples(&self) -> Vec<Example> {
@ -70,12 +66,8 @@ impl WholeStreamCommand for Cal {
}
}
pub async fn cal(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let args = args.evaluate_once(&registry).await?;
pub async fn cal(args: CommandArgs) -> Result<OutputStream, ShellError> {
let args = args.evaluate_once().await?;
let mut calendar_vec_deque = VecDeque::new();
let tag = args.call_info.name_tag.clone();
@ -345,6 +337,6 @@ mod tests {
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Cal {})?)
test_examples(Cal {})
}
}

View File

@ -1,16 +1,9 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use std::path::PathBuf;
use nu_engine::shell::CdArgs;
use nu_errors::ShellError;
use nu_protocol::{Signature, SyntaxShape};
use nu_source::Tagged;
#[derive(Deserialize)]
pub struct CdArgs {
pub(crate) path: Option<Tagged<PathBuf>>,
}
pub struct Cd;
@ -23,7 +16,7 @@ impl WholeStreamCommand for Cd {
fn signature(&self) -> Signature {
Signature::build("cd").optional(
"directory",
SyntaxShape::Path,
SyntaxShape::FilePath,
"the directory to change to",
)
}
@ -32,14 +25,10 @@ impl WholeStreamCommand for Cd {
"Change to a new path."
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let shell_manager = args.shell_manager.clone();
let (args, _): (CdArgs, _) = args.process(&registry).await?;
let (args, _): (CdArgs, _) = args.process().await?;
shell_manager.cd(args, name)
}
@ -78,6 +67,6 @@ mod tests {
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Cd {})?)
test_examples(Cd {})
}
}

View File

@ -0,0 +1,148 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct Char;
#[derive(Deserialize)]
struct CharArgs {
name: Tagged<String>,
unicode: bool,
}
#[async_trait]
impl WholeStreamCommand for Char {
fn name(&self) -> &str {
"char"
}
fn signature(&self) -> Signature {
Signature::build("char")
.required(
"character",
SyntaxShape::Any,
"the name of the character to output",
)
.switch("unicode", "unicode string i.e. 1f378", Some('u'))
}
fn usage(&self) -> &str {
"Output special characters (eg. 'newline')"
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Output newline",
example: r#"char newline"#,
result: Some(vec![Value::from("\n")]),
},
Example {
description: "Output prompt character, newline and a hamburger character",
example: r#"echo $(char prompt) $(char newline) $(char hamburger)"#,
result: Some(vec![
UntaggedValue::string("\u{25b6}").into(),
UntaggedValue::string("\n").into(),
UntaggedValue::string("\u{2261}").into(),
]),
},
Example {
description: "Output unicode character",
example: r#"char -u 1f378"#,
result: Some(vec![Value::from("\u{1f378}")]),
},
]
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let (CharArgs { name, unicode }, _) = args.process().await?;
if unicode {
let decoded_char = string_to_unicode_char(&name.item);
if let Some(output) = decoded_char {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(name.tag()),
)))
} else {
Err(ShellError::labeled_error(
"error decoding unicode character",
"error decoding unicode character",
name.tag(),
))
}
} else {
let special_character = str_to_character(&name.item);
if let Some(output) = special_character {
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::string(output).into_value(name.tag()),
)))
} else {
Err(ShellError::labeled_error(
"error finding named character",
"error finding named character",
name.tag(),
))
}
}
}
}
fn string_to_unicode_char(s: &str) -> Option<char> {
u32::from_str_radix(s, 16)
.ok()
.and_then(std::char::from_u32)
}
fn str_to_character(s: &str) -> Option<String> {
match s {
"newline" | "enter" | "nl" => Some("\n".into()),
"tab" => Some("\t".into()),
"sp" | "space" => Some(" ".into()),
// Unicode names came from https://www.compart.com/en/unicode
// Private Use Area (U+E000-U+F8FF)
// Unicode can't be mixed with Ansi or it will break width calculation
"branch" => Some('\u{e0a0}'.to_string()), // 
"segment" => Some('\u{e0b0}'.to_string()), // 
"identical_to" | "hamburger" => Some('\u{2261}'.to_string()), // ≡
"not_identical_to" | "branch_untracked" => Some('\u{2262}'.to_string()), // ≢
"strictly_equivalent_to" | "branch_identical" => Some('\u{2263}'.to_string()), // ≣
"upwards_arrow" | "branch_ahead" => Some('\u{2191}'.to_string()), // ↑
"downwards_arrow" | "branch_behind" => Some('\u{2193}'.to_string()), // ↓
"up_down_arrow" | "branch_ahead_behind" => Some('\u{2195}'.to_string()), // ↕
"black_right_pointing_triangle" | "prompt" => Some('\u{25b6}'.to_string()), // ▶
"vector_or_cross_product" | "failed" => Some('\u{2a2f}'.to_string()), //
"high_voltage_sign" | "elevated" => Some('\u{26a1}'.to_string()), // ⚡
"tilde" | "twiddle" | "squiggly" | "home" => Some("~".into()), // ~
"hash" | "hashtag" | "pound_sign" | "sharp" | "root" => Some("#".into()), // #
// Weather symbols
"sun" | "sunny" | "sunrise" => Some("☀️".to_string()),
"moon" => Some("🌛".to_string()),
"cloudy" | "cloud" | "clouds" => Some("☁️".to_string()),
"rainy" | "rain" => Some("🌦️".to_string()),
"foggy" | "fog" => Some("🌫️".to_string()),
"mist" | "haze" => Some("\u{2591}".to_string()),
"snowy" | "snow" => Some("❄️".to_string()),
"thunderstorm" | "thunder" => Some("🌩️".to_string()),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::Char;
use super::ShellError;
#[test]
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
test_examples(Char {})
}
}

View File

@ -1,5 +1,5 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
@ -20,21 +20,15 @@ impl WholeStreamCommand for Chart {
"Displays charts."
}
async fn run(
&self,
_args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
if registry.get_command("chart bar").is_none() {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
if args.scope.get_command("chart bar").is_none() {
return Err(ShellError::untagged_runtime_error(
"nu_plugin_chart not installed.",
));
}
let registry = registry.clone();
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
UntaggedValue::string(crate::commands::help::get_help(&Chart, &registry))
.into_value(Tag::unknown()),
UntaggedValue::string(get_help(&Chart, &args.scope)).into_value(Tag::unknown()),
))))
}
}
@ -48,6 +42,6 @@ mod tests {
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Chart {})?)
test_examples(Chart {})
}
}

View File

@ -1,8 +1,9 @@
use crate::commands::classified::maybe_text_codec::{MaybeTextCodec, StringOrBinary};
use crate::evaluate::evaluate_baseline_expr;
use crate::futures::ThreadedReceiver;
use crate::prelude::*;
use nu_engine::evaluate_baseline_expr;
use nu_engine::{MaybeTextCodec, StringOrBinary};
use std::borrow::Cow;
use std::io::Write;
use std::ops::Deref;
use std::process::{Command, Stdio};
@ -13,15 +14,16 @@ use futures_codec::FramedRead;
use log::trace;
use nu_errors::ShellError;
use nu_protocol::hir::Expression;
use nu_protocol::hir::{ExternalCommand, ExternalRedirection};
use nu_protocol::{Primitive, Scope, ShellTypeName, UntaggedValue, Value};
use nu_protocol::{Primitive, ShellTypeName, UntaggedValue, Value};
use nu_source::Tag;
use nu_stream::trace_stream;
pub(crate) async fn run_external_command(
command: ExternalCommand,
context: &mut EvaluationContext,
input: InputStream,
scope: Arc<Scope>,
external_redirection: ExternalRedirection,
) -> Result<InputStream, ShellError> {
trace!(target: "nu::run::external", "-> {}", command.name);
@ -34,14 +36,13 @@ pub(crate) async fn run_external_command(
));
}
run_with_stdin(command, context, input, scope, external_redirection).await
run_with_stdin(command, context, input, external_redirection).await
}
async fn run_with_stdin(
command: ExternalCommand,
context: &mut EvaluationContext,
input: InputStream,
scope: Arc<Scope>,
external_redirection: ExternalRedirection,
) -> Result<InputStream, ShellError> {
let path = context.shell_manager.path();
@ -50,7 +51,8 @@ async fn run_with_stdin(
let mut command_args = vec![];
for arg in command.args.iter() {
let value = evaluate_baseline_expr(arg, &context.registry, scope.clone()).await?;
let is_literal = matches!(arg.expr, Expression::Literal(_));
let value = evaluate_baseline_expr(arg, context).await?;
// Skip any arguments that don't really exist, treating them as optional
// FIXME: we may want to preserve the gap in the future, though it's hard to say
@ -65,8 +67,10 @@ async fn run_with_stdin(
for t in table {
match &t.value {
UntaggedValue::Primitive(_) => {
command_args
.push(t.convert_to_string().trim_end_matches('\n').to_string());
command_args.push((
t.convert_to_string().trim_end_matches('\n').to_string(),
is_literal,
));
}
_ => {
return Err(ShellError::labeled_error(
@ -80,19 +84,19 @@ async fn run_with_stdin(
}
_ => {
let trimmed_value_string = value.as_string()?.trim_end_matches('\n').to_string();
command_args.push(trimmed_value_string);
command_args.push((trimmed_value_string, is_literal));
}
}
}
let process_args = command_args
.iter()
.map(|arg| {
.map(|(arg, _is_literal)| {
let home_dir;
#[cfg(feature = "dirs")]
{
home_dir = dirs::home_dir;
home_dir = dirs_next::home_dir;
}
#[cfg(not(feature = "dirs"))]
{
@ -103,8 +107,9 @@ async fn run_with_stdin(
#[cfg(not(windows))]
{
if argument_contains_whitespace(&arg) && !argument_is_quoted(&arg) {
add_quotes(&arg)
if !_is_literal {
let escaped = escape_double_quotes(&arg);
add_double_quotes(&escaped)
} else {
arg.as_ref().to_string()
}
@ -126,7 +131,7 @@ async fn run_with_stdin(
&process_args[..],
input,
external_redirection,
scope,
&context.scope,
)
}
@ -136,7 +141,7 @@ fn spawn(
args: &[String],
input: InputStream,
external_redirection: ExternalRedirection,
scope: Arc<Scope>,
scope: &Scope,
) -> Result<InputStream, ShellError> {
let command = command.clone();
@ -167,7 +172,7 @@ fn spawn(
trace!(target: "nu::run::external", "cwd = {:?}", &path);
process.env_clear();
process.envs(scope.env());
process.envs(scope.get_env_vars());
// We want stdout regardless of what
// we are doing ($it case or pipe stdin)
@ -217,38 +222,20 @@ fn spawn(
for value in block_on_stream(input) {
match &value.value {
UntaggedValue::Primitive(Primitive::Nothing) => continue,
UntaggedValue::Primitive(Primitive::String(s))
| UntaggedValue::Primitive(Primitive::Line(s)) => {
if let Err(e) = stdin_write.write(s.as_bytes()) {
let message = format!("Unable to write to stdin (error = {})", e);
let _ = stdin_write_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
message,
"application may have closed before completing pipeline",
&stdin_name_tag,
)),
tag: stdin_name_tag,
}));
return Err(());
UntaggedValue::Primitive(Primitive::String(s)) => {
if stdin_write.write(s.as_bytes()).is_err() {
// Other side has closed, so exit
return Ok(());
}
}
UntaggedValue::Primitive(Primitive::Binary(b)) => {
if let Err(e) = stdin_write.write(b) {
let message = format!("Unable to write to stdin (error = {})", e);
let _ = stdin_write_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
message,
"application may have closed before completing pipeline",
&stdin_name_tag,
)),
tag: stdin_name_tag,
}));
return Err(());
if stdin_write.write(b).is_err() {
// Other side has closed, so exit
return Ok(());
}
}
unsupported => {
println!("Unsupported: {:?}", unsupported);
let _ = stdin_write_tx.send(Ok(Value {
value: UntaggedValue::Error(ShellError::labeled_error(
format!(
@ -477,7 +464,7 @@ pub fn did_find_command(#[allow(unused)] name: &str) -> bool {
let cmd_builtins = [
"assoc", "break", "color", "copy", "date", "del", "dir", "dpath", "echo", "erase",
"for", "ftype", "md", "mkdir", "mklink", "move", "path", "ren", "rename", "rd",
"rmdir", "set", "start", "time", "title", "type", "ver", "verify", "vol",
"rmdir", "start", "time", "title", "type", "ver", "verify", "vol",
];
cmd_builtins.contains(&name)
@ -494,11 +481,6 @@ where
shellexpand::tilde_with_context(input, home_dir)
}
#[allow(unused)]
pub fn argument_contains_whitespace(argument: &str) -> bool {
argument.chars().any(|c| c.is_whitespace())
}
fn argument_is_quoted(argument: &str) -> bool {
if argument.len() < 2 {
return false;
@ -509,10 +491,20 @@ fn argument_is_quoted(argument: &str) -> bool {
}
#[allow(unused)]
fn add_quotes(argument: &str) -> String {
fn add_double_quotes(argument: &str) -> String {
format!("\"{}\"", argument)
}
#[allow(unused)]
fn escape_double_quotes(argument: &str) -> Cow<'_, str> {
// allocate new string only if required
if argument.contains('"') {
Cow::Owned(argument.replace('"', r#"\""#))
} else {
Cow::Borrowed(argument)
}
}
#[allow(unused)]
fn remove_quotes(argument: &str) -> Option<&str> {
if !argument_is_quoted(argument) {
@ -538,20 +530,19 @@ fn shell_os_paths() -> Vec<std::path::PathBuf> {
#[cfg(test)]
mod tests {
use super::{
add_quotes, argument_contains_whitespace, argument_is_quoted, expand_tilde, remove_quotes,
add_double_quotes, argument_is_quoted, escape_double_quotes, expand_tilde, remove_quotes,
};
#[cfg(feature = "which")]
use super::{run_external_command, EvaluationContext, InputStream};
use super::{run_external_command, InputStream};
#[cfg(feature = "which")]
use futures::executor::block_on;
#[cfg(feature = "which")]
use nu_engine::basic_evaluation_context;
#[cfg(feature = "which")]
use nu_errors::ShellError;
#[cfg(feature = "which")]
use nu_protocol::Scope;
#[cfg(feature = "which")]
use nu_test_support::commands::ExternalBuilder;
// async fn read(mut stream: OutputStream) -> Option<Value> {
// match stream.try_next().await {
// Ok(val) => {
@ -572,17 +563,13 @@ mod tests {
let input = InputStream::empty();
let mut ctx =
EvaluationContext::basic().expect("There was a problem creating a basic context.");
basic_evaluation_context().expect("There was a problem creating a basic context.");
assert!(run_external_command(
cmd,
&mut ctx,
input,
Scope::create(),
ExternalRedirection::Stdout
)
assert!(
run_external_command(cmd, &mut ctx, input, ExternalRedirection::Stdout)
.await
.is_err());
.is_err()
);
Ok(())
}
@ -590,7 +577,7 @@ mod tests {
// async fn failure_run() -> Result<(), ShellError> {
// let cmd = ExternalBuilder::for_name("fail").build();
// let mut ctx = EvaluationContext::basic().expect("There was a problem creating a basic context.");
// let mut ctx = crate::cli::basic_evaluation_context().expect("There was a problem creating a basic context.");
// let stream = run_external_command(cmd, &mut ctx, None, false)
// .await?
// .expect("There was a problem running the external command.");
@ -618,10 +605,10 @@ mod tests {
}
#[test]
fn checks_contains_whitespace_from_argument_to_be_passed_in() {
assert_eq!(argument_contains_whitespace("andrés"), false);
assert_eq!(argument_contains_whitespace("and rés"), true);
assert_eq!(argument_contains_whitespace(r#"and\ rés"#), true);
fn checks_escape_double_quotes() {
assert_eq!(escape_double_quotes("andrés"), "andrés");
assert_eq!(escape_double_quotes(r#"an"drés"#), r#"an\"drés"#);
assert_eq!(escape_double_quotes(r#""an"drés""#), r#"\"an\"drés\""#);
}
#[test]
@ -649,9 +636,8 @@ mod tests {
}
#[test]
fn adds_quotes_to_argument_to_be_passed_in() {
assert_eq!(add_quotes("andrés"), "\"andrés\"");
//assert_eq!(add_quotes("\"andrés\""), "\"andrés\"");
fn adds_double_quotes_to_argument_to_be_passed_in() {
assert_eq!(add_double_quotes("andrés"), "\"andrés\"");
}
#[test]

View File

@ -0,0 +1,5 @@
mod dynamic;
pub(crate) mod external;
#[allow(unused_imports)]
pub(crate) use dynamic::Command as DynamicCommand;

View File

@ -1,5 +1,5 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::Signature;
use std::process::Command;
@ -20,7 +20,7 @@ impl WholeStreamCommand for Clear {
"Clears the terminal"
}
async fn run(&self, _: CommandArgs, _: &CommandRegistry) -> Result<OutputStream, ShellError> {
async fn run(&self, _: CommandArgs) -> Result<OutputStream, ShellError> {
if cfg!(windows) {
Command::new("cmd")
.args(&["/C", "cls"])

View File

@ -1,11 +1,10 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use futures::stream::StreamExt;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Signature, Value};
use clipboard::{ClipboardContext, ClipboardProvider};
use arboard::Clipboard;
pub struct Clip;
@ -23,12 +22,8 @@ impl WholeStreamCommand for Clip {
"Copy the contents of the pipeline to the copy/paste buffer"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
clip(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
clip(args).await
}
fn examples(&self) -> Vec<Example> {
@ -47,23 +42,19 @@ impl WholeStreamCommand for Clip {
}
}
pub async fn clip(
args: CommandArgs,
_registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
pub async fn clip(args: CommandArgs) -> Result<OutputStream, ShellError> {
let input = args.input;
let name = args.call_info.name_tag.clone();
let values: Vec<Value> = input.collect().await;
if let Ok(clip_context) = ClipboardProvider::new() {
let mut clip_context: ClipboardContext = clip_context;
if let Ok(mut clip_context) = Clipboard::new() {
let mut new_copy_data = String::new();
if !values.is_empty() {
let mut first = true;
for i in values.iter() {
if !first {
new_copy_data.push_str("\n");
new_copy_data.push('\n');
} else {
first = false;
}
@ -81,7 +72,7 @@ pub async fn clip(
}
}
match clip_context.set_contents(new_copy_data) {
match clip_context.set_text(new_copy_data) {
Ok(_) => {}
Err(_) => {
return Err(ShellError::labeled_error(
@ -110,6 +101,6 @@ mod tests {
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Clip {})?)
test_examples(Clip {})
}
}

View File

@ -0,0 +1,21 @@
use crate::prelude::*;
use nu_engine::Command;
use nu_errors::ShellError;
use parking_lot::Mutex;
use std::sync::atomic::AtomicBool;
pub struct RunnableContext {
pub input: InputStream,
pub shell_manager: ShellManager,
pub host: Arc<parking_lot::Mutex<Box<dyn Host>>>,
pub ctrl_c: Arc<AtomicBool>,
pub current_errors: Arc<Mutex<Vec<ShellError>>>,
pub scope: Scope,
pub name: Tag,
}
impl RunnableContext {
pub fn get_command(&self, name: &str) -> Option<Command> {
self.scope.get_command(name)
}
}

View File

@ -1,8 +1,7 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use futures::future;
use futures::stream::StreamExt;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
@ -28,12 +27,8 @@ impl WholeStreamCommand for Compact {
"Creates a table with non-empty rows"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
compact(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
compact(args).await
}
fn examples(&self) -> Vec<Example> {
@ -45,12 +40,8 @@ impl WholeStreamCommand for Compact {
}
}
pub async fn compact(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
let registry = registry.clone();
let (CompactArgs { rest: columns }, input) = args.process(&registry).await?;
pub async fn compact(args: CommandArgs) -> Result<OutputStream, ShellError> {
let (CompactArgs { rest: columns }, input) = args.process().await?;
Ok(input
.filter_map(move |item| {
future::ready(if columns.is_empty() {
@ -90,6 +81,6 @@ mod tests {
fn examples_work_as_expected() -> Result<(), ShellError> {
use crate::examples::test as test_examples;
Ok(test_examples(Compact {})?)
test_examples(Compact {})
}
}

View File

@ -1,6 +1,5 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
@ -20,12 +19,8 @@ impl WholeStreamCommand for SubCommand {
"clear the config"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
clear(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
clear(args).await
}
fn examples(&self) -> Vec<Example> {
@ -37,10 +32,7 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn clear(
args: CommandArgs,
_registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
pub async fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
// NOTE: None because we are not loading a new config file, we just want to read from the

View File

@ -1,8 +1,9 @@
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use crate::{CommandArgs, CommandRegistry, OutputStream};
use nu_engine::CommandArgs;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use nu_stream::OutputStream;
pub struct Command;
@ -20,11 +21,7 @@ impl WholeStreamCommand for Command {
"Configuration management."
}
async fn run(
&self,
args: CommandArgs,
_registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
let name = args.call_info.name_tag;
let result = nu_data::config::read(name_span, &None)?;

View File

@ -1,6 +1,5 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
@ -29,12 +28,8 @@ impl WholeStreamCommand for SubCommand {
"Gets a value from the config"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
get(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
get(args).await
}
fn examples(&self) -> Vec<Example> {
@ -46,12 +41,9 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn get(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (GetArgs { path }, _) = args.process(&registry).await?;
let (GetArgs { path }, _) = args.process().await?;
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config

View File

@ -1,6 +1,5 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
@ -22,7 +21,7 @@ impl WholeStreamCommand for SubCommand {
fn signature(&self) -> Signature {
Signature::build("config load").required(
"load",
SyntaxShape::Path,
SyntaxShape::FilePath,
"Path to load the config from",
)
}
@ -31,22 +30,15 @@ impl WholeStreamCommand for SubCommand {
"Loads the config from the path given"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
set(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set(args).await
}
}
pub async fn set(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let name_span = args.call_info.name_tag.clone();
let (LoadArgs { load }, _) = args.process(&registry).await?;
let (LoadArgs { load }, _) = args.process().await?;
let configuration = load.item().clone();

View File

@ -1,6 +1,5 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
@ -20,12 +19,8 @@ impl WholeStreamCommand for SubCommand {
"return the path to the config file"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
path(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
path(args).await
}
fn examples(&self) -> Vec<Example> {
@ -37,13 +32,10 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn path(
args: CommandArgs,
_registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
pub async fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
let path = config::default_path()?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::Path(path)).into_value(args.call_info.name_tag),
UntaggedValue::Primitive(Primitive::FilePath(path)).into_value(args.call_info.name_tag),
)))
}

View File

@ -1,6 +1,5 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
@ -30,12 +29,8 @@ impl WholeStreamCommand for SubCommand {
"Removes a value from the config"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
remove(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
remove(args).await
}
fn examples(&self) -> Vec<Example> {
@ -47,12 +42,9 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn remove(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
pub async fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
let (RemoveArgs { remove }, _) = args.process(&registry).await?;
let (RemoveArgs { remove }, _) = args.process().await?;
let mut result = nu_data::config::read(name_span, &None)?;

View File

@ -1,6 +1,5 @@
use crate::command_registry::CommandRegistry;
use crate::commands::WholeStreamCommand;
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
@ -28,12 +27,8 @@ impl WholeStreamCommand for SubCommand {
"Sets a value in the config"
}
async fn run(
&self,
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
set(args, registry).await
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set(args).await
}
fn examples(&self) -> Vec<Example> {
@ -62,12 +57,9 @@ impl WholeStreamCommand for SubCommand {
}
}
pub async fn set(
args: CommandArgs,
registry: &CommandRegistry,
) -> Result<OutputStream, ShellError> {
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (SetArgs { path, mut value }, _) = args.process(&registry).await?;
let (SetArgs { path, mut value }, _) = args.process().await?;
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config

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