mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 07:00:37 +02:00
Compare commits
81 Commits
Author | SHA1 | Date | |
---|---|---|---|
6cc4ef6c70 | |||
517173bb8c | |||
e415be6c0e | |||
59332562bb | |||
3d8d7787de | |||
611fe41788 | |||
a6118eed8d | |||
d4798d6ee1 | |||
5ea245badf | |||
5ee7847035 | |||
8a812cf03c | |||
9a1cedfd08 | |||
766d1ef374 | |||
beec658872 | |||
b90d701f89 | |||
be5d71ea47 | |||
f1bde69131 | |||
36ae384fb3 | |||
2c4048eb43 | |||
b9195c2668 | |||
bb968304da | |||
ca9bf19041 | |||
ecfee4c542 | |||
acb34561eb | |||
43aec8cdbe | |||
e46d610f77 | |||
1d95861a09 | |||
f48de73236 | |||
0b4daa66b0 | |||
412952182f | |||
014d36b17a | |||
f44f3a8af1 | |||
457514590d | |||
843d8c2242 | |||
ce4ae00a6f | |||
4f7f6a2932 | |||
7039602e4d | |||
acb7aff6fb | |||
8940ee6c3f | |||
3b26b4355e | |||
b56e603c58 | |||
66c2a36123 | |||
8838815737 | |||
834522d002 | |||
f281cd5aa3 | |||
5add5cbd12 | |||
902aad6016 | |||
13f87857cf | |||
e0cc2c9112 | |||
92ab8b831b | |||
6a7a60429f | |||
79fd7d54b2 | |||
ebca840d91 | |||
17b2bcc125 | |||
24a98f8999 | |||
e49b359848 | |||
c9fb381d69 | |||
8224ec49bc | |||
fe7e87ee02 | |||
a3dce8ff19 | |||
89f3cbf318 | |||
3f555a6836 | |||
4fdf5c663c | |||
1ec41a0ab4 | |||
ab0a6b6ca6 | |||
e3bf6fdfc0 | |||
46c0d29c08 | |||
76ccd5668a | |||
c6436eb32f | |||
b2c29117d9 | |||
ffb1dfb012 | |||
88c6fa9933 | |||
60df45a390 | |||
10aa86272b | |||
d37e6ba3b5 | |||
5eee33c7e4 | |||
5e748ae8fc | |||
50e53e788a | |||
7336e1df1a | |||
a724a8fe7d | |||
c731a4e275 |
27
.github/pull_request_template.md
vendored
27
.github/pull_request_template.md
vendored
@ -1,21 +1,26 @@
|
||||
# Description
|
||||
|
||||
(description of your pull request here)
|
||||
(Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience.)
|
||||
|
||||
# Tests
|
||||
# Major Changes
|
||||
|
||||
Make sure you've done the following:
|
||||
If you're considering making any major change to nushell, before starting work on it, seek feedback from regular contributors and get approval for the idea from the core team either on [Discord](https://discordapp.com/invite/NtAbbGn) or [GitHub issue](https://github.com/nushell/nushell/issues/new/choose).
|
||||
Making sure we're all on board with the change saves everybody's time.
|
||||
Thanks!
|
||||
|
||||
- [ ] Add tests that cover your changes, either in the command examples, the crate/tests folder, or in the /tests folder.
|
||||
- [ ] Try to think about corner cases and various ways how your changes could break. Cover them with tests.
|
||||
- [ ] If adding tests is not possible, please document in the PR body a minimal example with steps on how to reproduce so one can verify your change works.
|
||||
# Tests + Formatting
|
||||
|
||||
Make sure you've done the following, if applicable:
|
||||
|
||||
- Add tests that cover your changes (either in the command examples, the crate/tests folder, or in the /tests folder)
|
||||
- Try to think about corner cases and various ways how your changes could break. Cover those in the tests
|
||||
|
||||
Make sure you've run and fixed any issues with these commands:
|
||||
|
||||
- [ ] `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
||||
- [ ] `cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
||||
- [ ] `cargo test --workspace --features=extra` to check that all the tests pass
|
||||
- `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes)
|
||||
- `cargo clippy --workspace --features=extra -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style
|
||||
- `cargo test --workspace --features=extra` to check that all tests pass
|
||||
|
||||
# Documentation
|
||||
# After Submitting
|
||||
|
||||
- [ ] If your PR touches a user-facing nushell feature then make sure that there is an entry in the documentation (https://github.com/nushell/nushell.github.io) for the feature, and update it if necessary.
|
||||
* Help us keep the docs up to date: If your PR affects the user experience of Nushell (adding/removing a command, changing an input/output type, etc.), make sure the changes are reflected in the documentation (https://github.com/nushell/nushell.github.io) after the PR is merged.
|
||||
|
71
.github/workflows/ci.yml
vendored
71
.github/workflows/ci.yml
vendored
@ -20,29 +20,19 @@ jobs:
|
||||
NUSHELL_CARGO_TARGET: ci
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
# makes ci use rust-toolchain.toml
|
||||
# with:
|
||||
# profile: minimal
|
||||
# toolchain: ${{ matrix.rust }}
|
||||
# override: true
|
||||
# components: rustfmt, clippy
|
||||
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
with:
|
||||
key: "v2" # increment this to bust the cache if needed
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||
|
||||
- name: Rustfmt
|
||||
uses: actions-rs/cargo@v1
|
||||
uses: actions-rs/cargo@v1.0.1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
uses: actions-rs/cargo@v1.0.1
|
||||
with:
|
||||
command: clippy
|
||||
args: --features=extra --workspace --exclude nu_plugin_* -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||
@ -72,22 +62,13 @@ jobs:
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
# makes ci use rust-toolchain.toml
|
||||
# with:
|
||||
# profile: minimal
|
||||
# toolchain: ${{ matrix.rust }}
|
||||
# override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
with:
|
||||
key: ${{ matrix.style }}v3 # increment this to bust the cache if needed
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||
|
||||
- name: Tests
|
||||
uses: actions-rs/cargo@v1
|
||||
uses: actions-rs/cargo@v1.0.1
|
||||
with:
|
||||
command: test
|
||||
args: --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
||||
@ -108,28 +89,19 @@ jobs:
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
# makes ci use rust-toolchain.toml
|
||||
# with:
|
||||
# profile: minimal
|
||||
# toolchain: ${{ matrix.rust }}
|
||||
# override: true
|
||||
|
||||
- uses: Swatinem/rust-cache@v1
|
||||
with:
|
||||
key: "2" # increment this to bust the cache if needed
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||
|
||||
- name: Install Nushell
|
||||
uses: actions-rs/cargo@v1
|
||||
uses: actions-rs/cargo@v1.0.1
|
||||
with:
|
||||
command: install
|
||||
args: --path=. --profile ci --no-default-features
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
|
||||
@ -159,24 +131,19 @@ jobs:
|
||||
runs-on: ${{ matrix.platform }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
# makes ci use rust-toolchain.toml
|
||||
# with:
|
||||
# profile: minimal
|
||||
# toolchain: ${{ matrix.rust }}
|
||||
# override: true
|
||||
- name: Setup Rust toolchain and cache
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1.3.4
|
||||
|
||||
- name: Clippy
|
||||
uses: actions-rs/cargo@v1
|
||||
uses: actions-rs/cargo@v1.0.1
|
||||
with:
|
||||
command: clippy
|
||||
args: --package nu_plugin_* ${{ matrix.flags }} -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect
|
||||
|
||||
- name: Tests
|
||||
uses: actions-rs/cargo@v1
|
||||
uses: actions-rs/cargo@v1.0.1
|
||||
with:
|
||||
command: test
|
||||
args: --profile ci --package nu_plugin_*
|
||||
|
12
.github/workflows/release-pkg.nu
vendored
12
.github/workflows/release-pkg.nu
vendored
@ -90,10 +90,16 @@ if ($ver | str trim | is-empty) {
|
||||
cd $dist; $'(char nl)Creating release archive...'; hr-line
|
||||
if $os in ['ubuntu-latest', 'macos-latest'] {
|
||||
|
||||
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls
|
||||
let files = (ls | get name)
|
||||
let dest = $'($bin)-($version)-($target)'
|
||||
let archive = $'($dist)/($dest).tar.gz'
|
||||
|
||||
let archive = $'($dist)/($bin)-($version)-($target).tar.gz'
|
||||
tar czf $archive *
|
||||
mkdir $dest
|
||||
$files | each {|it| mv $it $dest } | ignore
|
||||
|
||||
$'(char nl)(ansi g)Archive contents:(ansi reset)'; hr-line; ls $dest
|
||||
|
||||
tar -czf $archive $dest
|
||||
print $'archive: ---> ($archive)'; ls $archive
|
||||
echo $'::set-output name=archive::($archive)'
|
||||
|
||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -22,6 +22,10 @@ debian/nu/
|
||||
# VSCode's IDE items
|
||||
.vscode/*
|
||||
|
||||
# Visual Studio Extension SourceGear Rust items
|
||||
VSWorkspaceSettings.json
|
||||
unstable_cargo_features.txt
|
||||
|
||||
# Helix configuration folder
|
||||
.helix/*
|
||||
.helix
|
||||
|
@ -1,8 +1,23 @@
|
||||
# Contributing
|
||||
|
||||
Welcome to Nushell!
|
||||
Welcome to Nushell and thank you for considering contributing!
|
||||
|
||||
To get live support from the community see our [Discord](https://discordapp.com/invite/NtAbbGn), [Twitter](https://twitter.com/nu_shell) or file an issue or feature request here on [GitHub](https://github.com/nushell/nushell/issues/new/choose)!
|
||||
## Review Process
|
||||
|
||||
First of all, before diving into the code, if you want to create a new feature, change something significantly, and especially if the change is user-facing, it is a good practice to first get an approval from the core team before starting to work on it.
|
||||
This saves both your and our time if we realize the change needs to go another direction before spending time on it.
|
||||
So, please, reach out and tell us what you want to do.
|
||||
This will significantly increase the chance of your PR being accepted.
|
||||
|
||||
The review process can be summarized as follows:
|
||||
1. You want to make some change to Nushell that is more involved than simple bug-fixing.
|
||||
2. Go to [Discord](https://discordapp.com/invite/NtAbbGn) or a [GitHub issue](https://github.com/nushell/nushell/issues/new/choose) and chat with some core team members and/or other contributors about it.
|
||||
3. After getting a green light from the core team, implement the feature, open a pull request (PR) and write a concise but comprehensive description of the change.
|
||||
4. If your PR includes any use-facing features (such as adding a flag to a command), clearly list them in the PR description.
|
||||
5. Then, core team members and other regular contributors will review the PR and suggest changes.
|
||||
6. When we all agree, the PR will be merged.
|
||||
7. If your PR includes any user-facing features, make sure the changes are also reflected in [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged.
|
||||
8. Congratulate yourself, you just improved Nushell! :-)
|
||||
|
||||
## Developing
|
||||
|
||||
|
325
Cargo.lock
generated
325
Cargo.lock
generated
@ -12,15 +12,6 @@ dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "addr2line"
|
||||
version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b"
|
||||
dependencies = [
|
||||
"gimli",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
@ -79,29 +70,21 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ansi-str"
|
||||
version = "0.4.0"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "132f03d534a71638c1be79251027872d5dda24aa5eeeb6886960351f3087bdde"
|
||||
checksum = "84252a7e1a0df81706ce70bbad85ed1e4916448a4093ccd52dd98c6a44a477cd"
|
||||
dependencies = [
|
||||
"ansitok",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansi_term"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ansitok"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2c6eb31f539d8fc1df948eb26452d6c781be4c9883663e7acb258644b71d5b1"
|
||||
checksum = "220044e6a1bb31ddee4e3db724d29767f352de47445a6cd75e1a173142136c83"
|
||||
dependencies = [
|
||||
"nom 7.1.1",
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -240,21 +223,6 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "backtrace"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cc",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
"rustc-demangle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bare-metal"
|
||||
version = "0.2.5"
|
||||
@ -403,12 +371,6 @@ version = "3.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d"
|
||||
|
||||
[[package]]
|
||||
name = "byte-order"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b021a13e4bf34a5679ada4609a01337ae82f2c4c97493b9d8cbf8aa9af9bd0f4"
|
||||
|
||||
[[package]]
|
||||
name = "byte-unit"
|
||||
version = "4.0.14"
|
||||
@ -914,7 +876,7 @@ version = "3.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d91974fbbe88ec1df0c24a4f00f99583667a7e2e6272b2b92d294d81e462173"
|
||||
dependencies = [
|
||||
"nix 0.25.0",
|
||||
"nix",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
@ -1045,12 +1007,6 @@ dependencies = [
|
||||
"rust_decimal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "453440c271cf5577fd2a40e4942540cb7d0d2f85e27c8d07dd0023c925a67541"
|
||||
|
||||
[[package]]
|
||||
name = "dyn-clone"
|
||||
version = "1.0.9"
|
||||
@ -1426,16 +1382,6 @@ dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gethostname"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1ebd34e35c46e00bb73e81363248d627782724609fe1b6396f553f68fe3862e"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
@ -1490,12 +1436,6 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.26.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d"
|
||||
|
||||
[[package]]
|
||||
name = "git2"
|
||||
version = "0.15.0"
|
||||
@ -1929,9 +1869,9 @@ checksum = "47631885425c482fcf2dc4b182fc973c3c5b81a8f43a028055559bd24cccfa6e"
|
||||
|
||||
[[package]]
|
||||
name = "json_to_table"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8fe97d58ab96691e90f26c10ecc1fc098a4c072714d2d126cc8a2234b89f3456"
|
||||
checksum = "408f09e42613f7a9902ecf2f30bcd053cf9f5668d0b1493ae1669070338d661b"
|
||||
dependencies = [
|
||||
"serde_json",
|
||||
"tabled",
|
||||
@ -2034,9 +1974,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.134"
|
||||
version = "0.2.135"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb"
|
||||
checksum = "68783febc7782c6c5cb401fbda4de5a9898be1762314da0bb2c10ced61f18b0c"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@ -2156,7 +2096,6 @@ version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "074bff749d092e2e818fe954952102f88e21f67fc69f4d350621aab15a1810f1"
|
||||
dependencies = [
|
||||
"ansi_term",
|
||||
"crossterm 0.24.0",
|
||||
]
|
||||
|
||||
@ -2267,18 +2206,6 @@ dependencies = [
|
||||
"nom 1.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "4.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c90329e44f9208b55f45711f9558cec15d7ef8295cc65ecd6d4188ae8edc58c"
|
||||
dependencies = [
|
||||
"miette-derive 4.7.1",
|
||||
"once_cell",
|
||||
"thiserror",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "5.3.0"
|
||||
@ -2286,8 +2213,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28d6092d7e94a90bb9ea8e6c26c99d5d112d49dda2afdb4f7ea8cf09e1a5a6d"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"backtrace",
|
||||
"miette-derive 5.3.0",
|
||||
"miette-derive",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"supports-color",
|
||||
@ -2299,17 +2225,6 @@ dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "4.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b5bc45b761bcf1b5e6e6c4128cd93b84c218721a8d9b894aa0aff4ed180174c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "5.3.0"
|
||||
@ -2467,18 +2382,6 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54"
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.0"
|
||||
@ -2544,15 +2447,6 @@ dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f"
|
||||
dependencies = [
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.4.0"
|
||||
@ -2564,7 +2458,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"assert_cmd",
|
||||
"atty",
|
||||
@ -2575,8 +2469,8 @@ dependencies = [
|
||||
"is_executable",
|
||||
"itertools",
|
||||
"log",
|
||||
"miette 5.3.0",
|
||||
"nix 0.24.2",
|
||||
"miette",
|
||||
"nix",
|
||||
"nu-ansi-term",
|
||||
"nu-cli",
|
||||
"nu-color-config",
|
||||
@ -2618,7 +2512,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-cli"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"chrono",
|
||||
@ -2628,7 +2522,7 @@ dependencies = [
|
||||
"is_executable",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"miette 5.3.0",
|
||||
"miette",
|
||||
"nu-ansi-term",
|
||||
"nu-color-config",
|
||||
"nu-command",
|
||||
@ -2641,14 +2535,13 @@ dependencies = [
|
||||
"percent-encoding",
|
||||
"reedline",
|
||||
"rstest",
|
||||
"strip-ansi-escapes",
|
||||
"sysinfo",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-color-config"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
"nu-json",
|
||||
@ -2659,7 +2552,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-command"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"Inflector",
|
||||
"alphanumeric-sort",
|
||||
@ -2689,6 +2582,7 @@ dependencies = [
|
||||
"is-root",
|
||||
"itertools",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
"lscolors",
|
||||
"md-5",
|
||||
@ -2735,7 +2629,6 @@ dependencies = [
|
||||
"sha2",
|
||||
"shadow-rs",
|
||||
"sqlparser",
|
||||
"strip-ansi-escapes",
|
||||
"sysinfo",
|
||||
"terminal_size 0.2.1",
|
||||
"thiserror",
|
||||
@ -2749,26 +2642,25 @@ dependencies = [
|
||||
"uuid",
|
||||
"wax",
|
||||
"which",
|
||||
"windows",
|
||||
"windows 0.42.0",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-engine"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"nu-glob",
|
||||
"nu-path",
|
||||
"nu-protocol",
|
||||
"nu-utils",
|
||||
"strip-ansi-escapes",
|
||||
"sysinfo",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-glob"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"doc-comment",
|
||||
"tempdir",
|
||||
@ -2776,7 +2668,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-json"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"fancy-regex",
|
||||
"lazy_static",
|
||||
@ -2789,13 +2681,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-parser"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"bytesize",
|
||||
"chrono",
|
||||
"itertools",
|
||||
"log",
|
||||
"miette 5.3.0",
|
||||
"miette",
|
||||
"nu-engine",
|
||||
"nu-path",
|
||||
"nu-plugin",
|
||||
@ -2806,32 +2698,30 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-path"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"dirs-next",
|
||||
"dunce",
|
||||
"omnipath",
|
||||
"pwd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-plugin"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"byte-order",
|
||||
"criterion",
|
||||
"nu-engine",
|
||||
"nu-protocol",
|
||||
"rmp",
|
||||
"rmp-serde",
|
||||
"rmpv",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-pretty-hex"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"heapless",
|
||||
"nu-ansi-term",
|
||||
@ -2840,16 +2730,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-protocol"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"byte-unit",
|
||||
"chrono",
|
||||
"chrono-humanize",
|
||||
"fancy-regex",
|
||||
"indexmap",
|
||||
"miette 5.3.0",
|
||||
"miette",
|
||||
"nu-json",
|
||||
"nu-path",
|
||||
"nu-utils",
|
||||
"num-format",
|
||||
"serde",
|
||||
@ -2861,16 +2750,17 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-system"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"chrono",
|
||||
"errno",
|
||||
"libc",
|
||||
"libproc",
|
||||
"log",
|
||||
"mach2",
|
||||
"nix 0.24.2",
|
||||
"ntapi 0.3.7",
|
||||
"nix",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"procfs",
|
||||
"winapi 0.3.9",
|
||||
@ -2878,28 +2768,28 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu-table"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"json_to_table",
|
||||
"nu-ansi-term",
|
||||
"nu-protocol",
|
||||
"nu-utils",
|
||||
"serde_json",
|
||||
"strip-ansi-escapes",
|
||||
"tabled",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-term-grid"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"strip-ansi-escapes",
|
||||
"nu-utils",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-test-support"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"getset",
|
||||
"hamcrest2",
|
||||
@ -2907,18 +2797,18 @@ dependencies = [
|
||||
"nu-glob",
|
||||
"nu-path",
|
||||
"nu-utils",
|
||||
"num-bigint 0.4.3",
|
||||
"num-format",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-utils"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"crossterm_winapi",
|
||||
"lscolors",
|
||||
"num-format",
|
||||
"strip-ansi-escapes",
|
||||
"sys-locale",
|
||||
]
|
||||
|
||||
@ -2934,7 +2824,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_example"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"nu-plugin",
|
||||
"nu-protocol",
|
||||
@ -2942,7 +2832,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_gstat"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"git2",
|
||||
"nu-engine",
|
||||
@ -2952,7 +2842,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_inc"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"nu-plugin",
|
||||
"nu-protocol",
|
||||
@ -2961,7 +2851,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "nu_plugin_query"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
dependencies = [
|
||||
"gjson",
|
||||
"nu-engine",
|
||||
@ -3020,7 +2910,6 @@ dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3136,13 +3025,10 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.29.0"
|
||||
name = "omnipath"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
checksum = "e7461858c5ac9bde3fcdeedc17f58ed0469ec74f2d737b6369fc31c0b0ef576c"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
@ -3234,15 +3120,14 @@ checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f"
|
||||
|
||||
[[package]]
|
||||
name = "papergrid"
|
||||
version = "0.6.0"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae9bed2481d5ab6e31056945f4704ca7348a3858148c30725b8946b7a7818498"
|
||||
checksum = "1526bb6aa9f10ec339fb10360f22c57edf81d5678d0278e93bc12a47ffbe4b01"
|
||||
dependencies = [
|
||||
"ansi-str",
|
||||
"ansitok",
|
||||
"bytecount",
|
||||
"fnv",
|
||||
"strip-ansi-escapes",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
@ -4070,14 +3955,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reedline"
|
||||
version = "0.13.0"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "555a4285d183a94d140d749712a6600b10e70d7cd6aef91e3cfb65ba09b1d75e"
|
||||
checksum = "75d91d2a38dbdaf0c748a96055c14aa4746be9add5f4aea4b1460e6e6492f770"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossterm 0.24.0",
|
||||
"fd-lock",
|
||||
"gethostname",
|
||||
"itertools",
|
||||
"nu-ansi-term",
|
||||
"rusqlite",
|
||||
@ -4209,16 +4093,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rmpv"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de8813b3a2f95c5138fe5925bfb8784175d88d6bff059ba8ce090aa891319754"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"rmp",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "roxmltree"
|
||||
version = "0.14.1"
|
||||
@ -4309,12 +4183,6 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
@ -4826,7 +4694,7 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "011cbb39cf7c1f62871aea3cc46e5817b0937b49e9447370c93cacbe93a766d8"
|
||||
dependencies = [
|
||||
"vte 0.10.1",
|
||||
"vte",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4951,7 +4819,7 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
"ntapi 0.4.0",
|
||||
"ntapi",
|
||||
"once_cell",
|
||||
"rayon",
|
||||
"winapi 0.3.9",
|
||||
@ -4959,15 +4827,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tabled"
|
||||
version = "0.9.0"
|
||||
version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c8a1ea336f84dc7dfae1025b73904551b3c6a42347f4243387e990f94325895"
|
||||
checksum = "56c3ee73732ffceaea7b8f6b719ce3bb17f253fa27461ffeaf568ebd0cdb4b85"
|
||||
dependencies = [
|
||||
"ansi-str",
|
||||
"papergrid",
|
||||
"tabled_derive",
|
||||
"unicode-width",
|
||||
"vte 0.11.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5262,7 +5129,7 @@ dependencies = [
|
||||
"once_cell",
|
||||
"scopeguard",
|
||||
"url",
|
||||
"windows",
|
||||
"windows 0.37.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5453,12 +5320,6 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||
|
||||
[[package]]
|
||||
name = "vec1"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fc1631c774f0f9570797191e01247cbefde789eebfbf128074cb934115a6133"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
@ -5491,17 +5352,6 @@ dependencies = [
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1aae21c12ad2ec2d168c236f369c38ff332bc1134f7246350dca641437365045"
|
||||
dependencies = [
|
||||
"arrayvec 0.7.2",
|
||||
"utf8parse",
|
||||
"vte_generate_state_changes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "vte_generate_state_changes"
|
||||
version = "0.1.1"
|
||||
@ -5635,14 +5485,12 @@ dependencies = [
|
||||
"bstr",
|
||||
"const_format",
|
||||
"itertools",
|
||||
"miette 4.7.1",
|
||||
"nom 7.1.1",
|
||||
"nom-supreme",
|
||||
"pori",
|
||||
"regex",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"vec1",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
@ -5723,6 +5571,21 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.37.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0286ba339aa753e70765d521bb0242cc48e1194562bfa2a2ad7ac8a6de28f5d5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc 0.42.0",
|
||||
"windows_i686_gnu 0.42.0",
|
||||
"windows_i686_msvc 0.42.0",
|
||||
"windows_x86_64_gnu 0.42.0",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc 0.42.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.36.1"
|
||||
@ -5736,6 +5599,12 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.36.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.36.1"
|
||||
@ -5748,6 +5617,12 @@ version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.36.1"
|
||||
@ -5760,6 +5635,12 @@ version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.36.1"
|
||||
@ -5772,6 +5653,12 @@ version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.36.1"
|
||||
@ -5784,6 +5671,18 @@ version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.36.1"
|
||||
@ -5796,6 +5695,12 @@ version = "0.37.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5"
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.10.1"
|
||||
|
38
Cargo.toml
38
Cargo.toml
@ -10,7 +10,7 @@ license = "MIT"
|
||||
name = "nu"
|
||||
repository = "https://github.com/nushell/nushell"
|
||||
rust-version = "1.60"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
@ -36,23 +36,23 @@ chrono = { version = "0.4.21", features = ["serde"] }
|
||||
crossterm = "0.24.0"
|
||||
ctrlc = "3.2.1"
|
||||
log = "0.4"
|
||||
miette = "5.1.0"
|
||||
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
|
||||
nu-ansi-term = "0.46.0"
|
||||
nu-cli = { path="./crates/nu-cli", version = "0.70.0" }
|
||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.70.0" }
|
||||
nu-command = { path="./crates/nu-command", version = "0.70.0" }
|
||||
nu-engine = { path="./crates/nu-engine", version = "0.70.0" }
|
||||
nu-json = { path="./crates/nu-json", version = "0.70.0" }
|
||||
nu-parser = { path="./crates/nu-parser", version = "0.70.0" }
|
||||
nu-path = { path="./crates/nu-path", version = "0.70.0" }
|
||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.70.0" }
|
||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.70.0" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.70.0" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.70.0" }
|
||||
nu-table = { path = "./crates/nu-table", version = "0.70.0" }
|
||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.70.0" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.70.0" }
|
||||
reedline = { version = "0.13.0", features = ["bashisms", "sqlite"]}
|
||||
nu-cli = { path="./crates/nu-cli", version = "0.71.0" }
|
||||
nu-color-config = { path = "./crates/nu-color-config", version = "0.71.0" }
|
||||
nu-command = { path="./crates/nu-command", version = "0.71.0" }
|
||||
nu-engine = { path="./crates/nu-engine", version = "0.71.0" }
|
||||
nu-json = { path="./crates/nu-json", version = "0.71.0" }
|
||||
nu-parser = { path="./crates/nu-parser", version = "0.71.0" }
|
||||
nu-path = { path="./crates/nu-path", version = "0.71.0" }
|
||||
nu-plugin = { path = "./crates/nu-plugin", optional = true, version = "0.71.0" }
|
||||
nu-pretty-hex = { path = "./crates/nu-pretty-hex", version = "0.71.0" }
|
||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.71.0" }
|
||||
nu-system = { path = "./crates/nu-system", version = "0.71.0" }
|
||||
nu-table = { path = "./crates/nu-table", version = "0.71.0" }
|
||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.71.0" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.71.0" }
|
||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||
|
||||
rayon = "1.5.1"
|
||||
is_executable = "1.0.1"
|
||||
@ -69,11 +69,11 @@ signal-hook = { version = "0.3.14", default-features = false }
|
||||
winres = "0.1"
|
||||
|
||||
[target.'cfg(target_family = "unix")'.dependencies]
|
||||
nix = "0.24"
|
||||
nix = { version = "0.25", default-features = false, features = ["signal", "process", "fs", "term"]}
|
||||
atty = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path="./crates/nu-test-support", version = "0.70.0" }
|
||||
nu-test-support = { path="./crates/nu-test-support", version = "0.71.0" }
|
||||
tempfile = "3.2.0"
|
||||
assert_cmd = "2.0.2"
|
||||
pretty_assertions = "1.0.0"
|
||||
|
@ -5,22 +5,22 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-cli"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path="../nu-test-support", version = "0.70.0" }
|
||||
nu-command = { path = "../nu-command", version = "0.70.0" }
|
||||
nu-test-support = { path="../nu-test-support", version = "0.71.0" }
|
||||
nu-command = { path = "../nu-command", version = "0.71.0" }
|
||||
rstest = {version = "0.15.0", default-features = false}
|
||||
|
||||
[dependencies]
|
||||
nu-engine = { path = "../nu-engine", version = "0.70.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.70.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.70.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.70.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.70.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.71.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.71.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.71.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.71.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.71.0" }
|
||||
nu-ansi-term = "0.46.0"
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.70.0" }
|
||||
reedline = { version = "0.13.0", features = ["bashisms", "sqlite"]}
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.71.0" }
|
||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||
|
||||
atty = "0.2.14"
|
||||
chrono = "0.4.21"
|
||||
@ -30,9 +30,8 @@ fuzzy-matcher = "0.3.7"
|
||||
is_executable = "1.0.1"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4"
|
||||
miette = { version = "5.1.0", features = ["fancy"] }
|
||||
miette = { version = "5.1.0", features = ["fancy-no-backtrace"] }
|
||||
percent-encoding = "2"
|
||||
strip-ansi-escapes = "0.1.1"
|
||||
sysinfo = "0.26.2"
|
||||
thiserror = "1.0.31"
|
||||
|
||||
|
@ -15,7 +15,6 @@ pub fn evaluate_commands(
|
||||
engine_state: &mut EngineState,
|
||||
stack: &mut Stack,
|
||||
input: PipelineData,
|
||||
is_perf_true: bool,
|
||||
table_mode: Option<Value>,
|
||||
) -> Result<Option<i64>> {
|
||||
// Translate environment variables from Strings to Values
|
||||
@ -68,9 +67,7 @@ pub fn evaluate_commands(
|
||||
}
|
||||
};
|
||||
|
||||
if is_perf_true {
|
||||
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
Ok(exit_code)
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ impl CommandCompletion {
|
||||
.matches_str(&x.to_string_lossy(), prefix)),
|
||||
Some(true)
|
||||
)
|
||||
&& is_executable::is_executable(&item.path())
|
||||
&& is_executable::is_executable(item.path())
|
||||
{
|
||||
if let Ok(name) = item.file_name().into_string() {
|
||||
executables.push(name);
|
||||
|
@ -23,7 +23,6 @@ pub fn read_plugin_file(
|
||||
stack: &mut Stack,
|
||||
plugin_file: Option<Spanned<String>>,
|
||||
storage_path: &str,
|
||||
is_perf_true: bool,
|
||||
) {
|
||||
// Reading signatures from signature file
|
||||
// The plugin.nu file stores the parsed signature collected from each registered plugin
|
||||
@ -31,7 +30,7 @@ pub fn read_plugin_file(
|
||||
|
||||
let plugin_path = engine_state.plugin_signatures.clone();
|
||||
if let Some(plugin_path) = plugin_path {
|
||||
let plugin_filename = plugin_path.to_string_lossy().to_owned();
|
||||
let plugin_filename = plugin_path.to_string_lossy();
|
||||
|
||||
if let Ok(contents) = std::fs::read(&plugin_path) {
|
||||
eval_source(
|
||||
@ -44,9 +43,7 @@ pub fn read_plugin_file(
|
||||
}
|
||||
}
|
||||
|
||||
if is_perf_true {
|
||||
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("read_plugin_file {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
|
||||
#[cfg(feature = "plugin")]
|
||||
@ -80,7 +77,7 @@ pub fn eval_config_contents(
|
||||
stack: &mut Stack,
|
||||
) {
|
||||
if config_path.exists() & config_path.is_file() {
|
||||
let config_filename = config_path.to_string_lossy().to_owned();
|
||||
let config_filename = config_path.to_string_lossy();
|
||||
|
||||
if let Ok(contents) = std::fs::read(&config_path) {
|
||||
eval_source(
|
||||
|
@ -19,7 +19,6 @@ pub fn evaluate_file(
|
||||
engine_state: &mut EngineState,
|
||||
stack: &mut Stack,
|
||||
input: PipelineData,
|
||||
is_perf_true: bool,
|
||||
) -> Result<()> {
|
||||
// Translate environment variables from Strings to Values
|
||||
if let Some(e) = convert_env_values(engine_state, stack) {
|
||||
@ -54,9 +53,7 @@ pub fn evaluate_file(
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
if is_perf_true {
|
||||
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("evaluate {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -372,7 +372,7 @@ impl DescriptionMenu {
|
||||
let description = self
|
||||
.get_value()
|
||||
.and_then(|suggestion| suggestion.description)
|
||||
.unwrap_or_else(|| "".to_string())
|
||||
.unwrap_or_default()
|
||||
.lines()
|
||||
.skip(self.skipped_rows)
|
||||
.take(self.working_details.description_rows)
|
||||
@ -610,7 +610,7 @@ impl Menu for DescriptionMenu {
|
||||
let description_rows = self
|
||||
.get_value()
|
||||
.and_then(|suggestion| suggestion.description)
|
||||
.unwrap_or_else(|| "".to_string())
|
||||
.unwrap_or_default()
|
||||
.lines()
|
||||
.count();
|
||||
|
||||
|
@ -17,6 +17,7 @@ pub struct NushellPrompt {
|
||||
default_vi_insert_prompt_indicator: Option<String>,
|
||||
default_vi_normal_prompt_indicator: Option<String>,
|
||||
default_multiline_indicator: Option<String>,
|
||||
render_right_prompt_on_last_line: bool,
|
||||
}
|
||||
|
||||
impl Default for NushellPrompt {
|
||||
@ -34,6 +35,7 @@ impl NushellPrompt {
|
||||
default_vi_insert_prompt_indicator: None,
|
||||
default_vi_normal_prompt_indicator: None,
|
||||
default_multiline_indicator: None,
|
||||
render_right_prompt_on_last_line: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,8 +43,13 @@ impl NushellPrompt {
|
||||
self.left_prompt_string = prompt_string;
|
||||
}
|
||||
|
||||
pub fn update_prompt_right(&mut self, prompt_string: Option<String>) {
|
||||
pub fn update_prompt_right(
|
||||
&mut self,
|
||||
prompt_string: Option<String>,
|
||||
render_right_prompt_on_last_line: bool,
|
||||
) {
|
||||
self.right_prompt_string = prompt_string;
|
||||
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
|
||||
}
|
||||
|
||||
pub fn update_prompt_indicator(&mut self, prompt_indicator_string: Option<String>) {
|
||||
@ -68,6 +75,7 @@ impl NushellPrompt {
|
||||
prompt_indicator_string: Option<String>,
|
||||
prompt_multiline_indicator_string: Option<String>,
|
||||
prompt_vi: (Option<String>, Option<String>),
|
||||
render_right_prompt_on_last_line: bool,
|
||||
) {
|
||||
let (prompt_vi_insert_string, prompt_vi_normal_string) = prompt_vi;
|
||||
|
||||
@ -78,6 +86,8 @@ impl NushellPrompt {
|
||||
|
||||
self.default_vi_insert_prompt_indicator = prompt_vi_insert_string;
|
||||
self.default_vi_normal_prompt_indicator = prompt_vi_normal_string;
|
||||
|
||||
self.render_right_prompt_on_last_line = render_right_prompt_on_last_line;
|
||||
}
|
||||
|
||||
fn default_wrapped_custom_string(&self, str: String) -> String {
|
||||
@ -162,4 +172,8 @@ impl Prompt for NushellPrompt {
|
||||
prefix, history_search.term
|
||||
))
|
||||
}
|
||||
|
||||
fn right_prompt_on_last_line(&self) -> bool {
|
||||
self.render_right_prompt_on_last_line
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ fn get_prompt_string(
|
||||
config: &Config,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
is_perf_true: bool,
|
||||
) -> Option<String> {
|
||||
stack
|
||||
.get_env_var(engine_state, prompt)
|
||||
@ -44,14 +43,12 @@ fn get_prompt_string(
|
||||
block,
|
||||
PipelineData::new(Span::new(0, 0)), // Don't try this at home, 0 span is ignored
|
||||
);
|
||||
if is_perf_true {
|
||||
info!(
|
||||
"get_prompt_string (block) {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
}
|
||||
info!(
|
||||
"get_prompt_string (block) {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
|
||||
match ret_val {
|
||||
Ok(ret_val) => Some(ret_val),
|
||||
@ -90,17 +87,10 @@ pub(crate) fn update_prompt<'prompt>(
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
nu_prompt: &'prompt mut NushellPrompt,
|
||||
is_perf_true: bool,
|
||||
) -> &'prompt dyn Prompt {
|
||||
let mut stack = stack.clone();
|
||||
|
||||
let left_prompt_string = get_prompt_string(
|
||||
PROMPT_COMMAND,
|
||||
config,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
is_perf_true,
|
||||
);
|
||||
let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, &mut stack);
|
||||
|
||||
// Now that we have the prompt string lets ansify it.
|
||||
// <133 A><prompt><133 B><command><133 C><command output>
|
||||
@ -116,45 +106,20 @@ pub(crate) fn update_prompt<'prompt>(
|
||||
left_prompt_string
|
||||
};
|
||||
|
||||
let right_prompt_string = get_prompt_string(
|
||||
PROMPT_COMMAND_RIGHT,
|
||||
config,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
is_perf_true,
|
||||
);
|
||||
let right_prompt_string =
|
||||
get_prompt_string(PROMPT_COMMAND_RIGHT, config, engine_state, &mut stack);
|
||||
|
||||
let prompt_indicator_string = get_prompt_string(
|
||||
PROMPT_INDICATOR,
|
||||
config,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
is_perf_true,
|
||||
);
|
||||
let prompt_indicator_string =
|
||||
get_prompt_string(PROMPT_INDICATOR, config, engine_state, &mut stack);
|
||||
|
||||
let prompt_multiline_string = get_prompt_string(
|
||||
PROMPT_MULTILINE_INDICATOR,
|
||||
config,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
is_perf_true,
|
||||
);
|
||||
let prompt_multiline_string =
|
||||
get_prompt_string(PROMPT_MULTILINE_INDICATOR, config, engine_state, &mut stack);
|
||||
|
||||
let prompt_vi_insert_string = get_prompt_string(
|
||||
PROMPT_INDICATOR_VI_INSERT,
|
||||
config,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
is_perf_true,
|
||||
);
|
||||
let prompt_vi_insert_string =
|
||||
get_prompt_string(PROMPT_INDICATOR_VI_INSERT, config, engine_state, &mut stack);
|
||||
|
||||
let prompt_vi_normal_string = get_prompt_string(
|
||||
PROMPT_INDICATOR_VI_NORMAL,
|
||||
config,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
is_perf_true,
|
||||
);
|
||||
let prompt_vi_normal_string =
|
||||
get_prompt_string(PROMPT_INDICATOR_VI_NORMAL, config, engine_state, &mut stack);
|
||||
|
||||
// apply the other indicators
|
||||
nu_prompt.update_all_prompt_strings(
|
||||
@ -163,12 +128,11 @@ pub(crate) fn update_prompt<'prompt>(
|
||||
prompt_indicator_string,
|
||||
prompt_multiline_string,
|
||||
(prompt_vi_insert_string, prompt_vi_normal_string),
|
||||
config.render_right_prompt_on_last_line,
|
||||
);
|
||||
|
||||
let ret_val = nu_prompt as &dyn Prompt;
|
||||
if is_perf_true {
|
||||
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("update_prompt {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
ret_val
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ pub(crate) fn add_menus(
|
||||
let res = eval_block(&engine_state, &mut temp_stack, &block, input, false, false)?;
|
||||
|
||||
if let PipelineData::Value(value, None) = res {
|
||||
for menu in create_menus(&value, config)? {
|
||||
for menu in create_menus(&value)? {
|
||||
line_editor =
|
||||
add_menu(line_editor, &menu, engine_state.clone(), stack, config)?;
|
||||
}
|
||||
@ -491,7 +491,7 @@ fn add_menu_keybindings(keybindings: &mut Keybindings) {
|
||||
KeyCode::Tab,
|
||||
ReedlineEvent::UntilFound(vec![
|
||||
ReedlineEvent::Menu("completion_menu".to_string()),
|
||||
ReedlineEvent::MenuNext,
|
||||
ReedlineEvent::Edit(vec![EditCommand::Complete]),
|
||||
]),
|
||||
);
|
||||
|
||||
@ -964,6 +964,7 @@ fn edit_from_record(
|
||||
let char = extract_char(value, config)?;
|
||||
EditCommand::MoveLeftBefore(char)
|
||||
}
|
||||
"complete" => EditCommand::Complete,
|
||||
e => {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
"reedline EditCommand".to_string(),
|
||||
|
@ -24,7 +24,6 @@ use std::{
|
||||
sync::atomic::Ordering,
|
||||
time::Instant,
|
||||
};
|
||||
use strip_ansi_escapes::strip;
|
||||
use sysinfo::SystemExt;
|
||||
|
||||
// According to Daniel Imms @Tyriar, we need to do these this way:
|
||||
@ -41,7 +40,6 @@ pub fn evaluate_repl(
|
||||
engine_state: &mut EngineState,
|
||||
stack: &mut Stack,
|
||||
nushell_path: &str,
|
||||
is_perf_true: bool,
|
||||
prerun_command: Option<Spanned<String>>,
|
||||
) -> Result<()> {
|
||||
use reedline::{FileBackedHistory, Reedline, Signal};
|
||||
@ -51,7 +49,7 @@ pub fn evaluate_repl(
|
||||
if !atty::is(atty::Stream::Stdin) {
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::NotFound,
|
||||
"Nushell launched as interactive REPL but STDIN is not a TTY, either launch in a valid terminal or provide arguments to invoke a script!",
|
||||
"Nushell launched as a REPL, but STDIN is not a TTY; either launch in a valid terminal or provide arguments to invoke a script!",
|
||||
))
|
||||
.into_diagnostic();
|
||||
}
|
||||
@ -60,14 +58,12 @@ pub fn evaluate_repl(
|
||||
|
||||
let mut nu_prompt = NushellPrompt::new();
|
||||
|
||||
if is_perf_true {
|
||||
info!(
|
||||
"translate environment vars {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
}
|
||||
info!(
|
||||
"translate environment vars {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
|
||||
// Translate environment variables from Strings to Values
|
||||
if let Some(e) = convert_env_values(engine_state, stack) {
|
||||
@ -92,18 +88,14 @@ pub fn evaluate_repl(
|
||||
},
|
||||
);
|
||||
|
||||
if is_perf_true {
|
||||
info!(
|
||||
"load config initially {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
}
|
||||
info!(
|
||||
"load config initially {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
|
||||
if is_perf_true {
|
||||
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("setup reedline {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
let mut line_editor = Reedline::create();
|
||||
|
||||
@ -121,9 +113,7 @@ pub fn evaluate_repl(
|
||||
engine_state.config.history_file_format,
|
||||
);
|
||||
if let Some(history_path) = history_path.as_deref() {
|
||||
if is_perf_true {
|
||||
info!("setup history {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("setup history {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
let history: Box<dyn reedline::History> = match engine_state.config.history_file_format {
|
||||
HistoryFileFormat::PlainText => Box::new(
|
||||
@ -149,15 +139,7 @@ pub fn evaluate_repl(
|
||||
if use_ansi {
|
||||
println!("{}", banner);
|
||||
} else {
|
||||
let stripped_string = {
|
||||
if let Ok(bytes) = strip(&banner) {
|
||||
String::from_utf8_lossy(&bytes).to_string()
|
||||
} else {
|
||||
banner
|
||||
}
|
||||
};
|
||||
|
||||
println!("{}", stripped_string);
|
||||
println!("{}", nu_utils::strip_ansi_string_likely(banner));
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,14 +155,12 @@ pub fn evaluate_repl(
|
||||
}
|
||||
|
||||
loop {
|
||||
if is_perf_true {
|
||||
info!(
|
||||
"load config each loop {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
}
|
||||
info!(
|
||||
"load config each loop {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
|
||||
let cwd = get_guaranteed_cwd(engine_state, stack);
|
||||
|
||||
@ -201,15 +181,11 @@ pub fn evaluate_repl(
|
||||
|
||||
let config = engine_state.get_config();
|
||||
|
||||
if is_perf_true {
|
||||
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("setup colors {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
let color_hm = get_color_config(config);
|
||||
|
||||
if is_perf_true {
|
||||
info!("update reedline {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("update reedline {}:{}:{}", file!(), line!(), column!());
|
||||
let engine_reference = std::sync::Arc::new(engine_state.clone());
|
||||
line_editor = line_editor
|
||||
.with_highlighter(Box::new(NuHighlighter {
|
||||
@ -266,18 +242,14 @@ pub fn evaluate_repl(
|
||||
};
|
||||
|
||||
if config.sync_history_on_enter {
|
||||
if is_perf_true {
|
||||
info!("sync history {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("sync history {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
if let Err(e) = line_editor.sync_history() {
|
||||
warn!("Failed to sync history: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
if is_perf_true {
|
||||
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("setup keybindings {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
// Changing the line editor based on the found keybindings
|
||||
line_editor = match create_keybindings(config) {
|
||||
@ -301,14 +273,12 @@ pub fn evaluate_repl(
|
||||
}
|
||||
};
|
||||
|
||||
if is_perf_true {
|
||||
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
|
||||
}
|
||||
info!("prompt_update {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
// Right before we start our prompt and take input from the user,
|
||||
// fire the "pre_prompt" hook
|
||||
if let Some(hook) = config.hooks.pre_prompt.clone() {
|
||||
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
|
||||
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
|
||||
report_error_new(engine_state, &err);
|
||||
}
|
||||
}
|
||||
@ -323,19 +293,16 @@ pub fn evaluate_repl(
|
||||
}
|
||||
|
||||
let config = engine_state.get_config();
|
||||
let prompt =
|
||||
prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt, is_perf_true);
|
||||
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
|
||||
|
||||
entry_num += 1;
|
||||
|
||||
if is_perf_true {
|
||||
info!(
|
||||
"finished setup, starting repl {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
}
|
||||
info!(
|
||||
"finished setup, starting repl {}:{}:{}",
|
||||
file!(),
|
||||
line!(),
|
||||
column!()
|
||||
);
|
||||
|
||||
let input = line_editor.read_line(prompt);
|
||||
let shell_integration = config.shell_integration;
|
||||
@ -367,7 +334,7 @@ pub fn evaluate_repl(
|
||||
// Right before we start running the code the user gave us,
|
||||
// fire the "pre_execution" hook
|
||||
if let Some(hook) = config.hooks.pre_execution.clone() {
|
||||
if let Err(err) = eval_hook(engine_state, stack, vec![], &hook) {
|
||||
if let Err(err) = eval_hook(engine_state, stack, None, vec![], &hook) {
|
||||
report_error_new(engine_state, &err);
|
||||
}
|
||||
}
|
||||
@ -719,6 +686,7 @@ pub fn eval_env_change_hook(
|
||||
eval_hook(
|
||||
engine_state,
|
||||
stack,
|
||||
None,
|
||||
vec![("$before".into(), before), ("$after".into(), after.clone())],
|
||||
hook_value,
|
||||
)?;
|
||||
@ -744,15 +712,17 @@ pub fn eval_env_change_hook(
|
||||
pub fn eval_hook(
|
||||
engine_state: &mut EngineState,
|
||||
stack: &mut Stack,
|
||||
input: Option<PipelineData>,
|
||||
arguments: Vec<(String, Value)>,
|
||||
value: &Value,
|
||||
) -> Result<(), ShellError> {
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let value_span = value.span()?;
|
||||
|
||||
let condition_path = PathMember::String {
|
||||
val: "condition".to_string(),
|
||||
span: value_span,
|
||||
};
|
||||
let mut output = PipelineData::new(Span::new(0, 0));
|
||||
|
||||
let code_path = PathMember::String {
|
||||
val: "code".to_string(),
|
||||
@ -762,7 +732,7 @@ pub fn eval_hook(
|
||||
match value {
|
||||
Value::List { vals, .. } => {
|
||||
for val in vals {
|
||||
eval_hook(engine_state, stack, arguments.clone(), val)?
|
||||
eval_hook(engine_state, stack, None, arguments.clone(), val)?;
|
||||
}
|
||||
}
|
||||
Value::Record { .. } => {
|
||||
@ -778,6 +748,7 @@ pub fn eval_hook(
|
||||
engine_state,
|
||||
stack,
|
||||
block_id,
|
||||
None,
|
||||
arguments.clone(),
|
||||
block_span,
|
||||
) {
|
||||
@ -857,7 +828,9 @@ pub fn eval_hook(
|
||||
.collect();
|
||||
|
||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||
Ok(_) => {}
|
||||
Ok(pipeline_data) => {
|
||||
output = pipeline_data;
|
||||
}
|
||||
Err(err) => {
|
||||
report_error_new(engine_state, &err);
|
||||
}
|
||||
@ -872,7 +845,14 @@ pub fn eval_hook(
|
||||
span: block_span,
|
||||
..
|
||||
} => {
|
||||
run_hook_block(engine_state, stack, block_id, arguments, block_span)?;
|
||||
run_hook_block(
|
||||
engine_state,
|
||||
stack,
|
||||
block_id,
|
||||
input,
|
||||
arguments,
|
||||
block_span,
|
||||
)?;
|
||||
}
|
||||
other => {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
@ -889,7 +869,17 @@ pub fn eval_hook(
|
||||
span: block_span,
|
||||
..
|
||||
} => {
|
||||
run_hook_block(engine_state, stack, *block_id, arguments, *block_span)?;
|
||||
output = PipelineData::Value(
|
||||
run_hook_block(
|
||||
engine_state,
|
||||
stack,
|
||||
*block_id,
|
||||
input,
|
||||
arguments,
|
||||
*block_span,
|
||||
)?,
|
||||
None,
|
||||
);
|
||||
}
|
||||
other => {
|
||||
return Err(ShellError::UnsupportedConfigValue(
|
||||
@ -903,19 +893,20 @@ pub fn eval_hook(
|
||||
let cwd = get_guaranteed_cwd(engine_state, stack);
|
||||
engine_state.merge_env(stack, cwd)?;
|
||||
|
||||
Ok(())
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
pub fn run_hook_block(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
block_id: BlockId,
|
||||
optional_input: Option<PipelineData>,
|
||||
arguments: Vec<(String, Value)>,
|
||||
span: Span,
|
||||
) -> Result<Value, ShellError> {
|
||||
let block = engine_state.get_block(block_id);
|
||||
|
||||
let input = PipelineData::new(span);
|
||||
let input = optional_input.unwrap_or_else(|| PipelineData::new(span));
|
||||
|
||||
let mut callee_stack = stack.gather_captures(&block.captures);
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
use log::trace;
|
||||
use nu_ansi_term::Style;
|
||||
use nu_color_config::get_shape_color;
|
||||
use nu_color_config::{get_matching_brackets_style, get_shape_color};
|
||||
use nu_parser::{flatten_block, parse, FlatShape};
|
||||
use nu_protocol::ast::{Argument, Block, Expr, Expression};
|
||||
use nu_protocol::engine::{EngineState, StateWorkingSet};
|
||||
use nu_protocol::Config;
|
||||
use nu_protocol::{Config, Span};
|
||||
use reedline::{Highlighter, StyledText};
|
||||
|
||||
pub struct NuHighlighter {
|
||||
@ -15,10 +16,12 @@ impl Highlighter for NuHighlighter {
|
||||
fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
|
||||
trace!("highlighting: {}", line);
|
||||
|
||||
let (shapes, global_span_offset) = {
|
||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||
let mut working_set = StateWorkingSet::new(&self.engine_state);
|
||||
let block = {
|
||||
let (block, _) = parse(&mut working_set, None, line.as_bytes(), false, &[]);
|
||||
|
||||
block
|
||||
};
|
||||
let (shapes, global_span_offset) = {
|
||||
let shapes = flatten_block(&working_set, &block);
|
||||
(shapes, self.engine_state.next_span_start())
|
||||
};
|
||||
@ -26,6 +29,15 @@ impl Highlighter for NuHighlighter {
|
||||
let mut output = StyledText::default();
|
||||
let mut last_seen_span = global_span_offset;
|
||||
|
||||
let global_cursor_offset = _cursor + global_span_offset;
|
||||
let matching_brackets_pos = find_matching_brackets(
|
||||
line,
|
||||
&working_set,
|
||||
&block,
|
||||
global_span_offset,
|
||||
global_cursor_offset,
|
||||
);
|
||||
|
||||
for shape in &shapes {
|
||||
if shape.0.end <= last_seen_span
|
||||
|| last_seen_span < global_span_offset
|
||||
@ -44,166 +56,71 @@ impl Highlighter for NuHighlighter {
|
||||
let next_token = line
|
||||
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
||||
.to_string();
|
||||
|
||||
macro_rules! add_colored_token_with_bracket_highlight {
|
||||
($shape:expr, $span:expr, $text:expr) => {{
|
||||
let spans = split_span_by_highlight_positions(
|
||||
line,
|
||||
&$span,
|
||||
&matching_brackets_pos,
|
||||
global_span_offset,
|
||||
);
|
||||
spans.iter().for_each(|(part, highlight)| {
|
||||
let start = part.start - $span.start;
|
||||
let end = part.end - $span.start;
|
||||
let text = (&next_token[start..end]).to_string();
|
||||
let mut style = get_shape_color($shape.to_string(), &self.config);
|
||||
if *highlight {
|
||||
style = get_matching_brackets_style(style, &self.config);
|
||||
}
|
||||
output.push((style, text));
|
||||
});
|
||||
}};
|
||||
}
|
||||
|
||||
macro_rules! add_colored_token {
|
||||
($shape:expr, $text:expr) => {
|
||||
output.push((get_shape_color($shape.to_string(), &self.config), $text))
|
||||
};
|
||||
}
|
||||
|
||||
match shape.1 {
|
||||
FlatShape::Garbage => output.push((
|
||||
// nushell Garbage
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::Nothing => output.push((
|
||||
// nushell Nothing
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::Binary => {
|
||||
// nushell ?
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::Bool => {
|
||||
// nushell ?
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::Int => {
|
||||
// nushell Int
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::Float => {
|
||||
// nushell Decimal
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::Range => output.push((
|
||||
// nushell DotDot ?
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::InternalCall => output.push((
|
||||
// nushell InternalCommand
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::External => {
|
||||
// nushell ExternalCommand
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::ExternalArg => {
|
||||
// nushell ExternalWord
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::Literal => {
|
||||
// nushell ?
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::Operator => output.push((
|
||||
// nushell Operator
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::Signature => output.push((
|
||||
// nushell ?
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::String => {
|
||||
// nushell String
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::StringInterpolation => {
|
||||
// nushell ???
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::DateTime => {
|
||||
// nushell ???
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::Garbage => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Nothing => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Binary => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Bool => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Int => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Float => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Range => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::InternalCall => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::External => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::ExternalArg => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Literal => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Operator => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Signature => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::String => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::StringInterpolation => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::DateTime => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::List => {
|
||||
// nushell ???
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||
}
|
||||
FlatShape::Table => {
|
||||
// nushell ???
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||
}
|
||||
FlatShape::Record => {
|
||||
// nushell ???
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||
}
|
||||
|
||||
FlatShape::Block => {
|
||||
// nushell ???
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
||||
}
|
||||
FlatShape::Filepath => output.push((
|
||||
// nushell Path
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::Directory => output.push((
|
||||
// nushell Directory
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::GlobPattern => output.push((
|
||||
// nushell GlobPattern
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::Variable => output.push((
|
||||
// nushell Variable
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
FlatShape::Flag => {
|
||||
// nushell Flag
|
||||
output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
))
|
||||
}
|
||||
FlatShape::Custom(..) => output.push((
|
||||
get_shape_color(shape.1.to_string(), &self.config),
|
||||
next_token,
|
||||
)),
|
||||
|
||||
FlatShape::Filepath => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Directory => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::GlobPattern => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Variable => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Flag => add_colored_token!(shape.1, next_token),
|
||||
FlatShape::Custom(..) => add_colored_token!(shape.1, next_token),
|
||||
}
|
||||
last_seen_span = shape.0.end;
|
||||
}
|
||||
@ -216,3 +133,297 @@ impl Highlighter for NuHighlighter {
|
||||
output
|
||||
}
|
||||
}
|
||||
|
||||
fn split_span_by_highlight_positions(
|
||||
line: &str,
|
||||
span: &Span,
|
||||
highlight_positions: &Vec<usize>,
|
||||
global_span_offset: usize,
|
||||
) -> Vec<(Span, bool)> {
|
||||
let mut start = span.start;
|
||||
let mut result: Vec<(Span, bool)> = Vec::new();
|
||||
for pos in highlight_positions {
|
||||
if start <= *pos && pos < &span.end {
|
||||
if start < *pos {
|
||||
result.push((Span { start, end: *pos }, false));
|
||||
}
|
||||
let span_str = &line[pos - global_span_offset..span.end - global_span_offset];
|
||||
let end = span_str
|
||||
.chars()
|
||||
.next()
|
||||
.map(|c| pos + get_char_length(c))
|
||||
.unwrap_or(pos + 1);
|
||||
result.push((Span { start: *pos, end }, true));
|
||||
start = end;
|
||||
}
|
||||
}
|
||||
if start < span.end {
|
||||
result.push((
|
||||
Span {
|
||||
start,
|
||||
end: span.end,
|
||||
},
|
||||
false,
|
||||
));
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
fn find_matching_brackets(
|
||||
line: &str,
|
||||
working_set: &StateWorkingSet,
|
||||
block: &Block,
|
||||
global_span_offset: usize,
|
||||
global_cursor_offset: usize,
|
||||
) -> Vec<usize> {
|
||||
const BRACKETS: &str = "{}[]()";
|
||||
|
||||
// calculate first bracket position
|
||||
let global_end_offset = line.len() + global_span_offset;
|
||||
let global_bracket_pos =
|
||||
if global_cursor_offset == global_end_offset && global_end_offset > global_span_offset {
|
||||
// cursor is at the end of a non-empty string -- find block end at the previous position
|
||||
if let Some(last_char) = line.chars().last() {
|
||||
global_cursor_offset - get_char_length(last_char)
|
||||
} else {
|
||||
global_cursor_offset
|
||||
}
|
||||
} else {
|
||||
// cursor is in the middle of a string -- find block end at the current position
|
||||
global_cursor_offset
|
||||
};
|
||||
|
||||
// check that position contains bracket
|
||||
let match_idx = global_bracket_pos - global_span_offset;
|
||||
if match_idx >= line.len()
|
||||
|| !BRACKETS.contains(get_char_at_index(line, match_idx).unwrap_or_default())
|
||||
{
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
// find matching bracket by finding matching block end
|
||||
let matching_block_end = find_matching_block_end_in_block(
|
||||
line,
|
||||
working_set,
|
||||
block,
|
||||
global_span_offset,
|
||||
global_bracket_pos,
|
||||
);
|
||||
if let Some(pos) = matching_block_end {
|
||||
let matching_idx = pos - global_span_offset;
|
||||
if BRACKETS.contains(get_char_at_index(line, matching_idx).unwrap_or_default()) {
|
||||
return if global_bracket_pos < pos {
|
||||
vec![global_bracket_pos, pos]
|
||||
} else {
|
||||
vec![pos, global_bracket_pos]
|
||||
};
|
||||
}
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
fn find_matching_block_end_in_block(
|
||||
line: &str,
|
||||
working_set: &StateWorkingSet,
|
||||
block: &Block,
|
||||
global_span_offset: usize,
|
||||
global_cursor_offset: usize,
|
||||
) -> Option<usize> {
|
||||
for p in &block.pipelines {
|
||||
for e in &p.expressions {
|
||||
if e.span.contains(global_cursor_offset) {
|
||||
if let Some(pos) = find_matching_block_end_in_expr(
|
||||
line,
|
||||
working_set,
|
||||
e,
|
||||
global_span_offset,
|
||||
global_cursor_offset,
|
||||
) {
|
||||
return Some(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn find_matching_block_end_in_expr(
|
||||
line: &str,
|
||||
working_set: &StateWorkingSet,
|
||||
expression: &Expression,
|
||||
global_span_offset: usize,
|
||||
global_cursor_offset: usize,
|
||||
) -> Option<usize> {
|
||||
macro_rules! find_in_expr_or_continue {
|
||||
($inner_expr:ident) => {
|
||||
if let Some(pos) = find_matching_block_end_in_expr(
|
||||
line,
|
||||
working_set,
|
||||
$inner_expr,
|
||||
global_span_offset,
|
||||
global_cursor_offset,
|
||||
) {
|
||||
return Some(pos);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if expression.span.contains(global_cursor_offset) && expression.span.start >= global_span_offset
|
||||
{
|
||||
let expr_first = expression.span.start;
|
||||
let span_str = &line
|
||||
[expression.span.start - global_span_offset..expression.span.end - global_span_offset];
|
||||
let expr_last = span_str
|
||||
.chars()
|
||||
.last()
|
||||
.map(|c| expression.span.end - get_char_length(c))
|
||||
.unwrap_or(expression.span.start);
|
||||
|
||||
return match &expression.expr {
|
||||
Expr::Bool(_) => None,
|
||||
Expr::Int(_) => None,
|
||||
Expr::Float(_) => None,
|
||||
Expr::Binary(_) => None,
|
||||
Expr::Range(..) => None,
|
||||
Expr::Var(_) => None,
|
||||
Expr::VarDecl(_) => None,
|
||||
Expr::ExternalCall(..) => None,
|
||||
Expr::Operator(_) => None,
|
||||
Expr::UnaryNot(_) => None,
|
||||
Expr::Keyword(..) => None,
|
||||
Expr::ValueWithUnit(..) => None,
|
||||
Expr::DateTime(_) => None,
|
||||
Expr::Filepath(_) => None,
|
||||
Expr::Directory(_) => None,
|
||||
Expr::GlobPattern(_) => None,
|
||||
Expr::String(_) => None,
|
||||
Expr::CellPath(_) => None,
|
||||
Expr::ImportPattern(_) => None,
|
||||
Expr::Overlay(_) => None,
|
||||
Expr::Signature(_) => None,
|
||||
Expr::Nothing => None,
|
||||
Expr::Garbage => None,
|
||||
|
||||
Expr::Table(hdr, rows) => {
|
||||
if expr_last == global_cursor_offset {
|
||||
// cursor is at table end
|
||||
Some(expr_first)
|
||||
} else if expr_first == global_cursor_offset {
|
||||
// cursor is at table start
|
||||
Some(expr_last)
|
||||
} else {
|
||||
// cursor is inside table
|
||||
for inner_expr in hdr {
|
||||
find_in_expr_or_continue!(inner_expr);
|
||||
}
|
||||
for row in rows {
|
||||
for inner_expr in row {
|
||||
find_in_expr_or_continue!(inner_expr);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Record(exprs) => {
|
||||
if expr_last == global_cursor_offset {
|
||||
// cursor is at record end
|
||||
Some(expr_first)
|
||||
} else if expr_first == global_cursor_offset {
|
||||
// cursor is at record start
|
||||
Some(expr_last)
|
||||
} else {
|
||||
// cursor is inside record
|
||||
for (k, v) in exprs {
|
||||
find_in_expr_or_continue!(k);
|
||||
find_in_expr_or_continue!(v);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Call(call) => {
|
||||
for arg in &call.arguments {
|
||||
let opt_expr = match arg {
|
||||
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
|
||||
Argument::Positional(inner_expr) => Some(inner_expr),
|
||||
};
|
||||
|
||||
if let Some(inner_expr) = opt_expr {
|
||||
find_in_expr_or_continue!(inner_expr);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
Expr::FullCellPath(b) => find_matching_block_end_in_expr(
|
||||
line,
|
||||
working_set,
|
||||
&b.head,
|
||||
global_span_offset,
|
||||
global_cursor_offset,
|
||||
),
|
||||
|
||||
Expr::BinaryOp(lhs, op, rhs) => {
|
||||
find_in_expr_or_continue!(lhs);
|
||||
find_in_expr_or_continue!(op);
|
||||
find_in_expr_or_continue!(rhs);
|
||||
None
|
||||
}
|
||||
|
||||
Expr::Block(block_id)
|
||||
| Expr::RowCondition(block_id)
|
||||
| Expr::Subexpression(block_id) => {
|
||||
if expr_last == global_cursor_offset {
|
||||
// cursor is at block end
|
||||
Some(expr_first)
|
||||
} else if expr_first == global_cursor_offset {
|
||||
// cursor is at block start
|
||||
Some(expr_last)
|
||||
} else {
|
||||
// cursor is inside block
|
||||
let nested_block = working_set.get_block(*block_id);
|
||||
find_matching_block_end_in_block(
|
||||
line,
|
||||
working_set,
|
||||
nested_block,
|
||||
global_span_offset,
|
||||
global_cursor_offset,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Expr::StringInterpolation(inner_expr) => {
|
||||
for inner_expr in inner_expr {
|
||||
find_in_expr_or_continue!(inner_expr);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
Expr::List(inner_expr) => {
|
||||
if expr_last == global_cursor_offset {
|
||||
// cursor is at list end
|
||||
Some(expr_first)
|
||||
} else if expr_first == global_cursor_offset {
|
||||
// cursor is at list start
|
||||
Some(expr_last)
|
||||
} else {
|
||||
// cursor is inside list
|
||||
for inner_expr in inner_expr {
|
||||
find_in_expr_or_continue!(inner_expr);
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn get_char_at_index(s: &str, index: usize) -> Option<char> {
|
||||
s[index..].chars().next()
|
||||
}
|
||||
|
||||
fn get_char_length(c: char) -> usize {
|
||||
c.to_string().len()
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
use log::trace;
|
||||
use crate::repl::eval_hook;
|
||||
use nu_engine::eval_block;
|
||||
use nu_parser::{escape_quote_string, lex, parse, unescape_unquote_string, Token, TokenContents};
|
||||
use nu_protocol::engine::StateWorkingSet;
|
||||
use nu_protocol::CliError;
|
||||
use nu_protocol::{
|
||||
engine::{EngineState, Stack},
|
||||
PipelineData, ShellError, Span, Value,
|
||||
print_if_stream, PipelineData, ShellError, Span, Value,
|
||||
};
|
||||
#[cfg(windows)]
|
||||
use nu_utils::enable_vt_processing;
|
||||
@ -204,8 +204,6 @@ pub fn eval_source(
|
||||
fname: &str,
|
||||
input: PipelineData,
|
||||
) -> bool {
|
||||
trace!("eval_source");
|
||||
|
||||
let (block, delta) = {
|
||||
let mut working_set = StateWorkingSet::new(engine_state);
|
||||
let (output, err) = parse(
|
||||
@ -232,7 +230,30 @@ pub fn eval_source(
|
||||
|
||||
match eval_block(engine_state, stack, &block, input, false, false) {
|
||||
Ok(pipeline_data) => {
|
||||
match pipeline_data.print(engine_state, stack, false, false) {
|
||||
let config = engine_state.get_config();
|
||||
let result;
|
||||
if let PipelineData::ExternalStream {
|
||||
stdout: stream,
|
||||
stderr: stderr_stream,
|
||||
exit_code,
|
||||
..
|
||||
} = pipeline_data
|
||||
{
|
||||
result = print_if_stream(stream, stderr_stream, false, exit_code);
|
||||
} else if let Some(hook) = config.hooks.display_output.clone() {
|
||||
match eval_hook(engine_state, stack, Some(pipeline_data), vec![], &hook) {
|
||||
Err(err) => {
|
||||
result = Err(err);
|
||||
}
|
||||
Ok(val) => {
|
||||
result = val.print(engine_state, stack, false, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = pipeline_data.print(engine_state, stack, false, false);
|
||||
}
|
||||
|
||||
match result {
|
||||
Err(err) => {
|
||||
let working_set = StateWorkingSet::new(engine_state);
|
||||
|
||||
|
@ -5,11 +5,11 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-color-config"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
|
||||
[dependencies]
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.70.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.71.0" }
|
||||
nu-ansi-term = "0.46.0"
|
||||
nu-json = { path = "../nu-json", version = "0.70.0" }
|
||||
nu-table = { path = "../nu-table", version = "0.70.0" }
|
||||
nu-json = { path = "../nu-json", version = "0.71.0" }
|
||||
nu-table = { path = "../nu-table", version = "0.71.0" }
|
||||
serde = { version="1.0.123", features=["derive"] }
|
||||
|
@ -1,7 +1,9 @@
|
||||
mod color_config;
|
||||
mod matching_brackets_style;
|
||||
mod nu_style;
|
||||
mod shape_color;
|
||||
|
||||
pub use color_config::*;
|
||||
pub use matching_brackets_style::*;
|
||||
pub use nu_style::*;
|
||||
pub use shape_color::*;
|
||||
|
30
crates/nu-color-config/src/matching_brackets_style.rs
Normal file
30
crates/nu-color-config/src/matching_brackets_style.rs
Normal file
@ -0,0 +1,30 @@
|
||||
use crate::color_config::lookup_ansi_color_style;
|
||||
use nu_ansi_term::Style;
|
||||
use nu_protocol::Config;
|
||||
|
||||
pub fn get_matching_brackets_style(default_style: Style, conf: &Config) -> Style {
|
||||
const MATCHING_BRACKETS_CONFIG_KEY: &str = "shape_matching_brackets";
|
||||
|
||||
match conf.color_config.get(MATCHING_BRACKETS_CONFIG_KEY) {
|
||||
Some(int_color) => match int_color.as_string() {
|
||||
Ok(int_color) => merge_styles(default_style, lookup_ansi_color_style(&int_color)),
|
||||
Err(_) => default_style,
|
||||
},
|
||||
None => default_style,
|
||||
}
|
||||
}
|
||||
|
||||
fn merge_styles(base: Style, extra: Style) -> Style {
|
||||
Style {
|
||||
foreground: extra.foreground.or(base.foreground),
|
||||
background: extra.background.or(base.background),
|
||||
is_bold: extra.is_bold || base.is_bold,
|
||||
is_dimmed: extra.is_dimmed || base.is_dimmed,
|
||||
is_italic: extra.is_italic || base.is_italic,
|
||||
is_underline: extra.is_underline || base.is_underline,
|
||||
is_blink: extra.is_blink || base.is_blink,
|
||||
is_reverse: extra.is_reverse || base.is_reverse,
|
||||
is_hidden: extra.is_hidden || base.is_hidden,
|
||||
is_strikethrough: extra.is_strikethrough || base.is_strikethrough,
|
||||
}
|
||||
}
|
@ -5,25 +5,24 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
name = "nu-command"
|
||||
version = "0.70.0"
|
||||
version = "0.71.0"
|
||||
build = "build.rs"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.70.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.70.0" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.70.0" }
|
||||
nu-json = { path = "../nu-json", version = "0.70.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.70.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.70.0" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.70.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.70.0" }
|
||||
nu-system = { path = "../nu-system", version = "0.70.0" }
|
||||
nu-table = { path = "../nu-table", version = "0.70.0" }
|
||||
nu-term-grid = { path = "../nu-term-grid", version = "0.70.0" }
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.70.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.70.0" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.71.0" }
|
||||
nu-engine = { path = "../nu-engine", version = "0.71.0" }
|
||||
nu-glob = { path = "../nu-glob", version = "0.71.0" }
|
||||
nu-json = { path = "../nu-json", version = "0.71.0" }
|
||||
nu-parser = { path = "../nu-parser", version = "0.71.0" }
|
||||
nu-path = { path = "../nu-path", version = "0.71.0" }
|
||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.71.0" }
|
||||
nu-protocol = { path = "../nu-protocol", version = "0.71.0" }
|
||||
nu-system = { path = "../nu-system", version = "0.71.0" }
|
||||
nu-table = { path = "../nu-table", version = "0.71.0" }
|
||||
nu-term-grid = { path = "../nu-term-grid", version = "0.71.0" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.71.0" }
|
||||
nu-ansi-term = "0.46.0"
|
||||
num-format = { version = "0.4.3" }
|
||||
|
||||
@ -55,7 +54,7 @@ is-root = "0.1.2"
|
||||
itertools = "0.10.0"
|
||||
lazy_static = "1.4.0"
|
||||
log = "0.4.14"
|
||||
lscolors = { version = "0.12.0", features = ["crossterm"]}
|
||||
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }
|
||||
md5 = { package = "md-5", version = "0.10.0" }
|
||||
meval = "0.2.0"
|
||||
mime = "0.3.16"
|
||||
@ -78,7 +77,6 @@ serde_yaml = "0.9.4"
|
||||
sha2 = "0.10.0"
|
||||
# Disable default features b/c the default features build Git (very slow to compile)
|
||||
shadow-rs = { version = "0.16.1", default-features = false }
|
||||
strip-ansi-escapes = "0.1.1"
|
||||
sysinfo = "0.26.2"
|
||||
terminal_size = "0.2.1"
|
||||
thiserror = "1.0.31"
|
||||
@ -88,8 +86,8 @@ unicode-segmentation = "1.8.0"
|
||||
url = "2.2.1"
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
which = { version = "4.3.0", optional = true }
|
||||
reedline = { version = "0.13.0", features = ["bashisms", "sqlite"]}
|
||||
wax = { version = "0.5.0", features = ["diagnostics"] }
|
||||
reedline = { version = "0.14.0", features = ["bashisms", "sqlite"]}
|
||||
wax = { version = "0.5.0" }
|
||||
rusqlite = { version = "0.28.0", features = ["bundled"], optional = true }
|
||||
sqlparser = { version = "0.23.0", features = ["serde"], optional = true }
|
||||
|
||||
@ -99,6 +97,7 @@ winreg = "0.10.1"
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
umask = "2.0.0"
|
||||
users = "0.11.0"
|
||||
libc = "0.2"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
||||
version = "2.1.3"
|
||||
@ -136,9 +135,8 @@ features = [
|
||||
]
|
||||
|
||||
[target.'cfg(windows)'.dependencies.windows]
|
||||
version = "0.37.0"
|
||||
version = "0.42.0"
|
||||
features = [
|
||||
"alloc",
|
||||
"Win32_Foundation",
|
||||
"Win32_Storage_FileSystem",
|
||||
"Win32_System_SystemServices",
|
||||
@ -155,6 +153,8 @@ database = ["sqlparser", "rusqlite"]
|
||||
shadow-rs = { version = "0.16.1", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
nu-test-support = { path = "../nu-test-support", version = "0.71.0" }
|
||||
|
||||
hamcrest2 = "0.3.0"
|
||||
dirs-next = "2.0.0"
|
||||
proptest = "1.0.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{operate, BytesArgument};
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
@ -10,12 +10,12 @@ struct Arguments {
|
||||
added_data: Vec<u8>,
|
||||
index: Option<usize>,
|
||||
end: bool,
|
||||
column_paths: Option<Vec<CellPath>>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl BytesArgument for Arguments {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.column_paths.take()
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
@ -62,12 +62,8 @@ impl Command for BytesAdd {
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let added_data: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let column_paths = if column_paths.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(column_paths)
|
||||
};
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let index: Option<usize> = call.get_flag(engine_state, stack, "index")?;
|
||||
let end = call.has_flag("end");
|
||||
|
||||
@ -75,7 +71,7 @@ impl Command for BytesAdd {
|
||||
added_data,
|
||||
index,
|
||||
end,
|
||||
column_paths,
|
||||
cell_paths,
|
||||
};
|
||||
operate(add, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
@ -118,7 +114,25 @@ impl Command for BytesAdd {
|
||||
}
|
||||
}
|
||||
|
||||
fn add(input: &[u8], args: &Arguments, span: Span) -> Value {
|
||||
fn add(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => add_impl(val, args, *val_span),
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn add_impl(input: &[u8], args: &Arguments, span: Span) -> Value {
|
||||
match args.index {
|
||||
None => {
|
||||
if args.end {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{operate, BytesArgument};
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
@ -15,12 +15,12 @@ struct Arguments {
|
||||
start: isize,
|
||||
end: isize,
|
||||
arg_span: Span,
|
||||
column_paths: Option<Vec<CellPath>>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl BytesArgument for Arguments {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.column_paths.take()
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,17 +141,13 @@ impl Command for BytesAt {
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let range: Value = call.req(engine_state, stack, 0)?;
|
||||
let (start, end, arg_span) = parse_range(range, call.head)?;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let column_paths = if column_paths.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(column_paths)
|
||||
};
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let arg = Arguments {
|
||||
start,
|
||||
end,
|
||||
arg_span,
|
||||
column_paths,
|
||||
cell_paths,
|
||||
};
|
||||
operate(at, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
@ -228,7 +224,25 @@ impl Command for BytesAt {
|
||||
}
|
||||
}
|
||||
|
||||
fn at(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
fn at(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => at_impl(val, args, *val_span),
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn at_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
let len: isize = input.len() as isize;
|
||||
|
||||
let start: isize = if arg.start < 0 {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{operate, BytesArgument};
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
@ -8,12 +8,12 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
|
||||
|
||||
struct Arguments {
|
||||
pattern: Vec<u8>,
|
||||
column_paths: Option<Vec<CellPath>>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl BytesArgument for Arguments {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.column_paths.take()
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,15 +53,11 @@ impl Command for BytesEndsWith {
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let column_paths = if column_paths.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(column_paths)
|
||||
};
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let arg = Arguments {
|
||||
pattern,
|
||||
column_paths,
|
||||
cell_paths,
|
||||
};
|
||||
operate(ends_with, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
@ -96,10 +92,24 @@ impl Command for BytesEndsWith {
|
||||
}
|
||||
}
|
||||
|
||||
fn ends_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
|
||||
Value::Bool {
|
||||
val: input.ends_with(pattern),
|
||||
span,
|
||||
fn ends_with(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => Value::Bool {
|
||||
val: val.ends_with(&args.pattern),
|
||||
span: *val_span,
|
||||
},
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{operate, BytesArgument};
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
@ -10,12 +10,12 @@ struct Arguments {
|
||||
pattern: Vec<u8>,
|
||||
end: bool,
|
||||
all: bool,
|
||||
column_paths: Option<Vec<CellPath>>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl BytesArgument for Arguments {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.column_paths.take()
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,17 +60,13 @@ impl Command for BytesIndexOf {
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let column_paths = if column_paths.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(column_paths)
|
||||
};
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let arg = Arguments {
|
||||
pattern,
|
||||
end: call.has_flag("end"),
|
||||
all: call.has_flag("all"),
|
||||
column_paths,
|
||||
cell_paths,
|
||||
};
|
||||
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
@ -126,7 +122,25 @@ impl Command for BytesIndexOf {
|
||||
}
|
||||
}
|
||||
|
||||
fn index_of(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
fn index_of(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => index_of_impl(val, args, *val_span),
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn index_of_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
if arg.all {
|
||||
search_all_index(input, &arg.pattern, arg.end, span)
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{operate, BytesArgument};
|
||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
@ -9,16 +9,6 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
|
||||
#[derive(Clone)]
|
||||
pub struct BytesLen;
|
||||
|
||||
struct Arguments {
|
||||
column_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl BytesArgument for Arguments {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.column_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
impl Command for BytesLen {
|
||||
fn name(&self) -> &str {
|
||||
"bytes length"
|
||||
@ -49,13 +39,8 @@ impl Command for BytesLen {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let column_paths = if column_paths.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(column_paths)
|
||||
};
|
||||
let arg = Arguments { column_paths };
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let arg = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
@ -78,10 +63,24 @@ impl Command for BytesLen {
|
||||
}
|
||||
}
|
||||
|
||||
fn length(input: &[u8], _arg: &Arguments, span: Span) -> Value {
|
||||
Value::Int {
|
||||
val: input.len() as i64,
|
||||
span,
|
||||
fn length(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => Value::Int {
|
||||
val: val.len() as i64,
|
||||
span: *val_span,
|
||||
},
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,11 +11,6 @@ mod replace;
|
||||
mod reverse;
|
||||
mod starts_with;
|
||||
|
||||
use nu_protocol::ast::CellPath;
|
||||
use nu_protocol::{PipelineData, ShellError, Span, Value};
|
||||
use std::sync::atomic::AtomicBool;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub use add::BytesAdd;
|
||||
pub use at::BytesAt;
|
||||
pub use build_::BytesBuild;
|
||||
@ -28,71 +23,3 @@ pub use remove::BytesRemove;
|
||||
pub use replace::BytesReplace;
|
||||
pub use reverse::BytesReverse;
|
||||
pub use starts_with::BytesStartsWith;
|
||||
|
||||
trait BytesArgument {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>>;
|
||||
}
|
||||
|
||||
/// map input pipeline data, for each elements, if it's Binary, invoke relative `cmd` with `arg`.
|
||||
fn operate<C, A>(
|
||||
cmd: C,
|
||||
mut arg: A,
|
||||
input: PipelineData,
|
||||
span: Span,
|
||||
ctrlc: Option<Arc<AtomicBool>>,
|
||||
) -> Result<PipelineData, ShellError>
|
||||
where
|
||||
A: BytesArgument + Send + Sync + 'static,
|
||||
C: Fn(&[u8], &A, Span) -> Value + Send + Sync + 'static + Clone + Copy,
|
||||
{
|
||||
match arg.take_column_paths() {
|
||||
None => input.map(
|
||||
move |v| match v {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => cmd(&val, &arg, val_span),
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
},
|
||||
ctrlc,
|
||||
),
|
||||
Some(column_paths) => {
|
||||
let arg = Arc::new(arg);
|
||||
input.map(
|
||||
move |mut v| {
|
||||
for path in &column_paths {
|
||||
let opt = arg.clone();
|
||||
let r = v.update_cell_path(
|
||||
&path.members,
|
||||
Box::new(move |old| {
|
||||
match old {
|
||||
Value::Binary {val, span: val_span} => cmd(val, &opt, *val_span),
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
}}}),
|
||||
);
|
||||
if let Err(error) = r {
|
||||
return Value::Error { error };
|
||||
}
|
||||
}
|
||||
v
|
||||
},
|
||||
ctrlc,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{operate, BytesArgument};
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
@ -9,13 +9,13 @@ use nu_protocol::{
|
||||
struct Arguments {
|
||||
pattern: Vec<u8>,
|
||||
end: bool,
|
||||
column_paths: Option<Vec<CellPath>>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
all: bool,
|
||||
}
|
||||
|
||||
impl BytesArgument for Arguments {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.column_paths.take()
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,12 +55,8 @@ impl Command for BytesRemove {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let column_paths = if column_paths.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(column_paths)
|
||||
};
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let pattern_to_remove = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
||||
if pattern_to_remove.item.is_empty() {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
@ -73,7 +69,7 @@ impl Command for BytesRemove {
|
||||
let arg = Arguments {
|
||||
pattern: pattern_to_remove,
|
||||
end: call.has_flag("end"),
|
||||
column_paths,
|
||||
cell_paths,
|
||||
all: call.has_flag("all"),
|
||||
};
|
||||
|
||||
@ -135,7 +131,25 @@ impl Command for BytesRemove {
|
||||
}
|
||||
}
|
||||
|
||||
fn remove(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
fn remove(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => remove_impl(val, args, *val_span),
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
let mut result = vec![];
|
||||
let remove_all = arg.all;
|
||||
let input_len = input.len();
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{operate, BytesArgument};
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
@ -9,13 +9,13 @@ use nu_protocol::{
|
||||
struct Arguments {
|
||||
find: Vec<u8>,
|
||||
replace: Vec<u8>,
|
||||
column_paths: Option<Vec<CellPath>>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
all: bool,
|
||||
}
|
||||
|
||||
impl BytesArgument for Arguments {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.column_paths.take()
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
@ -55,12 +55,8 @@ impl Command for BytesReplace {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
|
||||
let column_paths = if column_paths.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(column_paths)
|
||||
};
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 2)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let find = call.req::<Spanned<Vec<u8>>>(engine_state, stack, 0)?;
|
||||
if find.item.is_empty() {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
@ -72,7 +68,7 @@ impl Command for BytesReplace {
|
||||
let arg = Arguments {
|
||||
find: find.item,
|
||||
replace: call.req::<Vec<u8>>(engine_state, stack, 1)?,
|
||||
column_paths,
|
||||
cell_paths,
|
||||
all: call.has_flag("all"),
|
||||
};
|
||||
|
||||
@ -126,7 +122,25 @@ impl Command for BytesReplace {
|
||||
}
|
||||
}
|
||||
|
||||
fn replace(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
fn replace(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => replace_impl(val, args, *val_span),
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_impl(input: &[u8], arg: &Arguments, span: Span) -> Value {
|
||||
let mut replaced = vec![];
|
||||
let replace_all = arg.all;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{operate, BytesArgument};
|
||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
@ -6,16 +6,6 @@ use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::Category;
|
||||
use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value};
|
||||
|
||||
struct Arguments {
|
||||
column_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl BytesArgument for Arguments {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.column_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
||||
pub struct BytesReverse;
|
||||
@ -50,13 +40,8 @@ impl Command for BytesReverse {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let column_paths = if column_paths.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(column_paths)
|
||||
};
|
||||
let arg = Arguments { column_paths };
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let arg = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
@ -82,12 +67,28 @@ impl Command for BytesReverse {
|
||||
}
|
||||
}
|
||||
|
||||
fn reverse(input: &[u8], _args: &Arguments, span: Span) -> Value {
|
||||
let mut reversed_input = input.to_vec();
|
||||
reversed_input.reverse();
|
||||
Value::Binary {
|
||||
val: reversed_input,
|
||||
span,
|
||||
fn reverse(val: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => {
|
||||
let mut reversed_input = val.to_vec();
|
||||
reversed_input.reverse();
|
||||
Value::Binary {
|
||||
val: reversed_input,
|
||||
span: *val_span,
|
||||
}
|
||||
}
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use super::{operate, BytesArgument};
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
@ -8,12 +8,12 @@ use nu_protocol::{Example, PipelineData, ShellError, Signature, Span, SyntaxShap
|
||||
|
||||
struct Arguments {
|
||||
pattern: Vec<u8>,
|
||||
column_paths: Option<Vec<CellPath>>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl BytesArgument for Arguments {
|
||||
fn take_column_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.column_paths.take()
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,15 +53,11 @@ impl Command for BytesStartsWith {
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let pattern: Vec<u8> = call.req(engine_state, stack, 0)?;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let column_paths = if column_paths.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(column_paths)
|
||||
};
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let arg = Arguments {
|
||||
pattern,
|
||||
column_paths,
|
||||
cell_paths,
|
||||
};
|
||||
operate(
|
||||
starts_with,
|
||||
@ -102,10 +98,24 @@ impl Command for BytesStartsWith {
|
||||
}
|
||||
}
|
||||
|
||||
fn starts_with(input: &[u8], Arguments { pattern, .. }: &Arguments, span: Span) -> Value {
|
||||
Value::Bool {
|
||||
val: input.starts_with(pattern),
|
||||
span,
|
||||
fn starts_with(val: &Value, args: &Arguments, span: Span) -> Value {
|
||||
match val {
|
||||
Value::Binary {
|
||||
val,
|
||||
span: val_span,
|
||||
} => Value::Bool {
|
||||
val: val.starts_with(&args.pattern),
|
||||
span: *val_span,
|
||||
},
|
||||
other => Value::Error {
|
||||
error: ShellError::UnsupportedInput(
|
||||
format!(
|
||||
"Input's type is {}. This command only works with bytes.",
|
||||
other.get_type()
|
||||
),
|
||||
span,
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
@ -96,31 +97,12 @@ fn fmt(
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let head = call.head;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
|
||||
input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, head)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
let r =
|
||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
||||
if let Err(error) = r {
|
||||
return Value::Error { error };
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let args = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, span: Span) -> Value {
|
||||
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match input {
|
||||
Value::Int { val, .. } => fmt_it(*val, span),
|
||||
Value::Filesize { val, .. } => fmt_it(*val, span),
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
@ -100,7 +101,7 @@ fn into_binary(
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let head = call.head;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
|
||||
match input {
|
||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::Binary {
|
||||
@ -120,27 +121,10 @@ fn into_binary(
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
_ => input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, head)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
let r = ret.update_cell_path(
|
||||
&path.members,
|
||||
Box::new(move |old| action(old, head)),
|
||||
);
|
||||
if let Err(error) = r {
|
||||
return Value::Error { error };
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
),
|
||||
_ => {
|
||||
let arg = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(action, arg, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,7 +144,7 @@ fn float_to_endian(n: f64) -> Vec<u8> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, span: Span) -> Value {
|
||||
pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match input {
|
||||
Value::Binary { .. } => input.clone(),
|
||||
Value::Int { val, .. } => Value::Binary {
|
||||
@ -180,7 +164,7 @@ pub fn action(input: &Value, span: Span) -> Value {
|
||||
span,
|
||||
},
|
||||
Value::Bool { val, .. } => Value::Binary {
|
||||
val: int_to_endian(if *val { 1i64 } else { 0 }),
|
||||
val: int_to_endian(i64::from(*val)),
|
||||
span,
|
||||
},
|
||||
Value::Date { val, .. } => Value::Binary {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
@ -108,28 +109,9 @@ fn into_bool(
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
|
||||
input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, head)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
let r =
|
||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
||||
if let Err(error) = r {
|
||||
return Value::Error { error };
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let args = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
|
||||
@ -154,7 +136,7 @@ fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {
|
||||
}
|
||||
}
|
||||
|
||||
fn action(input: &Value, span: Span) -> Value {
|
||||
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
match input {
|
||||
Value::Bool { .. } => input.clone(),
|
||||
Value::Int { val, .. } => Value::Bool {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use crate::{generate_strftime_list, parse_date_from_string};
|
||||
use chrono::{DateTime, FixedOffset, Local, TimeZone, Utc};
|
||||
use nu_engine::CallExt;
|
||||
@ -5,14 +6,20 @@ use nu_protocol::ast::Call;
|
||||
use nu_protocol::ast::CellPath;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value,
|
||||
Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Span, Spanned,
|
||||
SyntaxShape, Value,
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
timezone: Option<Spanned<String>>,
|
||||
offset: Option<Spanned<i64>>,
|
||||
format: Option<String>,
|
||||
column_paths: Vec<CellPath>,
|
||||
zone_options: Option<Spanned<Zone>>,
|
||||
format_options: Option<DatetimeFormat>,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
// In case it may be confused with chrono::TimeZone
|
||||
@ -95,7 +102,36 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
operate(engine_state, stack, call, input)
|
||||
if call.has_flag("list") {
|
||||
Ok(generate_strftime_list(call.head, true).into_pipeline_data())
|
||||
} else {
|
||||
let cell_paths = call.rest(engine_state, stack, 0)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
|
||||
// if zone-offset is specified, then zone will be neglected
|
||||
let timezone = call.get_flag::<Spanned<String>>(engine_state, stack, "timezone")?;
|
||||
let zone_options =
|
||||
match &call.get_flag::<Spanned<i64>>(engine_state, stack, "offset")? {
|
||||
Some(zone_offset) => Some(Spanned {
|
||||
item: Zone::new(zone_offset.item),
|
||||
span: zone_offset.span,
|
||||
}),
|
||||
None => timezone.as_ref().map(|zone| Spanned {
|
||||
item: Zone::from_string(zone.item.clone()),
|
||||
span: zone.span,
|
||||
}),
|
||||
};
|
||||
let format_options = call
|
||||
.get_flag::<String>(engine_state, stack, "format")?
|
||||
.as_ref()
|
||||
.map(|fmt| DatetimeFormat(fmt.to_string()));
|
||||
let args = Arguments {
|
||||
format_options,
|
||||
zone_options,
|
||||
cell_paths,
|
||||
};
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
@ -162,72 +198,9 @@ impl Command for SubCommand {
|
||||
#[derive(Clone)]
|
||||
struct DatetimeFormat(String);
|
||||
|
||||
fn operate(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
|
||||
let options = Arguments {
|
||||
timezone: call.get_flag(engine_state, stack, "timezone")?,
|
||||
offset: call.get_flag(engine_state, stack, "offset")?,
|
||||
format: call.get_flag(engine_state, stack, "format")?,
|
||||
column_paths: call.rest(engine_state, stack, 0)?,
|
||||
};
|
||||
|
||||
// if zone-offset is specified, then zone will be neglected
|
||||
let zone_options = match &options.offset {
|
||||
Some(zone_offset) => Some(Spanned {
|
||||
item: Zone::new(zone_offset.item),
|
||||
span: zone_offset.span,
|
||||
}),
|
||||
None => options.timezone.as_ref().map(|zone| Spanned {
|
||||
item: Zone::from_string(zone.item.clone()),
|
||||
span: zone.span,
|
||||
}),
|
||||
};
|
||||
|
||||
let list_flag = call.has_flag("list");
|
||||
|
||||
let format_options = options
|
||||
.format
|
||||
.as_ref()
|
||||
.map(|fmt| DatetimeFormat(fmt.to_string()));
|
||||
|
||||
input.map(
|
||||
move |v| {
|
||||
if options.column_paths.is_empty() && !list_flag {
|
||||
action(&v, &zone_options, &format_options, head)
|
||||
} else if list_flag {
|
||||
generate_strftime_list(head, true)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &options.column_paths {
|
||||
let zone_options = zone_options.clone();
|
||||
let format_options = format_options.clone();
|
||||
let r = ret.update_cell_path(
|
||||
&path.members,
|
||||
Box::new(move |old| action(old, &zone_options, &format_options, head)),
|
||||
);
|
||||
if let Err(error) = r {
|
||||
return Value::Error { error };
|
||||
}
|
||||
}
|
||||
ret
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn action(
|
||||
input: &Value,
|
||||
timezone: &Option<Spanned<Zone>>,
|
||||
dateformat: &Option<DatetimeFormat>,
|
||||
head: Span,
|
||||
) -> Value {
|
||||
fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
||||
let timezone = &args.zone_options;
|
||||
let dateformat = &args.format_options;
|
||||
// Check to see if input looks like a Unix timestamp (i.e. can it be parsed to an int?)
|
||||
let timestamp = match input {
|
||||
Value::Int { val, .. } => Ok(*val),
|
||||
@ -359,7 +332,12 @@ mod tests {
|
||||
fn takes_a_date_format() {
|
||||
let date_str = Value::test_string("16.11.1984 8:00 am +0000");
|
||||
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
|
||||
let actual = action(&date_str, &None, &fmt_options, Span::test_data());
|
||||
let args = Arguments {
|
||||
zone_options: None,
|
||||
format_options: fmt_options,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: DateTime::parse_from_str("16.11.1984 8:00 am +0000", "%d.%m.%Y %H:%M %P %z")
|
||||
.unwrap(),
|
||||
@ -371,7 +349,12 @@ mod tests {
|
||||
#[test]
|
||||
fn takes_iso8601_date_format() {
|
||||
let date_str = Value::test_string("2020-08-04T16:39:18+00:00");
|
||||
let actual = action(&date_str, &None, &None, Span::test_data());
|
||||
let args = Arguments {
|
||||
zone_options: None,
|
||||
format_options: None,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: DateTime::parse_from_str("2020-08-04T16:39:18+00:00", "%Y-%m-%dT%H:%M:%S%z")
|
||||
.unwrap(),
|
||||
@ -387,7 +370,12 @@ mod tests {
|
||||
item: Zone::East(8),
|
||||
span: Span::test_data(),
|
||||
});
|
||||
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
|
||||
let args = Arguments {
|
||||
zone_options: timezone_option,
|
||||
format_options: None,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
.unwrap(),
|
||||
@ -404,7 +392,12 @@ mod tests {
|
||||
item: Zone::East(8),
|
||||
span: Span::test_data(),
|
||||
});
|
||||
let actual = action(&date_int, &timezone_option, &None, Span::test_data());
|
||||
let args = Arguments {
|
||||
zone_options: timezone_option,
|
||||
format_options: None,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_int, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z")
|
||||
.unwrap(),
|
||||
@ -421,7 +414,12 @@ mod tests {
|
||||
item: Zone::Local,
|
||||
span: Span::test_data(),
|
||||
});
|
||||
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
|
||||
let args = Arguments {
|
||||
zone_options: timezone_option,
|
||||
format_options: None,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
let expected = Value::Date {
|
||||
val: Local.timestamp(1614434140, 0).into(),
|
||||
span: Span::test_data(),
|
||||
@ -433,8 +431,12 @@ mod tests {
|
||||
#[test]
|
||||
fn takes_timestamp_without_timezone() {
|
||||
let date_str = Value::test_string("1614434140");
|
||||
let timezone_option = None;
|
||||
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
|
||||
let args = Arguments {
|
||||
zone_options: None,
|
||||
format_options: None,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
|
||||
let expected = Value::Date {
|
||||
val: Utc.timestamp(1614434140, 0).into(),
|
||||
@ -451,7 +453,12 @@ mod tests {
|
||||
item: Zone::Utc,
|
||||
span: Span::test_data(),
|
||||
});
|
||||
let actual = action(&date_str, &timezone_option, &None, Span::test_data());
|
||||
let args = Arguments {
|
||||
zone_options: timezone_option,
|
||||
format_options: None,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
|
||||
assert_eq!(actual.get_type(), Error);
|
||||
}
|
||||
@ -460,7 +467,12 @@ mod tests {
|
||||
fn communicates_parsing_error_given_an_invalid_datetimelike_string() {
|
||||
let date_str = Value::test_string("16.11.1984 8:00 am Oops0000");
|
||||
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
|
||||
let actual = action(&date_str, &None, &fmt_options, Span::test_data());
|
||||
let args = Arguments {
|
||||
zone_options: None,
|
||||
format_options: fmt_options,
|
||||
cell_paths: None,
|
||||
};
|
||||
let actual = action(&date_str, &args, Span::test_data());
|
||||
|
||||
assert_eq!(actual.get_type(), Error);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
@ -36,7 +37,9 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
operate(engine_state, stack, call, input)
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let args = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -72,37 +75,7 @@ impl Command for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn operate(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let head = call.head;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
|
||||
input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, head)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
let r =
|
||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
||||
if let Err(error) = r {
|
||||
return Value::Error { error };
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
fn action(input: &Value, head: Span) -> Value {
|
||||
fn action(input: &Value, _args: &CellPathOnlyArgs, head: Span) -> Value {
|
||||
match input {
|
||||
Value::String { val: s, span } => {
|
||||
let other = s.trim();
|
||||
@ -163,7 +136,7 @@ mod tests {
|
||||
let word = Value::test_string("3.1415");
|
||||
let expected = Value::test_float(3.1415);
|
||||
|
||||
let actual = action(&word, Span::test_data());
|
||||
let actual = action(&word, &CellPathOnlyArgs::from(vec![]), Span::test_data());
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
@ -171,7 +144,11 @@ mod tests {
|
||||
fn communicates_parsing_error_given_an_invalid_decimallike_string() {
|
||||
let decimal_str = Value::test_string("11.6anra");
|
||||
|
||||
let actual = action(&decimal_str, Span::test_data());
|
||||
let actual = action(
|
||||
&decimal_str,
|
||||
&CellPathOnlyArgs::from(vec![]),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
assert_eq!(actual.get_type(), Error);
|
||||
}
|
||||
@ -180,7 +157,11 @@ mod tests {
|
||||
fn int_to_decimal() {
|
||||
let decimal_str = Value::test_int(10);
|
||||
let expected = Value::test_float(10.0);
|
||||
let actual = action(&decimal_str, Span::test_data());
|
||||
let actual = action(
|
||||
&decimal_str,
|
||||
&CellPathOnlyArgs::from(vec![]),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::input_handler::{operate, CellPathOnlyArgs};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
@ -38,7 +39,9 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
into_filesize(engine_state, stack, call, input)
|
||||
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let args = CellPathOnlyArgs::from(cell_paths);
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -84,37 +87,7 @@ impl Command for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn into_filesize(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let head = call.head;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
|
||||
input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, head)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
let r =
|
||||
ret.update_cell_path(&path.members, Box::new(move |old| action(old, head)));
|
||||
if let Err(error) = r {
|
||||
return Value::Error { error };
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, span: Span) -> Value {
|
||||
pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
|
||||
if let Ok(value_span) = input.span() {
|
||||
match input {
|
||||
Value::Filesize { .. } => input.clone(),
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
@ -6,11 +7,17 @@ use nu_protocol::{
|
||||
};
|
||||
|
||||
struct Arguments {
|
||||
radix: Option<Value>,
|
||||
column_paths: Vec<CellPath>,
|
||||
radix: u32,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
little_endian: bool,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -46,7 +53,29 @@ impl Command for SubCommand {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
into_int(engine_state, stack, call, input)
|
||||
let cell_paths = call.rest(engine_state, stack, 0)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
|
||||
let radix = call.get_flag::<Value>(engine_state, stack, "radix")?;
|
||||
let radix: u32 = match radix {
|
||||
Some(Value::Int { val, span }) => {
|
||||
if !(2..=36).contains(&val) {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
"Radix must lie in the range [2, 36]".to_string(),
|
||||
span,
|
||||
));
|
||||
}
|
||||
val as u32
|
||||
}
|
||||
Some(_) => 10,
|
||||
None => 10,
|
||||
};
|
||||
let args = Arguments {
|
||||
radix,
|
||||
little_endian: call.has_flag("little-endian"),
|
||||
cell_paths,
|
||||
};
|
||||
operate(action, args, input, call.head, engine_state.ctrlc.clone())
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -121,59 +150,9 @@ impl Command for SubCommand {
|
||||
}
|
||||
}
|
||||
|
||||
fn into_int(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let head = call.head;
|
||||
|
||||
let options = Arguments {
|
||||
radix: call.get_flag(engine_state, stack, "radix")?,
|
||||
little_endian: call.has_flag("little-endian"),
|
||||
column_paths: call.rest(engine_state, stack, 0)?,
|
||||
};
|
||||
|
||||
let radix: u32 = match options.radix {
|
||||
Some(Value::Int { val, .. }) => val as u32,
|
||||
Some(_) => 10,
|
||||
None => 10,
|
||||
};
|
||||
|
||||
if let Some(val) = &options.radix {
|
||||
if !(2..=36).contains(&radix) {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
"Radix must lie in the range [2, 36]".to_string(),
|
||||
val.span()?,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
input.map(
|
||||
move |v| {
|
||||
if options.column_paths.is_empty() {
|
||||
action(&v, head, radix, options.little_endian)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &options.column_paths {
|
||||
let r = ret.update_cell_path(
|
||||
&path.members,
|
||||
Box::new(move |old| action(old, head, radix, options.little_endian)),
|
||||
);
|
||||
if let Err(error) = r {
|
||||
return Value::Error { error };
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn action(input: &Value, span: Span, radix: u32, little_endian: bool) -> Value {
|
||||
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
let radix = args.radix;
|
||||
let little_endian = args.little_endian;
|
||||
match input {
|
||||
Value::Int { val: _, .. } => {
|
||||
if radix == 10 {
|
||||
@ -401,21 +380,45 @@ mod test {
|
||||
let word = Value::test_string("10");
|
||||
let expected = Value::test_int(10);
|
||||
|
||||
let actual = action(&word, Span::test_data(), 10, false);
|
||||
let actual = action(
|
||||
&word,
|
||||
&Arguments {
|
||||
radix: 10,
|
||||
cell_paths: None,
|
||||
little_endian: false,
|
||||
},
|
||||
Span::test_data(),
|
||||
);
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turns_binary_to_integer() {
|
||||
let s = Value::test_string("0b101");
|
||||
let actual = action(&s, Span::test_data(), 10, false);
|
||||
let actual = action(
|
||||
&s,
|
||||
&Arguments {
|
||||
radix: 10,
|
||||
cell_paths: None,
|
||||
little_endian: false,
|
||||
},
|
||||
Span::test_data(),
|
||||
);
|
||||
assert_eq!(actual, Value::test_int(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn turns_hex_to_integer() {
|
||||
let s = Value::test_string("0xFF");
|
||||
let actual = action(&s, Span::test_data(), 16, false);
|
||||
let actual = action(
|
||||
&s,
|
||||
&Arguments {
|
||||
radix: 16,
|
||||
cell_paths: None,
|
||||
little_endian: false,
|
||||
},
|
||||
Span::test_data(),
|
||||
);
|
||||
assert_eq!(actual, Value::test_int(255));
|
||||
}
|
||||
|
||||
@ -423,7 +426,15 @@ mod test {
|
||||
fn communicates_parsing_error_given_an_invalid_integerlike_string() {
|
||||
let integer_str = Value::test_string("36anra");
|
||||
|
||||
let actual = action(&integer_str, Span::test_data(), 10, false);
|
||||
let actual = action(
|
||||
&integer_str,
|
||||
&Arguments {
|
||||
radix: 10,
|
||||
cell_paths: None,
|
||||
little_endian: false,
|
||||
},
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
assert_eq!(actual.get_type(), Error)
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::input_handler::{operate, CmdArgument};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::{Call, CellPath},
|
||||
@ -8,6 +9,19 @@ use nu_protocol::{
|
||||
use nu_utils::get_system_locale;
|
||||
use num_format::ToFormattedString;
|
||||
|
||||
struct Arguments {
|
||||
decimals_value: Option<i64>,
|
||||
decimals: bool,
|
||||
cell_paths: Option<Vec<CellPath>>,
|
||||
config: Config,
|
||||
}
|
||||
|
||||
impl CmdArgument for Arguments {
|
||||
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>> {
|
||||
self.cell_paths.take()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SubCommand;
|
||||
|
||||
@ -149,9 +163,6 @@ fn string_helper(
|
||||
let decimals = call.has_flag("decimals");
|
||||
let head = call.head;
|
||||
let decimals_value: Option<i64> = call.get_flag(engine_state, stack, "decimals")?;
|
||||
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
|
||||
let config = engine_state.get_config().clone();
|
||||
|
||||
if let Some(decimal_val) = decimals_value {
|
||||
if decimals && decimal_val.is_negative() {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
@ -160,6 +171,15 @@ fn string_helper(
|
||||
));
|
||||
}
|
||||
}
|
||||
let cell_paths = call.rest(engine_state, stack, 0)?;
|
||||
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
|
||||
let config = engine_state.get_config().clone();
|
||||
let args = Arguments {
|
||||
decimals_value,
|
||||
decimals,
|
||||
cell_paths,
|
||||
config,
|
||||
};
|
||||
|
||||
match input {
|
||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(Value::String {
|
||||
@ -179,45 +199,18 @@ fn string_helper(
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
_ => input.map(
|
||||
move |v| {
|
||||
if column_paths.is_empty() {
|
||||
action(&v, head, decimals, decimals_value, false, &config)
|
||||
} else {
|
||||
let mut ret = v;
|
||||
for path in &column_paths {
|
||||
let config = config.clone();
|
||||
let r = ret.update_cell_path(
|
||||
&path.members,
|
||||
Box::new(move |old| {
|
||||
action(old, head, decimals, decimals_value, false, &config)
|
||||
}),
|
||||
);
|
||||
if let Err(error) = r {
|
||||
return Value::Error { error };
|
||||
}
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
},
|
||||
engine_state.ctrlc.clone(),
|
||||
),
|
||||
_ => operate(action, args, input, head, engine_state.ctrlc.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn action(
|
||||
input: &Value,
|
||||
span: Span,
|
||||
decimals: bool,
|
||||
digits: Option<i64>,
|
||||
group_digits: bool,
|
||||
config: &Config,
|
||||
) -> Value {
|
||||
fn action(input: &Value, args: &Arguments, span: Span) -> Value {
|
||||
let decimals = args.decimals;
|
||||
let digits = args.decimals_value;
|
||||
let config = &args.config;
|
||||
match input {
|
||||
Value::Int { val, .. } => {
|
||||
let decimal_value = digits.unwrap_or(0) as usize;
|
||||
let res = format_int(*val, group_digits, decimal_value);
|
||||
let res = format_int(*val, false, decimal_value);
|
||||
Value::String { val: res, span }
|
||||
}
|
||||
Value::Float { val, .. } => {
|
||||
|
@ -49,10 +49,17 @@ impl Command for Alias {
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Alias ll to ls -l",
|
||||
example: "alias ll = ls -l",
|
||||
result: None,
|
||||
}]
|
||||
vec![
|
||||
Example {
|
||||
description: "Alias ll to ls -l",
|
||||
example: "alias ll = ls -l",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Make an alias that makes a list of all custom commands",
|
||||
example: "alias customs = ($nu.scope.commands | where is_custom | get command)",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ impl Command for Do {
|
||||
.required("block", SyntaxShape::Any, "the block to run")
|
||||
.switch(
|
||||
"ignore-errors",
|
||||
"ignore errors as the block runs",
|
||||
"ignore shell errors as the block runs",
|
||||
Some('i'),
|
||||
)
|
||||
.switch(
|
||||
@ -186,10 +186,15 @@ impl Command for Do {
|
||||
result: Some(Value::test_string("hello")),
|
||||
},
|
||||
Example {
|
||||
description: "Run the block and ignore errors",
|
||||
description: "Run the block and ignore shell errors",
|
||||
example: r#"do -i { thisisnotarealcommand }"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Abort the pipeline if a program returns a non-zero exit code",
|
||||
example: r#"do -c { nu -c 'exit 1' } | myscarycommand"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Run the block, with a positional parameter",
|
||||
example: r#"do {|x| 100 + $x } 50"#,
|
||||
|
@ -2,7 +2,7 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, ListStream, PipelineData, ShellError, Signature, SyntaxShape, Value,
|
||||
Category, Example, ListStream, PipelineData, ShellError, Signature, Span, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -14,7 +14,7 @@ impl Command for Echo {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Echo the arguments back to the user."
|
||||
"Returns its arguments, ignoring the piped-in value."
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
@ -24,7 +24,9 @@ impl Command for Echo {
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
"Unlike `print`, this command returns an actual value that will be passed to the next command of the pipeline."
|
||||
r#"When given no arguments, it returns an empty string. When given one argument,
|
||||
it returns it. Otherwise, it returns a list of the arguments. There is usually
|
||||
little reason to use this over just writing the values as-is."#
|
||||
}
|
||||
|
||||
fn run(
|
||||
@ -61,13 +63,17 @@ impl Command for Echo {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Put a hello message in the pipeline",
|
||||
example: "echo 'hello'",
|
||||
result: Some(Value::test_string("hello")),
|
||||
description: "Put a list of numbers in the pipeline. This is the same as [1 2 3].",
|
||||
example: "echo 1 2 3",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_int(1), Value::test_int(2), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Print the value of the special '$nu' variable",
|
||||
example: "echo $nu",
|
||||
description:
|
||||
"Returns the piped-in value, by using the special $in variable to obtain it.",
|
||||
example: "echo $in",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
|
@ -363,15 +363,12 @@ pub fn highlight_search_string(
|
||||
}
|
||||
};
|
||||
// strip haystack to remove existing ansi style
|
||||
let stripped_haystack: String = match strip_ansi_escapes::strip(haystack) {
|
||||
Ok(i) => String::from_utf8(i).unwrap_or_else(|_| String::from(haystack)),
|
||||
Err(_) => String::from(haystack),
|
||||
};
|
||||
let stripped_haystack = nu_utils::strip_ansi_likely(haystack);
|
||||
let mut last_match_end = 0;
|
||||
let style = Style::new().fg(White).on(Red);
|
||||
let mut highlighted = String::new();
|
||||
|
||||
for cap in regex.captures_iter(stripped_haystack.as_str()) {
|
||||
for cap in regex.captures_iter(stripped_haystack.as_ref()) {
|
||||
match cap {
|
||||
Ok(capture) => {
|
||||
let start = match capture.get(0) {
|
||||
|
@ -105,7 +105,7 @@ impl Command for OverlayHide {
|
||||
},
|
||||
Example {
|
||||
description: "Hide an overlay created from a file",
|
||||
example: r#"echo 'export alias f = "foo"' | save spam.nu
|
||||
example: r#"'export alias f = "foo"' | save spam.nu
|
||||
overlay use spam.nu
|
||||
overlay hide spam"#,
|
||||
result: None,
|
||||
|
@ -183,14 +183,14 @@ impl Command for OverlayUse {
|
||||
},
|
||||
Example {
|
||||
description: "Create an overlay with a prefix",
|
||||
example: r#"echo 'export def foo { "foo" }'
|
||||
example: r#"'export def foo { "foo" }'
|
||||
overlay use --prefix spam
|
||||
spam foo"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Create an overlay from a file",
|
||||
example: r#"echo 'export-env { let-env FOO = "foo" }' | save spam.nu
|
||||
example: r#"'export-env { let-env FOO = "foo" }' | save spam.nu
|
||||
overlay use spam.nu
|
||||
$env.FOO"#,
|
||||
result: None,
|
||||
|
@ -19,7 +19,7 @@ impl Command for GroupByDb {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Group by query"
|
||||
"Group-by query"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
|
@ -15,13 +15,12 @@ impl Command for ToDataBase {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Converts into an open db connection"
|
||||
"Converts the input into an open db connection"
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
"This function is used as type hint for parser, specially if the query is not started with 'from table'"
|
||||
"This function is used as a hint to Nushell to optimize the pipeline for database queries."
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(self.name())
|
||||
.input_type(Type::Any)
|
||||
@ -35,7 +34,7 @@ impl Command for ToDataBase {
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Converts an open file into a db object",
|
||||
description: "Converts an open file into a db object.",
|
||||
example: "open db.sqlite | into db",
|
||||
result: None,
|
||||
}]
|
||||
|
@ -248,14 +248,8 @@ fn nu_value_to_string(value: Value, separator: &str, config: &Config) -> String
|
||||
}
|
||||
Value::String { val, .. } => {
|
||||
// don't store ansi escape sequences in the database
|
||||
let stripped = {
|
||||
match strip_ansi_escapes::strip(&val) {
|
||||
Ok(item) => String::from_utf8(item).unwrap_or(val),
|
||||
Err(_) => val,
|
||||
}
|
||||
};
|
||||
// escape single quotes
|
||||
stripped.replace('\'', "''")
|
||||
nu_utils::strip_ansi_unlikely(&val).replace('\'', "''")
|
||||
}
|
||||
Value::List { vals: val, .. } => val
|
||||
.iter()
|
||||
|
@ -124,7 +124,8 @@ impl CustomValue for ExprDb {
|
||||
| Operator::ShiftLeft
|
||||
| Operator::ShiftRight
|
||||
| Operator::StartsWith
|
||||
| Operator::EndsWith => Err(ShellError::UnsupportedOperator(operator, op)),
|
||||
| Operator::EndsWith
|
||||
| Operator::Append => Err(ShellError::UnsupportedOperator(operator, op)),
|
||||
}?;
|
||||
|
||||
let expr = Expr::BinaryOp {
|
||||
|
@ -55,7 +55,6 @@ impl Command for ColumnsDF {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_collect)]
|
||||
fn command(
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
|
@ -56,7 +56,6 @@ impl Command for DataTypes {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::needless_collect)]
|
||||
fn command(
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
|
@ -103,14 +103,14 @@ impl SQLContext {
|
||||
let idx = match idx.parse::<usize>() {
|
||||
Ok(0)| Err(_) => Err(
|
||||
PolarsError::ComputeError(
|
||||
format!("Group By Error: Only positive number or expression are supported, got {idx}").into()
|
||||
format!("Group-By Error: Only positive number or expression are supported, got {idx}").into()
|
||||
)),
|
||||
Ok(idx) => Ok(idx)
|
||||
}?;
|
||||
Ok(projection[idx].clone())
|
||||
}
|
||||
SqlExpr::Value(_) => Err(
|
||||
PolarsError::ComputeError("Group By Error: Only positive number or expression are supported".into())
|
||||
PolarsError::ComputeError("Group-By Error: Only positive number or expression are supported".into())
|
||||
),
|
||||
_ => parse_sql_expr(e)
|
||||
}
|
||||
@ -124,7 +124,7 @@ impl SQLContext {
|
||||
// Return error on wild card, shouldn't process this
|
||||
if contain_wildcard {
|
||||
return Err(PolarsError::ComputeError(
|
||||
"Group By Error: Can't processed wildcard in groupby".into(),
|
||||
"Group-By Error: Can't process wildcard in group-by".into(),
|
||||
));
|
||||
}
|
||||
// Default polars group by will have group by columns at the front
|
||||
|
@ -15,7 +15,7 @@ impl Command for ToDataFrame {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Converts a List, Table or Dictionary into a dataframe"
|
||||
"Converts a list, table or record into a dataframe"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
|
@ -243,7 +243,7 @@ expr_command!(
|
||||
"max",
|
||||
"Creates a max expression",
|
||||
vec![Example {
|
||||
description: "Max aggregation for a group by",
|
||||
description: "Max aggregation for a group-by",
|
||||
example: r#"[[a b]; [one 2] [one 4] [two 1]]
|
||||
| into df
|
||||
| group-by a
|
||||
@ -274,7 +274,7 @@ expr_command!(
|
||||
"min",
|
||||
"Creates a min expression",
|
||||
vec![Example {
|
||||
description: "Min aggregation for a group by",
|
||||
description: "Min aggregation for a group-by",
|
||||
example: r#"[[a b]; [one 2] [one 4] [two 1]]
|
||||
| into df
|
||||
| group-by a
|
||||
@ -305,7 +305,7 @@ expr_command!(
|
||||
"sum",
|
||||
"Creates a sum expression for an aggregation",
|
||||
vec![Example {
|
||||
description: "Sum aggregation for a group by",
|
||||
description: "Sum aggregation for a group-by",
|
||||
example: r#"[[a b]; [one 2] [one 4] [two 1]]
|
||||
| into df
|
||||
| group-by a
|
||||
@ -336,7 +336,7 @@ expr_command!(
|
||||
"mean",
|
||||
"Creates a mean expression for an aggregation",
|
||||
vec![Example {
|
||||
description: "Mean aggregation for a group by",
|
||||
description: "Mean aggregation for a group-by",
|
||||
example: r#"[[a b]; [one 2] [one 4] [two 1]]
|
||||
| into df
|
||||
| group-by a
|
||||
@ -367,7 +367,7 @@ expr_command!(
|
||||
"median",
|
||||
"Creates a median expression for an aggregation",
|
||||
vec![Example {
|
||||
description: "Median aggregation for a group by",
|
||||
description: "Median aggregation for a group-by",
|
||||
example: r#"[[a b]; [one 2] [one 4] [two 1]]
|
||||
| into df
|
||||
| group-by a
|
||||
@ -398,7 +398,7 @@ expr_command!(
|
||||
"std",
|
||||
"Creates a std expression for an aggregation",
|
||||
vec![Example {
|
||||
description: "Std aggregation for a group by",
|
||||
description: "Std aggregation for a group-by",
|
||||
example: r#"[[a b]; [one 2] [one 2] [two 1] [two 1]]
|
||||
| into df
|
||||
| group-by a
|
||||
@ -429,7 +429,7 @@ expr_command!(
|
||||
"var",
|
||||
"Create a var expression for an aggregation",
|
||||
vec![Example {
|
||||
description: "Var aggregation for a group by",
|
||||
description: "Var aggregation for a group-by",
|
||||
example: r#"[[a b]; [one 2] [one 2] [two 1] [two 1]]
|
||||
| into df
|
||||
| group-by a
|
||||
|
@ -33,7 +33,7 @@ impl Command for ExprQuantile {
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Quantile aggregation for a group by",
|
||||
description: "Quantile aggregation for a group-by",
|
||||
example: r#"[[a b]; [one 2] [one 4] [two 1]]
|
||||
| into df
|
||||
| group-by a
|
||||
|
@ -17,13 +17,13 @@ impl Command for LazyAggregate {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Performs a series of aggregations from a group by"
|
||||
"Performs a series of aggregations from a group-by"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(self.name())
|
||||
.rest(
|
||||
"Group by expressions",
|
||||
"Group-by expressions",
|
||||
SyntaxShape::Any,
|
||||
"Expression(s) that define the aggregations to be applied",
|
||||
)
|
||||
|
@ -16,15 +16,15 @@ impl Command for ToLazyGroupBy {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Creates a groupby object that can be used for other aggregations"
|
||||
"Creates a group-by object that can be used for other aggregations"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build(self.name())
|
||||
.rest(
|
||||
"Group by expressions",
|
||||
"Group-by expressions",
|
||||
SyntaxShape::Any,
|
||||
"Expression(s) that define the lazy group by",
|
||||
"Expression(s) that define the lazy group-by",
|
||||
)
|
||||
.input_type(Type::Custom("dataframe".into()))
|
||||
.output_type(Type::Custom("dataframe".into()))
|
||||
|
@ -316,7 +316,7 @@ impl NuDataFrame {
|
||||
let column = conversion::create_column(&series, row, row + 1, span)?;
|
||||
|
||||
if column.len() == 0 {
|
||||
Err(ShellError::AccessBeyondEnd(series.len(), span))
|
||||
Err(ShellError::AccessEmptyContent(span))
|
||||
} else {
|
||||
let value = column
|
||||
.into_iter()
|
||||
|
@ -87,7 +87,10 @@ impl Command for SubCommand {
|
||||
Example {
|
||||
description: "Format a given date using a given format string.",
|
||||
example: r#""2021-10-22 20:00:12 +01:00" | date format "%Y-%m-%d""#,
|
||||
result: None,
|
||||
result: Some(Value::String {
|
||||
val: "2021-10-22".to_string(),
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
2
crates/nu-command/src/env/with_env.rs
vendored
2
crates/nu-command/src/env/with_env.rs
vendored
@ -63,7 +63,7 @@ impl Command for WithEnv {
|
||||
},
|
||||
Example {
|
||||
description: "Set by row(e.g. `open x.json` or `from json`)",
|
||||
example: r#"echo '{"X":"Y","W":"Z"}'|from json|with-env $in { echo $env.X $env.W }"#,
|
||||
example: r#"'{"X":"Y","W":"Z"}'|from json|with-env $in { echo $env.X $env.W }"#,
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
|
@ -6,6 +6,29 @@ use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
|
||||
};
|
||||
use std::path::Path;
|
||||
|
||||
// when the file under the fold executeable
|
||||
#[cfg(unix)]
|
||||
mod permission_mods {
|
||||
pub type Mode = u32;
|
||||
pub mod unix {
|
||||
use super::Mode;
|
||||
pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode;
|
||||
pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
|
||||
pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
|
||||
}
|
||||
}
|
||||
|
||||
// use to return the message of the result of change director
|
||||
// TODO: windows, maybe should use file_attributes function in https://doc.rust-lang.org/std/os/windows/fs/trait.MetadataExt.html
|
||||
// TODO: the meaning of the result of the function can be found in https://learn.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants
|
||||
// TODO: if have realize the logic on windows, remove the cfg
|
||||
#[derive(Debug)]
|
||||
enum PermissionResult<'a> {
|
||||
PermissionOk,
|
||||
PermissionDenied(&'a str),
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cd;
|
||||
@ -44,10 +67,7 @@ impl Command for Cd {
|
||||
let path_val = {
|
||||
if let Some(path) = path_val {
|
||||
Some(Spanned {
|
||||
item: match strip_ansi_escapes::strip(&path.item) {
|
||||
Ok(item) => String::from_utf8(item).unwrap_or(path.item),
|
||||
Err(_) => path.item,
|
||||
},
|
||||
item: nu_utils::strip_ansi_string_unlikely(path.item),
|
||||
span: path.span,
|
||||
})
|
||||
} else {
|
||||
@ -141,6 +161,7 @@ impl Command for Cd {
|
||||
}
|
||||
};
|
||||
|
||||
let path_tointo = path.clone();
|
||||
let path_value = Value::String { val: path, span };
|
||||
let cwd = Value::String {
|
||||
val: cwd.to_string_lossy().to_string(),
|
||||
@ -172,9 +193,16 @@ impl Command for Cd {
|
||||
|
||||
//FIXME: this only changes the current scope, but instead this environment variable
|
||||
//should probably be a block that loads the information from the state in the overlay
|
||||
|
||||
stack.add_env_var("PWD".into(), path_value);
|
||||
Ok(PipelineData::new(call.head))
|
||||
match have_permission(&path_tointo) {
|
||||
PermissionResult::PermissionOk => {
|
||||
stack.add_env_var("PWD".into(), path_value);
|
||||
Ok(PipelineData::new(call.head))
|
||||
}
|
||||
PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError(format!(
|
||||
"Cannot change directory to {}: {}",
|
||||
path_tointo, reason
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -197,3 +225,65 @@ impl Command for Cd {
|
||||
]
|
||||
}
|
||||
}
|
||||
#[cfg(windows)]
|
||||
fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
|
||||
match dir.as_ref().read_dir() {
|
||||
Err(e) => {
|
||||
if matches!(e.kind(), std::io::ErrorKind::PermissionDenied) {
|
||||
PermissionResult::PermissionDenied("Folder is unable to be read")
|
||||
} else {
|
||||
PermissionResult::PermissionOk
|
||||
}
|
||||
}
|
||||
Ok(_) => PermissionResult::PermissionOk,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
|
||||
match dir.as_ref().metadata() {
|
||||
Ok(metadata) => {
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
let bits = metadata.mode();
|
||||
let has_bit = |bit| bits & bit == bit;
|
||||
let current_user = users::get_current_uid();
|
||||
if current_user == 0 {
|
||||
return PermissionResult::PermissionOk;
|
||||
}
|
||||
let current_group = users::get_current_gid();
|
||||
let owner_user = metadata.uid();
|
||||
let owner_group = metadata.gid();
|
||||
match (current_user == owner_user, current_group == owner_group) {
|
||||
(true, _) => {
|
||||
if has_bit(permission_mods::unix::USER_EXECUTE) {
|
||||
PermissionResult::PermissionOk
|
||||
} else {
|
||||
PermissionResult::PermissionDenied(
|
||||
"You are the owner but do not have the execute permission",
|
||||
)
|
||||
}
|
||||
}
|
||||
(false, true) => {
|
||||
if has_bit(permission_mods::unix::GROUP_EXECUTE) {
|
||||
PermissionResult::PermissionOk
|
||||
} else {
|
||||
PermissionResult::PermissionDenied(
|
||||
"You are in the group but do not have the execute permission",
|
||||
)
|
||||
}
|
||||
}
|
||||
// other_user or root
|
||||
(false, false) => {
|
||||
if has_bit(permission_mods::unix::OTHER_EXECUTE) {
|
||||
PermissionResult::PermissionOk
|
||||
} else {
|
||||
PermissionResult::PermissionDenied(
|
||||
"You are neither the owner, in the group, nor the super user and do not have permission",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => PermissionResult::PermissionDenied("Could not retrieve the metadata"),
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ const GLOB_PARAMS: nu_glob::MatchOptions = nu_glob::MatchOptions {
|
||||
#[derive(Clone)]
|
||||
pub struct Cp;
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
impl Command for Cp {
|
||||
fn name(&self) -> &str {
|
||||
"cp"
|
||||
@ -74,10 +73,7 @@ impl Command for Cp {
|
||||
let src: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let src = {
|
||||
Spanned {
|
||||
item: match strip_ansi_escapes::strip(&src.item) {
|
||||
Ok(item) => String::from_utf8(item).unwrap_or(src.item),
|
||||
Err(_) => src.item,
|
||||
},
|
||||
item: nu_utils::strip_ansi_string_unlikely(src.item),
|
||||
span: src.span,
|
||||
}
|
||||
};
|
||||
|
@ -42,12 +42,12 @@ impl Command for Ls {
|
||||
.switch("all", "Show hidden files", Some('a'))
|
||||
.switch(
|
||||
"long",
|
||||
"List all available columns for each entry",
|
||||
"Get all available columns for each entry (slower; columns are platform-dependent)",
|
||||
Some('l'),
|
||||
)
|
||||
.switch(
|
||||
"short-names",
|
||||
"Only print the file names and not the path",
|
||||
"Only print the file names, and not the path",
|
||||
Some('s'),
|
||||
)
|
||||
.switch("full-paths", "display paths as absolute paths", Some('f'))
|
||||
@ -86,10 +86,7 @@ impl Command for Ls {
|
||||
let pattern_arg = {
|
||||
if let Some(path) = pattern_arg {
|
||||
Some(Spanned {
|
||||
item: match strip_ansi_escapes::strip(&path.item) {
|
||||
Ok(item) => String::from_utf8(item).unwrap_or(path.item),
|
||||
Err(_) => path.item,
|
||||
},
|
||||
item: nu_utils::strip_ansi_string_unlikely(path.item),
|
||||
span: path.span,
|
||||
})
|
||||
} else {
|
||||
@ -201,7 +198,7 @@ impl Command for Ls {
|
||||
} else if full_paths || absolute_path {
|
||||
Some(path.to_string_lossy().to_string())
|
||||
} else if let Some(prefix) = &prefix {
|
||||
if let Ok(remainder) = path.strip_prefix(&prefix) {
|
||||
if let Ok(remainder) = path.strip_prefix(prefix) {
|
||||
if directory {
|
||||
// When the path is the same as the cwd, path_diff should be "."
|
||||
let path_diff =
|
||||
@ -218,7 +215,7 @@ impl Command for Ls {
|
||||
|
||||
Some(path_diff)
|
||||
} else {
|
||||
let new_prefix = if let Some(pfx) = diff_paths(&prefix, &cwd) {
|
||||
let new_prefix = if let Some(pfx) = diff_paths(prefix, &cwd) {
|
||||
pfx
|
||||
} else {
|
||||
prefix.to_path_buf()
|
||||
@ -274,45 +271,44 @@ impl Command for Ls {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "List all files in the current directory",
|
||||
description: "List visible files in the current directory",
|
||||
example: "ls",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "List all files in a subdirectory",
|
||||
description: "List visible files in a subdirectory",
|
||||
example: "ls subdir",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "List all files with full path in the parent directory",
|
||||
description: "List visible files with full path in the parent directory",
|
||||
example: "ls -f ..",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "List all rust files",
|
||||
description: "List Rust files",
|
||||
example: "ls *.rs",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "List all files and directories whose name do not contain 'bar'",
|
||||
description: "List files and directories whose name do not contain 'bar'",
|
||||
example: "ls -s | where name !~ bar",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "List all dirs in your home directory",
|
||||
example: "ls ~ | where type == dir",
|
||||
example: "ls -a ~ | where type == dir",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description:
|
||||
"List all dirs in your home directory which have not been modified in 7 days",
|
||||
example: "ls -s ~ | where type == dir && modified < ((date now) - 7day)",
|
||||
example: "ls -as ~ | where type == dir && modified < ((date now) - 7day)",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "List given paths, show directories themselves",
|
||||
example:
|
||||
"['/path/to/directory' '/path/to/file'] | each { |it| ls -D $it } | flatten",
|
||||
description: "List given paths and show directories themselves",
|
||||
example: "['/path/to/directory' '/path/to/file'] | each { ls -D $in } | flatten",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
|
@ -20,7 +20,6 @@ const GLOB_PARAMS: nu_glob::MatchOptions = nu_glob::MatchOptions {
|
||||
#[derive(Clone)]
|
||||
pub struct Mv;
|
||||
|
||||
#[allow(unused_must_use)]
|
||||
impl Command for Mv {
|
||||
fn name(&self) -> &str {
|
||||
"mv"
|
||||
@ -51,8 +50,8 @@ impl Command for Mv {
|
||||
"make mv to be verbose, showing files been moved.",
|
||||
Some('v'),
|
||||
)
|
||||
.switch("force", "overwrite the destination.", Some('f'))
|
||||
.switch("interactive", "ask user to confirm action", Some('i'))
|
||||
// .switch("force", "suppress error when no file", Some('f'))
|
||||
.category(Category::FileSystem)
|
||||
}
|
||||
|
||||
@ -67,17 +66,14 @@ impl Command for Mv {
|
||||
let spanned_source: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||
let spanned_source = {
|
||||
Spanned {
|
||||
item: match strip_ansi_escapes::strip(&spanned_source.item) {
|
||||
Ok(item) => String::from_utf8(item).unwrap_or(spanned_source.item),
|
||||
Err(_) => spanned_source.item,
|
||||
},
|
||||
item: nu_utils::strip_ansi_string_unlikely(spanned_source.item),
|
||||
span: spanned_source.span,
|
||||
}
|
||||
};
|
||||
let spanned_destination: Spanned<String> = call.req(engine_state, stack, 1)?;
|
||||
let verbose = call.has_flag("verbose");
|
||||
let interactive = call.has_flag("interactive");
|
||||
// let force = call.has_flag("force");
|
||||
let force = call.has_flag("force");
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
|
||||
@ -102,12 +98,22 @@ impl Command for Mv {
|
||||
//
|
||||
// First, the destination exists.
|
||||
// - If a directory, move everything into that directory, otherwise
|
||||
// - if only a single source, overwrite the file, otherwise
|
||||
// - error.
|
||||
// - if only a single source, and --force (or -f) is provided overwrite the file,
|
||||
// - otherwise error.
|
||||
//
|
||||
// Second, the destination doesn't exist, so we can only rename a single source. Otherwise
|
||||
// it's an error.
|
||||
|
||||
if destination.exists() && !force && !destination.is_dir() && !source.is_dir() {
|
||||
return Err(ShellError::GenericError(
|
||||
"Destination file already exists".into(),
|
||||
"you can use -f, --force to force overwriting the destination".into(),
|
||||
Some(spanned_destination.span),
|
||||
None,
|
||||
Vec::new(),
|
||||
));
|
||||
}
|
||||
|
||||
if (destination.exists() && !destination.is_dir() && sources.len() > 1)
|
||||
|| (!destination.exists() && sources.len() > 1)
|
||||
{
|
||||
@ -289,7 +295,7 @@ fn move_file(
|
||||
fn move_item(from: &Path, from_span: Span, to: &Path) -> Result<(), ShellError> {
|
||||
// We first try a rename, which is a quick operation. If that doesn't work, we'll try a copy
|
||||
// and remove the old file/folder. This is necessary if we're moving across filesystems or devices.
|
||||
std::fs::rename(&from, &to).or_else(|_| {
|
||||
std::fs::rename(from, to).or_else(|_| {
|
||||
match if from.is_file() {
|
||||
let mut options = fs_extra::file::CopyOptions::new();
|
||||
options.overwrite = true;
|
||||
|
@ -53,10 +53,7 @@ impl Command for Open {
|
||||
let path = {
|
||||
if let Some(path_val) = path {
|
||||
Some(Spanned {
|
||||
item: match strip_ansi_escapes::strip(&path_val.item) {
|
||||
Ok(item) => String::from_utf8(item).unwrap_or(path_val.item),
|
||||
Err(_) => path_val.item,
|
||||
},
|
||||
item: nu_utils::strip_ansi_string_unlikely(path_val.item),
|
||||
span: path_val.span,
|
||||
})
|
||||
} else {
|
||||
@ -100,7 +97,7 @@ impl Command for Open {
|
||||
let path_no_whitespace = &path.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
|
||||
let path = Path::new(path_no_whitespace);
|
||||
|
||||
if permission_denied(&path) {
|
||||
if permission_denied(path) {
|
||||
#[cfg(unix)]
|
||||
let error_msg = match path.metadata() {
|
||||
Ok(md) => format!(
|
||||
@ -172,8 +169,17 @@ impl Command for Open {
|
||||
let block = engine_state.get_block(block_id);
|
||||
eval_block(engine_state, stack, block, output, false, false)
|
||||
} else {
|
||||
decl.run(engine_state, stack, &Call::new(arg_span), output)
|
||||
decl.run(engine_state, stack, &Call::new(call_span), output)
|
||||
}
|
||||
.map_err(|inner| {
|
||||
ShellError::GenericError(
|
||||
format!("Error while parsing as {}", ext),
|
||||
format!("Could not parse '{}' with `from {}`", path.display(), ext),
|
||||
Some(arg_span),
|
||||
Some(format!("Check out `help from {}` or `help from` for more options or open raw data with `open --raw '{}'`", ext, path.display())),
|
||||
vec![inner],
|
||||
)
|
||||
})
|
||||
}
|
||||
None => Ok(output),
|
||||
}
|
||||
|
@ -143,10 +143,7 @@ fn rm(
|
||||
|
||||
for (idx, path) in targets.clone().into_iter().enumerate() {
|
||||
let corrected_path = Spanned {
|
||||
item: match strip_ansi_escapes::strip(&path.item) {
|
||||
Ok(item) => String::from_utf8(item).unwrap_or(path.item),
|
||||
Err(_) => path.item,
|
||||
},
|
||||
item: nu_utils::strip_ansi_string_unlikely(path.item),
|
||||
span: path.span,
|
||||
};
|
||||
let _ = std::mem::replace(&mut targets[idx], corrected_path);
|
||||
@ -202,6 +199,7 @@ fn rm(
|
||||
|
||||
let path = current_dir(engine_state, stack)?;
|
||||
|
||||
let (mut target_exists, mut empty_span) = (false, call.head);
|
||||
let mut all_targets: HashMap<PathBuf, Span> = HashMap::new();
|
||||
for target in targets {
|
||||
if path.to_string_lossy() == target.item
|
||||
@ -232,6 +230,10 @@ fn rm(
|
||||
for file in files {
|
||||
match file {
|
||||
Ok(ref f) => {
|
||||
if !target_exists {
|
||||
target_exists = true;
|
||||
}
|
||||
|
||||
// It is not appropriate to try and remove the
|
||||
// current directory or its parent when using
|
||||
// glob patterns.
|
||||
@ -253,12 +255,17 @@ fn rm(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Target doesn't exists
|
||||
if !target_exists && empty_span.eq(&call.head) {
|
||||
empty_span = target.span;
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
return Err(ShellError::GenericError(
|
||||
e.to_string(),
|
||||
e.to_string(),
|
||||
Some(call.head),
|
||||
Some(target.span),
|
||||
None,
|
||||
Vec::new(),
|
||||
))
|
||||
@ -270,15 +277,15 @@ fn rm(
|
||||
return Err(ShellError::GenericError(
|
||||
"No valid paths".into(),
|
||||
"no valid paths".into(),
|
||||
Some(call.head),
|
||||
Some(empty_span),
|
||||
None,
|
||||
Vec::new(),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(all_targets
|
||||
.into_iter()
|
||||
.map(move |(f, _)| {
|
||||
.into_keys()
|
||||
.map(move |f| {
|
||||
let is_empty = || match f.read_dir() {
|
||||
Ok(mut p) => p.next().is_none(),
|
||||
Err(_) => false,
|
||||
|
@ -2,8 +2,10 @@ use nu_engine::CallExt;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
|
||||
Category, Example, PipelineData, RawStream, ShellError, Signature, Span, Spanned, SyntaxShape,
|
||||
Value,
|
||||
};
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, Write};
|
||||
use std::path::Path;
|
||||
|
||||
@ -35,6 +37,12 @@ impl Command for Save {
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("save")
|
||||
.required("filename", SyntaxShape::Filepath, "the filename to use")
|
||||
.named(
|
||||
"stderr",
|
||||
SyntaxShape::Filepath,
|
||||
"the filename used to save stderr, only works with `-r` flag",
|
||||
Some('e'),
|
||||
)
|
||||
.switch("raw", "save file as raw binary", Some('r'))
|
||||
.switch("append", "append input to the end of the file", Some('a'))
|
||||
.category(Category::FileSystem)
|
||||
@ -81,6 +89,35 @@ impl Command for Save {
|
||||
));
|
||||
}
|
||||
};
|
||||
let stderr_path = call.get_flag::<Spanned<String>>(engine_state, stack, "stderr")?;
|
||||
let stderr_file = match stderr_path {
|
||||
None => None,
|
||||
Some(stderr_path) => {
|
||||
let stderr_span = stderr_path.span;
|
||||
let stderr_path = Path::new(&stderr_path.item);
|
||||
if stderr_path == path {
|
||||
Some(file.try_clone()?)
|
||||
} else {
|
||||
match std::fs::File::create(stderr_path) {
|
||||
Ok(file) => Some(file),
|
||||
Err(err) => {
|
||||
return Ok(PipelineData::Value(
|
||||
Value::Error {
|
||||
error: ShellError::GenericError(
|
||||
"Permission denied".into(),
|
||||
err.to_string(),
|
||||
Some(stderr_span),
|
||||
None,
|
||||
Vec::new(),
|
||||
),
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let ext = if raw {
|
||||
None
|
||||
@ -148,33 +185,37 @@ impl Command for Save {
|
||||
match input {
|
||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::new(span)),
|
||||
PipelineData::ExternalStream {
|
||||
stdout: Some(mut stream),
|
||||
stdout: Some(stream),
|
||||
stderr,
|
||||
..
|
||||
} => {
|
||||
let mut writer = BufWriter::new(file);
|
||||
// delegate a thread to redirect stderr to result.
|
||||
let handler = stderr.map(|stderr_stream| match stderr_file {
|
||||
Some(stderr_file) => std::thread::spawn(move || {
|
||||
stream_to_file(stderr_stream, stderr_file, span)
|
||||
}),
|
||||
None => std::thread::spawn(move || {
|
||||
let _ = stderr_stream.into_bytes();
|
||||
Ok(PipelineData::new(span))
|
||||
}),
|
||||
});
|
||||
|
||||
stream
|
||||
.try_for_each(move |result| {
|
||||
let buf = match result {
|
||||
Ok(v) => match v {
|
||||
Value::String { val, .. } => val.into_bytes(),
|
||||
Value::Binary { val, .. } => val,
|
||||
_ => {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
format!("{:?} not supported", v.get_type()),
|
||||
v.span()?,
|
||||
));
|
||||
}
|
||||
},
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
if let Err(err) = writer.write(&buf) {
|
||||
return Err(ShellError::IOError(err.to_string()));
|
||||
let res = stream_to_file(stream, file, span);
|
||||
if let Some(h) = handler {
|
||||
match h.join() {
|
||||
Err(err) => {
|
||||
return Err(ShellError::ExternalCommand(
|
||||
"Fail to receive external commands stderr message".to_string(),
|
||||
format!("{err:?}"),
|
||||
span,
|
||||
))
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.map(|_| PipelineData::new(span))
|
||||
Ok(res) => res,
|
||||
}?;
|
||||
res
|
||||
} else {
|
||||
res
|
||||
}
|
||||
}
|
||||
input => match input.into_value(span) {
|
||||
Value::String { val, .. } => {
|
||||
@ -224,19 +265,60 @@ impl Command for Save {
|
||||
vec![
|
||||
Example {
|
||||
description: "Save a string to foo.txt in the current directory",
|
||||
example: r#"echo 'save me' | save foo.txt"#,
|
||||
example: r#"'save me' | save foo.txt"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Append a string to the end of foo.txt",
|
||||
example: r#"echo 'append me' | save --append foo.txt"#,
|
||||
example: r#"'append me' | save --append foo.txt"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Save a record to foo.json in the current directory",
|
||||
example: r#"echo { a: 1, b: 2 } | save foo.json"#,
|
||||
example: r#"{ a: 1, b: 2 } | save foo.json"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Save a running program's stderr to foo.txt",
|
||||
example: r#"do -i {} | save foo.txt --stderr foo.txt"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Save a running program's stderr to separate file",
|
||||
example: r#"do -i {} | save foo.txt --stderr bar.txt"#,
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn stream_to_file(
|
||||
mut stream: RawStream,
|
||||
file: File,
|
||||
span: Span,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let mut writer = BufWriter::new(file);
|
||||
|
||||
stream
|
||||
.try_for_each(move |result| {
|
||||
let buf = match result {
|
||||
Ok(v) => match v {
|
||||
Value::String { val, .. } => val.into_bytes(),
|
||||
Value::Binary { val, .. } => val,
|
||||
_ => {
|
||||
return Err(ShellError::UnsupportedInput(
|
||||
format!("{:?} not supported", v.get_type()),
|
||||
v.span()?,
|
||||
));
|
||||
}
|
||||
},
|
||||
Err(err) => return Err(err),
|
||||
};
|
||||
|
||||
if let Err(err) = writer.write(&buf) {
|
||||
return Err(ShellError::IOError(err.to_string()));
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.map(|_| PipelineData::new(span))
|
||||
}
|
||||
|
@ -14,20 +14,11 @@ pub struct FileStructure {
|
||||
pub resources: Vec<Resource>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl FileStructure {
|
||||
pub fn new() -> FileStructure {
|
||||
FileStructure { resources: vec![] }
|
||||
}
|
||||
|
||||
pub fn contains_more_than_one_file(&self) -> bool {
|
||||
self.resources.len() > 1
|
||||
}
|
||||
|
||||
pub fn contains_files(&self) -> bool {
|
||||
!self.resources.is_empty()
|
||||
}
|
||||
|
||||
pub fn paths_applying_with<F>(
|
||||
&mut self,
|
||||
to: F,
|
||||
|
@ -72,7 +72,7 @@ impl Command for Watch {
|
||||
.item
|
||||
.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
|
||||
|
||||
let path = match nu_path::canonicalize_with(path_no_whitespace, &cwd) {
|
||||
let path = match nu_path::canonicalize_with(path_no_whitespace, cwd) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
return Err(ShellError::DirectoryNotFound(
|
||||
|
@ -18,7 +18,7 @@ impl Command for All {
|
||||
.required(
|
||||
"predicate",
|
||||
SyntaxShape::RowCondition,
|
||||
"the predicate expression that must evaluate to a boolean",
|
||||
"the expression, or block, that must evaluate to a boolean",
|
||||
)
|
||||
.category(Category::Filters)
|
||||
}
|
||||
@ -34,18 +34,26 @@ impl Command for All {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Find if services are running",
|
||||
example: "echo [[status]; [UP] [UP]] | all status == UP",
|
||||
description: "Check if each row's status is the string 'UP'",
|
||||
example: "[[status]; [UP] [UP]] | all status == UP",
|
||||
result: Some(Value::test_bool(true)),
|
||||
},
|
||||
Example {
|
||||
description: "Check that all values are even",
|
||||
example: "echo [2 4 6 8] | all ($it mod 2) == 0",
|
||||
description:
|
||||
"Check that all of the values are even, using the built-in $it variable",
|
||||
example: "[2 4 6 8] | all ($it mod 2) == 0",
|
||||
result: Some(Value::test_bool(true)),
|
||||
},
|
||||
Example {
|
||||
description: "Check that all of the values are even, using a block",
|
||||
example: "[2 4 6 8] | all {|e| $e mod 2 == 0 }",
|
||||
result: Some(Value::test_bool(true)),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
// This is almost entirely a copy-paste of `any`'s run(), so make sure any changes to this are
|
||||
// reflected in the other!! (Or, you could figure out a way for both of them to use
|
||||
// the same function...)
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -53,7 +61,6 @@ impl Command for All {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
// let predicate = &call.positional[0];
|
||||
let span = call.head;
|
||||
|
||||
let capture_block: CaptureBlock = call.req(engine_state, stack, 0)?;
|
||||
@ -63,19 +70,24 @@ impl Command for All {
|
||||
let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
|
||||
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
||||
|
||||
let orig_env_vars = stack.env_vars.clone();
|
||||
let orig_env_hidden = stack.env_hidden.clone();
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
|
||||
for value in input.into_interruptible_iter(ctrlc) {
|
||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
||||
|
||||
if let Some(var_id) = var_id {
|
||||
stack.add_var(var_id, value);
|
||||
stack.add_var(var_id, value.clone());
|
||||
}
|
||||
|
||||
let eval = eval_block(
|
||||
&engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
PipelineData::new(span),
|
||||
value.into_pipeline_data(),
|
||||
call.redirect_stdout,
|
||||
call.redirect_stderr,
|
||||
);
|
||||
|
@ -18,7 +18,7 @@ impl Command for Any {
|
||||
.required(
|
||||
"predicate",
|
||||
SyntaxShape::RowCondition,
|
||||
"the predicate expression that should return a boolean",
|
||||
"the expression, or block, that should return a boolean",
|
||||
)
|
||||
.category(Category::Filters)
|
||||
}
|
||||
@ -34,18 +34,25 @@ impl Command for Any {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Find if a service is not running",
|
||||
example: "echo [[status]; [UP] [DOWN] [UP]] | any status == DOWN",
|
||||
description: "Check if any row's status is the string 'DOWN'",
|
||||
example: "[[status]; [UP] [DOWN] [UP]] | any status == DOWN",
|
||||
result: Some(Value::test_bool(true)),
|
||||
},
|
||||
Example {
|
||||
description: "Check if any of the values is odd",
|
||||
example: "echo [2 4 1 6 8] | any ($it mod 2) == 1",
|
||||
description: "Check if any of the values is odd, using the built-in $it variable",
|
||||
example: "[2 4 1 6 8] | any ($it mod 2) == 1",
|
||||
result: Some(Value::test_bool(true)),
|
||||
},
|
||||
Example {
|
||||
description: "Check if any of the values are odd, using a block",
|
||||
example: "[2 4 1 6 8] | any {|e| $e mod 2 == 1 }",
|
||||
result: Some(Value::test_bool(true)),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
// This is almost entirely a copy-paste of `all`'s run(), so make sure any changes to this are
|
||||
// reflected in the other!! Or, you could figure out a way for both of them to use
|
||||
// the same function...
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
@ -62,19 +69,24 @@ impl Command for Any {
|
||||
let var_id = block.signature.get_positional(0).and_then(|arg| arg.var_id);
|
||||
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
||||
|
||||
let orig_env_vars = stack.env_vars.clone();
|
||||
let orig_env_hidden = stack.env_hidden.clone();
|
||||
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
|
||||
for value in input.into_interruptible_iter(ctrlc) {
|
||||
stack.with_env(&orig_env_vars, &orig_env_hidden);
|
||||
|
||||
if let Some(var_id) = var_id {
|
||||
stack.add_var(var_id, value);
|
||||
stack.add_var(var_id, value.clone());
|
||||
}
|
||||
|
||||
let eval = eval_block(
|
||||
&engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
PipelineData::new(span),
|
||||
value.into_pipeline_data(),
|
||||
call.redirect_stdout,
|
||||
call.redirect_stderr,
|
||||
);
|
||||
|
@ -24,6 +24,13 @@ impl Command for Append {
|
||||
"Append any number of rows to a table."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
r#"Be aware that this command 'unwraps' lists passed to it. So, if you pass a variable to it,
|
||||
and you want the variable's contents to be appended without being unwrapped, it's wise to
|
||||
pre-emptively wrap the variable in a list, like so: `append [$val]`. This way, `append` will
|
||||
only unwrap the outer list, and leave the variable's contents untouched."#
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["add", "concatenate"]
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ impl Command for Compact {
|
||||
vec![
|
||||
Example {
|
||||
description: "Filter out all records where 'Hello' is null (returns nothing)",
|
||||
example: r#"echo [["Hello" "World"]; [$nothing 3]]| compact Hello"#,
|
||||
example: r#"[["Hello" "World"]; [null 3]]| compact Hello"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![],
|
||||
span: Span::test_data(),
|
||||
@ -48,7 +48,7 @@ impl Command for Compact {
|
||||
},
|
||||
Example {
|
||||
description: "Filter out all records where 'World' is null (Returns the table)",
|
||||
example: r#"echo [["Hello" "World"]; [$nothing 3]]| compact World"#,
|
||||
example: r#"[["Hello" "World"]; [null 3]]| compact World"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::Record {
|
||||
cols: vec!["Hello".into(), "World".into()],
|
||||
@ -60,7 +60,7 @@ impl Command for Compact {
|
||||
},
|
||||
Example {
|
||||
description: "Filter out all instances of nothing from a list (Returns [1,2])",
|
||||
example: r#"echo [1, $nothing, 2] | compact"#,
|
||||
example: r#"[1, null, 2] | compact"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_int(1), Value::test_int(2)],
|
||||
span: Span::test_data(),
|
||||
|
@ -50,8 +50,8 @@ impl Command for Default {
|
||||
result: None, // Some(Value::test_string("abc")),
|
||||
},
|
||||
Example {
|
||||
description: "Default the `$nothing` value in a list",
|
||||
example: "[1, 2, $nothing, 4] | default 3",
|
||||
description: "Replace the `null` value in a list",
|
||||
example: "[1, 2, null, 4] | default 3",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
|
@ -26,7 +26,7 @@ impl Command for Drop {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Remove the last number of rows or columns."
|
||||
"Remove the last several rows of the input. Counterpart of 'skip'. Opposite of 'last'."
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
|
@ -16,11 +16,21 @@ impl Command for Each {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Run a block on each element of input"
|
||||
"Run a block on each row of input"
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
r#"Since tables are lists of records, passing a table into 'each' will
|
||||
iterate over each record, not necessarily each cell within it.
|
||||
|
||||
Avoid passing single records to this command. Since a record is a
|
||||
one-row structure, 'each' will only run once, behaving similar to 'do'.
|
||||
To iterate over a record's values, try converting it to a table
|
||||
with 'transpose' first."#
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["for", "loop", "iterate"]
|
||||
vec!["for", "loop", "iterate", "map"]
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
|
@ -15,7 +15,7 @@ impl Command for EachWhile {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Run a block on each element of input until a $nothing is found"
|
||||
"Run a block on each element of input until a null is found"
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
@ -36,26 +36,44 @@ impl Command for EachWhile {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
let stream_test_1 = vec![
|
||||
Value::Int {
|
||||
val: 1,
|
||||
val: 2,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Int {
|
||||
val: 2,
|
||||
val: 4,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
];
|
||||
let stream_test_2 = vec![
|
||||
Value::String {
|
||||
val: "Output: 1".into(),
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::String {
|
||||
val: "Output: 2".into(),
|
||||
span: Span::test_data(),
|
||||
},
|
||||
];
|
||||
|
||||
vec![
|
||||
Example {
|
||||
example: "[1 2 3] | each while { |it| if $it < 3 {$it} else {$nothing} }",
|
||||
description: "Multiplies elements in list",
|
||||
example: "[1 2 3] | each while { |it| if $it < 3 { $it * 2 } else { null } }",
|
||||
description: "Multiplies elements below three by two",
|
||||
result: Some(Value::List {
|
||||
vals: stream_test_1,
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
example: r#"[1 2 3] | each while -n { |it| if $it.item < 2 { $"value ($it.item) at ($it.index)!"} else { $nothing } }"#,
|
||||
example: r#"[1 2 stop 3 4] | each while { |it| if $it == 'stop' { null } else { $"Output: ($it)" } }"#,
|
||||
description: "Output elements till reaching 'stop'",
|
||||
result: Some(Value::List {
|
||||
vals: stream_test_2,
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
example: r#"[1 2 3] | each while -n { |it| if $it.item < 2 { $"value ($it.item) at ($it.index)!"} else { null } }"#,
|
||||
description: "Iterate over each element, print the matching value and its index",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::String {
|
||||
@ -140,6 +158,7 @@ impl Command for EachWhile {
|
||||
Err(_) => None,
|
||||
}
|
||||
})
|
||||
.fuse()
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::new(call.head)),
|
||||
PipelineData::ExternalStream {
|
||||
@ -198,6 +217,7 @@ impl Command for EachWhile {
|
||||
Err(_) => None,
|
||||
}
|
||||
})
|
||||
.fuse()
|
||||
.into_pipeline_data(ctrlc)),
|
||||
PipelineData::Value(x, ..) => {
|
||||
if let Some(var) = block.signature.get_positional(0) {
|
||||
|
@ -1,13 +1,14 @@
|
||||
use crate::help::highlight_search_string;
|
||||
|
||||
use fancy_regex::Regex;
|
||||
use lscolors::{Color as LsColors_Color, Style as LsColors_Style};
|
||||
use lscolors::{Color as LsColors_Color, LsColors, Style as LsColors_Style};
|
||||
use nu_ansi_term::{Color, Color::Default, Style};
|
||||
use nu_color_config::get_color_config;
|
||||
use nu_engine::{env_to_string, eval_block, CallExt};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{CaptureBlock, Command, EngineState, Stack},
|
||||
Category, Example, IntoInterruptiblePipelineData, ListStream, PipelineData, ShellError,
|
||||
Category, Config, Example, IntoInterruptiblePipelineData, ListStream, PipelineData, ShellError,
|
||||
Signature, Span, SyntaxShape, Value,
|
||||
};
|
||||
use nu_utils::get_ls_colors;
|
||||
@ -67,7 +68,7 @@ impl Command for Find {
|
||||
},
|
||||
Example {
|
||||
description: "Search for a term in a string",
|
||||
example: r#"echo Cargo.toml | find toml"#,
|
||||
example: r#"'Cargo.toml' | find toml"#,
|
||||
result: Some(Value::test_string("Cargo.toml".to_owned()))
|
||||
},
|
||||
Example {
|
||||
@ -278,6 +279,72 @@ fn find_with_predicate(
|
||||
)
|
||||
}
|
||||
|
||||
fn highlight_terms_in_record(
|
||||
cols: &mut [String],
|
||||
vals: &mut Vec<Value>,
|
||||
span: &mut Span,
|
||||
config: &Config,
|
||||
terms: &[Value],
|
||||
string_style: Style,
|
||||
ls_colors: &LsColors,
|
||||
) -> Value {
|
||||
let mut output = vec![];
|
||||
for val in vals {
|
||||
let val_str = val.into_string("", config);
|
||||
let lower_val = val.into_string("", config).to_lowercase();
|
||||
let mut term_added_to_output = false;
|
||||
for term in terms {
|
||||
let term_str = term.into_string("", config);
|
||||
let lower_term = term.into_string("", config).to_lowercase();
|
||||
if lower_val.contains(&lower_term) {
|
||||
if config.use_ls_colors {
|
||||
// Get the original LS_COLORS color
|
||||
let style = ls_colors.style_for_path(val_str.clone());
|
||||
let ansi_style = style
|
||||
.map(LsColors_Style::to_crossterm_style)
|
||||
.unwrap_or_default();
|
||||
|
||||
let ls_colored_val = ansi_style.apply(&val_str).to_string();
|
||||
|
||||
let ansi_term_style = style
|
||||
.map(to_nu_ansi_term_style)
|
||||
.unwrap_or_else(|| string_style);
|
||||
|
||||
let hi =
|
||||
match highlight_search_string(&ls_colored_val, &term_str, &ansi_term_style)
|
||||
{
|
||||
Ok(hi) => hi,
|
||||
Err(_) => string_style.paint(term_str.to_string()).to_string(),
|
||||
};
|
||||
output.push(Value::String {
|
||||
val: hi,
|
||||
span: *span,
|
||||
});
|
||||
term_added_to_output = true;
|
||||
} else {
|
||||
// No LS_COLORS support, so just use the original value
|
||||
let hi = match highlight_search_string(&val_str, &term_str, &string_style) {
|
||||
Ok(hi) => hi,
|
||||
Err(_) => string_style.paint(term_str.to_string()).to_string(),
|
||||
};
|
||||
output.push(Value::String {
|
||||
val: hi,
|
||||
span: *span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if !term_added_to_output {
|
||||
output.push(val.clone());
|
||||
}
|
||||
}
|
||||
Value::Record {
|
||||
cols: cols.to_vec(),
|
||||
vals: output,
|
||||
span: *span,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_with_rest_and_highlight(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
@ -315,177 +382,129 @@ fn find_with_rest_and_highlight(
|
||||
let ls_colors = get_ls_colors(ls_colors_env_str);
|
||||
|
||||
match input {
|
||||
PipelineData::Value(_, _) => input.filter(
|
||||
move |value| {
|
||||
let lower_value = if let Ok(span) = value.span() {
|
||||
Value::string(value.into_string("", &config).to_lowercase(), span)
|
||||
} else {
|
||||
value.clone()
|
||||
};
|
||||
|
||||
lower_terms.iter().any(|term| match value {
|
||||
Value::Bool { .. }
|
||||
| Value::Int { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Block { .. }
|
||||
| Value::Nothing { .. }
|
||||
| Value::Error { .. } => lower_value
|
||||
.eq(span, term, span)
|
||||
.map_or(false, |val| val.is_true()),
|
||||
Value::String { .. }
|
||||
| Value::List { .. }
|
||||
| Value::CellPath { .. }
|
||||
| Value::CustomValue { .. } => term
|
||||
.r#in(span, &lower_value, span)
|
||||
.map_or(false, |val| val.is_true()),
|
||||
Value::Record { vals, .. } => vals.iter().any(|val| {
|
||||
if let Ok(span) = val.span() {
|
||||
let lower_val = Value::string(
|
||||
val.into_string("", &config).to_lowercase(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
term.r#in(span, &lower_val, span)
|
||||
.map_or(false, |aval| aval.is_true())
|
||||
} else {
|
||||
term.r#in(span, val, span)
|
||||
.map_or(false, |aval| aval.is_true())
|
||||
}
|
||||
}),
|
||||
Value::Binary { .. } => false,
|
||||
}) != invert
|
||||
},
|
||||
ctrlc,
|
||||
),
|
||||
PipelineData::ListStream(stream, meta) => {
|
||||
Ok(ListStream::from_stream(
|
||||
stream
|
||||
.map(move |mut x| match &mut x {
|
||||
Value::Record { cols, vals, span } => {
|
||||
let mut output = vec![];
|
||||
for val in vals {
|
||||
let val_str = val.into_string("", &config);
|
||||
let lower_val = val.into_string("", &config).to_lowercase();
|
||||
let mut term_added_to_output = false;
|
||||
for term in terms.clone() {
|
||||
let term_str = term.into_string("", &config);
|
||||
let lower_term = term.into_string("", &config).to_lowercase();
|
||||
if lower_val.contains(&lower_term) {
|
||||
if config.use_ls_colors {
|
||||
// Get the original LS_COLORS color
|
||||
let style = ls_colors.style_for_path(val_str.clone());
|
||||
let ansi_style = style
|
||||
.map(LsColors_Style::to_crossterm_style)
|
||||
.unwrap_or_default();
|
||||
|
||||
let ls_colored_val =
|
||||
ansi_style.apply(&val_str).to_string();
|
||||
|
||||
let ansi_term_style = style
|
||||
.map(to_nu_ansi_term_style)
|
||||
.unwrap_or_else(|| string_style);
|
||||
|
||||
let hi = match highlight_search_string(
|
||||
&ls_colored_val,
|
||||
&term_str,
|
||||
&ansi_term_style,
|
||||
) {
|
||||
Ok(hi) => hi,
|
||||
Err(_) => string_style
|
||||
.paint(term_str.to_string())
|
||||
.to_string(),
|
||||
};
|
||||
output.push(Value::String {
|
||||
val: hi,
|
||||
span: *span,
|
||||
});
|
||||
term_added_to_output = true;
|
||||
} else {
|
||||
// No LS_COLORS support, so just use the original value
|
||||
let hi = match highlight_search_string(
|
||||
&val_str,
|
||||
&term_str,
|
||||
&string_style,
|
||||
) {
|
||||
Ok(hi) => hi,
|
||||
Err(_) => string_style
|
||||
.paint(term_str.to_string())
|
||||
.to_string(),
|
||||
};
|
||||
output.push(Value::String {
|
||||
val: hi,
|
||||
span: *span,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if !term_added_to_output {
|
||||
output.push(val.clone());
|
||||
}
|
||||
}
|
||||
Value::Record {
|
||||
cols: cols.to_vec(),
|
||||
vals: output,
|
||||
span: *span,
|
||||
}
|
||||
}
|
||||
_ => x,
|
||||
})
|
||||
.filter(move |value| {
|
||||
let lower_value = if let Ok(span) = value.span() {
|
||||
Value::string(
|
||||
value.into_string("", &filter_config).to_lowercase(),
|
||||
span,
|
||||
)
|
||||
} else {
|
||||
value.clone()
|
||||
};
|
||||
|
||||
lower_terms.iter().any(|term| match value {
|
||||
Value::Bool { .. }
|
||||
| Value::Int { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Block { .. }
|
||||
| Value::Nothing { .. }
|
||||
| Value::Error { .. } => lower_value
|
||||
.eq(span, term, span)
|
||||
.map_or(false, |value| value.is_true()),
|
||||
Value::String { .. }
|
||||
| Value::List { .. }
|
||||
| Value::CellPath { .. }
|
||||
| Value::CustomValue { .. } => term
|
||||
.r#in(span, &lower_value, span)
|
||||
.map_or(false, |value| value.is_true()),
|
||||
Value::Record { vals, .. } => vals.iter().any(|val| {
|
||||
if let Ok(span) = val.span() {
|
||||
let lower_val = Value::string(
|
||||
val.into_string("", &filter_config).to_lowercase(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
term.r#in(span, &lower_val, span)
|
||||
.map_or(false, |value| value.is_true())
|
||||
} else {
|
||||
term.r#in(span, val, span)
|
||||
.map_or(false, |value| value.is_true())
|
||||
}
|
||||
}),
|
||||
Value::Binary { .. } => false,
|
||||
}) != invert
|
||||
}),
|
||||
PipelineData::Value(_, _) => input
|
||||
.map(
|
||||
move |mut x| match &mut x {
|
||||
Value::Record { cols, vals, span } => highlight_terms_in_record(
|
||||
cols,
|
||||
vals,
|
||||
span,
|
||||
&config,
|
||||
&terms,
|
||||
string_style,
|
||||
&ls_colors,
|
||||
),
|
||||
_ => x,
|
||||
},
|
||||
ctrlc.clone(),
|
||||
)
|
||||
.into_pipeline_data(ctrlc)
|
||||
.set_metadata(meta))
|
||||
}
|
||||
)?
|
||||
.filter(
|
||||
move |value| {
|
||||
let lower_value = if let Ok(span) = value.span() {
|
||||
Value::string(value.into_string("", &filter_config).to_lowercase(), span)
|
||||
} else {
|
||||
value.clone()
|
||||
};
|
||||
|
||||
lower_terms.iter().any(|term| match value {
|
||||
Value::Bool { .. }
|
||||
| Value::Int { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Block { .. }
|
||||
| Value::Nothing { .. }
|
||||
| Value::Error { .. } => lower_value
|
||||
.eq(span, term, span)
|
||||
.map_or(false, |val| val.is_true()),
|
||||
Value::String { .. }
|
||||
| Value::List { .. }
|
||||
| Value::CellPath { .. }
|
||||
| Value::CustomValue { .. } => term
|
||||
.r#in(span, &lower_value, span)
|
||||
.map_or(false, |val| val.is_true()),
|
||||
Value::Record { vals, .. } => vals.iter().any(|val| {
|
||||
if let Ok(span) = val.span() {
|
||||
let lower_val = Value::string(
|
||||
val.into_string("", &filter_config).to_lowercase(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
term.r#in(span, &lower_val, span)
|
||||
.map_or(false, |aval| aval.is_true())
|
||||
} else {
|
||||
term.r#in(span, val, span)
|
||||
.map_or(false, |aval| aval.is_true())
|
||||
}
|
||||
}),
|
||||
Value::Binary { .. } => false,
|
||||
}) != invert
|
||||
},
|
||||
ctrlc,
|
||||
),
|
||||
PipelineData::ListStream(stream, meta) => Ok(ListStream::from_stream(
|
||||
stream
|
||||
.map(move |mut x| match &mut x {
|
||||
Value::Record { cols, vals, span } => highlight_terms_in_record(
|
||||
cols,
|
||||
vals,
|
||||
span,
|
||||
&config,
|
||||
&terms,
|
||||
string_style,
|
||||
&ls_colors,
|
||||
),
|
||||
_ => x,
|
||||
})
|
||||
.filter(move |value| {
|
||||
let lower_value = if let Ok(span) = value.span() {
|
||||
Value::string(value.into_string("", &filter_config).to_lowercase(), span)
|
||||
} else {
|
||||
value.clone()
|
||||
};
|
||||
|
||||
lower_terms.iter().any(|term| match value {
|
||||
Value::Bool { .. }
|
||||
| Value::Int { .. }
|
||||
| Value::Filesize { .. }
|
||||
| Value::Duration { .. }
|
||||
| Value::Date { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::Float { .. }
|
||||
| Value::Block { .. }
|
||||
| Value::Nothing { .. }
|
||||
| Value::Error { .. } => lower_value
|
||||
.eq(span, term, span)
|
||||
.map_or(false, |value| value.is_true()),
|
||||
Value::String { .. }
|
||||
| Value::List { .. }
|
||||
| Value::CellPath { .. }
|
||||
| Value::CustomValue { .. } => term
|
||||
.r#in(span, &lower_value, span)
|
||||
.map_or(false, |value| value.is_true()),
|
||||
Value::Record { vals, .. } => vals.iter().any(|val| {
|
||||
if let Ok(span) = val.span() {
|
||||
let lower_val = Value::string(
|
||||
val.into_string("", &filter_config).to_lowercase(),
|
||||
Span::test_data(),
|
||||
);
|
||||
|
||||
term.r#in(span, &lower_val, span)
|
||||
.map_or(false, |value| value.is_true())
|
||||
} else {
|
||||
term.r#in(span, val, span)
|
||||
.map_or(false, |value| value.is_true())
|
||||
}
|
||||
}),
|
||||
Value::Binary { .. } => false,
|
||||
}) != invert
|
||||
}),
|
||||
ctrlc.clone(),
|
||||
)
|
||||
.into_pipeline_data(ctrlc)
|
||||
.set_metadata(meta)),
|
||||
PipelineData::ExternalStream { stdout: None, .. } => Ok(PipelineData::new(span)),
|
||||
PipelineData::ExternalStream {
|
||||
stdout: Some(stream),
|
||||
|
@ -25,7 +25,7 @@ impl Command for First {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Show only the first number of rows."
|
||||
"Return only the first several rows of the input. Counterpart of 'last'. Opposite of 'skip'."
|
||||
}
|
||||
|
||||
fn run(
|
||||
|
@ -25,7 +25,7 @@ impl Command for GroupBy {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Create a new table grouped."
|
||||
"Split a table into groups based on one column's values, and return a record with those groups."
|
||||
}
|
||||
|
||||
fn run(
|
||||
@ -38,16 +38,15 @@ impl Command for GroupBy {
|
||||
group_by(engine_state, stack, call, input)
|
||||
}
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "group items by column named \"type\"",
|
||||
description: "Group items by the \"type\" column's values",
|
||||
example: r#"ls | group-by type"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "you can also group by raw values by leaving out the argument",
|
||||
description: "You can also group by raw values by leaving out the argument",
|
||||
example: "echo ['1' '3' '1' '3' '2' '1' '1'] | group-by",
|
||||
result: Some(Value::Record {
|
||||
cols: vec!["1".to_string(), "3".to_string(), "2".to_string()],
|
||||
@ -211,7 +210,7 @@ pub fn data_group(
|
||||
value.as_string()
|
||||
};
|
||||
|
||||
let group = groups.entry(group_key?).or_insert(vec![]);
|
||||
let group = groups.entry(group_key?).or_default();
|
||||
group.push(value);
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ impl Command for Last {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Show only the last number of rows."
|
||||
"Return only the last several rows of the input. Counterpart of 'first'. Opposite of 'drop'."
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
|
@ -46,14 +46,14 @@ impl Command for Length {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Count the number of entries in a list",
|
||||
example: "echo [1 2 3 4 5] | length",
|
||||
description: "Count the number of items in a list",
|
||||
example: "[1 2 3 4 5] | length",
|
||||
result: Some(Value::test_int(5)),
|
||||
},
|
||||
Example {
|
||||
description: "Count the number of columns in the calendar table",
|
||||
example: "cal | length -c",
|
||||
result: Some(Value::test_int(7)),
|
||||
description: "Count the number of columns in a table",
|
||||
example: "[{columnA: A0 columnB: B0}] | length -c",
|
||||
result: Some(Value::test_int(2)),
|
||||
},
|
||||
]
|
||||
}
|
||||
@ -122,3 +122,15 @@ fn getcol(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(Length {})
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ impl Command for Lines {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Split multi-line string into lines",
|
||||
example: "echo $'two(char nl)lines' | lines",
|
||||
example: r#"echo $"two\nlines" | lines"#,
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_string("two"), Value::test_string("lines")],
|
||||
span: Span::test_data(),
|
||||
@ -252,3 +252,15 @@ impl RawStreamLinesAdapter {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(Lines {})
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, SyntaxShape, Value,
|
||||
Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
ShellError, Signature, Span, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -15,15 +15,25 @@ impl Command for Merge {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Merge a table into an input table"
|
||||
"Merge the input with a record or table, overwriting values in matching columns."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
r#"You may provide a column structure to merge, or a block
|
||||
that generates a column structure.
|
||||
|
||||
When merging tables, row 0 of the input table is overwritten
|
||||
with values from row 0 of the provided table, then
|
||||
repeating this process with row 1, and so on."#
|
||||
}
|
||||
|
||||
fn signature(&self) -> nu_protocol::Signature {
|
||||
Signature::build("merge")
|
||||
.required(
|
||||
"block",
|
||||
SyntaxShape::Block(Some(vec![])),
|
||||
"the block to run and merge into the table",
|
||||
// Both this and `update` should have a shape more like <record> | <table> | <block> than just <any>. -Leon 2022-10-27
|
||||
SyntaxShape::Any,
|
||||
"the new value to merge with, or a block that produces it",
|
||||
)
|
||||
.category(Category::Filters)
|
||||
}
|
||||
@ -32,7 +42,7 @@ impl Command for Merge {
|
||||
vec![
|
||||
Example {
|
||||
example: "[a b c] | wrap name | merge { [1 2 3] | wrap index }",
|
||||
description: "Merge an index column into the input table",
|
||||
description: "Add an 'index' column to the input table",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_record(
|
||||
@ -52,7 +62,7 @@ impl Command for Merge {
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
example: "{a: 1, b: 2} | merge { {c: 3} }",
|
||||
example: "{a: 1, b: 2} | merge {c: 3}",
|
||||
description: "Merge two records",
|
||||
result: Some(Value::test_record(
|
||||
vec!["a", "b", "c"],
|
||||
@ -60,8 +70,8 @@ impl Command for Merge {
|
||||
)),
|
||||
},
|
||||
Example {
|
||||
example: "{a: 1, b: 3} | merge { {b: 2, c: 4} }",
|
||||
description: "Merge two records with overlap key",
|
||||
example: "{a: 1, b: 3} | merge { { b: 2 } | merge { c: 4 } }",
|
||||
description: "Merge records, overwriting overlapping values",
|
||||
result: Some(Value::test_record(
|
||||
vec!["a", "b", "c"],
|
||||
vec![Value::test_int(1), Value::test_int(2), Value::test_int(4)],
|
||||
@ -77,35 +87,47 @@ impl Command for Merge {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
let block: CaptureBlock = call.req(engine_state, stack, 0)?;
|
||||
let mut stack = stack.captures_to_stack(&block.captures);
|
||||
let replacement: Value = call.req(engine_state, stack, 0)?;
|
||||
|
||||
let metadata = input.metadata();
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let block = engine_state.get_block(block.block_id);
|
||||
let call = call.clone();
|
||||
|
||||
let result = eval_block(
|
||||
engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
PipelineData::new(call.head),
|
||||
call.redirect_stdout,
|
||||
call.redirect_stderr,
|
||||
);
|
||||
let argument_was_block = replacement.as_block().is_ok();
|
||||
|
||||
let table = match result {
|
||||
Ok(res) => res,
|
||||
Err(e) => return Err(e),
|
||||
let merge_value: Value = if argument_was_block {
|
||||
// When given a block, run it to obtain the matching value.
|
||||
let capture_block: CaptureBlock = FromValue::from_value(&replacement)?;
|
||||
|
||||
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
||||
stack.with_env(&stack.env_vars.clone(), &stack.env_hidden.clone());
|
||||
|
||||
let block = engine_state.get_block(capture_block.block_id);
|
||||
let result = eval_block(
|
||||
engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
PipelineData::new(call.head),
|
||||
call.redirect_stdout,
|
||||
call.redirect_stderr,
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(res) => res.into_value(call.head),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
} else {
|
||||
// Otherwise, just use the passed-in value as-is.
|
||||
replacement
|
||||
};
|
||||
|
||||
match (&input, &table) {
|
||||
match (&input, merge_value) {
|
||||
// table (list of records)
|
||||
(
|
||||
PipelineData::Value(Value::List { .. }, ..) | PipelineData::ListStream { .. },
|
||||
PipelineData::Value(Value::List { .. }, ..) | PipelineData::ListStream { .. },
|
||||
Value::List { vals, .. },
|
||||
) => {
|
||||
let mut table_iter = table.into_iter();
|
||||
let mut table_iter = vals.into_iter();
|
||||
|
||||
let res =
|
||||
input
|
||||
@ -147,14 +169,11 @@ impl Command for Merge {
|
||||
},
|
||||
..,
|
||||
),
|
||||
PipelineData::Value(
|
||||
Value::Record {
|
||||
cols: to_merge_cols,
|
||||
vals: to_merge_vals,
|
||||
..
|
||||
},
|
||||
..,
|
||||
),
|
||||
Value::Record {
|
||||
cols: to_merge_cols,
|
||||
vals: to_merge_vals,
|
||||
..
|
||||
},
|
||||
) => {
|
||||
let (cols, vals) = do_merge(
|
||||
(inp_cols.to_vec(), inp_vals.to_vec()),
|
||||
@ -167,21 +186,29 @@ impl Command for Merge {
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
(_, PipelineData::Value(val, ..)) | (PipelineData::Value(val, ..), _) => {
|
||||
let span = if val.span()? == Span::test_data() {
|
||||
(PipelineData::Value(val, ..), merge_value) => {
|
||||
// Only point the "value originates here" arrow at the merge value
|
||||
// if it was generated from a block. Otherwise, point at the pipeline value. -Leon 2022-10-27
|
||||
let span = if argument_was_block {
|
||||
if merge_value.span()? == Span::test_data() {
|
||||
Span::new(call.head.start, call.head.start)
|
||||
} else {
|
||||
merge_value.span()?
|
||||
}
|
||||
} else if val.span()? == Span::test_data() {
|
||||
Span::new(call.head.start, call.head.start)
|
||||
} else {
|
||||
val.span()?
|
||||
};
|
||||
|
||||
Err(ShellError::PipelineMismatch(
|
||||
"record or table in both the input and the argument block".to_string(),
|
||||
"input, and argument, to be both record or both table".to_string(),
|
||||
call.head,
|
||||
span,
|
||||
))
|
||||
}
|
||||
_ => Err(ShellError::PipelineMismatch(
|
||||
"record or table in both the input and the argument block".to_string(),
|
||||
"input, and argument, to be both record or both table".to_string(),
|
||||
call.head,
|
||||
Span::new(call.head.start, call.head.start),
|
||||
)),
|
||||
|
@ -65,7 +65,6 @@ impl Command for ParEach {
|
||||
let numbered = call.has_flag("numbered");
|
||||
let metadata = input.metadata();
|
||||
let ctrlc = engine_state.ctrlc.clone();
|
||||
let engine_state = engine_state.clone();
|
||||
let block_id = capture_block.block_id;
|
||||
let mut stack = stack.captures_to_stack(&capture_block.captures);
|
||||
let span = call.head;
|
||||
@ -107,7 +106,7 @@ impl Command for ParEach {
|
||||
|
||||
let val_span = x.span();
|
||||
match eval_block(
|
||||
&engine_state,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
x.into_pipeline_data(),
|
||||
@ -159,7 +158,7 @@ impl Command for ParEach {
|
||||
|
||||
let val_span = x.span();
|
||||
match eval_block(
|
||||
&engine_state,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
x.into_pipeline_data(),
|
||||
@ -210,7 +209,7 @@ impl Command for ParEach {
|
||||
|
||||
let val_span = x.span();
|
||||
match eval_block(
|
||||
&engine_state,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
x.into_pipeline_data(),
|
||||
@ -269,7 +268,7 @@ impl Command for ParEach {
|
||||
}
|
||||
|
||||
match eval_block(
|
||||
&engine_state,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
x.into_pipeline_data(),
|
||||
@ -294,7 +293,7 @@ impl Command for ParEach {
|
||||
}
|
||||
|
||||
eval_block(
|
||||
&engine_state,
|
||||
engine_state,
|
||||
&mut stack,
|
||||
block,
|
||||
x.into_pipeline_data(),
|
||||
|
@ -28,6 +28,13 @@ impl Command for Prepend {
|
||||
"Prepend any number of rows to a table."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
r#"Be aware that this command 'unwraps' lists passed to it. So, if you pass a variable to it,
|
||||
and you want the variable's contents to be prepended without being unwrapped, it's wise to
|
||||
pre-emptively wrap the variable in a list, like so: `prepend [$val]`. This way, `prepend` will
|
||||
only unwrap the outer list, and leave the variable's contents untouched."#
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["add", "concatenate"]
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ impl Command for Shuffle {
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "Shuffle rows randomly (execute it several times and see the difference)",
|
||||
example: r#"echo [[version patch]; [1.0.0 false] [3.0.1 true] [2.0.0 false]] | shuffle"#,
|
||||
example: r#"[[version patch]; [1.0.0 false] [3.0.1 true] [2.0.0 false]] | shuffle"#,
|
||||
result: None,
|
||||
}]
|
||||
}
|
||||
|
@ -23,7 +23,11 @@ impl Command for Skip {
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Skip the first n elements of the input."
|
||||
"Skip the first several rows of the input. Counterpart of 'drop'. Opposite of 'first'."
|
||||
}
|
||||
|
||||
fn extra_usage(&self) -> &str {
|
||||
r#"To skip specific numbered rows, try 'drop nth'. To skip specific named columns, try 'reject'."#
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
|
@ -35,7 +35,6 @@ impl Command for SplitBy {
|
||||
split_by(engine_state, stack, call, input)
|
||||
}
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![Example {
|
||||
description: "split items by column named \"lang\"",
|
||||
|
@ -40,7 +40,7 @@ impl Command for Update {
|
||||
call: &Call,
|
||||
input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||
upsert(engine_state, stack, call, input)
|
||||
update(engine_state, stack, call, input)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
@ -70,7 +70,7 @@ impl Command for Update {
|
||||
}
|
||||
}
|
||||
|
||||
fn upsert(
|
||||
fn update(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
@ -138,6 +138,8 @@ fn upsert(
|
||||
for idx in 0..*val {
|
||||
if let Some(v) = input.next() {
|
||||
pre_elems.push(v);
|
||||
} else if idx == 0 {
|
||||
return Err(ShellError::AccessEmptyContent(*span));
|
||||
} else {
|
||||
return Err(ShellError::AccessBeyondEnd(idx - 1, *span));
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
use nu_engine::{eval_block, CallExt};
|
||||
use nu_protocol::ast::{Call, CellPath};
|
||||
use nu_protocol::ast::{Call, CellPath, PathMember};
|
||||
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, FromValue, IntoPipelineData, PipelineData, ShellError, Signature, Span,
|
||||
SyntaxShape, Value,
|
||||
Category, Example, FromValue, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
ShellError, Signature, Span, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -64,7 +64,29 @@ impl Command for Upsert {
|
||||
description: "Use in block form for more involved updating logic",
|
||||
example: "echo [[project, authors]; ['nu', ['Andrés', 'JT', 'Yehuda']]] | upsert authors {|a| $a.authors | str join ','}",
|
||||
result: Some(Value::List { vals: vec![Value::Record { cols: vec!["project".into(), "authors".into()], vals: vec![Value::test_string("nu"), Value::test_string("Andrés,JT,Yehuda")], span: Span::test_data()}], span: Span::test_data()}),
|
||||
}]
|
||||
},
|
||||
Example {
|
||||
description: "Upsert an int into a list, updating an existing value based on the index",
|
||||
example: "[1 2 3] | upsert 0 2",
|
||||
result: Some(Value::List {
|
||||
vals: vec![Value::test_int(2), Value::test_int(2), Value::test_int(3)],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
Example {
|
||||
description: "Upsert an int into a list, inserting a new value based on the index",
|
||||
example: "[1 2 3] | upsert 3 4",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::test_int(1),
|
||||
Value::test_int(2),
|
||||
Value::test_int(3),
|
||||
Value::test_int(4),
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,6 +151,28 @@ fn upsert(
|
||||
ctrlc,
|
||||
)
|
||||
} else {
|
||||
if let Some(PathMember::Int { val, span }) = cell_path.members.get(0) {
|
||||
let mut input = input.into_iter();
|
||||
let mut pre_elems = vec![];
|
||||
|
||||
for idx in 0..*val {
|
||||
if let Some(v) = input.next() {
|
||||
pre_elems.push(v);
|
||||
} else {
|
||||
return Err(ShellError::AccessBeyondEnd(idx - 1, *span));
|
||||
}
|
||||
}
|
||||
|
||||
// Skip over the replaced value
|
||||
let _ = input.next();
|
||||
|
||||
return Ok(pre_elems
|
||||
.into_iter()
|
||||
.chain(vec![replacement])
|
||||
.chain(input)
|
||||
.into_pipeline_data(ctrlc));
|
||||
}
|
||||
|
||||
input.map(
|
||||
move |mut input| {
|
||||
let replacement = replacement.clone();
|
||||
|
@ -4,7 +4,7 @@ use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{CaptureBlock, Command, EngineState, Stack};
|
||||
use nu_protocol::{
|
||||
Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, ShellError,
|
||||
Signature, Span, SyntaxShape, Value,
|
||||
Signature, SyntaxShape, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -238,23 +238,37 @@ impl Command for Where {
|
||||
example: "ls | where modified >= (date now) - 2wk",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Get all numbers above 3 with an existing block condition",
|
||||
example: "let a = {$in > 3}; [1, 2, 5, 6] | where -b $a",
|
||||
result: Some(Value::List {
|
||||
vals: vec![
|
||||
Value::Int {
|
||||
val: 5,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
Value::Int {
|
||||
val: 6,
|
||||
span: Span::test_data(),
|
||||
},
|
||||
],
|
||||
span: Span::test_data(),
|
||||
}),
|
||||
},
|
||||
// TODO: This should work but does not. (Note that `Let` must be present in the working_set in `example_test.rs`).
|
||||
// See https://github.com/nushell/nushell/issues/7034
|
||||
// Example {
|
||||
// description: "Get all numbers above 3 with an existing block condition",
|
||||
// example: "let a = {$in > 3}; [1, 2, 5, 6] | where -b $a",
|
||||
// result: Some(Value::List {
|
||||
// vals: vec![
|
||||
// Value::Int {
|
||||
// val: 5,
|
||||
// span: Span::test_data(),
|
||||
// },
|
||||
// Value::Int {
|
||||
// val: 6,
|
||||
// span: Span::test_data(),
|
||||
// },
|
||||
// ],
|
||||
// span: Span::test_data(),
|
||||
// }),
|
||||
// },
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use crate::test_examples;
|
||||
|
||||
test_examples(Where {})
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
use nu_engine::get_full_help;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, PipelineData, ShellError, Signature};
|
||||
use nu_protocol::{Category, IntoPipelineData, PipelineData, ShellError, Signature, Value};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct From;
|
||||
@ -20,11 +21,15 @@ impl Command for From {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||
Ok(PipelineData::new(call.head))
|
||||
Ok(Value::String {
|
||||
val: get_full_help(&From.signature(), &From.examples(), engine_state, stack),
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
}
|
||||
|
@ -84,21 +84,20 @@ impl Command for FromJson {
|
||||
|
||||
// TODO: turn this into a structured underline of the nu_json error
|
||||
if call.has_flag("objects") {
|
||||
#[allow(clippy::needless_collect)]
|
||||
let lines: Vec<String> = string_input.lines().map(|x| x.to_string()).collect();
|
||||
Ok(lines
|
||||
.into_iter()
|
||||
let converted_lines: Vec<Value> = string_input
|
||||
.lines()
|
||||
.filter_map(move |x| {
|
||||
if x.trim() == "" {
|
||||
None
|
||||
} else {
|
||||
match convert_string_to_value(x, span) {
|
||||
match convert_string_to_value(x.to_string(), span) {
|
||||
Ok(v) => Some(v),
|
||||
Err(error) => Some(Value::Error { error }),
|
||||
}
|
||||
}
|
||||
})
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
.collect();
|
||||
Ok(converted_lines.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
} else {
|
||||
Ok(convert_string_to_value(string_input, span)?.into_pipeline_data())
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ fn parse_aligned_columns<'a>(
|
||||
let parse_without_headers = |ls: Vec<&str>| {
|
||||
let mut indices = ls
|
||||
.iter()
|
||||
.flat_map(|s| find_indices(*s))
|
||||
.flat_map(|s| find_indices(s))
|
||||
.collect::<Vec<usize>>();
|
||||
|
||||
indices.sort_unstable();
|
||||
|
@ -48,27 +48,27 @@ impl Command for FromTsv {
|
||||
vec![
|
||||
Example {
|
||||
description: "Create a tsv file with header columns and open it",
|
||||
example: r#"echo $'c1(char tab)c2(char tab)c3(char nl)1(char tab)2(char tab)3' | save tsv-data | open tsv-data | from tsv"#,
|
||||
example: r#"$'c1(char tab)c2(char tab)c3(char nl)1(char tab)2(char tab)3' | save tsv-data | open tsv-data | from tsv"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Create a tsv file without header columns and open it",
|
||||
example: r#"echo $'a1(char tab)b1(char tab)c1(char nl)a2(char tab)b2(char tab)c2' | save tsv-data | open tsv-data | from tsv -n"#,
|
||||
example: r#"$'a1(char tab)b1(char tab)c1(char nl)a2(char tab)b2(char tab)c2' | save tsv-data | open tsv-data | from tsv -n"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Create a tsv file without header columns and open it, removing all unnecessary whitespaces",
|
||||
example: r#"echo $'a1(char tab)b1(char tab)c1(char nl)a2(char tab)b2(char tab)c2' | save tsv-data | open tsv-data | from tsv --trim all"#,
|
||||
example: r#"$'a1(char tab)b1(char tab)c1(char nl)a2(char tab)b2(char tab)c2' | save tsv-data | open tsv-data | from tsv --trim all"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Create a tsv file without header columns and open it, removing all unnecessary whitespaces in the header names",
|
||||
example: r#"echo $'a1(char tab)b1(char tab)c1(char nl)a2(char tab)b2(char tab)c2' | save tsv-data | open tsv-data | from tsv --trim headers"#,
|
||||
example: r#"$'a1(char tab)b1(char tab)c1(char nl)a2(char tab)b2(char tab)c2' | save tsv-data | open tsv-data | from tsv --trim headers"#,
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Create a tsv file without header columns and open it, removing all unnecessary whitespaces in the field values",
|
||||
example: r#"echo $'a1(char tab)b1(char tab)c1(char nl)a2(char tab)b2(char tab)c2' | save tsv-data | open tsv-data | from tsv --trim fields"#,
|
||||
example: r#"$'a1(char tab)b1(char tab)c1(char nl)a2(char tab)b2(char tab)c2' | save tsv-data | open tsv-data | from tsv --trim fields"#,
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
|
@ -1,6 +1,7 @@
|
||||
use nu_engine::get_full_help;
|
||||
use nu_protocol::ast::Call;
|
||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||
use nu_protocol::{Category, PipelineData, ShellError, Signature};
|
||||
use nu_protocol::{Category, IntoPipelineData, PipelineData, ShellError, Signature, Value};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct To;
|
||||
@ -20,11 +21,15 @@ impl Command for To {
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
_engine_state: &EngineState,
|
||||
_stack: &mut Stack,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||
Ok(PipelineData::new(call.head))
|
||||
Ok(Value::String {
|
||||
val: get_full_help(&To.signature(), &To.examples(), engine_state, stack),
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
}
|
||||
}
|
||||
|
@ -270,15 +270,10 @@ fn get_output_string(
|
||||
|
||||
output_string.push_str("\n|");
|
||||
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for i in 0..headers.len() {
|
||||
for &col_width in column_widths.iter().take(headers.len()) {
|
||||
if pretty {
|
||||
output_string.push(' ');
|
||||
output_string.push_str(&get_padded_string(
|
||||
String::from("-"),
|
||||
column_widths[i],
|
||||
'-',
|
||||
));
|
||||
output_string.push_str(&get_padded_string(String::from("-"), col_width, '-'));
|
||||
output_string.push(' ');
|
||||
} else {
|
||||
output_string.push('-');
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user