mirror of
https://github.com/nushell/nushell.git
synced 2025-07-01 23:22:10 +02:00
Compare commits
89 Commits
Author | SHA1 | Date | |
---|---|---|---|
60769ac1ba | |||
34e7bd861c | |||
d667b3c0bc | |||
b63c3514c4 | |||
7e9d32d64e | |||
621fb25670 | |||
a80273bd7b | |||
5473def7ef | |||
04746b8e2d | |||
31b3104af7 | |||
803bc9c63f | |||
e690e7aac0 | |||
e841fce0f9 | |||
d7b0dc1275 | |||
5627c95916 | |||
f122065772 | |||
4732507f46 | |||
5f45f6c223 | |||
a55d172e52 | |||
48e401834d | |||
d5946a9667 | |||
a432bf94ec | |||
0eabbb88dd | |||
80c8edcfb4 | |||
059167ac96 | |||
4e205cd9a7 | |||
18772b73b3 | |||
983014cc40 | |||
4ff33933dd | |||
035308bb1d | |||
e530e7d654 | |||
7d4449f021 | |||
ec3e0e593d | |||
ff09c7964e | |||
ce13ecfd10 | |||
c18e6bfca0 | |||
bc6947cd09 | |||
faaa12838e | |||
edee2a3c15 | |||
2ced9e4d19 | |||
926331dbfb | |||
eca2975b3d | |||
1cd0544a3f | |||
73e8de9753 | |||
4e83ccdf86 | |||
6d36941e55 | |||
2f44801414 | |||
9172b22985 | |||
1c37f4b958 | |||
56ed532038 | |||
b974f8f7e3 | |||
802bfed173 | |||
07e7c8c81f | |||
20b53067cd | |||
f4c0d9d45b | |||
85b06b22d9 | |||
63f00e78d1 | |||
ff1ad77130 | |||
af34d5c062 | |||
ed82f9ee18 | |||
d081e3386f | |||
ca8eb856e8 | |||
168835ecd2 | |||
4157ca711d | |||
d34a24db33 | |||
2c6b1471e1 | |||
ae5fed41ed | |||
ca73d85c09 | |||
f82c43f850 | |||
3dc9691aaa | |||
42531e017c | |||
928c57db41 | |||
d880241102 | |||
813aac89bd | |||
d2bf82d22b | |||
3f12b14053 | |||
8e2917b9ae | |||
3c3ec7891c | |||
6b839c3c32 | |||
ea22c319b6 | |||
7432e67da1 | |||
0576794e74 | |||
12f57dbc62 | |||
fe57c5c22e | |||
18161e5707 | |||
466b3899e0 | |||
7b82c6b482 | |||
e3f78b8793 | |||
c31291753c |
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@ -10,4 +10,4 @@ jobs:
|
|||||||
uses: actions/checkout@v4.1.7
|
uses: actions/checkout@v4.1.7
|
||||||
|
|
||||||
- name: Check spelling
|
- name: Check spelling
|
||||||
uses: crate-ci/typos@v1.23.3
|
uses: crate-ci/typos@v1.23.6
|
||||||
|
274
Cargo.lock
generated
274
Cargo.lock
generated
@ -297,6 +297,12 @@ version = "0.15.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ae037714f313c1353189ead58ef9eec30a8e8dc101b2622d461418fd59e28a9"
|
checksum = "9ae037714f313c1353189ead58ef9eec30a8e8dc101b2622d461418fd59e28a9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-waker"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -605,7 +611,7 @@ dependencies = [
|
|||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"quick-xml",
|
"quick-xml 0.31.0",
|
||||||
"serde",
|
"serde",
|
||||||
"zip",
|
"zip",
|
||||||
]
|
]
|
||||||
@ -1796,15 +1802,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.3.26"
|
version = "0.4.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"atomic-waker",
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"futures-util",
|
|
||||||
"http",
|
"http",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"slab",
|
"slab",
|
||||||
@ -1905,20 +1911,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "html5ever"
|
|
||||||
version = "0.26.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"mac",
|
|
||||||
"markup5ever 0.11.0",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.109",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "html5ever"
|
name = "html5ever"
|
||||||
version = "0.27.0"
|
version = "0.27.0"
|
||||||
@ -1927,7 +1919,7 @@ checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"mac",
|
"mac",
|
||||||
"markup5ever 0.12.1",
|
"markup5ever",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.60",
|
"syn 2.0.60",
|
||||||
@ -1935,9 +1927,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.12"
|
version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
|
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
@ -1946,12 +1938,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-body"
|
name = "http-body"
|
||||||
version = "0.4.6"
|
version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http",
|
"http",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "http-body-util"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1987,13 +1991,12 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.28"
|
version = "1.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2",
|
"h2",
|
||||||
"http",
|
"http",
|
||||||
@ -2002,10 +2005,23 @@ dependencies = [
|
|||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa",
|
"itoa",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
|
"smallvec",
|
||||||
|
"tokio",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hyper-util"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"hyper",
|
||||||
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
"want",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2052,9 +2068,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.2.6"
|
version = "2.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
|
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.14.5",
|
"hashbrown 0.14.5",
|
||||||
@ -2582,20 +2598,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "markup5ever"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"phf 0.10.1",
|
|
||||||
"phf_codegen 0.10.0",
|
|
||||||
"string_cache",
|
|
||||||
"string_cache_codegen",
|
|
||||||
"tendril",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "markup5ever"
|
name = "markup5ever"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -2616,8 +2618,8 @@ version = "0.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18"
|
checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"html5ever 0.27.0",
|
"html5ever",
|
||||||
"markup5ever 0.12.1",
|
"markup5ever",
|
||||||
"tendril",
|
"tendril",
|
||||||
"xml5ever",
|
"xml5ever",
|
||||||
]
|
]
|
||||||
@ -2732,13 +2734,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mockito"
|
name = "mockito"
|
||||||
version = "1.4.0"
|
version = "1.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d2f6e023aa5bdf392aa06c78e4a4e6d498baab5138d0c993503350ebbc37bf1e"
|
checksum = "09b34bd91b9e5c5b06338d392463e1318d683cf82ec3d3af4014609be6e2108d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert-json-diff",
|
"assert-json-diff",
|
||||||
"futures-core",
|
"bytes",
|
||||||
|
"futures-util",
|
||||||
|
"http",
|
||||||
|
"http-body",
|
||||||
|
"http-body-util",
|
||||||
"hyper",
|
"hyper",
|
||||||
|
"hyper-util",
|
||||||
"log",
|
"log",
|
||||||
"rand",
|
"rand",
|
||||||
"regex",
|
"regex",
|
||||||
@ -2748,6 +2755,20 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "multipart-rs"
|
||||||
|
version = "0.1.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22ea34e5c0fa65ba84707cfaf5bf43d500f1c5a4c6c36327bf5541c5bcd17e98"
|
||||||
|
dependencies = [
|
||||||
|
"bytes",
|
||||||
|
"futures-core",
|
||||||
|
"futures-util",
|
||||||
|
"memchr",
|
||||||
|
"mime",
|
||||||
|
"uuid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "multiversion"
|
name = "multiversion"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
@ -2868,7 +2889,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu"
|
name = "nu"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
@ -2877,6 +2898,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"miette",
|
"miette",
|
||||||
"mimalloc",
|
"mimalloc",
|
||||||
|
"multipart-rs",
|
||||||
"nix",
|
"nix",
|
||||||
"nu-cli",
|
"nu-cli",
|
||||||
"nu-cmd-base",
|
"nu-cmd-base",
|
||||||
@ -2922,7 +2944,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"crossterm",
|
"crossterm",
|
||||||
@ -2957,7 +2979,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-cmd-base"
|
name = "nu-cmd-base"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"miette",
|
"miette",
|
||||||
@ -2969,7 +2991,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-cmd-extra"
|
name = "nu-cmd-extra"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
@ -2994,7 +3016,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools 0.12.1",
|
"itertools 0.12.1",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -3006,7 +3028,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-cmd-plugin"
|
name = "nu-cmd-plugin"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itertools 0.12.1",
|
"itertools 0.12.1",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -3017,7 +3039,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -3029,7 +3051,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"alphanumeric-sort",
|
"alphanumeric-sort",
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
@ -3053,7 +3075,6 @@ dependencies = [
|
|||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
"filesize",
|
"filesize",
|
||||||
"filetime",
|
"filetime",
|
||||||
"fs_extra",
|
|
||||||
"human-date-parser",
|
"human-date-parser",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
@ -3064,6 +3085,7 @@ dependencies = [
|
|||||||
"mime",
|
"mime",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
"mockito",
|
"mockito",
|
||||||
|
"multipart-rs",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"nix",
|
"nix",
|
||||||
"notify-debouncer-full",
|
"notify-debouncer-full",
|
||||||
@ -3094,7 +3116,7 @@ dependencies = [
|
|||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"print-positions",
|
"print-positions",
|
||||||
"procfs",
|
"procfs",
|
||||||
"quick-xml",
|
"quick-xml 0.32.0",
|
||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quickcheck_macros",
|
"quickcheck_macros",
|
||||||
"rand",
|
"rand",
|
||||||
@ -3139,7 +3161,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-derive-value"
|
name = "nu-derive-value"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"proc-macro-error",
|
"proc-macro-error",
|
||||||
@ -3150,18 +3172,19 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-engine"
|
name = "nu-engine"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"nu-glob",
|
"nu-glob",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
|
"terminal_size",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-explore"
|
name = "nu-explore"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi-str",
|
"ansi-str",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@ -3186,14 +3209,14 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-glob"
|
name = "nu-glob"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"doc-comment",
|
"doc-comment",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-json"
|
name = "nu-json"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
"linked-hash-map",
|
"linked-hash-map",
|
||||||
@ -3206,7 +3229,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-lsp"
|
name = "nu-lsp"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"assert-json-diff",
|
"assert-json-diff",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
@ -3227,7 +3250,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-parser"
|
name = "nu-parser"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytesize",
|
"bytesize",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -3243,7 +3266,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-path"
|
name = "nu-path"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dirs",
|
"dirs",
|
||||||
"omnipath",
|
"omnipath",
|
||||||
@ -3252,7 +3275,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-plugin"
|
name = "nu-plugin"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"nix",
|
"nix",
|
||||||
@ -3268,7 +3291,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-plugin-core"
|
name = "nu-plugin-core"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"interprocess",
|
"interprocess",
|
||||||
"log",
|
"log",
|
||||||
@ -3282,7 +3305,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-plugin-engine"
|
name = "nu-plugin-engine"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"nu-engine",
|
"nu-engine",
|
||||||
@ -3298,7 +3321,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-plugin-protocol"
|
name = "nu-plugin-protocol"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bincode",
|
"bincode",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
@ -3310,7 +3333,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-plugin-test-support"
|
name = "nu-plugin-test-support"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"nu-cmd-lang",
|
"nu-cmd-lang",
|
||||||
@ -3328,7 +3351,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-pretty-hex"
|
name = "nu-pretty-hex"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heapless",
|
"heapless",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
@ -3337,7 +3360,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-protocol"
|
name = "nu-protocol"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"brotli",
|
"brotli",
|
||||||
"byte-unit",
|
"byte-unit",
|
||||||
@ -3374,7 +3397,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-std"
|
name = "nu-std"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"miette",
|
"miette",
|
||||||
@ -3385,7 +3408,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-system"
|
name = "nu-system"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"itertools 0.12.1",
|
"itertools 0.12.1",
|
||||||
@ -3403,7 +3426,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-table"
|
name = "nu-table"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
@ -3417,7 +3440,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-term-grid"
|
name = "nu-term-grid"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-utils",
|
"nu-utils",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
@ -3425,7 +3448,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-test-support"
|
name = "nu-test-support"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-glob",
|
"nu-glob",
|
||||||
"nu-path",
|
"nu-path",
|
||||||
@ -3437,7 +3460,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu-utils"
|
name = "nu-utils"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crossterm_winapi",
|
"crossterm_winapi",
|
||||||
"log",
|
"log",
|
||||||
@ -3463,7 +3486,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_example"
|
name = "nu_plugin_example"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-cmd-lang",
|
"nu-cmd-lang",
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
@ -3473,20 +3496,22 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_formats"
|
name = "nu_plugin_formats"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"eml-parser",
|
"eml-parser",
|
||||||
"ical",
|
"ical",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
"nu-plugin-test-support",
|
"nu-plugin-test-support",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
|
"plist",
|
||||||
"rust-ini",
|
"rust-ini",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_gstat"
|
name = "nu_plugin_gstat"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"git2",
|
"git2",
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
@ -3495,7 +3520,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_inc"
|
name = "nu_plugin_inc"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
"nu-protocol",
|
"nu-protocol",
|
||||||
@ -3504,7 +3529,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_polars"
|
name = "nu_plugin_polars"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"chrono-tz 0.9.0",
|
"chrono-tz 0.9.0",
|
||||||
@ -3538,7 +3563,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_query"
|
name = "nu_plugin_query"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gjson",
|
"gjson",
|
||||||
"nu-plugin",
|
"nu-plugin",
|
||||||
@ -3553,7 +3578,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nu_plugin_stress_internals"
|
name = "nu_plugin_stress_internals"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"interprocess",
|
"interprocess",
|
||||||
"serde",
|
"serde",
|
||||||
@ -3679,7 +3704,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nuon"
|
name = "nuon"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"fancy-regex",
|
"fancy-regex",
|
||||||
@ -4147,6 +4172,19 @@ dependencies = [
|
|||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "plist"
|
||||||
|
version = "1.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "42cf17e9a1800f5f396bc67d193dc9411b59012a5876445ef450d449881e1016"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"indexmap",
|
||||||
|
"quick-xml 0.32.0",
|
||||||
|
"serde",
|
||||||
|
"time",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polars"
|
name = "polars"
|
||||||
version = "0.41.2"
|
version = "0.41.2"
|
||||||
@ -4815,6 +4853,15 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-xml"
|
||||||
|
version = "0.32.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d3a6e5838b60e0e8fa7a43f22ade549a37d61f8bdbe636d0d7816191de969c2"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quickcheck"
|
name = "quickcheck"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@ -5004,9 +5051,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reedline"
|
name = "reedline"
|
||||||
version = "0.33.0"
|
version = "0.34.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f8c676a3f3814a23c6a0fc9dff6b6c35b2e04df8134aae6f3929cc34de21a53"
|
checksum = "dc271368d0d3f395745b40fababc0c9061f3fc2978189a8bc76f889e47255b01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arboard",
|
"arboard",
|
||||||
"chrono",
|
"chrono",
|
||||||
@ -5366,14 +5413,14 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scraper"
|
name = "scraper"
|
||||||
version = "0.19.0"
|
version = "0.20.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5b80b33679ff7a0ea53d37f3b39de77ea0c75b12c5805ac43ec0c33b3051af1b"
|
checksum = "b90460b31bfe1fc07be8262e42c665ad97118d4585869de9345a84d501a9eaf0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash 0.8.11",
|
"ahash 0.8.11",
|
||||||
"cssparser",
|
"cssparser",
|
||||||
"ego-tree",
|
"ego-tree",
|
||||||
"html5ever 0.26.0",
|
"html5ever",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"selectors",
|
"selectors",
|
||||||
"tendril",
|
"tendril",
|
||||||
@ -5589,9 +5636,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shadow-rs"
|
name = "shadow-rs"
|
||||||
version = "0.29.0"
|
version = "0.31.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0a600f795d0894cda22235b44eea4b85c2a35b405f65523645ac8e35b306817a"
|
checksum = "02c282402d25101f9c893e9cd7e4cae535fe7db18b81291de973026c219ddf1e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"const_format",
|
"const_format",
|
||||||
"is_debug",
|
"is_debug",
|
||||||
@ -5666,9 +5713,9 @@ checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "similar"
|
name = "similar"
|
||||||
version = "2.5.0"
|
version = "2.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640"
|
checksum = "1de1d4f81173b03af4c0cbed3c898f6bff5b870e4a7f5d6f4057d62a7a4b686e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "simplelog"
|
name = "simplelog"
|
||||||
@ -5967,9 +6014,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sysinfo"
|
name = "sysinfo"
|
||||||
version = "0.30.11"
|
version = "0.30.13"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "87341a165d73787554941cd5ef55ad728011566fe714e987d1b976c15dbc3a83"
|
checksum = "0a5b4ddaee55fb2bea2bf0e5000747e5f5c0de765e5a5ff87f4cd106439f4bb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
@ -6284,12 +6331,6 @@ dependencies = [
|
|||||||
"winnow 0.6.7",
|
"winnow 0.6.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tower-service"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.40"
|
version = "0.1.40"
|
||||||
@ -6345,12 +6386,6 @@ version = "0.1.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
|
checksum = "343e926fc669bc8cde4fa3129ab681c63671bae288b1f1081ceee6d9d37904fc"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "try-lock"
|
|
||||||
version = "0.2.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typed-arena"
|
name = "typed-arena"
|
||||||
version = "1.7.0"
|
version = "1.7.0"
|
||||||
@ -6730,15 +6765,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "want"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
|
||||||
dependencies = [
|
|
||||||
"try-lock",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.11.0+wasi-snapshot-preview1"
|
version = "0.11.0+wasi-snapshot-preview1"
|
||||||
@ -6872,7 +6898,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283"
|
checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quick-xml",
|
"quick-xml 0.31.0",
|
||||||
"quote",
|
"quote",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -6894,7 +6920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "70862efc041d46e6bbaa82bb9c34ae0596d090e86cbd14bd9e93b36ee6802eac"
|
checksum = "70862efc041d46e6bbaa82bb9c34ae0596d090e86cbd14bd9e93b36ee6802eac"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"curl",
|
"curl",
|
||||||
"html5ever 0.27.0",
|
"html5ever",
|
||||||
"markup5ever_rcdom",
|
"markup5ever_rcdom",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@ -7321,7 +7347,7 @@ checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"log",
|
||||||
"mac",
|
"mac",
|
||||||
"markup5ever 0.12.1",
|
"markup5ever",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
60
Cargo.toml
60
Cargo.toml
@ -10,8 +10,8 @@ homepage = "https://www.nushell.sh"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu"
|
name = "nu"
|
||||||
repository = "https://github.com/nushell/nushell"
|
repository = "https://github.com/nushell/nushell"
|
||||||
rust-version = "1.77.2"
|
rust-version = "1.78.0"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -90,12 +90,10 @@ encoding_rs = "0.8"
|
|||||||
fancy-regex = "0.13"
|
fancy-regex = "0.13"
|
||||||
filesize = "0.2"
|
filesize = "0.2"
|
||||||
filetime = "0.2"
|
filetime = "0.2"
|
||||||
fs_extra = "1.3"
|
|
||||||
fuzzy-matcher = "0.3"
|
fuzzy-matcher = "0.3"
|
||||||
hamcrest2 = "0.3"
|
|
||||||
heck = "0.5.0"
|
heck = "0.5.0"
|
||||||
human-date-parser = "0.1.1"
|
human-date-parser = "0.1.1"
|
||||||
indexmap = "2.2"
|
indexmap = "2.4"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
interprocess = "2.2.0"
|
interprocess = "2.2.0"
|
||||||
is_executable = "1.0"
|
is_executable = "1.0"
|
||||||
@ -110,9 +108,10 @@ lsp-types = "0.95.0"
|
|||||||
mach2 = "0.4"
|
mach2 = "0.4"
|
||||||
md5 = { version = "0.10", package = "md-5" }
|
md5 = { version = "0.10", package = "md-5" }
|
||||||
miette = "7.2"
|
miette = "7.2"
|
||||||
mime = "0.3"
|
mime = "0.3.17"
|
||||||
mime_guess = "2.0"
|
mime_guess = "2.0"
|
||||||
mockito = { version = "1.4", default-features = false }
|
mockito = { version = "1.5", default-features = false }
|
||||||
|
multipart-rs = "0.1.11"
|
||||||
native-tls = "0.2"
|
native-tls = "0.2"
|
||||||
nix = { version = "0.28", default-features = false }
|
nix = { version = "0.28", default-features = false }
|
||||||
notify-debouncer-full = { version = "0.3", default-features = false }
|
notify-debouncer-full = { version = "0.3", default-features = false }
|
||||||
@ -131,14 +130,14 @@ proc-macro-error = { version = "1.0", default-features = false }
|
|||||||
proc-macro2 = "1.0"
|
proc-macro2 = "1.0"
|
||||||
procfs = "0.16.0"
|
procfs = "0.16.0"
|
||||||
pwd = "1.3"
|
pwd = "1.3"
|
||||||
quick-xml = "0.31.0"
|
quick-xml = "0.32.0"
|
||||||
quickcheck = "1.0"
|
quickcheck = "1.0"
|
||||||
quickcheck_macros = "1.0"
|
quickcheck_macros = "1.0"
|
||||||
quote = "1.0"
|
quote = "1.0"
|
||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
ratatui = "0.26"
|
ratatui = "0.26"
|
||||||
rayon = "1.10"
|
rayon = "1.10"
|
||||||
reedline = "0.33.0"
|
reedline = "0.34.0"
|
||||||
regex = "1.9.5"
|
regex = "1.9.5"
|
||||||
rmp = "0.8"
|
rmp = "0.8"
|
||||||
rmp-serde = "1.3"
|
rmp-serde = "1.3"
|
||||||
@ -183,22 +182,22 @@ windows-sys = "0.48"
|
|||||||
winreg = "0.52"
|
winreg = "0.52"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cli = { path = "./crates/nu-cli", version = "0.96.1" }
|
nu-cli = { path = "./crates/nu-cli", version = "0.97.1" }
|
||||||
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.96.1" }
|
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.97.1" }
|
||||||
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.96.1" }
|
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.97.1" }
|
||||||
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.96.1", optional = true }
|
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.97.1", optional = true }
|
||||||
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.96.1" }
|
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.97.1" }
|
||||||
nu-command = { path = "./crates/nu-command", version = "0.96.1" }
|
nu-command = { path = "./crates/nu-command", version = "0.97.1" }
|
||||||
nu-engine = { path = "./crates/nu-engine", version = "0.96.1" }
|
nu-engine = { path = "./crates/nu-engine", version = "0.97.1" }
|
||||||
nu-explore = { path = "./crates/nu-explore", version = "0.96.1" }
|
nu-explore = { path = "./crates/nu-explore", version = "0.97.1" }
|
||||||
nu-lsp = { path = "./crates/nu-lsp/", version = "0.96.1" }
|
nu-lsp = { path = "./crates/nu-lsp/", version = "0.97.1" }
|
||||||
nu-parser = { path = "./crates/nu-parser", version = "0.96.1" }
|
nu-parser = { path = "./crates/nu-parser", version = "0.97.1" }
|
||||||
nu-path = { path = "./crates/nu-path", version = "0.96.1" }
|
nu-path = { path = "./crates/nu-path", version = "0.97.1" }
|
||||||
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.96.1" }
|
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.97.1" }
|
||||||
nu-protocol = { path = "./crates/nu-protocol", version = "0.96.1" }
|
nu-protocol = { path = "./crates/nu-protocol", version = "0.97.1" }
|
||||||
nu-std = { path = "./crates/nu-std", version = "0.96.1" }
|
nu-std = { path = "./crates/nu-std", version = "0.97.1" }
|
||||||
nu-system = { path = "./crates/nu-system", version = "0.96.1" }
|
nu-system = { path = "./crates/nu-system", version = "0.97.1" }
|
||||||
nu-utils = { path = "./crates/nu-utils", version = "0.96.1" }
|
nu-utils = { path = "./crates/nu-utils", version = "0.97.1" }
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
crossterm = { workspace = true }
|
crossterm = { workspace = true }
|
||||||
@ -207,6 +206,7 @@ dirs = { workspace = true }
|
|||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
|
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
|
||||||
mimalloc = { version = "0.1.42", default-features = false, optional = true }
|
mimalloc = { version = "0.1.42", default-features = false, optional = true }
|
||||||
|
multipart-rs = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
simplelog = "0.12"
|
simplelog = "0.12"
|
||||||
time = "0.3"
|
time = "0.3"
|
||||||
@ -227,9 +227,9 @@ nix = { workspace = true, default-features = false, features = [
|
|||||||
] }
|
] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "./crates/nu-test-support", version = "0.96.1" }
|
nu-test-support = { path = "./crates/nu-test-support", version = "0.97.1" }
|
||||||
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.96.1" }
|
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.97.1" }
|
||||||
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.96.1" }
|
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.97.1" }
|
||||||
assert_cmd = "2.0"
|
assert_cmd = "2.0"
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
tango-bench = "0.5"
|
tango-bench = "0.5"
|
||||||
@ -305,7 +305,7 @@ bench = false
|
|||||||
|
|
||||||
# To use a development version of a dependency please use a global override here
|
# To use a development version of a dependency please use a global override here
|
||||||
# changing versions in each sub-crate of the workspace is tedious
|
# changing versions in each sub-crate of the workspace is tedious
|
||||||
# [patch.crates-io]
|
[patch.crates-io]
|
||||||
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
|
||||||
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
||||||
|
|
||||||
@ -313,4 +313,4 @@ bench = false
|
|||||||
# Run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
|
# Run individual benchmarks like `cargo bench -- <regex>` e.g. `cargo bench -- parse`
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "benchmarks"
|
name = "benchmarks"
|
||||||
harness = false
|
harness = false
|
||||||
|
29
SECURITY.md
Normal file
29
SECURITY.md
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
As a shell and programming language Nushell provides you with great powers and the potential to do dangerous things to your computer and data. Whenever there is a risk that a malicious actor can abuse a bug or a violation of documented behavior/assumptions in Nushell to harm you this is a *security* risk.
|
||||||
|
We want to fix those issues without exposing our users to unnecessary risk. Thus we want to explain our security policy.
|
||||||
|
Additional issues may be part of *safety* where the behavior of Nushell as designed and implemented can cause unintended harm or a bug causes damage without the involvement of a third party.
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
As Nushell is still under very active pre-stable development, the only version the core team prioritizes for security and safety fixes is the [most recent version as published on GitHub](https://github.com/nushell/nushell/releases/latest).
|
||||||
|
Only if you provide a strong reasoning and the necessary resources, will we consider blessing a backported fix with an official patch release for a previous version.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you suspect that a bug or behavior of Nushell can affect security or may be potentially exploitable, please report the issue to us in private.
|
||||||
|
Either reach out to the core team on [our Discord server](https://discord.gg/NtAbbGn) to arrange a private channel or use the [GitHub vulnerability reporting form](https://github.com/nushell/nushell/security/advisories/new).
|
||||||
|
Please try to answer the following questions:
|
||||||
|
- How can we reach you for further questions?
|
||||||
|
- What is the bug? Which system of Nushell may be affected?
|
||||||
|
- Do you have proof-of-concept for a potential exploit or have you observed an exploit in the wild?
|
||||||
|
- What is your assessment of the severity based on what could be impacted should the bug be exploited?
|
||||||
|
- Are additional people aware of the issue or deserve credit for identifying the issue?
|
||||||
|
|
||||||
|
We will try to get back to you within a week with:
|
||||||
|
- acknowledging the receipt of the report
|
||||||
|
- an initial plan of how we want to address this including the primary points of contact for further communication
|
||||||
|
- our preliminary assessment of how severe we judge the issue
|
||||||
|
- a proposal for how we can coordinate responsible disclosure (e.g. how we ship the bugfix, if we need to coordinate with distribution maintainers, when you can release a blog post if you want to etc.)
|
||||||
|
|
||||||
|
For purely *safety* related issues where the impact is severe by direct user action instead of malicious input or third parties, feel free to open a regular issue. If we deem that there may be an additional *security* risk on a *safety* issue we may continue discussions in a restricted forum.
|
@ -5,27 +5,27 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cli"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cli"
|
name = "nu-cli"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.96.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.97.1" }
|
||||||
nu-command = { path = "../nu-command", version = "0.96.1" }
|
nu-command = { path = "../nu-command", version = "0.97.1" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.96.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.97.1" }
|
||||||
rstest = { workspace = true, default-features = false }
|
rstest = { workspace = true, default-features = false }
|
||||||
tempfile = { workspace = true }
|
tempfile = { workspace = true }
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.96.1" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.97.1" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.96.1" }
|
nu-engine = { path = "../nu-engine", version = "0.97.1" }
|
||||||
nu-path = { path = "../nu-path", version = "0.96.1" }
|
nu-path = { path = "../nu-path", version = "0.97.1" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.96.1" }
|
nu-parser = { path = "../nu-parser", version = "0.97.1" }
|
||||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.96.1", optional = true }
|
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.97.1", optional = true }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.96.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.97.1" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.96.1" }
|
nu-utils = { path = "../nu-utils", version = "0.97.1" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.96.1" }
|
nu-color-config = { path = "../nu-color-config", version = "0.97.1" }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
|
||||||
|
|
||||||
|
@ -67,7 +67,7 @@ impl Command for History {
|
|||||||
} else {
|
} else {
|
||||||
let history_reader: Option<Box<dyn ReedlineHistory>> = match history.file_format {
|
let history_reader: Option<Box<dyn ReedlineHistory>> = match history.file_format {
|
||||||
HistoryFileFormat::Sqlite => {
|
HistoryFileFormat::Sqlite => {
|
||||||
SqliteBackedHistory::with_file(history_path.clone(), None, None)
|
SqliteBackedHistory::with_file(history_path.clone().into(), None, None)
|
||||||
.map(|inner| {
|
.map(|inner| {
|
||||||
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
||||||
boxed
|
boxed
|
||||||
@ -77,7 +77,7 @@ impl Command for History {
|
|||||||
|
|
||||||
HistoryFileFormat::PlainText => FileBackedHistory::with_file(
|
HistoryFileFormat::PlainText => FileBackedHistory::with_file(
|
||||||
history.max_size as usize,
|
history.max_size as usize,
|
||||||
history_path.clone(),
|
history_path.clone().into(),
|
||||||
)
|
)
|
||||||
.map(|inner| {
|
.map(|inner| {
|
||||||
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
let boxed: Box<dyn ReedlineHistory> = Box::new(inner);
|
||||||
@ -156,58 +156,34 @@ fn create_history_record(idx: usize, entry: HistoryItem, long: bool, head: Span)
|
|||||||
//2. Create a record of either short or long columns and values
|
//2. Create a record of either short or long columns and values
|
||||||
|
|
||||||
let item_id_value = Value::int(
|
let item_id_value = Value::int(
|
||||||
match entry.id {
|
entry
|
||||||
Some(id) => {
|
.id
|
||||||
let ids = id.to_string();
|
.and_then(|id| id.to_string().parse::<i64>().ok())
|
||||||
match ids.parse::<i64>() {
|
.unwrap_or_default(),
|
||||||
Ok(i) => i,
|
|
||||||
_ => 0i64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => 0i64,
|
|
||||||
},
|
|
||||||
head,
|
head,
|
||||||
);
|
);
|
||||||
let start_timestamp_value = Value::string(
|
let start_timestamp_value = Value::string(
|
||||||
match entry.start_timestamp {
|
entry
|
||||||
Some(time) => time.to_string(),
|
.start_timestamp
|
||||||
None => "".into(),
|
.map(|time| time.to_string())
|
||||||
},
|
.unwrap_or_default(),
|
||||||
head,
|
head,
|
||||||
);
|
);
|
||||||
let command_value = Value::string(entry.command_line, head);
|
let command_value = Value::string(entry.command_line, head);
|
||||||
let session_id_value = Value::int(
|
let session_id_value = Value::int(
|
||||||
match entry.session_id {
|
entry
|
||||||
Some(sid) => {
|
.session_id
|
||||||
let sids = sid.to_string();
|
.and_then(|id| id.to_string().parse::<i64>().ok())
|
||||||
match sids.parse::<i64>() {
|
.unwrap_or_default(),
|
||||||
Ok(i) => i,
|
|
||||||
_ => 0i64,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => 0i64,
|
|
||||||
},
|
|
||||||
head,
|
|
||||||
);
|
|
||||||
let hostname_value = Value::string(
|
|
||||||
match entry.hostname {
|
|
||||||
Some(host) => host,
|
|
||||||
None => "".into(),
|
|
||||||
},
|
|
||||||
head,
|
|
||||||
);
|
|
||||||
let cwd_value = Value::string(
|
|
||||||
match entry.cwd {
|
|
||||||
Some(cwd) => cwd,
|
|
||||||
None => "".into(),
|
|
||||||
},
|
|
||||||
head,
|
head,
|
||||||
);
|
);
|
||||||
|
let hostname_value = Value::string(entry.hostname.unwrap_or_default(), head);
|
||||||
|
let cwd_value = Value::string(entry.cwd.unwrap_or_default(), head);
|
||||||
let duration_value = Value::duration(
|
let duration_value = Value::duration(
|
||||||
match entry.duration {
|
entry
|
||||||
Some(d) => d.as_nanos().try_into().unwrap_or(0),
|
.duration
|
||||||
None => 0,
|
.and_then(|d| d.as_nanos().try_into().ok())
|
||||||
},
|
.unwrap_or(0),
|
||||||
head,
|
head,
|
||||||
);
|
);
|
||||||
let exit_status_value = Value::int(entry.exit_status.unwrap_or(0), head);
|
let exit_status_value = Value::int(entry.exit_status.unwrap_or(0), head);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
completions::{Completer, CompletionOptions, MatchAlgorithm, SortBy},
|
completions::{Completer, CompletionOptions, MatchAlgorithm},
|
||||||
SuggestionKind,
|
SuggestionKind,
|
||||||
};
|
};
|
||||||
use nu_parser::FlatShape;
|
use nu_parser::FlatShape;
|
||||||
@ -99,10 +99,9 @@ impl CommandCompletion {
|
|||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(&x.0).to_string(),
|
value: String::from_utf8_lossy(&x.0).to_string(),
|
||||||
description: x.1,
|
description: x.1,
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span::new(span.start - offset, span.end - offset),
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Command(x.2)),
|
kind: Some(SuggestionKind::Command(x.2)),
|
||||||
})
|
})
|
||||||
@ -118,11 +117,9 @@ impl CommandCompletion {
|
|||||||
.map(move |x| SemanticSuggestion {
|
.map(move |x| SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: x,
|
value: x,
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span::new(span.start - offset, span.end - offset),
|
span: reedline::Span::new(span.start - offset, span.end - offset),
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO: is there a way to create a test?
|
// TODO: is there a way to create a test?
|
||||||
kind: None,
|
kind: None,
|
||||||
@ -136,11 +133,9 @@ impl CommandCompletion {
|
|||||||
results.push(SemanticSuggestion {
|
results.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: format!("^{}", external.suggestion.value),
|
value: format!("^{}", external.suggestion.value),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: external.suggestion.span,
|
span: external.suggestion.span,
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: external.kind,
|
kind: external.kind,
|
||||||
})
|
})
|
||||||
@ -198,11 +193,7 @@ impl Completer for CommandCompletion {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if !subcommands.is_empty() {
|
if !subcommands.is_empty() {
|
||||||
return sort_suggestions(
|
return sort_suggestions(&String::from_utf8_lossy(&prefix), subcommands, options);
|
||||||
&String::from_utf8_lossy(&prefix),
|
|
||||||
subcommands,
|
|
||||||
SortBy::LevenshteinDistance,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let config = working_set.get_config();
|
let config = working_set.get_config();
|
||||||
@ -227,11 +218,7 @@ impl Completer for CommandCompletion {
|
|||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
sort_suggestions(
|
sort_suggestions(&String::from_utf8_lossy(&prefix), commands, options)
|
||||||
&String::from_utf8_lossy(&prefix),
|
|
||||||
commands,
|
|
||||||
SortBy::LevenshteinDistance,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ impl NuCompleter {
|
|||||||
let options = CompletionOptions {
|
let options = CompletionOptions {
|
||||||
case_sensitive: config.case_sensitive_completions,
|
case_sensitive: config.case_sensitive_completions,
|
||||||
match_algorithm: config.completion_algorithm.into(),
|
match_algorithm: config.completion_algorithm.into(),
|
||||||
|
sort: config.completion_sort,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -443,14 +444,11 @@ pub fn map_value_completions<'a>(
|
|||||||
return Some(SemanticSuggestion {
|
return Some(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: s,
|
value: s,
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: span.start - offset,
|
start: span.start - offset,
|
||||||
end: span.end - offset,
|
end: span.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Type(x.get_type())),
|
kind: Some(SuggestionKind::Type(x.get_type())),
|
||||||
});
|
});
|
||||||
@ -460,14 +458,11 @@ pub fn map_value_completions<'a>(
|
|||||||
if let Ok(record) = x.as_record() {
|
if let Ok(record) = x.as_record() {
|
||||||
let mut suggestion = Suggestion {
|
let mut suggestion = Suggestion {
|
||||||
value: String::from(""), // Initialize with empty string
|
value: String::from(""), // Initialize with empty string
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: span.start - offset,
|
start: span.start - offset,
|
||||||
end: span.end - offset,
|
end: span.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Iterate the cols looking for `value` and `description`
|
// Iterate the cols looking for `value` and `description`
|
||||||
|
@ -2,19 +2,18 @@ use crate::{
|
|||||||
completions::{matches, CompletionOptions},
|
completions::{matches, CompletionOptions},
|
||||||
SemanticSuggestion,
|
SemanticSuggestion,
|
||||||
};
|
};
|
||||||
|
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use nu_engine::env_to_string;
|
use nu_engine::env_to_string;
|
||||||
use nu_path::{expand_to_real_path, home_dir};
|
use nu_path::{expand_to_real_path, home_dir};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
levenshtein_distance, Span,
|
CompletionSort, Span,
|
||||||
};
|
};
|
||||||
use nu_utils::get_ls_colors;
|
use nu_utils::get_ls_colors;
|
||||||
use std::path::{
|
use std::path::{is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP};
|
||||||
is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::SortBy;
|
use super::MatchAlgorithm;
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct PathBuiltFromString {
|
pub struct PathBuiltFromString {
|
||||||
@ -22,12 +21,21 @@ pub struct PathBuiltFromString {
|
|||||||
isdir: bool,
|
isdir: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn complete_rec(
|
/// Recursively goes through paths that match a given `partial`.
|
||||||
|
/// built: State struct for a valid matching path built so far.
|
||||||
|
///
|
||||||
|
/// `isdir`: whether the current partial path has a trailing slash.
|
||||||
|
/// Parsing a path string into a pathbuf loses that bit of information.
|
||||||
|
///
|
||||||
|
/// want_directory: Whether we want only directories as completion matches.
|
||||||
|
/// Some commands like `cd` can only be run on directories whereas others
|
||||||
|
/// like `ls` can be run on regular files as well.
|
||||||
|
pub fn complete_rec(
|
||||||
partial: &[&str],
|
partial: &[&str],
|
||||||
built: &PathBuiltFromString,
|
built: &PathBuiltFromString,
|
||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
options: &CompletionOptions,
|
options: &CompletionOptions,
|
||||||
dir: bool,
|
want_directory: bool,
|
||||||
isdir: bool,
|
isdir: bool,
|
||||||
) -> Vec<PathBuiltFromString> {
|
) -> Vec<PathBuiltFromString> {
|
||||||
let mut completions = vec![];
|
let mut completions = vec![];
|
||||||
@ -37,7 +45,7 @@ fn complete_rec(
|
|||||||
let mut built = built.clone();
|
let mut built = built.clone();
|
||||||
built.parts.push(base.to_string());
|
built.parts.push(base.to_string());
|
||||||
built.isdir = true;
|
built.isdir = true;
|
||||||
return complete_rec(rest, &built, cwd, options, dir, isdir);
|
return complete_rec(rest, &built, cwd, options, want_directory, isdir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,24 +66,41 @@ fn complete_rec(
|
|||||||
built.parts.push(entry_name.clone());
|
built.parts.push(entry_name.clone());
|
||||||
built.isdir = entry_isdir;
|
built.isdir = entry_isdir;
|
||||||
|
|
||||||
if !dir || entry_isdir {
|
if !want_directory || entry_isdir {
|
||||||
entries.push((entry_name, built));
|
entries.push((entry_name, built));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let prefix = partial.first().unwrap_or(&"");
|
let prefix = partial.first().unwrap_or(&"");
|
||||||
let sorted_entries = sort_completions(prefix, entries, SortBy::Ascending, |(entry, _)| entry);
|
let sorted_entries = sort_completions(prefix, entries, options, |(entry, _)| entry);
|
||||||
|
|
||||||
for (entry_name, built) in sorted_entries {
|
for (entry_name, built) in sorted_entries {
|
||||||
match partial.split_first() {
|
match partial.split_first() {
|
||||||
Some((base, rest)) => {
|
Some((base, rest)) => {
|
||||||
if matches(base, &entry_name, options) {
|
if matches(base, &entry_name, options) {
|
||||||
|
// We use `isdir` to confirm that the current component has
|
||||||
|
// at least one next component or a slash.
|
||||||
|
// Serves as confirmation to ignore longer completions for
|
||||||
|
// components in between.
|
||||||
if !rest.is_empty() || isdir {
|
if !rest.is_empty() || isdir {
|
||||||
completions.extend(complete_rec(rest, &built, cwd, options, dir, isdir));
|
completions.extend(complete_rec(
|
||||||
|
rest,
|
||||||
|
&built,
|
||||||
|
cwd,
|
||||||
|
options,
|
||||||
|
want_directory,
|
||||||
|
isdir,
|
||||||
|
));
|
||||||
} else {
|
} else {
|
||||||
completions.push(built);
|
completions.push(built);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if entry_name.eq(base)
|
||||||
|
&& matches!(options.match_algorithm, MatchAlgorithm::Prefix)
|
||||||
|
&& isdir
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
completions.push(built);
|
completions.push(built);
|
||||||
@ -93,16 +118,16 @@ enum OriginalCwd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl OriginalCwd {
|
impl OriginalCwd {
|
||||||
fn apply(&self, mut p: PathBuiltFromString) -> String {
|
fn apply(&self, mut p: PathBuiltFromString, path_separator: char) -> String {
|
||||||
match self {
|
match self {
|
||||||
Self::None => {}
|
Self::None => {}
|
||||||
Self::Home => p.parts.insert(0, "~".to_string()),
|
Self::Home => p.parts.insert(0, "~".to_string()),
|
||||||
Self::Prefix(s) => p.parts.insert(0, s.clone()),
|
Self::Prefix(s) => p.parts.insert(0, s.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut ret = p.parts.join(MAIN_SEPARATOR_STR);
|
let mut ret = p.parts.join(&path_separator.to_string());
|
||||||
if p.isdir {
|
if p.isdir {
|
||||||
ret.push(SEP);
|
ret.push(path_separator);
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
@ -133,6 +158,14 @@ pub fn complete_item(
|
|||||||
) -> Vec<(nu_protocol::Span, String, Option<Style>)> {
|
) -> Vec<(nu_protocol::Span, String, Option<Style>)> {
|
||||||
let partial = surround_remove(partial);
|
let partial = surround_remove(partial);
|
||||||
let isdir = partial.ends_with(is_separator);
|
let isdir = partial.ends_with(is_separator);
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let path_separator = SEP;
|
||||||
|
#[cfg(windows)]
|
||||||
|
let path_separator = partial
|
||||||
|
.chars()
|
||||||
|
.rfind(|c: &char| is_separator(*c))
|
||||||
|
.unwrap_or(SEP);
|
||||||
let cwd_pathbuf = Path::new(cwd).to_path_buf();
|
let cwd_pathbuf = Path::new(cwd).to_path_buf();
|
||||||
let ls_colors = (engine_state.config.use_ls_colors_completions
|
let ls_colors = (engine_state.config.use_ls_colors_completions
|
||||||
&& engine_state.config.use_ansi_coloring)
|
&& engine_state.config.use_ansi_coloring)
|
||||||
@ -170,7 +203,7 @@ pub fn complete_item(
|
|||||||
}
|
}
|
||||||
Some(Component::Normal(home)) if home.to_string_lossy() == "~" => {
|
Some(Component::Normal(home)) if home.to_string_lossy() == "~" => {
|
||||||
components.next();
|
components.next();
|
||||||
cwd = home_dir().unwrap_or(cwd_pathbuf);
|
cwd = home_dir().map(Into::into).unwrap_or(cwd_pathbuf);
|
||||||
prefix_len = 1;
|
prefix_len = 1;
|
||||||
original_cwd = OriginalCwd::Home;
|
original_cwd = OriginalCwd::Home;
|
||||||
}
|
}
|
||||||
@ -195,7 +228,7 @@ pub fn complete_item(
|
|||||||
)
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| {
|
.map(|p| {
|
||||||
let path = original_cwd.apply(p);
|
let path = original_cwd.apply(p, path_separator);
|
||||||
let style = ls_colors.as_ref().map(|lsc| {
|
let style = ls_colors.as_ref().map(|lsc| {
|
||||||
lsc.style_for_path_with_metadata(
|
lsc.style_for_path_with_metadata(
|
||||||
&path,
|
&path,
|
||||||
@ -273,33 +306,37 @@ pub fn adjust_if_intermediate(
|
|||||||
pub fn sort_suggestions(
|
pub fn sort_suggestions(
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
items: Vec<SemanticSuggestion>,
|
items: Vec<SemanticSuggestion>,
|
||||||
sort_by: SortBy,
|
options: &CompletionOptions,
|
||||||
) -> Vec<SemanticSuggestion> {
|
) -> Vec<SemanticSuggestion> {
|
||||||
sort_completions(prefix, items, sort_by, |it| &it.suggestion.value)
|
sort_completions(prefix, items, options, |it| &it.suggestion.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `prefix` - What the user's typed, for sorting by Levenshtein distance
|
/// * `prefix` - What the user's typed, for sorting by fuzzy matcher score
|
||||||
pub fn sort_completions<T>(
|
pub fn sort_completions<T>(
|
||||||
prefix: &str,
|
prefix: &str,
|
||||||
mut items: Vec<T>,
|
mut items: Vec<T>,
|
||||||
sort_by: SortBy,
|
options: &CompletionOptions,
|
||||||
get_value: fn(&T) -> &str,
|
get_value: fn(&T) -> &str,
|
||||||
) -> Vec<T> {
|
) -> Vec<T> {
|
||||||
// Sort items
|
// Sort items
|
||||||
match sort_by {
|
if options.sort == CompletionSort::Smart && options.match_algorithm == MatchAlgorithm::Fuzzy {
|
||||||
SortBy::LevenshteinDistance => {
|
let mut matcher = SkimMatcherV2::default();
|
||||||
items.sort_by(|a, b| {
|
if options.case_sensitive {
|
||||||
let a_distance = levenshtein_distance(prefix, get_value(a));
|
matcher = matcher.respect_case();
|
||||||
let b_distance = levenshtein_distance(prefix, get_value(b));
|
} else {
|
||||||
a_distance.cmp(&b_distance)
|
matcher = matcher.ignore_case();
|
||||||
});
|
};
|
||||||
}
|
items.sort_by(|a, b| {
|
||||||
SortBy::Ascending => {
|
let a_str = get_value(a);
|
||||||
items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
|
let b_str = get_value(b);
|
||||||
}
|
let a_score = matcher.fuzzy_match(a_str, prefix).unwrap_or_default();
|
||||||
SortBy::None => {}
|
let b_score = matcher.fuzzy_match(b_str, prefix).unwrap_or_default();
|
||||||
};
|
b_score.cmp(&a_score).then(a_str.cmp(b_str))
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
|
||||||
|
}
|
||||||
|
|
||||||
items
|
items
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,10 @@
|
|||||||
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
use fuzzy_matcher::{skim::SkimMatcherV2, FuzzyMatcher};
|
||||||
use nu_parser::trim_quotes_str;
|
use nu_parser::trim_quotes_str;
|
||||||
use nu_protocol::CompletionAlgorithm;
|
use nu_protocol::{CompletionAlgorithm, CompletionSort};
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub enum SortBy {
|
|
||||||
LevenshteinDistance,
|
|
||||||
Ascending,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Describes how suggestions should be matched.
|
/// Describes how suggestions should be matched.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub enum MatchAlgorithm {
|
pub enum MatchAlgorithm {
|
||||||
/// Only show suggestions which begin with the given input
|
/// Only show suggestions which begin with the given input
|
||||||
///
|
///
|
||||||
@ -96,6 +89,7 @@ pub struct CompletionOptions {
|
|||||||
pub case_sensitive: bool,
|
pub case_sensitive: bool,
|
||||||
pub positional: bool,
|
pub positional: bool,
|
||||||
pub match_algorithm: MatchAlgorithm,
|
pub match_algorithm: MatchAlgorithm,
|
||||||
|
pub sort: CompletionSort,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for CompletionOptions {
|
impl Default for CompletionOptions {
|
||||||
@ -104,6 +98,7 @@ impl Default for CompletionOptions {
|
|||||||
case_sensitive: true,
|
case_sensitive: true,
|
||||||
positional: true,
|
positional: true,
|
||||||
match_algorithm: MatchAlgorithm::Prefix,
|
match_algorithm: MatchAlgorithm::Prefix,
|
||||||
|
sort: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{
|
||||||
completer::map_value_completions, Completer, CompletionOptions, MatchAlgorithm,
|
completer::map_value_completions, Completer, CompletionOptions, MatchAlgorithm,
|
||||||
SemanticSuggestion, SortBy,
|
SemanticSuggestion,
|
||||||
};
|
};
|
||||||
use nu_engine::eval_call;
|
use nu_engine::eval_call;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Argument, Call, Expr, Expression},
|
ast::{Argument, Call, Expr, Expression},
|
||||||
debugger::WithoutDebug,
|
debugger::WithoutDebug,
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
PipelineData, Span, Type, Value,
|
CompletionSort, PipelineData, Span, Type, Value,
|
||||||
};
|
};
|
||||||
use nu_utils::IgnoreCaseExt;
|
use nu_utils::IgnoreCaseExt;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
@ -18,7 +18,6 @@ pub struct CustomCompletion {
|
|||||||
stack: Stack,
|
stack: Stack,
|
||||||
decl_id: usize,
|
decl_id: usize,
|
||||||
line: String,
|
line: String,
|
||||||
sort_by: SortBy,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CustomCompletion {
|
impl CustomCompletion {
|
||||||
@ -27,7 +26,6 @@ impl CustomCompletion {
|
|||||||
stack,
|
stack,
|
||||||
decl_id,
|
decl_id,
|
||||||
line,
|
line,
|
||||||
sort_by: SortBy::None,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,10 +91,6 @@ impl Completer for CustomCompletion {
|
|||||||
.and_then(|val| val.as_bool().ok())
|
.and_then(|val| val.as_bool().ok())
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
if should_sort {
|
|
||||||
self.sort_by = SortBy::Ascending;
|
|
||||||
}
|
|
||||||
|
|
||||||
custom_completion_options = Some(CompletionOptions {
|
custom_completion_options = Some(CompletionOptions {
|
||||||
case_sensitive: options
|
case_sensitive: options
|
||||||
.get("case_sensitive")
|
.get("case_sensitive")
|
||||||
@ -114,6 +108,11 @@ impl Completer for CustomCompletion {
|
|||||||
.unwrap_or(MatchAlgorithm::Prefix),
|
.unwrap_or(MatchAlgorithm::Prefix),
|
||||||
None => completion_options.match_algorithm,
|
None => completion_options.match_algorithm,
|
||||||
},
|
},
|
||||||
|
sort: if should_sort {
|
||||||
|
CompletionSort::Alphabetical
|
||||||
|
} else {
|
||||||
|
CompletionSort::Smart
|
||||||
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,12 +123,11 @@ impl Completer for CustomCompletion {
|
|||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
|
||||||
let suggestions = if let Some(custom_completion_options) = custom_completion_options {
|
let options = custom_completion_options
|
||||||
filter(&prefix, suggestions, &custom_completion_options)
|
.as_ref()
|
||||||
} else {
|
.unwrap_or(completion_options);
|
||||||
filter(&prefix, suggestions, completion_options)
|
let suggestions = filter(&prefix, suggestions, completion_options);
|
||||||
};
|
sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, options)
|
||||||
sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, self.sort_by)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,14 +48,12 @@ impl Completer for DirectoryCompletion {
|
|||||||
.map(move |x| SemanticSuggestion {
|
.map(move |x| SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: x.1,
|
value: x.1,
|
||||||
description: None,
|
|
||||||
style: x.2,
|
style: x.2,
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: x.0.start - offset,
|
start: x.0.start - offset,
|
||||||
end: x.0.end - offset,
|
end: x.0.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
|
@ -6,7 +6,7 @@ use nu_protocol::{
|
|||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
|
use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
|
||||||
|
|
||||||
use super::{completion_common::sort_suggestions, SemanticSuggestion, SortBy};
|
use super::{completion_common::sort_suggestions, SemanticSuggestion};
|
||||||
|
|
||||||
#[derive(Clone, Default)]
|
#[derive(Clone, Default)]
|
||||||
pub struct DotNuCompletion {}
|
pub struct DotNuCompletion {}
|
||||||
@ -116,14 +116,13 @@ impl Completer for DotNuCompletion {
|
|||||||
.map(move |x| SemanticSuggestion {
|
.map(move |x| SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: x.1,
|
value: x.1,
|
||||||
description: None,
|
|
||||||
style: x.2,
|
style: x.2,
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: x.0.start - offset,
|
start: x.0.start - offset,
|
||||||
end: x.0.end - offset,
|
end: x.0.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
@ -131,6 +130,6 @@ impl Completer for DotNuCompletion {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
sort_suggestions(&prefix_str, output, SortBy::Ascending)
|
sort_suggestions(&prefix_str, output, options)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,14 +53,12 @@ impl Completer for FileCompletion {
|
|||||||
.map(move |x| SemanticSuggestion {
|
.map(move |x| SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: x.1,
|
value: x.1,
|
||||||
description: None,
|
|
||||||
style: x.2,
|
style: x.2,
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: x.0.start - offset,
|
start: x.0.start - offset,
|
||||||
end: x.0.end - offset,
|
end: x.0.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
use crate::completions::{
|
use crate::completions::{completion_common::sort_suggestions, Completer, CompletionOptions};
|
||||||
completion_common::sort_suggestions, Completer, CompletionOptions, SortBy,
|
|
||||||
};
|
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Expr, Expression},
|
ast::{Expr, Expression},
|
||||||
engine::{Stack, StateWorkingSet},
|
engine::{Stack, StateWorkingSet},
|
||||||
@ -51,13 +49,12 @@ impl Completer for FlagCompletion {
|
|||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(&named).to_string(),
|
value: String::from_utf8_lossy(&named).to_string(),
|
||||||
description: Some(flag_desc.to_string()),
|
description: Some(flag_desc.to_string()),
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: span.start - offset,
|
start: span.start - offset,
|
||||||
end: span.end - offset,
|
end: span.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
@ -78,13 +75,12 @@ impl Completer for FlagCompletion {
|
|||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(&named).to_string(),
|
value: String::from_utf8_lossy(&named).to_string(),
|
||||||
description: Some(flag_desc.to_string()),
|
description: Some(flag_desc.to_string()),
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: span.start - offset,
|
start: span.start - offset,
|
||||||
end: span.end - offset,
|
end: span.end - offset,
|
||||||
},
|
},
|
||||||
append_whitespace: true,
|
append_whitespace: true,
|
||||||
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO????
|
// TODO????
|
||||||
kind: None,
|
kind: None,
|
||||||
@ -92,7 +88,7 @@ impl Completer for FlagCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sort_suggestions(&String::from_utf8_lossy(&prefix), output, SortBy::Ascending);
|
return sort_suggestions(&String::from_utf8_lossy(&prefix), output, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
vec![]
|
vec![]
|
||||||
|
@ -13,7 +13,7 @@ mod variable_completions;
|
|||||||
pub use base::{Completer, SemanticSuggestion, SuggestionKind};
|
pub use base::{Completer, SemanticSuggestion, SuggestionKind};
|
||||||
pub use command_completions::CommandCompletion;
|
pub use command_completions::CommandCompletion;
|
||||||
pub use completer::NuCompleter;
|
pub use completer::NuCompleter;
|
||||||
pub use completion_options::{CompletionOptions, MatchAlgorithm, SortBy};
|
pub use completion_options::{CompletionOptions, MatchAlgorithm};
|
||||||
pub use custom_completions::CustomCompletion;
|
pub use custom_completions::CustomCompletion;
|
||||||
pub use directory_completions::DirectoryCompletion;
|
pub use directory_completions::DirectoryCompletion;
|
||||||
pub use dotnu_completions::DotNuCompletion;
|
pub use dotnu_completions::DotNuCompletion;
|
||||||
|
@ -9,7 +9,7 @@ use nu_protocol::{
|
|||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use super::{completion_common::sort_suggestions, SortBy};
|
use super::completion_common::sort_suggestions;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct VariableCompletion {
|
pub struct VariableCompletion {
|
||||||
@ -72,7 +72,7 @@ impl Completer for VariableCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
|
return sort_suggestions(&prefix_str, output, options);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No nesting provided, return all env vars
|
// No nesting provided, return all env vars
|
||||||
@ -85,18 +85,15 @@ impl Completer for VariableCompletion {
|
|||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: env_var.0,
|
value: env_var.0,
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Type(env_var.1.get_type())),
|
kind: Some(SuggestionKind::Type(env_var.1.get_type())),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
|
return sort_suggestions(&prefix_str, output, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +117,7 @@ impl Completer for VariableCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
|
return sort_suggestions(&prefix_str, output, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +139,7 @@ impl Completer for VariableCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
|
return sort_suggestions(&prefix_str, output, options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -157,11 +154,8 @@ impl Completer for VariableCompletion {
|
|||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: builtin.to_string(),
|
value: builtin.to_string(),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
// TODO is there a way to get the VarId to get the type???
|
// TODO is there a way to get the VarId to get the type???
|
||||||
kind: None,
|
kind: None,
|
||||||
@ -184,11 +178,8 @@ impl Completer for VariableCompletion {
|
|||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(v.0).to_string(),
|
value: String::from_utf8_lossy(v.0).to_string(),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Type(
|
kind: Some(SuggestionKind::Type(
|
||||||
working_set.get_variable(*v.1).ty.clone(),
|
working_set.get_variable(*v.1).ty.clone(),
|
||||||
@ -215,11 +206,8 @@ impl Completer for VariableCompletion {
|
|||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: String::from_utf8_lossy(v.0).to_string(),
|
value: String::from_utf8_lossy(v.0).to_string(),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(SuggestionKind::Type(
|
kind: Some(SuggestionKind::Type(
|
||||||
working_set.get_variable(*v.1).ty.clone(),
|
working_set.get_variable(*v.1).ty.clone(),
|
||||||
@ -229,7 +217,7 @@ impl Completer for VariableCompletion {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output = sort_suggestions(&prefix_str, output, SortBy::Ascending);
|
output = sort_suggestions(&prefix_str, output, options);
|
||||||
|
|
||||||
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
|
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
|
||||||
|
|
||||||
@ -255,11 +243,8 @@ fn nested_suggestions(
|
|||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: col.clone(),
|
value: col.clone(),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(kind.clone()),
|
kind: Some(kind.clone()),
|
||||||
});
|
});
|
||||||
@ -272,11 +257,8 @@ fn nested_suggestions(
|
|||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
suggestion: Suggestion {
|
suggestion: Suggestion {
|
||||||
value: column_name,
|
value: column_name,
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
span: current_span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
},
|
},
|
||||||
kind: Some(kind.clone()),
|
kind: Some(kind.clone()),
|
||||||
});
|
});
|
||||||
|
@ -192,7 +192,8 @@ pub fn add_plugin_file(
|
|||||||
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
} else if let Some(mut plugin_path) = nu_path::config_dir() {
|
||||||
// Path to store plugins signatures
|
// Path to store plugins signatures
|
||||||
plugin_path.push(storage_path);
|
plugin_path.push(storage_path);
|
||||||
let mut plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
let mut plugin_path =
|
||||||
|
canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path.into());
|
||||||
plugin_path.push(PLUGIN_FILE);
|
plugin_path.push(PLUGIN_FILE);
|
||||||
let plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
let plugin_path = canonicalize_with(&plugin_path, &cwd).unwrap_or(plugin_path);
|
||||||
engine_state.plugin_path = Some(plugin_path);
|
engine_state.plugin_path = Some(plugin_path);
|
||||||
@ -247,7 +248,7 @@ pub(crate) fn get_history_path(storage_path: &str, mode: HistoryFileFormat) -> O
|
|||||||
HistoryFileFormat::PlainText => HISTORY_FILE_TXT,
|
HistoryFileFormat::PlainText => HISTORY_FILE_TXT,
|
||||||
HistoryFileFormat::Sqlite => HISTORY_FILE_SQLITE,
|
HistoryFileFormat::Sqlite => HISTORY_FILE_SQLITE,
|
||||||
});
|
});
|
||||||
history_path
|
history_path.into()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::documentation::get_flags_section;
|
use nu_engine::documentation::{get_flags_section, HelpStyle};
|
||||||
use nu_protocol::{engine::EngineState, levenshtein_distance, Config};
|
use nu_protocol::{engine::EngineState, levenshtein_distance, Config};
|
||||||
use nu_utils::IgnoreCaseExt;
|
use nu_utils::IgnoreCaseExt;
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
@ -20,6 +20,9 @@ impl NuHelpCompleter {
|
|||||||
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
|
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
|
||||||
let folded_line = line.to_folded_case();
|
let folded_line = line.to_folded_case();
|
||||||
|
|
||||||
|
let mut help_style = HelpStyle::default();
|
||||||
|
help_style.update_from_config(&self.engine_state, &self.config);
|
||||||
|
|
||||||
let mut commands = self
|
let mut commands = self
|
||||||
.engine_state
|
.engine_state
|
||||||
.get_decls_sorted(false)
|
.get_decls_sorted(false)
|
||||||
@ -60,12 +63,9 @@ impl NuHelpCompleter {
|
|||||||
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
|
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
|
||||||
|
|
||||||
if !sig.named.is_empty() {
|
if !sig.named.is_empty() {
|
||||||
long_desc.push_str(&get_flags_section(
|
long_desc.push_str(&get_flags_section(&sig, &help_style, |v| {
|
||||||
Some(&self.engine_state),
|
v.to_parsable_string(", ", &self.config)
|
||||||
Some(&self.config),
|
}))
|
||||||
&sig,
|
|
||||||
|v| v.to_parsable_string(", ", &self.config),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if !sig.required_positional.is_empty()
|
if !sig.required_positional.is_empty()
|
||||||
@ -110,13 +110,12 @@ impl NuHelpCompleter {
|
|||||||
Suggestion {
|
Suggestion {
|
||||||
value: decl.name().into(),
|
value: decl.name().into(),
|
||||||
description: Some(long_desc),
|
description: Some(long_desc),
|
||||||
style: None,
|
|
||||||
extra: Some(extra),
|
extra: Some(extra),
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: pos - line.len(),
|
start: pos - line.len(),
|
||||||
end: pos,
|
end: pos,
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -142,10 +142,9 @@ fn convert_to_suggestions(
|
|||||||
vec![Suggestion {
|
vec![Suggestion {
|
||||||
value: text,
|
value: text,
|
||||||
description,
|
description,
|
||||||
style: None,
|
|
||||||
extra,
|
extra,
|
||||||
span,
|
span,
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
}]
|
}]
|
||||||
}
|
}
|
||||||
Value::List { vals, .. } => vals
|
Value::List { vals, .. } => vals
|
||||||
@ -154,9 +153,6 @@ fn convert_to_suggestions(
|
|||||||
.collect(),
|
.collect(),
|
||||||
_ => vec![Suggestion {
|
_ => vec![Suggestion {
|
||||||
value: format!("Not a record: {value:?}"),
|
value: format!("Not a record: {value:?}"),
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: reedline::Span {
|
span: reedline::Span {
|
||||||
start: if only_buffer_difference {
|
start: if only_buffer_difference {
|
||||||
pos - line.len()
|
pos - line.len()
|
||||||
@ -169,7 +165,7 @@ fn convert_to_suggestions(
|
|||||||
line.len()
|
line.len()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
append_whitespace: false,
|
..Suggestion::default()
|
||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,11 @@ impl Command for Print {
|
|||||||
Some('n'),
|
Some('n'),
|
||||||
)
|
)
|
||||||
.switch("stderr", "print to stderr instead of stdout", Some('e'))
|
.switch("stderr", "print to stderr instead of stdout", Some('e'))
|
||||||
|
.switch(
|
||||||
|
"raw",
|
||||||
|
"print without formatting (including binary data)",
|
||||||
|
Some('r'),
|
||||||
|
)
|
||||||
.category(Category::Strings)
|
.category(Category::Strings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,15 +55,25 @@ Since this command has no output, there is no point in piping it with other comm
|
|||||||
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
let args: Vec<Value> = call.rest(engine_state, stack, 0)?;
|
||||||
let no_newline = call.has_flag(engine_state, stack, "no-newline")?;
|
let no_newline = call.has_flag(engine_state, stack, "no-newline")?;
|
||||||
let to_stderr = call.has_flag(engine_state, stack, "stderr")?;
|
let to_stderr = call.has_flag(engine_state, stack, "stderr")?;
|
||||||
|
let raw = call.has_flag(engine_state, stack, "raw")?;
|
||||||
|
|
||||||
// This will allow for easy printing of pipelines as well
|
// This will allow for easy printing of pipelines as well
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
arg.into_pipeline_data()
|
if raw {
|
||||||
.print(engine_state, stack, no_newline, to_stderr)?;
|
arg.into_pipeline_data()
|
||||||
|
.print_raw(engine_state, no_newline, to_stderr)?;
|
||||||
|
} else {
|
||||||
|
arg.into_pipeline_data()
|
||||||
|
.print(engine_state, stack, no_newline, to_stderr)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if !input.is_nothing() {
|
} else if !input.is_nothing() {
|
||||||
input.print(engine_state, stack, no_newline, to_stderr)?;
|
if raw {
|
||||||
|
input.print_raw(engine_state, no_newline, to_stderr)?;
|
||||||
|
} else {
|
||||||
|
input.print(engine_state, stack, no_newline, to_stderr)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
@ -76,6 +91,11 @@ Since this command has no output, there is no point in piping it with other comm
|
|||||||
example: r#"print (2 + 3)"#,
|
example: r#"print (2 + 3)"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Print 'ABC' from binary data",
|
||||||
|
example: r#"0x[41 42 43] | print --raw"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,10 @@ pub(crate) const VSCODE_POST_EXECUTION_MARKER_PREFIX: &str = "\x1b]633;D;";
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) const VSCODE_POST_EXECUTION_MARKER_SUFFIX: &str = "\x1b\\";
|
pub(crate) const VSCODE_POST_EXECUTION_MARKER_SUFFIX: &str = "\x1b\\";
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) const VSCODE_COMMANDLINE_MARKER: &str = "\x1b]633;E\x1b\\";
|
//"\x1b]633;E;{}\x1b\\"
|
||||||
|
pub(crate) const VSCODE_COMMANDLINE_MARKER_PREFIX: &str = "\x1b]633;E;";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const VSCODE_COMMANDLINE_MARKER_SUFFIX: &str = "\x1b\\";
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
// "\x1b]633;P;Cwd={}\x1b\\"
|
// "\x1b]633;P;Cwd={}\x1b\\"
|
||||||
pub(crate) const VSCODE_CWD_PROPERTY_MARKER_PREFIX: &str = "\x1b]633;P;Cwd=";
|
pub(crate) const VSCODE_CWD_PROPERTY_MARKER_PREFIX: &str = "\x1b]633;P;Cwd=";
|
||||||
|
@ -193,6 +193,29 @@ fn get_style(record: &Record, name: &str, span: Span) -> Option<Style> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_menu_style<M: MenuBuilder>(mut menu: M, style: &Value) -> M {
|
||||||
|
let span = style.span();
|
||||||
|
let Value::Record { val, .. } = &style else {
|
||||||
|
return menu;
|
||||||
|
};
|
||||||
|
if let Some(style) = get_style(val, "text", span) {
|
||||||
|
menu = menu.with_text_style(style);
|
||||||
|
}
|
||||||
|
if let Some(style) = get_style(val, "selected_text", span) {
|
||||||
|
menu = menu.with_selected_text_style(style);
|
||||||
|
}
|
||||||
|
if let Some(style) = get_style(val, "description_text", span) {
|
||||||
|
menu = menu.with_description_text_style(style);
|
||||||
|
}
|
||||||
|
if let Some(style) = get_style(val, "match_text", span) {
|
||||||
|
menu = menu.with_match_text_style(style);
|
||||||
|
}
|
||||||
|
if let Some(style) = get_style(val, "selected_match_text", span) {
|
||||||
|
menu = menu.with_selected_match_text_style(style);
|
||||||
|
}
|
||||||
|
menu
|
||||||
|
}
|
||||||
|
|
||||||
// Adds a columnar menu to the editor engine
|
// Adds a columnar menu to the editor engine
|
||||||
pub(crate) fn add_columnar_menu(
|
pub(crate) fn add_columnar_menu(
|
||||||
line_editor: Reedline,
|
line_editor: Reedline,
|
||||||
@ -231,24 +254,7 @@ pub(crate) fn add_columnar_menu(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = menu.style.span();
|
columnar_menu = set_menu_style(columnar_menu, &menu.style);
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
|
||||||
if let Some(style) = get_style(val, "text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_selected_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "description_text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_description_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "match_text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_match_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_match_text", span) {
|
|
||||||
columnar_menu = columnar_menu.with_selected_match_text_style(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", config);
|
let marker = menu.marker.to_expanded_string("", config);
|
||||||
columnar_menu = columnar_menu.with_marker(&marker);
|
columnar_menu = columnar_menu.with_marker(&marker);
|
||||||
@ -304,18 +310,7 @@ pub(crate) fn add_list_menu(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = menu.style.span();
|
list_menu = set_menu_style(list_menu, &menu.style);
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
|
||||||
if let Some(style) = get_style(val, "text", span) {
|
|
||||||
list_menu = list_menu.with_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_text", span) {
|
|
||||||
list_menu = list_menu.with_selected_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "description_text", span) {
|
|
||||||
list_menu = list_menu.with_description_text_style(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", &config);
|
let marker = menu.marker.to_expanded_string("", &config);
|
||||||
list_menu = list_menu.with_marker(&marker);
|
list_menu = list_menu.with_marker(&marker);
|
||||||
@ -496,24 +491,7 @@ pub(crate) fn add_ide_menu(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = menu.style.span();
|
ide_menu = set_menu_style(ide_menu, &menu.style);
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
|
||||||
if let Some(style) = get_style(val, "text", span) {
|
|
||||||
ide_menu = ide_menu.with_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_text", span) {
|
|
||||||
ide_menu = ide_menu.with_selected_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "description_text", span) {
|
|
||||||
ide_menu = ide_menu.with_description_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "match_text", span) {
|
|
||||||
ide_menu = ide_menu.with_match_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_match_text", span) {
|
|
||||||
ide_menu = ide_menu.with_selected_match_text_style(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", &config);
|
let marker = menu.marker.to_expanded_string("", &config);
|
||||||
ide_menu = ide_menu.with_marker(&marker);
|
ide_menu = ide_menu.with_marker(&marker);
|
||||||
@ -601,18 +579,7 @@ pub(crate) fn add_description_menu(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
let span = menu.style.span();
|
description_menu = set_menu_style(description_menu, &menu.style);
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
|
||||||
if let Some(style) = get_style(val, "text", span) {
|
|
||||||
description_menu = description_menu.with_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "selected_text", span) {
|
|
||||||
description_menu = description_menu.with_selected_text_style(style);
|
|
||||||
}
|
|
||||||
if let Some(style) = get_style(val, "description_text", span) {
|
|
||||||
description_menu = description_menu.with_description_text_style(style);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", &config);
|
let marker = menu.marker.to_expanded_string("", &config);
|
||||||
description_menu = description_menu.with_marker(&marker);
|
description_menu = description_menu.with_marker(&marker);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::prompt_update::{
|
use crate::prompt_update::{
|
||||||
POST_EXECUTION_MARKER_PREFIX, POST_EXECUTION_MARKER_SUFFIX, PRE_EXECUTION_MARKER,
|
POST_EXECUTION_MARKER_PREFIX, POST_EXECUTION_MARKER_SUFFIX, PRE_EXECUTION_MARKER,
|
||||||
RESET_APPLICATION_MODE, VSCODE_CWD_PROPERTY_MARKER_PREFIX, VSCODE_CWD_PROPERTY_MARKER_SUFFIX,
|
RESET_APPLICATION_MODE, VSCODE_COMMANDLINE_MARKER_PREFIX, VSCODE_COMMANDLINE_MARKER_SUFFIX,
|
||||||
|
VSCODE_CWD_PROPERTY_MARKER_PREFIX, VSCODE_CWD_PROPERTY_MARKER_SUFFIX,
|
||||||
VSCODE_POST_EXECUTION_MARKER_PREFIX, VSCODE_POST_EXECUTION_MARKER_SUFFIX,
|
VSCODE_POST_EXECUTION_MARKER_PREFIX, VSCODE_POST_EXECUTION_MARKER_SUFFIX,
|
||||||
VSCODE_PRE_EXECUTION_MARKER,
|
VSCODE_PRE_EXECUTION_MARKER,
|
||||||
};
|
};
|
||||||
@ -131,7 +132,26 @@ pub fn evaluate_repl(
|
|||||||
run_shell_integration_osc9_9(engine_state, &mut unique_stack, use_color);
|
run_shell_integration_osc9_9(engine_state, &mut unique_stack, use_color);
|
||||||
}
|
}
|
||||||
if shell_integration_osc633 {
|
if shell_integration_osc633 {
|
||||||
run_shell_integration_osc633(engine_state, &mut unique_stack, use_color);
|
// escape a few things because this says so
|
||||||
|
// https://code.visualstudio.com/docs/terminal/shell-integration#_vs-code-custom-sequences-osc-633-st
|
||||||
|
let cmd_text = line_editor.current_buffer_contents().to_string();
|
||||||
|
|
||||||
|
let replaced_cmd_text = cmd_text
|
||||||
|
.chars()
|
||||||
|
.map(|c| match c {
|
||||||
|
'\n' => '\x0a',
|
||||||
|
'\r' => '\x0d',
|
||||||
|
'\x1b' => '\x1b',
|
||||||
|
_ => c,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
run_shell_integration_osc633(
|
||||||
|
engine_state,
|
||||||
|
&mut unique_stack,
|
||||||
|
use_color,
|
||||||
|
replaced_cmd_text,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
|
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
|
||||||
@ -452,14 +472,19 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
|
|
||||||
let line_editor_input_time = std::time::Instant::now();
|
let line_editor_input_time = std::time::Instant::now();
|
||||||
match input {
|
match input {
|
||||||
Ok(Signal::Success(s)) => {
|
Ok(Signal::Success(repl_cmd_line_text)) => {
|
||||||
let history_supports_meta = matches!(
|
let history_supports_meta = matches!(
|
||||||
engine_state.history_config().map(|h| h.file_format),
|
engine_state.history_config().map(|h| h.file_format),
|
||||||
Some(HistoryFileFormat::Sqlite)
|
Some(HistoryFileFormat::Sqlite)
|
||||||
);
|
);
|
||||||
|
|
||||||
if history_supports_meta {
|
if history_supports_meta {
|
||||||
prepare_history_metadata(&s, hostname, engine_state, &mut line_editor);
|
prepare_history_metadata(
|
||||||
|
&repl_cmd_line_text,
|
||||||
|
hostname,
|
||||||
|
engine_state,
|
||||||
|
&mut line_editor,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For pre_exec_hook
|
// For pre_exec_hook
|
||||||
@ -470,7 +495,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
if let Some(hook) = config.hooks.pre_execution.clone() {
|
if let Some(hook) = config.hooks.pre_execution.clone() {
|
||||||
// Set the REPL buffer to the current command for the "pre_execution" hook
|
// Set the REPL buffer to the current command for the "pre_execution" hook
|
||||||
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
|
||||||
repl.buffer = s.to_string();
|
repl.buffer = repl_cmd_line_text.to_string();
|
||||||
drop(repl);
|
drop(repl);
|
||||||
|
|
||||||
if let Err(err) = eval_hook(
|
if let Err(err) = eval_hook(
|
||||||
@ -531,7 +556,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
// Actual command execution logic starts from here
|
// Actual command execution logic starts from here
|
||||||
let cmd_execution_start_time = Instant::now();
|
let cmd_execution_start_time = Instant::now();
|
||||||
|
|
||||||
match parse_operation(s.clone(), engine_state, &stack) {
|
match parse_operation(repl_cmd_line_text.clone(), engine_state, &stack) {
|
||||||
Ok(operation) => match operation {
|
Ok(operation) => match operation {
|
||||||
ReplOperation::AutoCd { cwd, target, span } => {
|
ReplOperation::AutoCd { cwd, target, span } => {
|
||||||
do_auto_cd(target, cwd, &mut stack, engine_state, span);
|
do_auto_cd(target, cwd, &mut stack, engine_state, span);
|
||||||
@ -577,7 +602,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
|
|
||||||
if history_supports_meta {
|
if history_supports_meta {
|
||||||
if let Err(e) = fill_in_result_related_history_metadata(
|
if let Err(e) = fill_in_result_related_history_metadata(
|
||||||
&s,
|
&repl_cmd_line_text,
|
||||||
engine_state,
|
engine_state,
|
||||||
cmd_duration,
|
cmd_duration,
|
||||||
&mut stack,
|
&mut stack,
|
||||||
@ -597,7 +622,12 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
run_shell_integration_osc9_9(engine_state, &mut stack, use_color);
|
run_shell_integration_osc9_9(engine_state, &mut stack, use_color);
|
||||||
}
|
}
|
||||||
if shell_integration_osc633 {
|
if shell_integration_osc633 {
|
||||||
run_shell_integration_osc633(engine_state, &mut stack, use_color);
|
run_shell_integration_osc633(
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
use_color,
|
||||||
|
repl_cmd_line_text,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if shell_integration_reset_application_mode {
|
if shell_integration_reset_application_mode {
|
||||||
run_shell_integration_reset_application_mode();
|
run_shell_integration_reset_application_mode();
|
||||||
@ -911,7 +941,12 @@ fn run_shell_integration_osc2(
|
|||||||
|
|
||||||
// Try to abbreviate string for windows title
|
// Try to abbreviate string for windows title
|
||||||
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
|
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
|
||||||
path.replace(&p.as_path().display().to_string(), "~")
|
let home_dir_str = p.as_path().display().to_string();
|
||||||
|
if path.starts_with(&home_dir_str) {
|
||||||
|
path.replacen(&home_dir_str, "~", 1)
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
path
|
path
|
||||||
};
|
};
|
||||||
@ -988,7 +1023,12 @@ fn run_shell_integration_osc9_9(engine_state: &EngineState, stack: &mut Stack, u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_shell_integration_osc633(engine_state: &EngineState, stack: &mut Stack, use_color: bool) {
|
fn run_shell_integration_osc633(
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
use_color: bool,
|
||||||
|
repl_cmd_line_text: String,
|
||||||
|
) {
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
if let Ok(path) = current_dir_str(engine_state, stack) {
|
if let Ok(path) = current_dir_str(engine_state, stack) {
|
||||||
// Supported escape sequences of Microsoft's Visual Studio Code (vscode)
|
// Supported escape sequences of Microsoft's Visual Studio Code (vscode)
|
||||||
@ -1008,6 +1048,27 @@ fn run_shell_integration_osc633(engine_state: &EngineState, stack: &mut Stack, u
|
|||||||
start_time,
|
start_time,
|
||||||
use_color
|
use_color
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// escape a few things because this says so
|
||||||
|
// https://code.visualstudio.com/docs/terminal/shell-integration#_vs-code-custom-sequences-osc-633-st
|
||||||
|
|
||||||
|
let replaced_cmd_text: String = repl_cmd_line_text
|
||||||
|
.chars()
|
||||||
|
.map(|c| match c {
|
||||||
|
'\n' => '\x0a',
|
||||||
|
'\r' => '\x0d',
|
||||||
|
'\x1b' => '\x1b',
|
||||||
|
_ => c,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
//OSC 633 ; E ; <commandline> [; <nonce] ST - Explicitly set the command line with an optional nonce.
|
||||||
|
run_ansi_sequence(&format!(
|
||||||
|
"{}{}{}",
|
||||||
|
VSCODE_COMMANDLINE_MARKER_PREFIX,
|
||||||
|
replaced_cmd_text,
|
||||||
|
VSCODE_COMMANDLINE_MARKER_SUFFIX
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1337,20 +1398,26 @@ fn are_session_ids_in_sync() {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test_auto_cd {
|
mod test_auto_cd {
|
||||||
use super::{do_auto_cd, parse_operation, ReplOperation};
|
use super::{do_auto_cd, parse_operation, ReplOperation};
|
||||||
|
use nu_path::AbsolutePath;
|
||||||
use nu_protocol::engine::{EngineState, Stack};
|
use nu_protocol::engine::{EngineState, Stack};
|
||||||
use std::path::Path;
|
|
||||||
use tempfile::tempdir;
|
use tempfile::tempdir;
|
||||||
|
|
||||||
/// Create a symlink. Works on both Unix and Windows.
|
/// Create a symlink. Works on both Unix and Windows.
|
||||||
#[cfg(any(unix, windows))]
|
#[cfg(any(unix, windows))]
|
||||||
fn symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> std::io::Result<()> {
|
fn symlink(
|
||||||
|
original: impl AsRef<AbsolutePath>,
|
||||||
|
link: impl AsRef<AbsolutePath>,
|
||||||
|
) -> std::io::Result<()> {
|
||||||
|
let original = original.as_ref();
|
||||||
|
let link = link.as_ref();
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
{
|
{
|
||||||
std::os::unix::fs::symlink(original, link)
|
std::os::unix::fs::symlink(original, link)
|
||||||
}
|
}
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
{
|
{
|
||||||
if original.as_ref().is_dir() {
|
if original.is_dir() {
|
||||||
std::os::windows::fs::symlink_dir(original, link)
|
std::os::windows::fs::symlink_dir(original, link)
|
||||||
} else {
|
} else {
|
||||||
std::os::windows::fs::symlink_file(original, link)
|
std::os::windows::fs::symlink_file(original, link)
|
||||||
@ -1362,11 +1429,11 @@ mod test_auto_cd {
|
|||||||
/// `before`, and after `input` is parsed and evaluated, PWD should be
|
/// `before`, and after `input` is parsed and evaluated, PWD should be
|
||||||
/// changed to `after`.
|
/// changed to `after`.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check(before: impl AsRef<Path>, input: &str, after: impl AsRef<Path>) {
|
fn check(before: impl AsRef<AbsolutePath>, input: &str, after: impl AsRef<AbsolutePath>) {
|
||||||
// Setup EngineState and Stack.
|
// Setup EngineState and Stack.
|
||||||
let mut engine_state = EngineState::new();
|
let mut engine_state = EngineState::new();
|
||||||
let mut stack = Stack::new();
|
let mut stack = Stack::new();
|
||||||
stack.set_cwd(before).unwrap();
|
stack.set_cwd(before.as_ref()).unwrap();
|
||||||
|
|
||||||
// Parse the input. It must be an auto-cd operation.
|
// Parse the input. It must be an auto-cd operation.
|
||||||
let op = parse_operation(input.to_string(), &engine_state, &stack).unwrap();
|
let op = parse_operation(input.to_string(), &engine_state, &stack).unwrap();
|
||||||
@ -1382,54 +1449,66 @@ mod test_auto_cd {
|
|||||||
// don't have to be byte-wise equal (on Windows, the 8.3 filename
|
// don't have to be byte-wise equal (on Windows, the 8.3 filename
|
||||||
// conversion messes things up),
|
// conversion messes things up),
|
||||||
let updated_cwd = std::fs::canonicalize(updated_cwd).unwrap();
|
let updated_cwd = std::fs::canonicalize(updated_cwd).unwrap();
|
||||||
let after = std::fs::canonicalize(after).unwrap();
|
let after = std::fs::canonicalize(after.as_ref()).unwrap();
|
||||||
assert_eq!(updated_cwd, after);
|
assert_eq!(updated_cwd, after);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_root() {
|
fn auto_cd_root() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let root = if cfg!(windows) { r"C:\" } else { "/" };
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
check(&tempdir, root, root);
|
|
||||||
|
let input = if cfg!(windows) { r"C:\" } else { "/" };
|
||||||
|
let root = AbsolutePath::try_new(input).unwrap();
|
||||||
|
check(tempdir, input, root);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_tilde() {
|
fn auto_cd_tilde() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
let home = nu_path::home_dir().unwrap();
|
let home = nu_path::home_dir().unwrap();
|
||||||
check(&tempdir, "~", home);
|
check(tempdir, "~", home);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_dot() {
|
fn auto_cd_dot() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
check(&tempdir, ".", &tempdir);
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
|
check(tempdir, ".", tempdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_double_dot() {
|
fn auto_cd_double_dot() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo");
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
check(dir, "..", &tempdir);
|
check(dir, "..", tempdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_triple_dot() {
|
fn auto_cd_triple_dot() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo").join("bar");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo").join("bar");
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
check(dir, "...", &tempdir);
|
check(dir, "...", tempdir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_relative() {
|
fn auto_cd_relative() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let foo = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
let bar = tempdir.path().join("bar");
|
|
||||||
|
let foo = tempdir.join("foo");
|
||||||
|
let bar = tempdir.join("bar");
|
||||||
std::fs::create_dir_all(&foo).unwrap();
|
std::fs::create_dir_all(&foo).unwrap();
|
||||||
std::fs::create_dir_all(&bar).unwrap();
|
std::fs::create_dir_all(&bar).unwrap();
|
||||||
|
|
||||||
let input = if cfg!(windows) { r"..\bar" } else { "../bar" };
|
let input = if cfg!(windows) { r"..\bar" } else { "../bar" };
|
||||||
check(foo, input, bar);
|
check(foo, input, bar);
|
||||||
}
|
}
|
||||||
@ -1437,32 +1516,35 @@ mod test_auto_cd {
|
|||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_trailing_slash() {
|
fn auto_cd_trailing_slash() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo");
|
||||||
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
let input = if cfg!(windows) { r"foo\" } else { "foo/" };
|
let input = if cfg!(windows) { r"foo\" } else { "foo/" };
|
||||||
check(&tempdir, input, dir);
|
check(tempdir, input, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn auto_cd_symlink() {
|
fn auto_cd_symlink() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
std::fs::create_dir_all(&dir).unwrap();
|
|
||||||
let link = tempdir.path().join("link");
|
|
||||||
symlink(&dir, &link).unwrap();
|
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo");
|
||||||
|
std::fs::create_dir_all(&dir).unwrap();
|
||||||
|
let link = tempdir.join("link");
|
||||||
|
symlink(&dir, &link).unwrap();
|
||||||
let input = if cfg!(windows) { r".\link" } else { "./link" };
|
let input = if cfg!(windows) { r".\link" } else { "./link" };
|
||||||
check(&tempdir, input, link);
|
check(tempdir, input, link);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "was not parsed into an auto-cd operation")]
|
#[should_panic(expected = "was not parsed into an auto-cd operation")]
|
||||||
fn auto_cd_nonexistent_directory() {
|
fn auto_cd_nonexistent_directory() {
|
||||||
let tempdir = tempdir().unwrap();
|
let tempdir = tempdir().unwrap();
|
||||||
let dir = tempdir.path().join("foo");
|
let tempdir = AbsolutePath::try_new(tempdir.path()).unwrap();
|
||||||
|
|
||||||
|
let dir = tempdir.join("foo");
|
||||||
let input = if cfg!(windows) { r"foo\" } else { "foo/" };
|
let input = if cfg!(windows) { r"foo\" } else { "foo/" };
|
||||||
check(&tempdir, input, dir);
|
check(tempdir, input, dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -321,16 +321,10 @@ mod test {
|
|||||||
|
|
||||||
let env = engine_state.render_env_vars();
|
let env = engine_state.render_env_vars();
|
||||||
|
|
||||||
assert!(
|
assert!(matches!(env.get("FOO"), Some(&Value::String { val, .. }) if val == "foo"));
|
||||||
matches!(env.get(&"FOO".to_string()), Some(&Value::String { val, .. }) if val == "foo")
|
assert!(matches!(env.get("SYMBOLS"), Some(&Value::String { val, .. }) if val == symbols));
|
||||||
);
|
assert!(matches!(env.get(symbols), Some(&Value::String { val, .. }) if val == "symbols"));
|
||||||
assert!(
|
assert!(env.contains_key("PWD"));
|
||||||
matches!(env.get(&"SYMBOLS".to_string()), Some(&Value::String { val, .. }) if val == symbols)
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
matches!(env.get(&symbols.to_string()), Some(&Value::String { val, .. }) if val == "symbols")
|
|
||||||
);
|
|
||||||
assert!(env.get(&"PWD".to_string()).is_some());
|
|
||||||
assert_eq!(env.len(), 4);
|
assert_eq!(env.len(), 4);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,6 @@ fn completer() -> NuCompleter {
|
|||||||
fn completer_strings() -> NuCompleter {
|
fn completer_strings() -> NuCompleter {
|
||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
// Add record value as example
|
// Add record value as example
|
||||||
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
|
let record = r#"def animals [] { ["cat", "dog", "eel" ] }
|
||||||
def my-command [animal: string@animals] { print $animal }"#;
|
def my-command [animal: string@animals] { print $animal }"#;
|
||||||
@ -90,14 +89,12 @@ fn subcommand_completer() -> NuCompleter {
|
|||||||
// Create a new engine
|
// Create a new engine
|
||||||
let (dir, _, mut engine, mut stack) = new_engine();
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
// Use fuzzy matching, because subcommands are sorted by Levenshtein distance,
|
|
||||||
// and that's not very useful with prefix matching
|
|
||||||
let commands = r#"
|
let commands = r#"
|
||||||
$env.config.completions.algorithm = "fuzzy"
|
$env.config.completions.algorithm = "fuzzy"
|
||||||
def foo [] {}
|
def foo [] {}
|
||||||
def "foo bar" [] {}
|
def "foo bar" [] {}
|
||||||
def "foo abaz" [] {}
|
def "foo abaz" [] {}
|
||||||
def "foo aabrr" [] {}
|
def "foo aabcrr" [] {}
|
||||||
def food [] {}
|
def food [] {}
|
||||||
"#;
|
"#;
|
||||||
assert!(support::merge_input(commands.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
assert!(support::merge_input(commands.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
@ -106,6 +103,22 @@ fn subcommand_completer() -> NuCompleter {
|
|||||||
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Use fuzzy completions but sort in alphabetical order
|
||||||
|
#[fixture]
|
||||||
|
fn fuzzy_alpha_sort_completer() -> NuCompleter {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, _, mut engine, mut stack) = new_engine();
|
||||||
|
|
||||||
|
let config = r#"
|
||||||
|
$env.config.completions.algorithm = "fuzzy"
|
||||||
|
$env.config.completions.sort = "alphabetical"
|
||||||
|
"#;
|
||||||
|
assert!(support::merge_input(config.as_bytes(), &mut engine, &mut stack, dir).is_ok());
|
||||||
|
|
||||||
|
// Instantiate a new completer
|
||||||
|
NuCompleter::new(Arc::new(engine), Arc::new(stack))
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn variables_dollar_sign_with_variablecompletion() {
|
fn variables_dollar_sign_with_variablecompletion() {
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
@ -123,28 +136,28 @@ fn variables_double_dash_argument_with_flagcompletion(mut completer: NuCompleter
|
|||||||
let suggestions = completer.complete("tst --", 6);
|
let suggestions = completer.complete("tst --", 6);
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into()];
|
||||||
// dbg!(&expected, &suggestions);
|
// dbg!(&expected, &suggestions);
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
|
fn variables_single_dash_argument_with_flagcompletion(mut completer: NuCompleter) {
|
||||||
let suggestions = completer.complete("tst -", 5);
|
let suggestions = completer.complete("tst -", 5);
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) {
|
fn variables_command_with_commandcompletion(mut completer_strings: NuCompleter) {
|
||||||
let suggestions = completer_strings.complete("my-c ", 4);
|
let suggestions = completer_strings.complete("my-c ", 4);
|
||||||
let expected: Vec<String> = vec!["my-command".into()];
|
let expected: Vec<String> = vec!["my-command".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn variables_subcommands_with_customcompletion(mut completer_strings: NuCompleter) {
|
fn variables_subcommands_with_customcompletion(mut completer_strings: NuCompleter) {
|
||||||
let suggestions = completer_strings.complete("my-command ", 11);
|
let suggestions = completer_strings.complete("my-command ", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
@ -153,7 +166,7 @@ fn variables_customcompletion_subcommands_with_customcompletion_2(
|
|||||||
) {
|
) {
|
||||||
let suggestions = completer_strings.complete("my-command ", 11);
|
let suggestions = completer_strings.complete("my-command ", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -182,19 +195,19 @@ fn dotnu_completions() {
|
|||||||
let completion_str = "source-env ".to_string();
|
let completion_str = "source-env ".to_string();
|
||||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||||
|
|
||||||
match_suggestions(expected.clone(), suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test use completion
|
// Test use completion
|
||||||
let completion_str = "use ".to_string();
|
let completion_str = "use ".to_string();
|
||||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||||
|
|
||||||
match_suggestions(expected.clone(), suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test overlay use completion
|
// Test overlay use completion
|
||||||
let completion_str = "overlay use ".to_string();
|
let completion_str = "overlay use ".to_string();
|
||||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||||
|
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -258,8 +271,22 @@ fn file_completions() {
|
|||||||
folder(dir.join(".hidden_folder")),
|
folder(dir.join(".hidden_folder")),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let separator = '/';
|
||||||
|
let target_dir = format!("cp {dir_str}{separator}");
|
||||||
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
let expected_slash_paths: Vec<String> = expected_paths
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.replace('\\', "/"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
match_suggestions(&expected_slash_paths, &slash_suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completions for a file
|
// Test completions for a file
|
||||||
let target_dir = format!("cp {}", folder(dir.join("another")));
|
let target_dir = format!("cp {}", folder(dir.join("another")));
|
||||||
@ -269,17 +296,91 @@ fn file_completions() {
|
|||||||
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
|
let expected_paths: Vec<String> = vec![file(dir.join("another").join("newfile"))];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completions for hidden files
|
// Test completions for hidden files
|
||||||
let target_dir = format!("ls {}/.", folder(dir.join(".hidden_folder")));
|
let target_dir = format!("ls {}{MAIN_SEPARATOR}.", folder(dir.join(".hidden_folder")));
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
let expected_paths: Vec<String> =
|
let expected_paths: Vec<String> =
|
||||||
vec![file(dir.join(".hidden_folder").join(".hidden_subfile"))];
|
vec![file(dir.join(".hidden_folder").join(".hidden_subfile"))];
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let target_dir = format!("ls {}/.", folder(dir.join(".hidden_folder")));
|
||||||
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
let expected_slash: Vec<String> = expected_paths
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.replace('\\', "/"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
match_suggestions(&expected_slash, &slash_suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn file_completions_with_mixed_separators() {
|
||||||
|
// Create a new engine
|
||||||
|
let (dir, dir_str, engine, stack) = new_dotnu_engine();
|
||||||
|
|
||||||
|
// Instantiate a new completer
|
||||||
|
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
|
||||||
|
|
||||||
|
// Create Expected values
|
||||||
|
let expected_paths: Vec<String> = vec![
|
||||||
|
file(dir.join("lib-dir1").join("bar.nu")),
|
||||||
|
file(dir.join("lib-dir1").join("baz.nu")),
|
||||||
|
file(dir.join("lib-dir1").join("xyzzy.nu")),
|
||||||
|
];
|
||||||
|
let expecetd_slash_paths: Vec<String> = expected_paths
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.replace(MAIN_SEPARATOR, "/"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}/lib-dir1/");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expecetd_slash_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("cp {dir_str}\\lib-dir1/");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expecetd_slash_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}/lib-dir1\\/");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expecetd_slash_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}\\lib-dir1\\/");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expecetd_slash_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}\\lib-dir1\\");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}/lib-dir1\\");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}/lib-dir1/\\");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
|
let target_dir = format!("ls {dir_str}\\lib-dir1/\\");
|
||||||
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -303,7 +404,7 @@ fn partial_completions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completions for the files whose name begin with "h"
|
// Test completions for the files whose name begin with "h"
|
||||||
// and are present under directories whose names begin with "pa"
|
// and are present under directories whose names begin with "pa"
|
||||||
@ -324,7 +425,7 @@ fn partial_completions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion for all files under directories whose names begin with "pa"
|
// Test completion for all files under directories whose names begin with "pa"
|
||||||
let dir_str = folder(dir.join("pa"));
|
let dir_str = folder(dir.join("pa"));
|
||||||
@ -345,7 +446,7 @@ fn partial_completions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion for a single file
|
// Test completion for a single file
|
||||||
let dir_str = file(dir.join("fi").join("so"));
|
let dir_str = file(dir.join("fi").join("so"));
|
||||||
@ -356,7 +457,7 @@ fn partial_completions() {
|
|||||||
let expected_paths: Vec<String> = vec![file(dir.join("final_partial").join("somefile"))];
|
let expected_paths: Vec<String> = vec![file(dir.join("final_partial").join("somefile"))];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion where there is a sneaky `..` in the path
|
// Test completion where there is a sneaky `..` in the path
|
||||||
let dir_str = file(dir.join("par").join("..").join("fi").join("so"));
|
let dir_str = file(dir.join("par").join("..").join("fi").join("so"));
|
||||||
@ -392,7 +493,7 @@ fn partial_completions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion for all files under directories whose names begin with "pa"
|
// Test completion for all files under directories whose names begin with "pa"
|
||||||
let file_str = file(dir.join("partial-a").join("have"));
|
let file_str = file(dir.join("partial-a").join("have"));
|
||||||
@ -406,7 +507,7 @@ fn partial_completions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
// Test completion for all files under directories whose names begin with "pa"
|
// Test completion for all files under directories whose names begin with "pa"
|
||||||
let file_str = file(dir.join("partial-a").join("have_ext."));
|
let file_str = file(dir.join("partial-a").join("have_ext."));
|
||||||
@ -420,7 +521,7 @@ fn partial_completions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -455,15 +556,16 @@ fn command_ls_with_filecompletion() {
|
|||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
let target_dir = "ls custom_completion.";
|
let target_dir = "ls custom_completion.";
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
|
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn command_open_with_filecompletion() {
|
fn command_open_with_filecompletion() {
|
||||||
let (_, _, engine, stack) = new_engine();
|
let (_, _, engine, stack) = new_engine();
|
||||||
@ -496,14 +598,14 @@ fn command_open_with_filecompletion() {
|
|||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
let target_dir = "open custom_completion.";
|
let target_dir = "open custom_completion.";
|
||||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||||
|
|
||||||
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
|
let expected_paths: Vec<String> = vec!["custom_completion.nu".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -538,7 +640,7 @@ fn command_rm_with_globcompletion() {
|
|||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -573,7 +675,7 @@ fn command_cp_with_globcompletion() {
|
|||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -608,7 +710,7 @@ fn command_save_with_filecompletion() {
|
|||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -643,7 +745,7 @@ fn command_touch_with_filecompletion() {
|
|||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -678,7 +780,7 @@ fn command_watch_with_filecompletion() {
|
|||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
@ -686,19 +788,19 @@ fn subcommand_completions(mut subcommand_completer: NuCompleter) {
|
|||||||
let prefix = "foo br";
|
let prefix = "foo br";
|
||||||
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
||||||
match_suggestions(
|
match_suggestions(
|
||||||
vec!["foo bar".to_string(), "foo aabrr".to_string()],
|
&vec!["foo bar".to_string(), "foo aabcrr".to_string()],
|
||||||
suggestions,
|
&suggestions,
|
||||||
);
|
);
|
||||||
|
|
||||||
let prefix = "foo b";
|
let prefix = "foo b";
|
||||||
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
let suggestions = subcommand_completer.complete(prefix, prefix.len());
|
||||||
match_suggestions(
|
match_suggestions(
|
||||||
vec![
|
&vec![
|
||||||
"foo bar".to_string(),
|
"foo bar".to_string(),
|
||||||
|
"foo aabcrr".to_string(),
|
||||||
"foo abaz".to_string(),
|
"foo abaz".to_string(),
|
||||||
"foo aabrr".to_string(),
|
|
||||||
],
|
],
|
||||||
suggestions,
|
&suggestions,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -724,7 +826,7 @@ fn file_completion_quoted() {
|
|||||||
format!("`{}`", folder("test dir")),
|
format!("`{}`", folder("test dir")),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
|
|
||||||
let dir: PathBuf = "test dir".into();
|
let dir: PathBuf = "test dir".into();
|
||||||
let target_dir = format!("open '{}'", folder(dir.clone()));
|
let target_dir = format!("open '{}'", folder(dir.clone()));
|
||||||
@ -735,7 +837,7 @@ fn file_completion_quoted() {
|
|||||||
format!("`{}`", file(dir.join("single quote"))),
|
format!("`{}`", file(dir.join("single quote"))),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -770,7 +872,7 @@ fn flag_completions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -794,8 +896,21 @@ fn folder_with_directorycompletions() {
|
|||||||
folder(dir.join(".hidden_folder")),
|
folder(dir.join(".hidden_folder")),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
let target_dir = format!("cd {dir_str}/");
|
||||||
|
let slash_suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
|
let expected_slash_paths: Vec<String> = expected_paths
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.replace('\\', "/"))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
match_suggestions(&expected_slash_paths, &slash_suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
// Match the results
|
// Match the results
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -837,7 +952,7 @@ fn variables_completions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for $nu.h (filter)
|
// Test completions for $nu.h (filter)
|
||||||
let suggestions = completer.complete("$nu.h", 5);
|
let suggestions = completer.complete("$nu.h", 5);
|
||||||
@ -851,7 +966,7 @@ fn variables_completions() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for $nu.os-info
|
// Test completions for $nu.os-info
|
||||||
let suggestions = completer.complete("$nu.os-info.", 12);
|
let suggestions = completer.complete("$nu.os-info.", 12);
|
||||||
@ -863,7 +978,7 @@ fn variables_completions() {
|
|||||||
"name".into(),
|
"name".into(),
|
||||||
];
|
];
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for custom var
|
// Test completions for custom var
|
||||||
let suggestions = completer.complete("$actor.", 7);
|
let suggestions = completer.complete("$actor.", 7);
|
||||||
@ -873,7 +988,7 @@ fn variables_completions() {
|
|||||||
let expected: Vec<String> = vec!["age".into(), "name".into()];
|
let expected: Vec<String> = vec!["age".into(), "name".into()];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for custom var (filtering)
|
// Test completions for custom var (filtering)
|
||||||
let suggestions = completer.complete("$actor.n", 8);
|
let suggestions = completer.complete("$actor.n", 8);
|
||||||
@ -883,7 +998,7 @@ fn variables_completions() {
|
|||||||
let expected: Vec<String> = vec!["name".into()];
|
let expected: Vec<String> = vec!["name".into()];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for $env
|
// Test completions for $env
|
||||||
let suggestions = completer.complete("$env.", 5);
|
let suggestions = completer.complete("$env.", 5);
|
||||||
@ -896,7 +1011,7 @@ fn variables_completions() {
|
|||||||
let expected: Vec<String> = vec!["PATH".into(), "PWD".into(), "TEST".into()];
|
let expected: Vec<String> = vec!["PATH".into(), "PWD".into(), "TEST".into()];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
// Test completions for $env
|
// Test completions for $env
|
||||||
let suggestions = completer.complete("$env.T", 6);
|
let suggestions = completer.complete("$env.T", 6);
|
||||||
@ -906,12 +1021,12 @@ fn variables_completions() {
|
|||||||
let expected: Vec<String> = vec!["TEST".into()];
|
let expected: Vec<String> = vec!["TEST".into()];
|
||||||
|
|
||||||
// Match results
|
// Match results
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
|
||||||
let suggestions = completer.complete("$", 1);
|
let suggestions = completer.complete("$", 1);
|
||||||
let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()];
|
let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()];
|
||||||
|
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -930,7 +1045,7 @@ fn alias_of_command_and_flags() {
|
|||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -949,7 +1064,7 @@ fn alias_of_basic_command() {
|
|||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -971,7 +1086,7 @@ fn alias_of_another_alias() {
|
|||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
let expected_paths: Vec<String> = vec!["test_a/".to_string(), "test_b/".to_string()];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
fn run_external_completion(completer: &str, input: &str) -> Vec<Suggestion> {
|
||||||
@ -1034,35 +1149,35 @@ fn unknown_command_completion() {
|
|||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions)
|
match_suggestions(&expected_paths, &suggestions)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
|
fn flagcompletion_triggers_after_cursor(mut completer: NuCompleter) {
|
||||||
let suggestions = completer.complete("tst -h", 5);
|
let suggestions = completer.complete("tst -h", 5);
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
|
fn customcompletion_triggers_after_cursor(mut completer_strings: NuCompleter) {
|
||||||
let suggestions = completer_strings.complete("my-command c", 11);
|
let suggestions = completer_strings.complete("my-command c", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
|
fn customcompletion_triggers_after_cursor_piped(mut completer_strings: NuCompleter) {
|
||||||
let suggestions = completer_strings.complete("my-command c | ls", 11);
|
let suggestions = completer_strings.complete("my-command c | ls", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
|
fn flagcompletion_triggers_after_cursor_piped(mut completer: NuCompleter) {
|
||||||
let suggestions = completer.complete("tst -h | ls", 5);
|
let suggestions = completer.complete("tst -h | ls", 5);
|
||||||
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
let expected: Vec<String> = vec!["--help".into(), "--mod".into(), "-h".into(), "-s".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1096,77 +1211,88 @@ fn filecompletions_triggers_after_cursor() {
|
|||||||
".hidden_folder/".to_string(),
|
".hidden_folder/".to_string(),
|
||||||
];
|
];
|
||||||
|
|
||||||
match_suggestions(expected_paths, suggestions);
|
match_suggestions(&expected_paths, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_positional(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam ", 5);
|
let suggestions = extern_completer.complete("spam ", 5);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_long_flag_1(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam --foo=", 11);
|
let suggestions = extern_completer.complete("spam --foo=", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_long_flag_2(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam --foo ", 11);
|
let suggestions = extern_completer.complete("spam --foo ", 11);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_long_flag_short(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam -f ", 8);
|
let suggestions = extern_completer.complete("spam -f ", 8);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
|
fn extern_custom_completion_short_flag(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam -b ", 8);
|
let suggestions = extern_completer.complete("spam -b ", 8);
|
||||||
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
let expected: Vec<String> = vec!["cat".into(), "dog".into(), "eel".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn extern_complete_flags(mut extern_completer: NuCompleter) {
|
fn extern_complete_flags(mut extern_completer: NuCompleter) {
|
||||||
let suggestions = extern_completer.complete("spam -", 6);
|
let suggestions = extern_completer.complete("spam -", 6);
|
||||||
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
|
let expected: Vec<String> = vec!["--foo".into(), "-b".into(), "-f".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn custom_completer_triggers_cursor_before_word(mut custom_completer: NuCompleter) {
|
fn custom_completer_triggers_cursor_before_word(mut custom_completer: NuCompleter) {
|
||||||
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
||||||
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
|
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn custom_completer_triggers_cursor_on_word_left_boundary(mut custom_completer: NuCompleter) {
|
fn custom_completer_triggers_cursor_on_word_left_boundary(mut custom_completer: NuCompleter) {
|
||||||
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
let suggestions = custom_completer.complete("cmd foo bar", 8);
|
||||||
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
|
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn custom_completer_triggers_cursor_next_to_word(mut custom_completer: NuCompleter) {
|
fn custom_completer_triggers_cursor_next_to_word(mut custom_completer: NuCompleter) {
|
||||||
let suggestions = custom_completer.complete("cmd foo bar", 11);
|
let suggestions = custom_completer.complete("cmd foo bar", 11);
|
||||||
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into()];
|
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[rstest]
|
#[rstest]
|
||||||
fn custom_completer_triggers_cursor_after_word(mut custom_completer: NuCompleter) {
|
fn custom_completer_triggers_cursor_after_word(mut custom_completer: NuCompleter) {
|
||||||
let suggestions = custom_completer.complete("cmd foo bar ", 12);
|
let suggestions = custom_completer.complete("cmd foo bar ", 12);
|
||||||
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into(), "".into()];
|
let expected: Vec<String> = vec!["cmd".into(), "foo".into(), "bar".into(), "".into()];
|
||||||
match_suggestions(expected, suggestions);
|
match_suggestions(&expected, &suggestions);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rstest]
|
||||||
|
fn sort_fuzzy_completions_in_alphabetical_order(mut fuzzy_alpha_sort_completer: NuCompleter) {
|
||||||
|
let suggestions = fuzzy_alpha_sort_completer.complete("ls nu", 5);
|
||||||
|
// Even though "nushell" is a better match, it should come second because
|
||||||
|
// the completions should be sorted in alphabetical order
|
||||||
|
match_suggestions(
|
||||||
|
&vec!["custom_completion.nu".into(), "nushell".into()],
|
||||||
|
&suggestions,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ignore = "was reverted, still needs fixing"]
|
#[ignore = "was reverted, still needs fixing"]
|
||||||
|
@ -186,7 +186,7 @@ pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// match a list of suggestions with the expected values
|
// match a list of suggestions with the expected values
|
||||||
pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
|
pub fn match_suggestions(expected: &Vec<String>, suggestions: &Vec<Suggestion>) {
|
||||||
let expected_len = expected.len();
|
let expected_len = expected.len();
|
||||||
let suggestions_len = suggestions.len();
|
let suggestions_len = suggestions.len();
|
||||||
if expected_len != suggestions_len {
|
if expected_len != suggestions_len {
|
||||||
@ -196,13 +196,13 @@ pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
|
|||||||
Expected: {expected:#?}\n"
|
Expected: {expected:#?}\n"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
assert_eq!(
|
|
||||||
expected,
|
let suggestoins_str = suggestions
|
||||||
suggestions
|
.iter()
|
||||||
.into_iter()
|
.map(|it| it.value.clone())
|
||||||
.map(|it| it.value)
|
.collect::<Vec<_>>();
|
||||||
.collect::<Vec<_>>()
|
|
||||||
);
|
assert_eq!(expected, &suggestoins_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
// append the separator to the converted path
|
// append the separator to the converted path
|
||||||
|
@ -5,15 +5,15 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-base"
|
name = "nu-cmd-base"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-base"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.96.1" }
|
nu-engine = { path = "../nu-engine", version = "0.97.1" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.96.1" }
|
nu-parser = { path = "../nu-parser", version = "0.97.1" }
|
||||||
nu-path = { path = "../nu-path", version = "0.96.1" }
|
nu-path = { path = "../nu-path", version = "0.97.1" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.96.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.97.1" }
|
||||||
|
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
miette = { workspace = true }
|
miette = { workspace = true }
|
||||||
|
@ -3,21 +3,26 @@ use nu_protocol::{
|
|||||||
engine::{EngineState, Stack},
|
engine::{EngineState, Stack},
|
||||||
Range, ShellError, Span, Value,
|
Range, ShellError, Span, Value,
|
||||||
};
|
};
|
||||||
use std::{ops::Bound, path::PathBuf};
|
use std::ops::Bound;
|
||||||
|
|
||||||
pub fn get_init_cwd() -> PathBuf {
|
pub fn get_init_cwd() -> AbsolutePathBuf {
|
||||||
std::env::current_dir().unwrap_or_else(|_| {
|
std::env::current_dir()
|
||||||
std::env::var("PWD")
|
.ok()
|
||||||
.map(Into::into)
|
.and_then(|path| AbsolutePathBuf::try_from(path).ok())
|
||||||
.unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default())
|
.or_else(|| {
|
||||||
})
|
std::env::var("PWD")
|
||||||
|
.ok()
|
||||||
|
.and_then(|path| AbsolutePathBuf::try_from(path).ok())
|
||||||
|
})
|
||||||
|
.or_else(nu_path::home_dir)
|
||||||
|
.expect("Failed to get current working directory")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> AbsolutePathBuf {
|
||||||
engine_state
|
engine_state
|
||||||
.cwd(Some(stack))
|
.cwd(Some(stack))
|
||||||
.map(AbsolutePathBuf::into_std_path_buf)
|
.ok()
|
||||||
.unwrap_or(crate::util::get_init_cwd())
|
.unwrap_or_else(get_init_cwd)
|
||||||
}
|
}
|
||||||
|
|
||||||
type MakeRangeError = fn(&str, Span) -> ShellError;
|
type MakeRangeError = fn(&str, Span) -> ShellError;
|
||||||
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-extra"
|
name = "nu-cmd-extra"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-extra"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -13,13 +13,13 @@ version = "0.96.1"
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.96.1" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.97.1" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.96.1" }
|
nu-engine = { path = "../nu-engine", version = "0.97.1" }
|
||||||
nu-json = { version = "0.96.1", path = "../nu-json" }
|
nu-json = { version = "0.97.1", path = "../nu-json" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.96.1" }
|
nu-parser = { path = "../nu-parser", version = "0.97.1" }
|
||||||
nu-pretty-hex = { version = "0.96.1", path = "../nu-pretty-hex" }
|
nu-pretty-hex = { version = "0.97.1", path = "../nu-pretty-hex" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.96.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.97.1" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.96.1" }
|
nu-utils = { path = "../nu-utils", version = "0.97.1" }
|
||||||
|
|
||||||
# Potential dependencies for extras
|
# Potential dependencies for extras
|
||||||
heck = { workspace = true }
|
heck = { workspace = true }
|
||||||
@ -33,6 +33,6 @@ v_htmlescape = { workspace = true }
|
|||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.96.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.97.1" }
|
||||||
nu-command = { path = "../nu-command", version = "0.96.1" }
|
nu-command = { path = "../nu-command", version = "0.97.1" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.96.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.97.1" }
|
@ -6,22 +6,22 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-lang"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-lang"
|
name = "nu-cmd-lang"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.96.1" }
|
nu-engine = { path = "../nu-engine", version = "0.97.1" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.96.1" }
|
nu-parser = { path = "../nu-parser", version = "0.97.1" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.96.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.97.1" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.96.1" }
|
nu-utils = { path = "../nu-utils", version = "0.97.1" }
|
||||||
|
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
shadow-rs = { version = "0.29", default-features = false }
|
shadow-rs = { version = "0.31", default-features = false }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
shadow-rs = { version = "0.29", default-features = false }
|
shadow-rs = { version = "0.31", default-features = false }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
mimalloc = []
|
mimalloc = []
|
||||||
|
@ -70,6 +70,11 @@ impl Command for Def {
|
|||||||
example: r#"def --wrapped my-echo [...rest] { ^echo ...$rest }; my-echo -e 'spam\tspam'"#,
|
example: r#"def --wrapped my-echo [...rest] { ^echo ...$rest }; my-echo -e 'spam\tspam'"#,
|
||||||
result: Some(Value::test_string("spam\tspam")),
|
result: Some(Value::test_string("spam\tspam")),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Define a custom command with a type signature. Passing a non-int value will result in an error",
|
||||||
|
example: r#"def only_int []: int -> int { $in }; 42 | only_int"#,
|
||||||
|
result: Some(Value::test_int(42)),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,7 @@ pub fn check_example_input_and_output_types_match_command_signature(
|
|||||||
witnessed_type_transformations
|
witnessed_type_transformations
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eval_pipeline_without_terminal_expression(
|
pub fn eval_pipeline_without_terminal_expression(
|
||||||
src: &str,
|
src: &str,
|
||||||
cwd: &std::path::Path,
|
cwd: &std::path::Path,
|
||||||
engine_state: &mut Box<EngineState>,
|
engine_state: &mut Box<EngineState>,
|
||||||
|
@ -5,15 +5,15 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-cmd-plugin"
|
name = "nu-cmd-plugin"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-plugin"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-cmd-plugin"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-engine = { path = "../nu-engine", version = "0.96.1" }
|
nu-engine = { path = "../nu-engine", version = "0.97.1" }
|
||||||
nu-path = { path = "../nu-path", version = "0.96.1" }
|
nu-path = { path = "../nu-path", version = "0.97.1" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.96.1", features = ["plugin"] }
|
nu-protocol = { path = "../nu-protocol", version = "0.97.1", features = ["plugin"] }
|
||||||
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.96.1" }
|
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.97.1" }
|
||||||
|
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
|
|
||||||
|
@ -5,18 +5,18 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-color-confi
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-color-config"
|
name = "nu-color-config"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.96.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.97.1" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.96.1" }
|
nu-engine = { path = "../nu-engine", version = "0.97.1" }
|
||||||
nu-json = { path = "../nu-json", version = "0.96.1" }
|
nu-json = { path = "../nu-json", version = "0.97.1" }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
|
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.96.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.97.1" }
|
@ -5,7 +5,7 @@ edition = "2021"
|
|||||||
license = "MIT"
|
license = "MIT"
|
||||||
name = "nu-command"
|
name = "nu-command"
|
||||||
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-command"
|
||||||
version = "0.96.1"
|
version = "0.97.1"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
@ -13,21 +13,21 @@ version = "0.96.1"
|
|||||||
bench = false
|
bench = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-cmd-base = { path = "../nu-cmd-base", version = "0.96.1" }
|
nu-cmd-base = { path = "../nu-cmd-base", version = "0.97.1" }
|
||||||
nu-color-config = { path = "../nu-color-config", version = "0.96.1" }
|
nu-color-config = { path = "../nu-color-config", version = "0.97.1" }
|
||||||
nu-engine = { path = "../nu-engine", version = "0.96.1" }
|
nu-engine = { path = "../nu-engine", version = "0.97.1" }
|
||||||
nu-glob = { path = "../nu-glob", version = "0.96.1" }
|
nu-glob = { path = "../nu-glob", version = "0.97.1" }
|
||||||
nu-json = { path = "../nu-json", version = "0.96.1" }
|
nu-json = { path = "../nu-json", version = "0.97.1" }
|
||||||
nu-parser = { path = "../nu-parser", version = "0.96.1" }
|
nu-parser = { path = "../nu-parser", version = "0.97.1" }
|
||||||
nu-path = { path = "../nu-path", version = "0.96.1" }
|
nu-path = { path = "../nu-path", version = "0.97.1" }
|
||||||
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.96.1" }
|
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.97.1" }
|
||||||
nu-protocol = { path = "../nu-protocol", version = "0.96.1" }
|
nu-protocol = { path = "../nu-protocol", version = "0.97.1" }
|
||||||
nu-system = { path = "../nu-system", version = "0.96.1" }
|
nu-system = { path = "../nu-system", version = "0.97.1" }
|
||||||
nu-table = { path = "../nu-table", version = "0.96.1" }
|
nu-table = { path = "../nu-table", version = "0.97.1" }
|
||||||
nu-term-grid = { path = "../nu-term-grid", version = "0.96.1" }
|
nu-term-grid = { path = "../nu-term-grid", version = "0.97.1" }
|
||||||
nu-utils = { path = "../nu-utils", version = "0.96.1" }
|
nu-utils = { path = "../nu-utils", version = "0.97.1" }
|
||||||
nu-ansi-term = { workspace = true }
|
nu-ansi-term = { workspace = true }
|
||||||
nuon = { path = "../nuon", version = "0.96.1" }
|
nuon = { path = "../nuon", version = "0.97.1" }
|
||||||
|
|
||||||
alphanumeric-sort = { workspace = true }
|
alphanumeric-sort = { workspace = true }
|
||||||
base64 = { workspace = true }
|
base64 = { workspace = true }
|
||||||
@ -50,7 +50,6 @@ encoding_rs = { workspace = true }
|
|||||||
fancy-regex = { workspace = true }
|
fancy-regex = { workspace = true }
|
||||||
filesize = { workspace = true }
|
filesize = { workspace = true }
|
||||||
filetime = { workspace = true }
|
filetime = { workspace = true }
|
||||||
fs_extra = { workspace = true }
|
|
||||||
human-date-parser = { workspace = true }
|
human-date-parser = { workspace = true }
|
||||||
indexmap = { workspace = true }
|
indexmap = { workspace = true }
|
||||||
indicatif = { workspace = true }
|
indicatif = { workspace = true }
|
||||||
@ -60,6 +59,7 @@ lscolors = { workspace = true, default-features = false, features = ["nu-ansi-te
|
|||||||
md5 = { workspace = true }
|
md5 = { workspace = true }
|
||||||
mime = { workspace = true }
|
mime = { workspace = true }
|
||||||
mime_guess = { workspace = true }
|
mime_guess = { workspace = true }
|
||||||
|
multipart-rs = { workspace = true }
|
||||||
native-tls = { workspace = true }
|
native-tls = { workspace = true }
|
||||||
notify-debouncer-full = { workspace = true, default-features = false }
|
notify-debouncer-full = { workspace = true, default-features = false }
|
||||||
num-format = { workspace = true }
|
num-format = { workspace = true }
|
||||||
@ -87,7 +87,7 @@ sysinfo = { workspace = true }
|
|||||||
tabled = { workspace = true, features = ["color"], default-features = false }
|
tabled = { workspace = true, features = ["color"], default-features = false }
|
||||||
terminal_size = { workspace = true }
|
terminal_size = { workspace = true }
|
||||||
titlecase = { workspace = true }
|
titlecase = { workspace = true }
|
||||||
toml = { workspace = true, features = ["preserve_order"]}
|
toml = { workspace = true, features = ["preserve_order"] }
|
||||||
unicode-segmentation = { workspace = true }
|
unicode-segmentation = { workspace = true }
|
||||||
ureq = { workspace = true, default-features = false, features = ["charset", "gzip", "json", "native-tls"] }
|
ureq = { workspace = true, default-features = false, features = ["charset", "gzip", "json", "native-tls"] }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
@ -137,8 +137,8 @@ sqlite = ["rusqlite"]
|
|||||||
trash-support = ["trash"]
|
trash-support = ["trash"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.96.1" }
|
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.97.1" }
|
||||||
nu-test-support = { path = "../nu-test-support", version = "0.96.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.97.1" }
|
||||||
|
|
||||||
dirs = { workspace = true }
|
dirs = { workspace = true }
|
||||||
mockito = { workspace = true, default-features = false }
|
mockito = { workspace = true, default-features = false }
|
||||||
|
@ -177,11 +177,9 @@ fn run_histogram(
|
|||||||
match v {
|
match v {
|
||||||
// parse record, and fill valid value to actual input.
|
// parse record, and fill valid value to actual input.
|
||||||
Value::Record { val, .. } => {
|
Value::Record { val, .. } => {
|
||||||
for (c, v) in val.iter() {
|
if let Some(v) = val.get(col_name) {
|
||||||
if c == col_name {
|
if let Ok(v) = HashableValue::from_value(v.clone(), head_span) {
|
||||||
if let Ok(v) = HashableValue::from_value(v.clone(), head_span) {
|
inputs.push(v);
|
||||||
inputs.push(v);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,13 +150,9 @@ fn fill(
|
|||||||
FillAlignment::Left
|
FillAlignment::Left
|
||||||
};
|
};
|
||||||
|
|
||||||
let width = if let Some(arg) = width_arg { arg } else { 1 };
|
let width = width_arg.unwrap_or(1);
|
||||||
|
|
||||||
let character = if let Some(arg) = character_arg {
|
let character = character_arg.unwrap_or_else(|| " ".to_string());
|
||||||
arg
|
|
||||||
} else {
|
|
||||||
" ".to_string()
|
|
||||||
};
|
|
||||||
|
|
||||||
let arg = Arguments {
|
let arg = Arguments {
|
||||||
width,
|
width,
|
||||||
|
@ -379,42 +379,47 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
|||||||
|
|
||||||
// If input is not a timestamp, try parsing it as a string
|
// If input is not a timestamp, try parsing it as a string
|
||||||
let span = input.span();
|
let span = input.span();
|
||||||
match input {
|
|
||||||
Value::String { val, .. } => {
|
let parse_as_string = |val: &str| {
|
||||||
match dateformat {
|
match dateformat {
|
||||||
Some(dt) => match DateTime::parse_from_str(val, &dt.0) {
|
Some(dt) => match DateTime::parse_from_str(val, &dt.0) {
|
||||||
Ok(d) => Value::date ( d, head ),
|
Ok(d) => Value::date ( d, head ),
|
||||||
Err(reason) => {
|
Err(reason) => {
|
||||||
match NaiveDateTime::parse_from_str(val, &dt.0) {
|
match NaiveDateTime::parse_from_str(val, &dt.0) {
|
||||||
Ok(d) => Value::date (
|
Ok(d) => Value::date (
|
||||||
DateTime::from_naive_utc_and_offset(
|
DateTime::from_naive_utc_and_offset(
|
||||||
d,
|
d,
|
||||||
*Local::now().offset(),
|
*Local::now().offset(),
|
||||||
),
|
|
||||||
head,
|
|
||||||
),
|
),
|
||||||
Err(_) => {
|
head,
|
||||||
Value::error (
|
),
|
||||||
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
|
Err(_) => {
|
||||||
head,
|
Value::error (
|
||||||
)
|
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
|
||||||
}
|
head,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Tries to automatically parse the date
|
// Tries to automatically parse the date
|
||||||
// (i.e. without a format string)
|
// (i.e. without a format string)
|
||||||
// and assumes the system's local timezone if none is specified
|
// and assumes the system's local timezone if none is specified
|
||||||
None => match parse_date_from_string(val, span) {
|
None => match parse_date_from_string(val, span) {
|
||||||
Ok(date) => Value::date (
|
Ok(date) => Value::date (
|
||||||
date,
|
date,
|
||||||
span,
|
span,
|
||||||
),
|
),
|
||||||
Err(err) => err,
|
Err(err) => err,
|
||||||
},
|
},
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match input {
|
||||||
|
Value::String { val, .. } => parse_as_string(val),
|
||||||
|
Value::Int { val, .. } => parse_as_string(&val.to_string()),
|
||||||
|
|
||||||
// Propagate errors by explicitly matching them before the final case.
|
// Propagate errors by explicitly matching them before the final case.
|
||||||
Value::Error { .. } => input.clone(),
|
Value::Error { .. } => input.clone(),
|
||||||
other => Value::error(
|
other => Value::error(
|
||||||
@ -575,6 +580,24 @@ mod tests {
|
|||||||
assert_eq!(actual, expected)
|
assert_eq!(actual, expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn takes_int_with_formatstring() {
|
||||||
|
let date_int = Value::test_int(1_614_434_140);
|
||||||
|
let fmt_options = Some(DatetimeFormat("%s".to_string()));
|
||||||
|
let args = Arguments {
|
||||||
|
zone_options: None,
|
||||||
|
format_options: fmt_options,
|
||||||
|
cell_paths: None,
|
||||||
|
};
|
||||||
|
let actual = action(&date_int, &args, Span::test_data());
|
||||||
|
let expected = Value::date(
|
||||||
|
DateTime::parse_from_str("2021-02-27 21:55:40 +08:00", "%Y-%m-%d %H:%M:%S %z").unwrap(),
|
||||||
|
Span::test_data(),
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(actual, expected)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn takes_timestamp() {
|
fn takes_timestamp() {
|
||||||
let date_str = Value::test_string("1614434140000000000");
|
let date_str = Value::test_string("1614434140000000000");
|
||||||
|
@ -424,11 +424,7 @@ pub fn value_to_sql(value: Value) -> Result<Box<dyn rusqlite::ToSql>, ShellError
|
|||||||
Value::Filesize { val, .. } => Box::new(val),
|
Value::Filesize { val, .. } => Box::new(val),
|
||||||
Value::Duration { val, .. } => Box::new(val),
|
Value::Duration { val, .. } => Box::new(val),
|
||||||
Value::Date { val, .. } => Box::new(val),
|
Value::Date { val, .. } => Box::new(val),
|
||||||
Value::String { val, .. } => {
|
Value::String { val, .. } => Box::new(val),
|
||||||
// don't store ansi escape sequences in the database
|
|
||||||
// escape single quotes
|
|
||||||
Box::new(nu_utils::strip_ansi_unlikely(&val).into_owned())
|
|
||||||
}
|
|
||||||
Value::Binary { val, .. } => Box::new(val),
|
Value::Binary { val, .. } => Box::new(val),
|
||||||
Value::Nothing { .. } => Box::new(rusqlite::types::Null),
|
Value::Nothing { .. } => Box::new(rusqlite::types::Null),
|
||||||
val => {
|
val => {
|
||||||
|
@ -397,6 +397,7 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
|||||||
RandomFloat,
|
RandomFloat,
|
||||||
RandomInt,
|
RandomInt,
|
||||||
RandomUuid,
|
RandomUuid,
|
||||||
|
RandomBinary
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generators
|
// Generators
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use nu_cmd_base::util::get_init_cwd;
|
use nu_cmd_base::util::get_init_cwd;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_path::AbsolutePathBuf;
|
|
||||||
use nu_utils::filesystem::{have_permission, PermissionResult};
|
use nu_utils::filesystem::{have_permission, PermissionResult};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -46,8 +45,8 @@ impl Command for Cd {
|
|||||||
// user can use `cd` to recover PWD to a good state.
|
// user can use `cd` to recover PWD to a good state.
|
||||||
let cwd = engine_state
|
let cwd = engine_state
|
||||||
.cwd(Some(stack))
|
.cwd(Some(stack))
|
||||||
.map(AbsolutePathBuf::into_std_path_buf)
|
.ok()
|
||||||
.unwrap_or(get_init_cwd());
|
.unwrap_or_else(get_init_cwd);
|
||||||
|
|
||||||
let path_val = {
|
let path_val = {
|
||||||
if let Some(path) = path_val {
|
if let Some(path) = path_val {
|
||||||
@ -66,7 +65,7 @@ impl Command for Cd {
|
|||||||
if let Some(oldpwd) = stack.get_env_var(engine_state, "OLDPWD") {
|
if let Some(oldpwd) = stack.get_env_var(engine_state, "OLDPWD") {
|
||||||
oldpwd.to_path()?
|
oldpwd.to_path()?
|
||||||
} else {
|
} else {
|
||||||
cwd
|
cwd.into()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Trim whitespace from the end of path.
|
// Trim whitespace from the end of path.
|
||||||
@ -135,7 +134,7 @@ impl Command for Cd {
|
|||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Change to the previous working directory ($OLDPWD)",
|
description: r#"Change to the previous working directory (same as "cd $env.OLDPWD")"#,
|
||||||
example: r#"cd -"#,
|
example: r#"cd -"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
@ -144,6 +143,16 @@ impl Command for Cd {
|
|||||||
example: r#"def --env gohome [] { cd ~ }"#,
|
example: r#"def --env gohome [] { cd ~ }"#,
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Move two directories up in the tree (the parent directory's parent). Additional dots can be added for additional levels.",
|
||||||
|
example: r#"cd ..."#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "The cd command itself is often optional. Simply entering a path to a directory will cd to it.",
|
||||||
|
example: r#"/home"#,
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ impl Command for Glob {
|
|||||||
fn signature(&self) -> Signature {
|
fn signature(&self) -> Signature {
|
||||||
Signature::build("glob")
|
Signature::build("glob")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::String)))])
|
.input_output_types(vec![(Type::Nothing, Type::List(Box::new(Type::String)))])
|
||||||
.required("glob", SyntaxShape::String, "The glob expression.")
|
.required("glob", SyntaxShape::OneOf(vec![SyntaxShape::String, SyntaxShape::GlobPattern]), "The glob expression.")
|
||||||
.named(
|
.named(
|
||||||
"depth",
|
"depth",
|
||||||
SyntaxShape::Int,
|
SyntaxShape::Int,
|
||||||
@ -126,12 +126,12 @@ impl Command for Glob {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
let glob_pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let glob_pattern_input: Value = call.req(engine_state, stack, 0)?;
|
||||||
|
let glob_span = glob_pattern_input.span();
|
||||||
let depth = call.get_flag(engine_state, stack, "depth")?;
|
let depth = call.get_flag(engine_state, stack, "depth")?;
|
||||||
let no_dirs = call.has_flag(engine_state, stack, "no-dir")?;
|
let no_dirs = call.has_flag(engine_state, stack, "no-dir")?;
|
||||||
let no_files = call.has_flag(engine_state, stack, "no-file")?;
|
let no_files = call.has_flag(engine_state, stack, "no-file")?;
|
||||||
let no_symlinks = call.has_flag(engine_state, stack, "no-symlink")?;
|
let no_symlinks = call.has_flag(engine_state, stack, "no-symlink")?;
|
||||||
|
|
||||||
let paths_to_exclude: Option<Value> = call.get_flag(engine_state, stack, "exclude")?;
|
let paths_to_exclude: Option<Value> = call.get_flag(engine_state, stack, "exclude")?;
|
||||||
|
|
||||||
let (not_patterns, not_pattern_span): (Vec<String>, Span) = match paths_to_exclude {
|
let (not_patterns, not_pattern_span): (Vec<String>, Span) = match paths_to_exclude {
|
||||||
@ -148,11 +148,22 @@ impl Command for Glob {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if glob_pattern.item.is_empty() {
|
let glob_pattern =
|
||||||
|
match glob_pattern_input {
|
||||||
|
Value::String { val, .. } | Value::Glob { val, .. } => val,
|
||||||
|
_ => return Err(ShellError::IncorrectValue {
|
||||||
|
msg: "Incorrect glob pattern supplied to glob. Please use string or glob only."
|
||||||
|
.to_string(),
|
||||||
|
val_span: call.head,
|
||||||
|
call_span: glob_span,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
if glob_pattern.is_empty() {
|
||||||
return Err(ShellError::GenericError {
|
return Err(ShellError::GenericError {
|
||||||
error: "glob pattern must not be empty".into(),
|
error: "glob pattern must not be empty".into(),
|
||||||
msg: "glob pattern is empty".into(),
|
msg: "glob pattern is empty".into(),
|
||||||
span: Some(glob_pattern.span),
|
span: Some(glob_span),
|
||||||
help: Some("add characters to the glob pattern".into()),
|
help: Some("add characters to the glob pattern".into()),
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
});
|
});
|
||||||
@ -164,13 +175,13 @@ impl Command for Glob {
|
|||||||
usize::MAX
|
usize::MAX
|
||||||
};
|
};
|
||||||
|
|
||||||
let (prefix, glob) = match WaxGlob::new(&glob_pattern.item) {
|
let (prefix, glob) = match WaxGlob::new(&glob_pattern) {
|
||||||
Ok(p) => p.partition(),
|
Ok(p) => p.partition(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(ShellError::GenericError {
|
return Err(ShellError::GenericError {
|
||||||
error: "error with glob pattern".into(),
|
error: "error with glob pattern".into(),
|
||||||
msg: format!("{e}"),
|
msg: format!("{e}"),
|
||||||
span: Some(glob_pattern.span),
|
span: Some(glob_span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
})
|
})
|
||||||
@ -189,7 +200,7 @@ impl Command for Glob {
|
|||||||
return Err(ShellError::GenericError {
|
return Err(ShellError::GenericError {
|
||||||
error: "error in canonicalize".into(),
|
error: "error in canonicalize".into(),
|
||||||
msg: format!("{e}"),
|
msg: format!("{e}"),
|
||||||
span: Some(glob_pattern.span),
|
span: Some(glob_span),
|
||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
})
|
})
|
||||||
|
@ -5,7 +5,7 @@ use nu_engine::glob_from;
|
|||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, env::current_dir};
|
use nu_engine::{command_prelude::*, env::current_dir};
|
||||||
use nu_glob::MatchOptions;
|
use nu_glob::MatchOptions;
|
||||||
use nu_path::expand_to_real_path;
|
use nu_path::{expand_path_with, expand_to_real_path};
|
||||||
use nu_protocol::{DataSource, NuGlob, PipelineMetadata, Signals};
|
use nu_protocol::{DataSource, NuGlob, PipelineMetadata, Signals};
|
||||||
use pathdiff::diff_paths;
|
use pathdiff::diff_paths;
|
||||||
|
|
||||||
@ -412,6 +412,7 @@ fn ls_for_one_pattern(
|
|||||||
du,
|
du,
|
||||||
&signals,
|
&signals,
|
||||||
use_mime_type,
|
use_mime_type,
|
||||||
|
args.full_paths,
|
||||||
);
|
);
|
||||||
match entry {
|
match entry {
|
||||||
Ok(value) => Some(value),
|
Ok(value) => Some(value),
|
||||||
@ -522,6 +523,7 @@ pub(crate) fn dir_entry_dict(
|
|||||||
du: bool,
|
du: bool,
|
||||||
signals: &Signals,
|
signals: &Signals,
|
||||||
use_mime_type: bool,
|
use_mime_type: bool,
|
||||||
|
full_symlink_target: bool,
|
||||||
) -> Result<Value, ShellError> {
|
) -> Result<Value, ShellError> {
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
if metadata.is_none() {
|
if metadata.is_none() {
|
||||||
@ -551,7 +553,23 @@ pub(crate) fn dir_entry_dict(
|
|||||||
"target",
|
"target",
|
||||||
if md.file_type().is_symlink() {
|
if md.file_type().is_symlink() {
|
||||||
if let Ok(path_to_link) = filename.read_link() {
|
if let Ok(path_to_link) = filename.read_link() {
|
||||||
Value::string(path_to_link.to_string_lossy(), span)
|
// Actually `filename` should always have a parent because it's a symlink.
|
||||||
|
// But for safety, we check `filename.parent().is_some()` first.
|
||||||
|
if full_symlink_target && filename.parent().is_some() {
|
||||||
|
Value::string(
|
||||||
|
expand_path_with(
|
||||||
|
path_to_link,
|
||||||
|
filename
|
||||||
|
.parent()
|
||||||
|
.expect("already check the filename have a parent"),
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
.to_string_lossy(),
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Value::string(path_to_link.to_string_lossy(), span)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Value::string("Could not obtain target file's path", span)
|
Value::string("Could not obtain target file's path", span)
|
||||||
}
|
}
|
||||||
|
@ -146,11 +146,19 @@ impl Command for Open {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let content_type = if raw {
|
||||||
|
path.extension()
|
||||||
|
.map(|ext| ext.to_string_lossy().to_string())
|
||||||
|
.and_then(|ref s| detect_content_type(s))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
let stream = PipelineData::ByteStream(
|
let stream = PipelineData::ByteStream(
|
||||||
ByteStream::file(file, call_span, engine_state.signals().clone()),
|
ByteStream::file(file, call_span, engine_state.signals().clone()),
|
||||||
Some(PipelineMetadata {
|
Some(PipelineMetadata {
|
||||||
data_source: DataSource::FilePath(path.to_path_buf()),
|
data_source: DataSource::FilePath(path.to_path_buf()),
|
||||||
content_type: None,
|
content_type,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -268,3 +276,22 @@ fn extract_extensions(filename: &str) -> Vec<String> {
|
|||||||
|
|
||||||
extensions
|
extensions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn detect_content_type(extension: &str) -> Option<String> {
|
||||||
|
// This will allow the overriding of metadata to be consistent with
|
||||||
|
// the content type
|
||||||
|
match extension {
|
||||||
|
// Per RFC-9512, application/yaml should be used
|
||||||
|
"yaml" | "yml" => Some("application/yaml".to_string()),
|
||||||
|
_ => mime_guess::from_ext(extension)
|
||||||
|
.first()
|
||||||
|
.map(|mime| mime.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type() {}
|
||||||
|
}
|
||||||
|
@ -135,12 +135,9 @@ fn rm(
|
|||||||
let home: Option<String> = nu_path::home_dir().map(|path| {
|
let home: Option<String> = nu_path::home_dir().map(|path| {
|
||||||
{
|
{
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
match nu_path::canonicalize_with(&path, ¤tdir_path) {
|
nu_path::canonicalize_with(&path, ¤tdir_path).unwrap_or(path.into())
|
||||||
Ok(canon_path) => canon_path,
|
|
||||||
Err(_) => path,
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
path
|
path.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.to_string_lossy()
|
.to_string_lossy()
|
||||||
|
@ -121,9 +121,11 @@ impl Command for Save {
|
|||||||
} else {
|
} else {
|
||||||
match stderr {
|
match stderr {
|
||||||
ChildPipe::Pipe(mut pipe) => {
|
ChildPipe::Pipe(mut pipe) => {
|
||||||
io::copy(&mut pipe, &mut io::sink())
|
io::copy(&mut pipe, &mut io::stderr())
|
||||||
|
}
|
||||||
|
ChildPipe::Tee(mut tee) => {
|
||||||
|
io::copy(&mut tee, &mut io::stderr())
|
||||||
}
|
}
|
||||||
ChildPipe::Tee(mut tee) => io::copy(&mut tee, &mut io::sink()),
|
|
||||||
}
|
}
|
||||||
.err_span(span)?;
|
.err_span(span)?;
|
||||||
}
|
}
|
||||||
|
@ -179,11 +179,6 @@ impl Command for Touch {
|
|||||||
example: "touch -m fixture.json",
|
example: "touch -m fixture.json",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
|
||||||
description: "Changes the last modified time of files a, b and c to a date",
|
|
||||||
example: r#"touch -m -d "yesterday" a b c"#,
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
Example {
|
||||||
description: r#"Changes the last modified time of file d and e to "fixture.json"'s last modified time"#,
|
description: r#"Changes the last modified time of file d and e to "fixture.json"'s last modified time"#,
|
||||||
example: r#"touch -m -r fixture.json d e"#,
|
example: r#"touch -m -r fixture.json d e"#,
|
||||||
|
@ -86,17 +86,22 @@ impl Command for UCp {
|
|||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Copy only if source file is newer than target file",
|
description: "Copy only if source file is newer than target file",
|
||||||
example: "cp -u a b",
|
example: "cp -u myfile newfile",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Copy file preserving mode and timestamps attributes",
|
description: "Copy file preserving mode and timestamps attributes",
|
||||||
example: "cp --preserve [ mode timestamps ] a b",
|
example: "cp --preserve [ mode timestamps ] myfile newfile",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Copy file erasing all attributes",
|
description: "Copy file erasing all attributes",
|
||||||
example: "cp --preserve [] a b",
|
example: "cp --preserve [] myfile newfile",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
|
Example {
|
||||||
|
description: "Copy file to a directory three levels above its current location",
|
||||||
|
example: "cp myfile ....",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
@ -235,7 +240,7 @@ impl Command for UCp {
|
|||||||
for (sources, need_expand_tilde) in sources.iter_mut() {
|
for (sources, need_expand_tilde) in sources.iter_mut() {
|
||||||
for src in sources.iter_mut() {
|
for src in sources.iter_mut() {
|
||||||
if !src.is_absolute() {
|
if !src.is_absolute() {
|
||||||
*src = nu_path::expand_path_with(&src, &cwd, *need_expand_tilde);
|
*src = nu_path::expand_path_with(&*src, &cwd, *need_expand_tilde);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,11 +30,21 @@ impl Command for UMv {
|
|||||||
example: "mv test.txt my/subdirectory",
|
example: "mv test.txt my/subdirectory",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Move only if source file is newer than target file",
|
||||||
|
example: "mv -u new/test.txt old/",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Move many files into a directory",
|
description: "Move many files into a directory",
|
||||||
example: "mv *.txt my/subdirectory",
|
example: "mv *.txt my/subdirectory",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: r#"Move a file into the "my" directory two levels up in the directory tree"#,
|
||||||
|
example: "mv test.txt .../my/",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,6 +59,11 @@ impl Command for UMv {
|
|||||||
.switch("verbose", "explain what is being done.", Some('v'))
|
.switch("verbose", "explain what is being done.", Some('v'))
|
||||||
.switch("progress", "display a progress bar", Some('p'))
|
.switch("progress", "display a progress bar", Some('p'))
|
||||||
.switch("interactive", "prompt before overwriting", Some('i'))
|
.switch("interactive", "prompt before overwriting", Some('i'))
|
||||||
|
.switch(
|
||||||
|
"update",
|
||||||
|
"move and overwrite only when the SOURCE file is newer than the destination file or when the destination file is missing",
|
||||||
|
Some('u')
|
||||||
|
)
|
||||||
.switch("no-clobber", "do not overwrite an existing file", Some('n'))
|
.switch("no-clobber", "do not overwrite an existing file", Some('n'))
|
||||||
.rest(
|
.rest(
|
||||||
"paths",
|
"paths",
|
||||||
@ -77,6 +92,11 @@ impl Command for UMv {
|
|||||||
} else {
|
} else {
|
||||||
uu_mv::OverwriteMode::Force
|
uu_mv::OverwriteMode::Force
|
||||||
};
|
};
|
||||||
|
let update = if call.has_flag(engine_state, stack, "update")? {
|
||||||
|
UpdateMode::ReplaceIfOlder
|
||||||
|
} else {
|
||||||
|
UpdateMode::ReplaceAll
|
||||||
|
};
|
||||||
|
|
||||||
#[allow(deprecated)]
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
@ -141,7 +161,7 @@ impl Command for UMv {
|
|||||||
for (files, need_expand_tilde) in files.iter_mut() {
|
for (files, need_expand_tilde) in files.iter_mut() {
|
||||||
for src in files.iter_mut() {
|
for src in files.iter_mut() {
|
||||||
if !src.is_absolute() {
|
if !src.is_absolute() {
|
||||||
*src = nu_path::expand_path_with(&src, &cwd, *need_expand_tilde);
|
*src = nu_path::expand_path_with(&*src, &cwd, *need_expand_tilde);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,7 +184,7 @@ impl Command for UMv {
|
|||||||
verbose,
|
verbose,
|
||||||
suffix: String::from("~"),
|
suffix: String::from("~"),
|
||||||
backup: BackupMode::NoBackup,
|
backup: BackupMode::NoBackup,
|
||||||
update: UpdateMode::ReplaceAll,
|
update,
|
||||||
target_dir: None,
|
target_dir: None,
|
||||||
no_target_dir: false,
|
no_target_dir: false,
|
||||||
strip_slashes: false,
|
strip_slashes: false,
|
||||||
|
@ -27,7 +27,7 @@ impl Command for Default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Sets a default row's column if missing."
|
"Sets a default value if a row's column is missing or null."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -66,6 +66,20 @@ impl Command for Default {
|
|||||||
Span::test_data(),
|
Span::test_data(),
|
||||||
)),
|
)),
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: r#"Replace the missing value in the "a" column of a list"#,
|
||||||
|
example: "[{a:1 b:2} {b:1}] | default 'N/A' a",
|
||||||
|
result: Some(Value::test_list(vec![
|
||||||
|
Value::test_record(record! {
|
||||||
|
"a" => Value::test_int(1),
|
||||||
|
"b" => Value::test_int(2),
|
||||||
|
}),
|
||||||
|
Value::test_record(record! {
|
||||||
|
"a" => Value::test_string("N/A"),
|
||||||
|
"b" => Value::test_int(1),
|
||||||
|
}),
|
||||||
|
])),
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,19 +102,13 @@ fn default(
|
|||||||
val: ref mut record,
|
val: ref mut record,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
let mut found = false;
|
let record = record.to_mut();
|
||||||
|
if let Some(val) = record.get_mut(&column.item) {
|
||||||
for (col, val) in record.to_mut().iter_mut() {
|
if matches!(val, Value::Nothing { .. }) {
|
||||||
if *col == column.item {
|
*val = value.clone();
|
||||||
found = true;
|
|
||||||
if matches!(val, Value::Nothing { .. }) {
|
|
||||||
*val = value.clone();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
record.push(column.item.clone(), value.clone());
|
||||||
if !found {
|
|
||||||
record.to_mut().push(column.item.clone(), value.clone());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
item
|
item
|
||||||
|
@ -120,8 +120,7 @@ impl Command for Reduce {
|
|||||||
engine_state.signals().check(head)?;
|
engine_state.signals().check(head)?;
|
||||||
acc = closure
|
acc = closure
|
||||||
.add_arg(value)
|
.add_arg(value)
|
||||||
.add_arg(acc)
|
.run_with_value(acc)?
|
||||||
.run_with_input(PipelineData::Empty)?
|
|
||||||
.into_value(head)?;
|
.into_value(head)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::io::{BufRead, Cursor};
|
use std::io::{BufRead, Cursor};
|
||||||
|
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{ListStream, PipelineMetadata, Signals};
|
use nu_protocol::{ListStream, Signals};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct FromJson;
|
pub struct FromJson;
|
||||||
@ -83,7 +83,7 @@ impl Command for FromJson {
|
|||||||
strict,
|
strict,
|
||||||
engine_state.signals().clone(),
|
engine_state.signals().clone(),
|
||||||
),
|
),
|
||||||
update_metadata(metadata),
|
metadata,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
PipelineData::ByteStream(stream, metadata)
|
PipelineData::ByteStream(stream, metadata)
|
||||||
@ -92,7 +92,7 @@ impl Command for FromJson {
|
|||||||
if let Some(reader) = stream.reader() {
|
if let Some(reader) = stream.reader() {
|
||||||
Ok(PipelineData::ListStream(
|
Ok(PipelineData::ListStream(
|
||||||
read_json_lines(reader, span, strict, Signals::empty()),
|
read_json_lines(reader, span, strict, Signals::empty()),
|
||||||
update_metadata(metadata),
|
metadata,
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(PipelineData::Empty)
|
Ok(PipelineData::Empty)
|
||||||
@ -115,10 +115,10 @@ impl Command for FromJson {
|
|||||||
|
|
||||||
if strict {
|
if strict {
|
||||||
Ok(convert_string_to_value_strict(&string_input, span)?
|
Ok(convert_string_to_value_strict(&string_input, span)?
|
||||||
.into_pipeline_data_with_metadata(update_metadata(metadata)))
|
.into_pipeline_data_with_metadata(metadata))
|
||||||
} else {
|
} else {
|
||||||
Ok(convert_string_to_value(&string_input, span)?
|
Ok(convert_string_to_value(&string_input, span)?
|
||||||
.into_pipeline_data_with_metadata(update_metadata(metadata)))
|
.into_pipeline_data_with_metadata(metadata))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -265,14 +265,6 @@ fn convert_string_to_value_strict(string_input: &str, span: Span) -> Result<Valu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_metadata(metadata: Option<PipelineMetadata>) -> Option<PipelineMetadata> {
|
|
||||||
metadata
|
|
||||||
.map(|md| md.with_content_type(Some("application/json".into())))
|
|
||||||
.or_else(|| {
|
|
||||||
Some(PipelineMetadata::default().with_content_type(Some("application/json".into())))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -4,6 +4,8 @@ use crate::formats::to::delimited::to_delimited_data;
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::Config;
|
use nu_protocol::Config;
|
||||||
|
|
||||||
|
use super::delimited::ToDelimitedDataArgs;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ToCsv;
|
pub struct ToCsv;
|
||||||
|
|
||||||
@ -116,17 +118,62 @@ fn to_csv(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
to_delimited_data(noheaders, sep, columns, "CSV", input, head, config)
|
to_delimited_data(
|
||||||
|
ToDelimitedDataArgs {
|
||||||
|
noheaders,
|
||||||
|
separator: sep,
|
||||||
|
columns,
|
||||||
|
format_name: "CSV",
|
||||||
|
input,
|
||||||
|
head,
|
||||||
|
content_type: Some(mime::TEXT_CSV.to_string()),
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_examples() {
|
fn test_examples() {
|
||||||
use crate::test_examples;
|
use crate::test_examples;
|
||||||
|
|
||||||
test_examples(ToCsv {})
|
test_examples(ToCsv {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToCsv {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to csv | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("text/csv"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,18 +69,36 @@ fn make_cant_convert_error(value: &Value, format_name: &'static str) -> ShellErr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ToDelimitedDataArgs {
|
||||||
|
pub noheaders: bool,
|
||||||
|
pub separator: Spanned<char>,
|
||||||
|
pub columns: Option<Vec<String>>,
|
||||||
|
pub format_name: &'static str,
|
||||||
|
pub input: PipelineData,
|
||||||
|
pub head: Span,
|
||||||
|
pub content_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_delimited_data(
|
pub fn to_delimited_data(
|
||||||
noheaders: bool,
|
ToDelimitedDataArgs {
|
||||||
separator: Spanned<char>,
|
noheaders,
|
||||||
columns: Option<Vec<String>>,
|
separator,
|
||||||
format_name: &'static str,
|
columns,
|
||||||
input: PipelineData,
|
format_name,
|
||||||
head: Span,
|
input,
|
||||||
|
head,
|
||||||
|
content_type,
|
||||||
|
}: ToDelimitedDataArgs,
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let mut input = input;
|
let mut input = input;
|
||||||
let span = input.span().unwrap_or(head);
|
let span = input.span().unwrap_or(head);
|
||||||
let metadata = input.metadata();
|
let metadata = Some(
|
||||||
|
input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(content_type),
|
||||||
|
);
|
||||||
|
|
||||||
let separator = u8::try_from(separator.item).map_err(|_| ShellError::IncorrectValue {
|
let separator = u8::try_from(separator.item).map_err(|_| ShellError::IncorrectValue {
|
||||||
msg: "separator must be an ASCII character".into(),
|
msg: "separator must be an ASCII character".into(),
|
||||||
|
@ -64,7 +64,7 @@ impl Command for ToJson {
|
|||||||
let res = Value::string(serde_json_string, span);
|
let res = Value::string(serde_json_string, span);
|
||||||
let metadata = PipelineMetadata {
|
let metadata = PipelineMetadata {
|
||||||
data_source: nu_protocol::DataSource::None,
|
data_source: nu_protocol::DataSource::None,
|
||||||
content_type: Some("application/json".to_string()),
|
content_type: Some(mime::APPLICATION_JSON.to_string()),
|
||||||
};
|
};
|
||||||
Ok(PipelineData::Value(res, Some(metadata)))
|
Ok(PipelineData::Value(res, Some(metadata)))
|
||||||
}
|
}
|
||||||
@ -159,6 +159,10 @@ fn json_list(input: &[Value]) -> Result<Vec<nu_json::Value>, ShellError> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -167,4 +171,34 @@ mod test {
|
|||||||
|
|
||||||
test_examples(ToJson {})
|
test_examples(ToJson {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToJson {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to json | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("application/json"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,12 @@ fn to_md(
|
|||||||
config: &Config,
|
config: &Config,
|
||||||
head: Span,
|
head: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
// text/markdown became a valid mimetype with rfc7763
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("text/markdown".into()));
|
||||||
|
|
||||||
let (grouped_input, single_list) = group_by(input, head, config);
|
let (grouped_input, single_list) = group_by(input, head, config);
|
||||||
if per_element || single_list {
|
if per_element || single_list {
|
||||||
return Ok(Value::string(
|
return Ok(Value::string(
|
||||||
@ -95,9 +101,10 @@ fn to_md(
|
|||||||
.join(""),
|
.join(""),
|
||||||
head,
|
head,
|
||||||
)
|
)
|
||||||
.into_pipeline_data());
|
.into_pipeline_data_with_metadata(Some(metadata)));
|
||||||
}
|
}
|
||||||
Ok(Value::string(table(grouped_input, pretty, config), head).into_pipeline_data())
|
Ok(Value::string(table(grouped_input, pretty, config), head)
|
||||||
|
.into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fragment(input: Value, pretty: bool, config: &Config) -> String {
|
fn fragment(input: Value, pretty: bool, config: &Config) -> String {
|
||||||
@ -328,7 +335,10 @@ fn get_padded_string(text: String, desired_length: usize, padding_character: cha
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
use nu_protocol::{record, Config, IntoPipelineData, Value};
|
use nu_protocol::{record, Config, IntoPipelineData, Value};
|
||||||
|
|
||||||
fn one(string: &str) -> String {
|
fn one(string: &str) -> String {
|
||||||
@ -453,4 +463,35 @@ mod tests {
|
|||||||
"#)
|
"#)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let state_delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToMd {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
let delta = state_delta;
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to md | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("text/markdown"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,13 +74,18 @@ MessagePack: https://msgpack.org/
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("application/x-msgpack".into()));
|
||||||
|
|
||||||
let value_span = input.span().unwrap_or(call.head);
|
let value_span = input.span().unwrap_or(call.head);
|
||||||
let value = input.into_value(value_span)?;
|
let value = input.into_value(value_span)?;
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
|
|
||||||
write_value(&mut out, &value, 0)?;
|
write_value(&mut out, &value, 0)?;
|
||||||
|
|
||||||
Ok(Value::binary(out, call.head).into_pipeline_data())
|
Ok(Value::binary(out, call.head).into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,6 +273,10 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -276,4 +285,36 @@ mod test {
|
|||||||
|
|
||||||
test_examples(ToMsgpack {})
|
test_examples(ToMsgpack {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToMsgpack {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to msgpack | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(
|
||||||
|
record!("content_type" => Value::test_string("application/x-msgpack"))
|
||||||
|
),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,11 @@ impl Command for ToNuon {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("application/x-nuon".into()));
|
||||||
|
|
||||||
let style = if call.has_flag(engine_state, stack, "raw")? {
|
let style = if call.has_flag(engine_state, stack, "raw")? {
|
||||||
nuon::ToStyle::Raw
|
nuon::ToStyle::Raw
|
||||||
} else if let Some(t) = call.get_flag(engine_state, stack, "tabs")? {
|
} else if let Some(t) = call.get_flag(engine_state, stack, "tabs")? {
|
||||||
@ -56,9 +61,8 @@ impl Command for ToNuon {
|
|||||||
let value = input.into_value(span)?;
|
let value = input.into_value(span)?;
|
||||||
|
|
||||||
match nuon::to_nuon(&value, style, Some(span)) {
|
match nuon::to_nuon(&value, style, Some(span)) {
|
||||||
Ok(serde_nuon_string) => {
|
Ok(serde_nuon_string) => Ok(Value::string(serde_nuon_string, span)
|
||||||
Ok(Value::string(serde_nuon_string, span).into_pipeline_data())
|
.into_pipeline_data_with_metadata(Some(metadata))),
|
||||||
}
|
|
||||||
_ => Ok(Value::error(
|
_ => Ok(Value::error(
|
||||||
ShellError::CantConvert {
|
ShellError::CantConvert {
|
||||||
to_type: "NUON".into(),
|
to_type: "NUON".into(),
|
||||||
@ -68,7 +72,7 @@ impl Command for ToNuon {
|
|||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
.into_pipeline_data()),
|
.into_pipeline_data_with_metadata(Some(metadata))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,10 +104,45 @@ impl Command for ToNuon {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_examples() {
|
fn test_examples() {
|
||||||
use super::ToNuon;
|
use super::ToNuon;
|
||||||
use crate::test_examples;
|
use crate::test_examples;
|
||||||
test_examples(ToNuon {})
|
test_examples(ToNuon {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToNuon {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to nuon | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("application/x-nuon"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,14 +134,18 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
|||||||
|
|
||||||
fn update_metadata(metadata: Option<PipelineMetadata>) -> Option<PipelineMetadata> {
|
fn update_metadata(metadata: Option<PipelineMetadata>) -> Option<PipelineMetadata> {
|
||||||
metadata
|
metadata
|
||||||
.map(|md| md.with_content_type(Some("text/plain".to_string())))
|
.map(|md| md.with_content_type(Some(mime::TEXT_PLAIN.to_string())))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
Some(PipelineMetadata::default().with_content_type(Some("text/plain".to_string())))
|
Some(PipelineMetadata::default().with_content_type(Some(mime::TEXT_PLAIN.to_string())))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -150,4 +154,34 @@ mod test {
|
|||||||
|
|
||||||
test_examples(ToText {})
|
test_examples(ToText {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToText {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to text | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("text/plain"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use chrono::{DateTime, Datelike, FixedOffset, Timelike};
|
use chrono::{DateTime, Datelike, FixedOffset, Timelike};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::ast::PathMember;
|
use nu_protocol::{ast::PathMember, PipelineMetadata};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ToToml;
|
pub struct ToToml;
|
||||||
@ -100,9 +100,18 @@ fn toml_into_pipeline_data(
|
|||||||
toml_value: &toml::Value,
|
toml_value: &toml::Value,
|
||||||
value_type: Type,
|
value_type: Type,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
metadata: Option<PipelineMetadata>,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let new_md = Some(
|
||||||
|
metadata
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("text/x-toml".into())),
|
||||||
|
);
|
||||||
|
|
||||||
match toml::to_string_pretty(&toml_value) {
|
match toml::to_string_pretty(&toml_value) {
|
||||||
Ok(serde_toml_string) => Ok(Value::string(serde_toml_string, span).into_pipeline_data()),
|
Ok(serde_toml_string) => {
|
||||||
|
Ok(Value::string(serde_toml_string, span).into_pipeline_data_with_metadata(new_md))
|
||||||
|
}
|
||||||
_ => Ok(Value::error(
|
_ => Ok(Value::error(
|
||||||
ShellError::CantConvert {
|
ShellError::CantConvert {
|
||||||
to_type: "TOML".into(),
|
to_type: "TOML".into(),
|
||||||
@ -112,7 +121,7 @@ fn toml_into_pipeline_data(
|
|||||||
},
|
},
|
||||||
span,
|
span,
|
||||||
)
|
)
|
||||||
.into_pipeline_data()),
|
.into_pipeline_data_with_metadata(new_md)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +148,7 @@ fn to_toml(
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
span: Span,
|
span: Span,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let metadata = input.metadata();
|
||||||
let value = input.into_value(span)?;
|
let value = input.into_value(span)?;
|
||||||
|
|
||||||
let toml_value = value_to_toml_value(engine_state, &value, span)?;
|
let toml_value = value_to_toml_value(engine_state, &value, span)?;
|
||||||
@ -148,10 +158,11 @@ fn to_toml(
|
|||||||
vec.iter().next().expect("this should never trigger"),
|
vec.iter().next().expect("this should never trigger"),
|
||||||
value.get_type(),
|
value.get_type(),
|
||||||
span,
|
span,
|
||||||
|
metadata,
|
||||||
),
|
),
|
||||||
_ => toml_into_pipeline_data(&toml_value, value.get_type(), span),
|
_ => toml_into_pipeline_data(&toml_value, value.get_type(), span, metadata),
|
||||||
},
|
},
|
||||||
_ => toml_into_pipeline_data(&toml_value, value.get_type(), span),
|
_ => toml_into_pipeline_data(&toml_value, value.get_type(), span, metadata),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,8 @@ use crate::formats::to::delimited::to_delimited_data;
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::Config;
|
use nu_protocol::Config;
|
||||||
|
|
||||||
|
use super::delimited::ToDelimitedDataArgs;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ToTsv;
|
pub struct ToTsv;
|
||||||
|
|
||||||
@ -82,11 +84,26 @@ fn to_tsv(
|
|||||||
item: '\t',
|
item: '\t',
|
||||||
span: head,
|
span: head,
|
||||||
};
|
};
|
||||||
to_delimited_data(noheaders, sep, columns, "TSV", input, head, config)
|
to_delimited_data(
|
||||||
|
ToDelimitedDataArgs {
|
||||||
|
noheaders,
|
||||||
|
separator: sep,
|
||||||
|
columns,
|
||||||
|
format_name: "TSV",
|
||||||
|
input,
|
||||||
|
head,
|
||||||
|
content_type: Some(mime::TEXT_TAB_SEPARATED_VALUES.to_string()),
|
||||||
|
},
|
||||||
|
config,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -95,4 +112,36 @@ mod test {
|
|||||||
|
|
||||||
test_examples(ToTsv {})
|
test_examples(ToTsv {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToTsv {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to tsv | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(
|
||||||
|
record!("content_type" => Value::test_string("text/tab-separated-values"))
|
||||||
|
),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,10 @@ impl Job {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(mut self, input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
fn run(mut self, input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
.with_content_type(Some("application/xml".into()));
|
||||||
let value = input.into_value(head)?;
|
let value = input.into_value(head)?;
|
||||||
|
|
||||||
self.write_xml_entry(value, true).and_then(|_| {
|
self.write_xml_entry(value, true).and_then(|_| {
|
||||||
@ -141,7 +145,7 @@ impl Job {
|
|||||||
} else {
|
} else {
|
||||||
return Err(ShellError::NonUtf8 { span: head });
|
return Err(ShellError::NonUtf8 { span: head });
|
||||||
};
|
};
|
||||||
Ok(Value::string(s, head).into_pipeline_data())
|
Ok(Value::string(s, head).into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -508,6 +512,10 @@ impl Job {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -516,4 +524,34 @@ mod test {
|
|||||||
|
|
||||||
test_examples(ToXml {})
|
test_examples(ToXml {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToXml {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{tag: note attributes: {} content : [{tag: remember attributes: {} content : [{tag: null attributes: null content : Event}]}]} | to xml | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("application/xml"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,11 +95,19 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn to_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
fn to_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError> {
|
||||||
|
let metadata = input
|
||||||
|
.metadata()
|
||||||
|
.unwrap_or_default()
|
||||||
|
// Per RFC-9512, application/yaml should be used
|
||||||
|
.with_content_type(Some("application/yaml".into()));
|
||||||
let value = input.into_value(head)?;
|
let value = input.into_value(head)?;
|
||||||
|
|
||||||
let yaml_value = value_to_yaml_value(&value)?;
|
let yaml_value = value_to_yaml_value(&value)?;
|
||||||
match serde_yaml::to_string(&yaml_value) {
|
match serde_yaml::to_string(&yaml_value) {
|
||||||
Ok(serde_yaml_string) => Ok(Value::string(serde_yaml_string, head).into_pipeline_data()),
|
Ok(serde_yaml_string) => {
|
||||||
|
Ok(Value::string(serde_yaml_string, head)
|
||||||
|
.into_pipeline_data_with_metadata(Some(metadata)))
|
||||||
|
}
|
||||||
_ => Ok(Value::error(
|
_ => Ok(Value::error(
|
||||||
ShellError::CantConvert {
|
ShellError::CantConvert {
|
||||||
to_type: "YAML".into(),
|
to_type: "YAML".into(),
|
||||||
@ -109,12 +117,16 @@ fn to_yaml(input: PipelineData, head: Span) -> Result<PipelineData, ShellError>
|
|||||||
},
|
},
|
||||||
head,
|
head,
|
||||||
)
|
)
|
||||||
.into_pipeline_data()),
|
.into_pipeline_data_with_metadata(Some(metadata))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use nu_cmd_lang::eval_pipeline_without_terminal_expression;
|
||||||
|
|
||||||
|
use crate::Metadata;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -123,4 +135,34 @@ mod test {
|
|||||||
|
|
||||||
test_examples(ToYaml {})
|
test_examples(ToYaml {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_content_type_metadata() {
|
||||||
|
let mut engine_state = Box::new(EngineState::new());
|
||||||
|
let delta = {
|
||||||
|
// Base functions that are needed for testing
|
||||||
|
// Try to keep this working set small to keep tests running as fast as possible
|
||||||
|
let mut working_set = StateWorkingSet::new(&engine_state);
|
||||||
|
|
||||||
|
working_set.add_decl(Box::new(ToYaml {}));
|
||||||
|
working_set.add_decl(Box::new(Metadata {}));
|
||||||
|
|
||||||
|
working_set.render()
|
||||||
|
};
|
||||||
|
|
||||||
|
engine_state
|
||||||
|
.merge_delta(delta)
|
||||||
|
.expect("Error merging delta");
|
||||||
|
|
||||||
|
let cmd = "{a: 1 b: 2} | to yaml | metadata | get content_type";
|
||||||
|
let result = eval_pipeline_without_terminal_expression(
|
||||||
|
cmd,
|
||||||
|
std::env::temp_dir().as_ref(),
|
||||||
|
&mut engine_state,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Value::test_record(record!("content_type" => Value::test_string("application/yaml"))),
|
||||||
|
result.expect("There should be a result")
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,10 @@ impl Command for SubCommand {
|
|||||||
vec!["absolute", "modulus", "positive", "distance"]
|
vec!["absolute", "modulus", "positive", "distance"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -45,6 +49,19 @@ impl Command for SubCommand {
|
|||||||
input.map(move |value| abs_helper(value, head), engine_state.signals())
|
input.map(move |value| abs_helper(value, head), engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
input.map(
|
||||||
|
move |value| abs_helper(value, head),
|
||||||
|
working_set.permanent().signals(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Compute absolute value of each number in a list of numbers",
|
description: "Compute absolute value of each number in a list of numbers",
|
||||||
|
@ -38,6 +38,10 @@ impl Command for SubCommand {
|
|||||||
vec!["average", "mean", "statistics"]
|
vec!["average", "mean", "statistics"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
@ -48,6 +52,15 @@ impl Command for SubCommand {
|
|||||||
run_with_function(call, input, average)
|
run_with_function(call, input, average)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
_working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
run_with_function(call, input, average)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -29,6 +29,10 @@ impl Command for SubCommand {
|
|||||||
vec!["ceiling", "round up", "rounding", "integer"]
|
vec!["ceiling", "round up", "rounding", "integer"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -44,6 +48,23 @@ impl Command for SubCommand {
|
|||||||
input.map(move |value| operate(value, head), engine_state.signals())
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
// This doesn't match explicit nulls
|
||||||
|
if matches!(input, PipelineData::Empty) {
|
||||||
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
|
}
|
||||||
|
input.map(
|
||||||
|
move |value| operate(value, head),
|
||||||
|
working_set.permanent().signals(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Apply the ceil function to a list of numbers",
|
description: "Apply the ceil function to a list of numbers",
|
||||||
|
@ -29,6 +29,10 @@ impl Command for SubCommand {
|
|||||||
vec!["round down", "rounding", "integer"]
|
vec!["round down", "rounding", "integer"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -44,6 +48,23 @@ impl Command for SubCommand {
|
|||||||
input.map(move |value| operate(value, head), engine_state.signals())
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
// This doesn't match explicit nulls
|
||||||
|
if matches!(input, PipelineData::Empty) {
|
||||||
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
|
}
|
||||||
|
input.map(
|
||||||
|
move |value| operate(value, head),
|
||||||
|
working_set.permanent().signals(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Apply the floor function to a list of numbers",
|
description: "Apply the floor function to a list of numbers",
|
||||||
|
@ -34,6 +34,10 @@ impl Command for SubCommand {
|
|||||||
vec!["base", "exponent", "inverse", "euler"]
|
vec!["base", "exponent", "inverse", "euler"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -63,6 +67,34 @@ impl Command for SubCommand {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
let base: Spanned<f64> = call.req_const(working_set, 0)?;
|
||||||
|
|
||||||
|
if base.item <= 0.0f64 {
|
||||||
|
return Err(ShellError::UnsupportedInput {
|
||||||
|
msg: "Base has to be greater 0".into(),
|
||||||
|
input: "value originates from here".into(),
|
||||||
|
msg_span: head,
|
||||||
|
input_span: base.span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// This doesn't match explicit nulls
|
||||||
|
if matches!(input, PipelineData::Empty) {
|
||||||
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
|
}
|
||||||
|
let base = base.item;
|
||||||
|
input.map(
|
||||||
|
move |value| operate(value, head, base),
|
||||||
|
working_set.permanent().signals(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -35,6 +35,10 @@ impl Command for SubCommand {
|
|||||||
vec!["maximum", "largest"]
|
vec!["maximum", "largest"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
@ -45,6 +49,15 @@ impl Command for SubCommand {
|
|||||||
run_with_function(call, input, maximum)
|
run_with_function(call, input, maximum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
_working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
run_with_function(call, input, maximum)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -32,6 +32,10 @@ impl Command for SubCommand {
|
|||||||
vec!["middle", "statistics"]
|
vec!["middle", "statistics"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
@ -42,6 +46,15 @@ impl Command for SubCommand {
|
|||||||
run_with_function(call, input, median)
|
run_with_function(call, input, median)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
_working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
run_with_function(call, input, median)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -35,6 +35,10 @@ impl Command for SubCommand {
|
|||||||
vec!["minimum", "smallest"]
|
vec!["minimum", "smallest"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
@ -45,6 +49,15 @@ impl Command for SubCommand {
|
|||||||
run_with_function(call, input, minimum)
|
run_with_function(call, input, minimum)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
_working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
run_with_function(call, input, minimum)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -62,6 +62,10 @@ impl Command for SubCommand {
|
|||||||
vec!["common", "often"]
|
vec!["common", "often"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
@ -72,6 +76,15 @@ impl Command for SubCommand {
|
|||||||
run_with_function(call, input, mode)
|
run_with_function(call, input, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
_working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
run_with_function(call, input, mode)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -32,6 +32,10 @@ impl Command for SubCommand {
|
|||||||
vec!["times", "multiply", "x", "*"]
|
vec!["times", "multiply", "x", "*"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
@ -42,6 +46,15 @@ impl Command for SubCommand {
|
|||||||
run_with_function(call, input, product)
|
run_with_function(call, input, product)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
_working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
run_with_function(call, input, product)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -35,6 +35,10 @@ impl Command for SubCommand {
|
|||||||
vec!["approx", "closest", "nearest"]
|
vec!["approx", "closest", "nearest"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -54,6 +58,24 @@ impl Command for SubCommand {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let precision_param: Option<i64> = call.get_flag_const(working_set, "precision")?;
|
||||||
|
let head = call.head;
|
||||||
|
// This doesn't match explicit nulls
|
||||||
|
if matches!(input, PipelineData::Empty) {
|
||||||
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
|
}
|
||||||
|
input.map(
|
||||||
|
move |value| operate(value, head, precision_param),
|
||||||
|
working_set.permanent().signals(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -29,6 +29,10 @@ impl Command for SubCommand {
|
|||||||
vec!["square", "root"]
|
vec!["square", "root"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -44,6 +48,23 @@ impl Command for SubCommand {
|
|||||||
input.map(move |value| operate(value, head), engine_state.signals())
|
input.map(move |value| operate(value, head), engine_state.signals())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let head = call.head;
|
||||||
|
// This doesn't match explicit nulls
|
||||||
|
if matches!(input, PipelineData::Empty) {
|
||||||
|
return Err(ShellError::PipelineEmpty { dst_span: head });
|
||||||
|
}
|
||||||
|
input.map(
|
||||||
|
move |value| operate(value, head),
|
||||||
|
working_set.permanent().signals(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![Example {
|
vec![Example {
|
||||||
description: "Compute the square root of each number in a list",
|
description: "Compute the square root of each number in a list",
|
||||||
|
@ -41,6 +41,10 @@ impl Command for SubCommand {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -52,6 +56,16 @@ impl Command for SubCommand {
|
|||||||
run_with_function(call, input, compute_stddev(sample))
|
run_with_function(call, input, compute_stddev(sample))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let sample = call.has_flag_const(working_set, "sample")?;
|
||||||
|
run_with_function(call, input, compute_stddev(sample))
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -34,6 +34,10 @@ impl Command for SubCommand {
|
|||||||
vec!["plus", "add", "total", "+"]
|
vec!["plus", "add", "total", "+"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
@ -44,6 +48,15 @@ impl Command for SubCommand {
|
|||||||
run_with_function(call, input, summation)
|
run_with_function(call, input, summation)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
_working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
run_with_function(call, input, summation)
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -33,6 +33,10 @@ impl Command for SubCommand {
|
|||||||
vec!["deviation", "dispersion", "variation", "statistics"]
|
vec!["deviation", "dispersion", "variation", "statistics"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_const(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
&self,
|
&self,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
@ -44,6 +48,16 @@ impl Command for SubCommand {
|
|||||||
run_with_function(call, input, compute_variance(sample))
|
run_with_function(call, input, compute_variance(sample))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_const(
|
||||||
|
&self,
|
||||||
|
working_set: &StateWorkingSet,
|
||||||
|
call: &Call,
|
||||||
|
input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let sample = call.has_flag_const(working_set, "sample")?;
|
||||||
|
run_with_function(call, input, compute_variance(sample))
|
||||||
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
|
@ -4,10 +4,12 @@ use base64::{
|
|||||||
engine::{general_purpose::PAD, GeneralPurpose},
|
engine::{general_purpose::PAD, GeneralPurpose},
|
||||||
Engine,
|
Engine,
|
||||||
};
|
};
|
||||||
|
use multipart_rs::MultipartWriter;
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::{ByteStream, Signals};
|
use nu_protocol::{ByteStream, LabeledError, Signals};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
io::Cursor,
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
sync::mpsc::{self, RecvTimeoutError},
|
sync::mpsc::{self, RecvTimeoutError},
|
||||||
@ -20,6 +22,7 @@ use url::Url;
|
|||||||
pub enum BodyType {
|
pub enum BodyType {
|
||||||
Json,
|
Json,
|
||||||
Form,
|
Form,
|
||||||
|
Multipart,
|
||||||
Unknown,
|
Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,17 +144,17 @@ pub fn request_add_authorization_header(
|
|||||||
let login = match (user, password) {
|
let login = match (user, password) {
|
||||||
(Some(user), Some(password)) => {
|
(Some(user), Some(password)) => {
|
||||||
let mut enc_str = String::new();
|
let mut enc_str = String::new();
|
||||||
base64_engine.encode_string(&format!("{user}:{password}"), &mut enc_str);
|
base64_engine.encode_string(format!("{user}:{password}"), &mut enc_str);
|
||||||
Some(enc_str)
|
Some(enc_str)
|
||||||
}
|
}
|
||||||
(Some(user), _) => {
|
(Some(user), _) => {
|
||||||
let mut enc_str = String::new();
|
let mut enc_str = String::new();
|
||||||
base64_engine.encode_string(&format!("{user}:"), &mut enc_str);
|
base64_engine.encode_string(format!("{user}:"), &mut enc_str);
|
||||||
Some(enc_str)
|
Some(enc_str)
|
||||||
}
|
}
|
||||||
(_, Some(password)) => {
|
(_, Some(password)) => {
|
||||||
let mut enc_str = String::new();
|
let mut enc_str = String::new();
|
||||||
base64_engine.encode_string(&format!(":{password}"), &mut enc_str);
|
base64_engine.encode_string(format!(":{password}"), &mut enc_str);
|
||||||
Some(enc_str)
|
Some(enc_str)
|
||||||
}
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
@ -210,6 +213,7 @@ pub fn send_request(
|
|||||||
let (body_type, req) = match content_type {
|
let (body_type, req) = match content_type {
|
||||||
Some(it) if it == "application/json" => (BodyType::Json, request),
|
Some(it) if it == "application/json" => (BodyType::Json, request),
|
||||||
Some(it) if it == "application/x-www-form-urlencoded" => (BodyType::Form, request),
|
Some(it) if it == "application/x-www-form-urlencoded" => (BodyType::Form, request),
|
||||||
|
Some(it) if it == "multipart/form-data" => (BodyType::Multipart, request),
|
||||||
Some(it) => {
|
Some(it) => {
|
||||||
let r = request.clone().set("Content-Type", &it);
|
let r = request.clone().set("Content-Type", &it);
|
||||||
(BodyType::Unknown, r)
|
(BodyType::Unknown, r)
|
||||||
@ -265,6 +269,48 @@ pub fn send_request(
|
|||||||
};
|
};
|
||||||
send_cancellable_request(&request_url, Box::new(request_fn), span, signals)
|
send_cancellable_request(&request_url, Box::new(request_fn), span, signals)
|
||||||
}
|
}
|
||||||
|
// multipart form upload
|
||||||
|
Value::Record { val, .. } if body_type == BodyType::Multipart => {
|
||||||
|
let mut builder = MultipartWriter::new();
|
||||||
|
|
||||||
|
let err = |e| {
|
||||||
|
ShellErrorOrRequestError::ShellError(ShellError::IOError {
|
||||||
|
msg: format!("failed to build multipart data: {}", e),
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
for (col, val) in val.into_owned() {
|
||||||
|
if let Value::Binary { val, .. } = val {
|
||||||
|
let headers = [
|
||||||
|
"Content-Type: application/octet-stream".to_string(),
|
||||||
|
"Content-Transfer-Encoding: binary".to_string(),
|
||||||
|
format!(
|
||||||
|
"Content-Disposition: form-data; name=\"{}\"; filename=\"{}\"",
|
||||||
|
col, col
|
||||||
|
),
|
||||||
|
format!("Content-Length: {}", val.len()),
|
||||||
|
];
|
||||||
|
builder
|
||||||
|
.add(&mut Cursor::new(val), &headers.join("\n"))
|
||||||
|
.map_err(err)?;
|
||||||
|
} else {
|
||||||
|
let headers =
|
||||||
|
format!(r#"Content-Disposition: form-data; name="{}""#, col);
|
||||||
|
builder
|
||||||
|
.add(val.coerce_into_string()?.as_bytes(), &headers)
|
||||||
|
.map_err(err)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.finish();
|
||||||
|
|
||||||
|
let (boundary, data) = (builder.boundary, builder.data);
|
||||||
|
let content_type = format!("multipart/form-data; boundary={}", boundary);
|
||||||
|
|
||||||
|
let request_fn =
|
||||||
|
move || req.set("Content-Type", &content_type).send_bytes(&data);
|
||||||
|
|
||||||
|
send_cancellable_request(&request_url, Box::new(request_fn), span, signals)
|
||||||
|
}
|
||||||
Value::List { vals, .. } if body_type == BodyType::Form => {
|
Value::List { vals, .. } if body_type == BodyType::Form => {
|
||||||
if vals.len() % 2 != 0 {
|
if vals.len() % 2 != 0 {
|
||||||
return Err(ShellErrorOrRequestError::ShellError(ShellError::IOError {
|
return Err(ShellErrorOrRequestError::ShellError(ShellError::IOError {
|
||||||
@ -514,23 +560,23 @@ fn transform_response_using_content_type(
|
|||||||
resp: Response,
|
resp: Response,
|
||||||
content_type: &str,
|
content_type: &str,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let content_type =
|
let content_type = mime::Mime::from_str(content_type)
|
||||||
mime::Mime::from_str(content_type).map_err(|_| ShellError::GenericError {
|
// there are invalid content types in the wild, so we try to recover
|
||||||
error: format!("MIME type unknown: {content_type}"),
|
// Example: `Content-Type: "text/plain"; charset="utf8"` (note the quotes)
|
||||||
msg: "".into(),
|
.or_else(|_| mime::Mime::from_str(&content_type.replace('"', "")))
|
||||||
span: None,
|
.or_else(|_| mime::Mime::from_str("text/plain"))
|
||||||
help: Some("given unknown MIME type".into()),
|
.expect("Failed to parse content type, and failed to default to text/plain");
|
||||||
inner: vec![],
|
|
||||||
})?;
|
|
||||||
let ext = match (content_type.type_(), content_type.subtype()) {
|
let ext = match (content_type.type_(), content_type.subtype()) {
|
||||||
(mime::TEXT, mime::PLAIN) => {
|
(mime::TEXT, mime::PLAIN) => {
|
||||||
let path_extension = url::Url::parse(requested_url)
|
let path_extension = url::Url::parse(requested_url)
|
||||||
.map_err(|_| ShellError::GenericError {
|
.map_err(|err| {
|
||||||
error: format!("Cannot parse URL: {requested_url}"),
|
LabeledError::new(err.to_string())
|
||||||
msg: "".into(),
|
.with_help("cannot parse")
|
||||||
span: None,
|
.with_label(
|
||||||
help: Some("cannot parse".into()),
|
format!("Cannot parse URL: {requested_url}"),
|
||||||
inner: vec![],
|
Span::unknown(),
|
||||||
|
)
|
||||||
})?
|
})?
|
||||||
.path_segments()
|
.path_segments()
|
||||||
.and_then(|segments| segments.last())
|
.and_then(|segments| segments.last())
|
||||||
|
@ -127,6 +127,11 @@ impl Command for SubCommand {
|
|||||||
example: "open foo.json | http post https://www.example.com",
|
example: "open foo.json | http post https://www.example.com",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
Example {
|
||||||
|
description: "Upload a file to example.com",
|
||||||
|
example: "http post --content-type multipart/form-data https://www.example.com { audio: (open -r file.mp3) }",
|
||||||
|
result: None,
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
64
crates/nu-command/src/random/binary.rs
Normal file
64
crates/nu-command/src/random/binary.rs
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
use nu_engine::command_prelude::*;
|
||||||
|
|
||||||
|
use rand::{thread_rng, RngCore};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct SubCommand;
|
||||||
|
|
||||||
|
impl Command for SubCommand {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"random binary"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signature(&self) -> Signature {
|
||||||
|
Signature::build("random binary")
|
||||||
|
.input_output_types(vec![(Type::Nothing, Type::Binary)])
|
||||||
|
.allow_variants_without_examples(true)
|
||||||
|
.required("length", SyntaxShape::Int, "Length of the output binary.")
|
||||||
|
.category(Category::Random)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usage(&self) -> &str {
|
||||||
|
"Generate random bytes."
|
||||||
|
}
|
||||||
|
|
||||||
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
|
vec!["generate", "bytes"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(
|
||||||
|
&self,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
call: &Call,
|
||||||
|
_input: PipelineData,
|
||||||
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let length = call.req(engine_state, stack, 0)?;
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
let mut out = vec![0u8; length];
|
||||||
|
rng.fill_bytes(&mut out);
|
||||||
|
|
||||||
|
Ok(Value::binary(out, call.head).into_pipeline_data())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn examples(&self) -> Vec<Example> {
|
||||||
|
vec![Example {
|
||||||
|
description: "Generate 16 random bytes",
|
||||||
|
example: "random binary 16",
|
||||||
|
result: None,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_examples() {
|
||||||
|
use crate::test_examples;
|
||||||
|
|
||||||
|
test_examples(SubCommand {})
|
||||||
|
}
|
||||||
|
}
|
@ -19,12 +19,17 @@ impl Command for SubCommand {
|
|||||||
Signature::build("random chars")
|
Signature::build("random chars")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::String)])
|
.input_output_types(vec![(Type::Nothing, Type::String)])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.named("length", SyntaxShape::Int, "Number of chars", Some('l'))
|
.named(
|
||||||
|
"length",
|
||||||
|
SyntaxShape::Int,
|
||||||
|
"Number of chars (default 25)",
|
||||||
|
Some('l'),
|
||||||
|
)
|
||||||
.category(Category::Random)
|
.category(Category::Random)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
fn usage(&self) -> &str {
|
||||||
"Generate random chars."
|
"Generate random chars uniformly distributed over ASCII letters and numbers: a-z, A-Z and 0-9."
|
||||||
}
|
}
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
fn search_terms(&self) -> Vec<&str> {
|
||||||
@ -44,7 +49,7 @@ impl Command for SubCommand {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Generate random chars",
|
description: "Generate a string with 25 random chars",
|
||||||
example: "random chars",
|
example: "random chars",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
|
@ -15,7 +15,11 @@ impl Command for SubCommand {
|
|||||||
Signature::build("random int")
|
Signature::build("random int")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Int)])
|
.input_output_types(vec![(Type::Nothing, Type::Int)])
|
||||||
.allow_variants_without_examples(true)
|
.allow_variants_without_examples(true)
|
||||||
.optional("range", SyntaxShape::Range, "Range of values.")
|
.optional(
|
||||||
|
"range",
|
||||||
|
SyntaxShape::Range,
|
||||||
|
"Range of potential values, inclusive of both start and end values.",
|
||||||
|
)
|
||||||
.category(Category::Random)
|
.category(Category::Random)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,12 +44,12 @@ impl Command for SubCommand {
|
|||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
vec![
|
vec![
|
||||||
Example {
|
Example {
|
||||||
description: "Generate an unconstrained random integer",
|
description: "Generate a non-negative random integer",
|
||||||
example: "random int",
|
example: "random int",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Generate a random integer less than or equal to 500",
|
description: "Generate a random integer between 0 (inclusive) and 500 (inclusive)",
|
||||||
example: "random int ..500",
|
example: "random int ..500",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
@ -55,8 +59,8 @@ impl Command for SubCommand {
|
|||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
Example {
|
Example {
|
||||||
description: "Generate a random integer between 1 and 10",
|
description: "Generate a random integer between -10 (inclusive) and 10 (inclusive)",
|
||||||
example: "random int 1..10",
|
example: "random int (-10)..10",
|
||||||
result: None,
|
result: None,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
mod binary;
|
||||||
mod bool;
|
mod bool;
|
||||||
mod chars;
|
mod chars;
|
||||||
mod dice;
|
mod dice;
|
||||||
@ -6,6 +7,7 @@ mod int;
|
|||||||
mod random_;
|
mod random_;
|
||||||
mod uuid;
|
mod uuid;
|
||||||
|
|
||||||
|
pub use self::binary::SubCommand as RandomBinary;
|
||||||
pub use self::bool::SubCommand as RandomBool;
|
pub use self::bool::SubCommand as RandomBool;
|
||||||
pub use self::chars::SubCommand as RandomChars;
|
pub use self::chars::SubCommand as RandomChars;
|
||||||
pub use self::dice::SubCommand as RandomDice;
|
pub use self::dice::SubCommand as RandomDice;
|
||||||
|
@ -187,7 +187,7 @@ fn split_words_helper(v: &Value, word_length: Option<usize>, span: Span, graphem
|
|||||||
// [^[:alpha:]\'] = do not match any uppercase or lowercase letters or apostrophes
|
// [^[:alpha:]\'] = do not match any uppercase or lowercase letters or apostrophes
|
||||||
// [^\p{L}\'] = do not match any unicode uppercase or lowercase letters or apostrophes
|
// [^\p{L}\'] = do not match any unicode uppercase or lowercase letters or apostrophes
|
||||||
// Let's go with the unicode one in hopes that it works on more than just ascii characters
|
// Let's go with the unicode one in hopes that it works on more than just ascii characters
|
||||||
let regex_replace = Regex::new(r"[^\p{L}\']").expect("regular expression error");
|
let regex_replace = Regex::new(r"[^\p{L}\p{N}\']").expect("regular expression error");
|
||||||
let v_span = v.span();
|
let v_span = v.span();
|
||||||
|
|
||||||
match v {
|
match v {
|
||||||
@ -422,4 +422,9 @@ mod test {
|
|||||||
|
|
||||||
test_examples(SubCommand {})
|
test_examples(SubCommand {})
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn mixed_letter_number() {
|
||||||
|
let actual = nu!(r#"echo "a1 b2 c3" | split words | str join ','"#);
|
||||||
|
assert_eq!(actual.out, "a1,b2,c3");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,12 +115,13 @@ fn run_ps(
|
|||||||
help: None,
|
help: None,
|
||||||
inner: vec![],
|
inner: vec![],
|
||||||
})?;
|
})?;
|
||||||
// If we can't get the start time, just use the current time
|
record.push(
|
||||||
let proc_start = proc_stat
|
"start_time",
|
||||||
.starttime()
|
match proc_stat.starttime().get() {
|
||||||
.get()
|
Ok(ts) => Value::date(ts.into(), span),
|
||||||
.unwrap_or_else(|_| chrono::Local::now());
|
Err(_) => Value::nothing(span),
|
||||||
record.push("start_time", Value::date(proc_start.into(), span));
|
},
|
||||||
|
);
|
||||||
record.push("user_id", Value::int(proc.curr_proc.owner() as i64, span));
|
record.push("user_id", Value::int(proc.curr_proc.owner() as i64, span));
|
||||||
// These work and may be helpful, but it just seemed crowded
|
// These work and may be helpful, but it just seemed crowded
|
||||||
// record.push("group_id", Value::int(proc_stat.pgrp as i64, span));
|
// record.push("group_id", Value::int(proc_stat.pgrp as i64, span));
|
||||||
@ -180,7 +181,5 @@ fn run_ps(
|
|||||||
output.push(Value::record(record, span));
|
output.push(Value::record(record, span));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(output
|
Ok(output.into_pipeline_data(span, engine_state.signals().clone()))
|
||||||
.into_iter()
|
|
||||||
.into_pipeline_data(span, engine_state.signals().clone()))
|
|
||||||
}
|
}
|
||||||
|
@ -473,6 +473,15 @@ pub fn command_not_found(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we find a file, it's likely that the user forgot to set permissions
|
||||||
|
if Path::new(name).is_file() {
|
||||||
|
return ShellError::ExternalCommand {
|
||||||
|
label: format!("Command `{name}` not found"),
|
||||||
|
help: format!("`{name}` refers to a file that is not executable. Did you forget to to set execute permissions?"),
|
||||||
|
span,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// We found nothing useful. Give up and return a generic error message.
|
// We found nothing useful. Give up and return a generic error message.
|
||||||
ShellError::ExternalCommand {
|
ShellError::ExternalCommand {
|
||||||
label: format!("Command `{name}` not found"),
|
label: format!("Command `{name}` not found"),
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
use nu_path::Path;
|
||||||
use nu_test_support::fs::Stub::EmptyFile;
|
use nu_test_support::fs::Stub::EmptyFile;
|
||||||
use nu_test_support::nu;
|
use nu_test_support::nu;
|
||||||
use nu_test_support::playground::Playground;
|
use nu_test_support::playground::Playground;
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cd_works_with_in_var() {
|
fn cd_works_with_in_var() {
|
||||||
@ -22,7 +22,7 @@ fn filesystem_change_from_current_directory_using_relative_path() {
|
|||||||
Playground::setup("cd_test_1", |dirs, _| {
|
Playground::setup("cd_test_1", |dirs, _| {
|
||||||
let actual = nu!( cwd: dirs.root(), "cd cd_test_1; $env.PWD");
|
let actual = nu!( cwd: dirs.root(), "cd cd_test_1; $env.PWD");
|
||||||
|
|
||||||
assert_eq!(PathBuf::from(actual.out), *dirs.test());
|
assert_eq!(Path::new(&actual.out), dirs.test());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ fn filesystem_change_from_current_directory_using_relative_path_with_trailing_sl
|
|||||||
// Intentionally not using correct path sep because this should work on Windows
|
// Intentionally not using correct path sep because this should work on Windows
|
||||||
let actual = nu!( cwd: dirs.root(), "cd cd_test_1_slash/; $env.PWD");
|
let actual = nu!( cwd: dirs.root(), "cd cd_test_1_slash/; $env.PWD");
|
||||||
|
|
||||||
assert_eq!(PathBuf::from(actual.out), *dirs.test());
|
assert_eq!(Path::new(&actual.out), *dirs.test());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ fn filesystem_change_from_current_directory_using_absolute_path() {
|
|||||||
dirs.formats().display()
|
dirs.formats().display()
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(PathBuf::from(actual.out), dirs.formats());
|
assert_eq!(Path::new(&actual.out), dirs.formats());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ fn filesystem_change_from_current_directory_using_absolute_path_with_trailing_sl
|
|||||||
std::path::MAIN_SEPARATOR_STR,
|
std::path::MAIN_SEPARATOR_STR,
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(PathBuf::from(actual.out), dirs.formats());
|
assert_eq!(Path::new(&actual.out), dirs.formats());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ fn filesystem_switch_back_to_previous_working_directory() {
|
|||||||
dirs.test().display()
|
dirs.test().display()
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(PathBuf::from(actual.out), dirs.test().join("odin"));
|
assert_eq!(Path::new(&actual.out), dirs.test().join("odin"));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,10 +101,7 @@ fn filesystem_change_from_current_directory_using_relative_path_and_dash() {
|
|||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(Path::new(&actual.out), dirs.test().join("odin").join("-"));
|
||||||
PathBuf::from(actual.out),
|
|
||||||
dirs.test().join("odin").join("-")
|
|
||||||
);
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,7 +116,7 @@ fn filesystem_change_current_directory_to_parent_directory() {
|
|||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(PathBuf::from(actual.out), *dirs.root());
|
assert_eq!(Path::new(&actual.out), *dirs.root());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,7 +133,7 @@ fn filesystem_change_current_directory_to_two_parents_up_using_multiple_dots() {
|
|||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(PathBuf::from(actual.out), *dirs.test());
|
assert_eq!(Path::new(&actual.out), *dirs.test());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,7 +148,7 @@ fn filesystem_change_to_home_directory() {
|
|||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(Some(PathBuf::from(actual.out)), dirs::home_dir());
|
assert_eq!(Path::new(&actual.out), dirs::home_dir().unwrap());
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -169,7 +166,7 @@ fn filesystem_change_to_a_directory_containing_spaces() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
PathBuf::from(actual.out),
|
Path::new(&actual.out),
|
||||||
dirs.test().join("robalino turner katz")
|
dirs.test().join("robalino turner katz")
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
@ -234,7 +231,7 @@ fn filesystem_change_directory_to_symlink_relative() {
|
|||||||
$env.PWD
|
$env.PWD
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
assert_eq!(PathBuf::from(actual.out), dirs.test().join("foo_link"));
|
assert_eq!(Path::new(&actual.out), dirs.test().join("foo_link"));
|
||||||
|
|
||||||
let actual = nu!(
|
let actual = nu!(
|
||||||
cwd: dirs.test().join("boo"),
|
cwd: dirs.test().join("boo"),
|
||||||
@ -243,7 +240,7 @@ fn filesystem_change_directory_to_symlink_relative() {
|
|||||||
$env.PWD
|
$env.PWD
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
assert_eq!(PathBuf::from(actual.out), dirs.test().join("foo"));
|
assert_eq!(Path::new(&actual.out), dirs.test().join("foo"));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,13 +95,13 @@ fn capture_error_with_both_stdout_stderr_messages_not_hang_nushell() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn combined_pipe_redirection() {
|
fn combined_pipe_redirection() {
|
||||||
let actual = nu!("$env.FOO = hello; $env.BAR = world; nu --testbin echo_env_mixed out-err FOO BAR o+e>| complete | get stdout");
|
let actual = nu!("$env.FOO = 'hello'; $env.BAR = 'world'; nu --testbin echo_env_mixed out-err FOO BAR o+e>| complete | get stdout");
|
||||||
assert_eq!(actual.out, "helloworld");
|
assert_eq!(actual.out, "helloworld");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn err_pipe_redirection() {
|
fn err_pipe_redirection() {
|
||||||
let actual =
|
let actual =
|
||||||
nu!("$env.FOO = hello; nu --testbin echo_env_stderr FOO e>| complete | get stdout");
|
nu!("$env.FOO = 'hello'; nu --testbin echo_env_stderr FOO e>| complete | get stdout");
|
||||||
assert_eq!(actual.out, "hello");
|
assert_eq!(actual.out, "hello");
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use std::{io::Write, path::PathBuf};
|
|
||||||
|
|
||||||
use chrono::{DateTime, FixedOffset};
|
use chrono::{DateTime, FixedOffset};
|
||||||
|
use nu_path::AbsolutePathBuf;
|
||||||
use nu_protocol::{ast::PathMember, record, Span, Value};
|
use nu_protocol::{ast::PathMember, record, Span, Value};
|
||||||
use nu_test_support::{
|
use nu_test_support::{
|
||||||
fs::{line_ending, Stub},
|
fs::{line_ending, Stub},
|
||||||
@ -13,6 +12,7 @@ use rand::{
|
|||||||
rngs::StdRng,
|
rngs::StdRng,
|
||||||
Rng, SeedableRng,
|
Rng, SeedableRng,
|
||||||
};
|
};
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn into_sqlite_schema() {
|
fn into_sqlite_schema() {
|
||||||
@ -453,7 +453,7 @@ impl Distribution<TestRow> for Standard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_sqlite_db(dirs: &Dirs, nu_table: &str) -> PathBuf {
|
fn make_sqlite_db(dirs: &Dirs, nu_table: &str) -> AbsolutePathBuf {
|
||||||
let testdir = dirs.test();
|
let testdir = dirs.test();
|
||||||
let testdb_path =
|
let testdb_path =
|
||||||
testdir.join(testdir.file_name().unwrap().to_str().unwrap().to_owned() + ".db");
|
testdir.join(testdir.file_name().unwrap().to_str().unwrap().to_owned() + ".db");
|
||||||
@ -465,7 +465,7 @@ fn make_sqlite_db(dirs: &Dirs, nu_table: &str) -> PathBuf {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert!(nucmd.status.success());
|
assert!(nucmd.status.success());
|
||||||
testdb_path.into()
|
testdb_path
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_test_rows(dirs: &Dirs, nu_table: &str, sql_query: Option<&str>, expected: Vec<TestRow>) {
|
fn insert_test_rows(dirs: &Dirs, nu_table: &str, sql_query: Option<&str>, expected: Vec<TestRow>) {
|
||||||
|
@ -23,6 +23,13 @@ fn let_takes_pipeline() {
|
|||||||
assert_eq!(actual.out, "11");
|
assert_eq!(actual.out, "11");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_takes_pipeline_with_declared_type() {
|
||||||
|
let actual = nu!(r#"let x: list<string> = [] | append "hello world"; print $x.0"#);
|
||||||
|
|
||||||
|
assert_eq!(actual.out, "hello world");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn let_pipeline_allows_in() {
|
fn let_pipeline_allows_in() {
|
||||||
let actual =
|
let actual =
|
||||||
@ -38,6 +45,13 @@ fn mut_takes_pipeline() {
|
|||||||
assert_eq!(actual.out, "11");
|
assert_eq!(actual.out, "11");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mut_takes_pipeline_with_declared_type() {
|
||||||
|
let actual = nu!(r#"mut x: list<string> = [] | append "hello world"; print $x.0"#);
|
||||||
|
|
||||||
|
assert_eq!(actual.out, "hello world");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mut_pipeline_allows_in() {
|
fn mut_pipeline_allows_in() {
|
||||||
let actual =
|
let actual =
|
||||||
|
@ -808,3 +808,28 @@ fn list_inside_tilde_glob_metachars_dir() {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn list_symlink_with_full_path() {
|
||||||
|
Playground::setup("list_symlink_with_full_path", |dirs, sandbox| {
|
||||||
|
sandbox.with_files(&[EmptyFile("test_file.txt")]);
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let _ = std::os::unix::fs::symlink("test_file.txt", dirs.test().join("test_link1"));
|
||||||
|
#[cfg(windows)]
|
||||||
|
let _ = std::os::windows::fs::symlink_file("test_file.txt", dirs.test().join("test_link1"));
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(),
|
||||||
|
"ls -l test_link1 | get target.0"
|
||||||
|
);
|
||||||
|
assert_eq!(actual.out, "test_file.txt");
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test(),
|
||||||
|
"ls -lf test_link1 | get target.0"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
actual.out,
|
||||||
|
dirs.test().join("test_file.txt").to_string_lossy()
|
||||||
|
);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
7
crates/nu-command/tests/commands/math/abs.rs
Normal file
7
crates/nu-command/tests/commands/math/abs.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_abs() {
|
||||||
|
let actual = nu!("const ABS = -5.5 | math abs; $ABS");
|
||||||
|
assert_eq!(actual.out, "5.5");
|
||||||
|
}
|
@ -20,3 +20,9 @@ fn can_average_bytes() {
|
|||||||
|
|
||||||
assert_eq!(actual.out, "34985870");
|
assert_eq!(actual.out, "34985870");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_avg() {
|
||||||
|
let actual = nu!("const AVG = [1 3 5] | math avg; $AVG");
|
||||||
|
assert_eq!(actual.out, "3");
|
||||||
|
}
|
||||||
|
7
crates/nu-command/tests/commands/math/ceil.rs
Normal file
7
crates/nu-command/tests/commands/math/ceil.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_ceil() {
|
||||||
|
let actual = nu!("const CEIL = 1.5 | math ceil; $CEIL");
|
||||||
|
assert_eq!(actual.out, "2");
|
||||||
|
}
|
7
crates/nu-command/tests/commands/math/floor.rs
Normal file
7
crates/nu-command/tests/commands/math/floor.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_floor() {
|
||||||
|
let actual = nu!("const FLOOR = 15.5 | math floor; $FLOOR");
|
||||||
|
assert_eq!(actual.out, "15");
|
||||||
|
}
|
7
crates/nu-command/tests/commands/math/log.rs
Normal file
7
crates/nu-command/tests/commands/math/log.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_log() {
|
||||||
|
let actual = nu!("const LOG = 16 | math log 2; $LOG");
|
||||||
|
assert_eq!(actual.out, "4");
|
||||||
|
}
|
7
crates/nu-command/tests/commands/math/max.rs
Normal file
7
crates/nu-command/tests/commands/math/max.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use nu_test_support::nu;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_max() {
|
||||||
|
let actual = nu!("const MAX = [1 3 5] | math max; $MAX");
|
||||||
|
assert_eq!(actual.out, "5");
|
||||||
|
}
|
@ -35,3 +35,9 @@ fn median_mixed_numbers() {
|
|||||||
|
|
||||||
assert_eq!(actual.out, "-11.5")
|
assert_eq!(actual.out, "-11.5")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn const_median() {
|
||||||
|
let actual = nu!("const MEDIAN = [1 3 5] | math median; $MEDIAN");
|
||||||
|
assert_eq!(actual.out, "3");
|
||||||
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user