Merge branch 'main' into derive-value

This commit is contained in:
Tim 'Piepmatz' Hesse 2024-06-06 22:58:25 +02:00
commit 5542545222
145 changed files with 4537 additions and 3790 deletions

View File

@ -36,7 +36,7 @@ jobs:
token: ${{ secrets.WORKFLOW_TOKEN }} token: ${{ secrets.WORKFLOW_TOKEN }}
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v3.10 uses: hustcer/setup-nu@v3.11
if: github.repository == 'nushell/nightly' if: github.repository == 'nushell/nightly'
with: with:
version: 0.93.0 version: 0.93.0
@ -128,7 +128,7 @@ jobs:
rustflags: '' rustflags: ''
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v3.10 uses: hustcer/setup-nu@v3.11
with: with:
version: 0.93.0 version: 0.93.0
@ -186,7 +186,7 @@ jobs:
ref: main ref: main
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v3.10 uses: hustcer/setup-nu@v3.11
with: with:
version: 0.93.0 version: 0.93.0

View File

@ -76,7 +76,7 @@ jobs:
rustflags: '' rustflags: ''
- name: Setup Nushell - name: Setup Nushell
uses: hustcer/setup-nu@v3.10 uses: hustcer/setup-nu@v3.11
with: with:
version: 0.93.0 version: 0.93.0

View File

@ -10,4 +10,4 @@ jobs:
uses: actions/checkout@v4.1.6 uses: actions/checkout@v4.1.6
- name: Check spelling - name: Check spelling
uses: crate-ci/typos@v1.21.0 uses: crate-ci/typos@v1.22.0

26
CITATION.cff Normal file
View File

@ -0,0 +1,26 @@
cff-version: 1.2.0
title: 'Nushell'
message: >-
If you use this software and wish to cite it,
you can use the metadata from this file.
type: software
authors:
- name: "The Nushell Project Team"
identifiers:
- type: url
value: 'https://github.com/nushell/nushell'
description: Repository
repository-code: 'https://github.com/nushell/nushell'
url: 'https://www.nushell.sh/'
abstract: >-
The goal of the Nushell project is to take the Unix
philosophy of shells, where pipes connect simple commands
together, and bring it to the modern style of development.
Thus, rather than being either a shell, or a programming
language, Nushell connects both by bringing a rich
programming language and a full-featured shell together
into one package.
keywords:
- nushell
- shell
license: MIT

232
Cargo.lock generated
View File

@ -478,17 +478,6 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ada7f35ca622a86a4d6c27be2633fc6c243ecc834859628fcce0681d8e76e1c8" checksum = "ada7f35ca622a86a4d6c27be2633fc6c243ecc834859628fcce0681d8e76e1c8"
[[package]]
name = "brotli"
version = "3.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor 2.5.1",
]
[[package]] [[package]]
name = "brotli" name = "brotli"
version = "5.0.0" version = "5.0.0"
@ -497,17 +486,7 @@ checksum = "19483b140a7ac7174d34b5a581b406c64f84da5409d3e09cf4fff604f9270e67"
dependencies = [ dependencies = [
"alloc-no-stdlib", "alloc-no-stdlib",
"alloc-stdlib", "alloc-stdlib",
"brotli-decompressor 4.0.0", "brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
] ]
[[package]] [[package]]
@ -871,7 +850,7 @@ checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7"
dependencies = [ dependencies = [
"crossterm", "crossterm",
"strum", "strum",
"strum_macros 0.26.2", "strum_macros",
"unicode-width", "unicode-width",
] ]
@ -1304,6 +1283,9 @@ name = "either"
version = "1.11.0" version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "eml-parser" name = "eml-parser"
@ -1803,6 +1785,7 @@ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"allocator-api2", "allocator-api2",
"rayon", "rayon",
"serde",
] ]
[[package]] [[package]]
@ -2779,7 +2762,7 @@ dependencies = [
[[package]] [[package]]
name = "nu" name = "nu"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"assert_cmd", "assert_cmd",
"crossterm", "crossterm",
@ -2832,7 +2815,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cli" name = "nu-cli"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"chrono", "chrono",
"crossterm", "crossterm",
@ -2867,7 +2850,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-base" name = "nu-cmd-base"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"indexmap", "indexmap",
"miette", "miette",
@ -2879,7 +2862,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-extra" name = "nu-cmd-extra"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"heck 0.5.0", "heck 0.5.0",
@ -2904,7 +2887,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-lang" name = "nu-cmd-lang"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"itertools 0.12.1", "itertools 0.12.1",
"nu-engine", "nu-engine",
@ -2916,7 +2899,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-cmd-plugin" name = "nu-cmd-plugin"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"itertools 0.12.1", "itertools 0.12.1",
"nu-engine", "nu-engine",
@ -2927,7 +2910,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-color-config" name = "nu-color-config"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"nu-ansi-term", "nu-ansi-term",
"nu-engine", "nu-engine",
@ -2939,12 +2922,12 @@ dependencies = [
[[package]] [[package]]
name = "nu-command" name = "nu-command"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"alphanumeric-sort", "alphanumeric-sort",
"base64 0.22.1", "base64 0.22.1",
"bracoxide", "bracoxide",
"brotli 5.0.0", "brotli",
"byteorder", "byteorder",
"bytesize", "bytesize",
"calamine", "calamine",
@ -3048,7 +3031,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-derive-value" name = "nu-derive-value"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"convert_case", "convert_case",
"proc-macro-error", "proc-macro-error",
@ -3059,7 +3042,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-engine" name = "nu-engine"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"nu-glob", "nu-glob",
"nu-path", "nu-path",
@ -3069,7 +3052,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-explore" name = "nu-explore"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"ansi-str", "ansi-str",
"anyhow", "anyhow",
@ -3094,14 +3077,14 @@ dependencies = [
[[package]] [[package]]
name = "nu-glob" name = "nu-glob"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"doc-comment", "doc-comment",
] ]
[[package]] [[package]]
name = "nu-json" name = "nu-json"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"linked-hash-map", "linked-hash-map",
"num-traits", "num-traits",
@ -3111,7 +3094,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-lsp" name = "nu-lsp"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"assert-json-diff", "assert-json-diff",
"crossbeam-channel", "crossbeam-channel",
@ -3132,7 +3115,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-parser" name = "nu-parser"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"bytesize", "bytesize",
"chrono", "chrono",
@ -3148,7 +3131,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-path" name = "nu-path"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"dirs-next", "dirs-next",
"omnipath", "omnipath",
@ -3157,7 +3140,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin" name = "nu-plugin"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"log", "log",
"nix", "nix",
@ -3172,7 +3155,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-core" name = "nu-plugin-core"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"interprocess", "interprocess",
"log", "log",
@ -3186,7 +3169,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-engine" name = "nu-plugin-engine"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"log", "log",
"nu-engine", "nu-engine",
@ -3201,7 +3184,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-protocol" name = "nu-plugin-protocol"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"bincode", "bincode",
"nu-protocol", "nu-protocol",
@ -3213,7 +3196,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-plugin-test-support" name = "nu-plugin-test-support"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"nu-ansi-term", "nu-ansi-term",
"nu-cmd-lang", "nu-cmd-lang",
@ -3231,7 +3214,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-pretty-hex" name = "nu-pretty-hex"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"heapless", "heapless",
"nu-ansi-term", "nu-ansi-term",
@ -3240,9 +3223,9 @@ dependencies = [
[[package]] [[package]]
name = "nu-protocol" name = "nu-protocol"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"brotli 5.0.0", "brotli",
"byte-unit", "byte-unit",
"chrono", "chrono",
"chrono-humanize", "chrono-humanize",
@ -3264,7 +3247,7 @@ dependencies = [
"serde", "serde",
"serde_json", "serde_json",
"strum", "strum",
"strum_macros 0.26.2", "strum_macros",
"tempfile", "tempfile",
"thiserror", "thiserror",
"typetag", "typetag",
@ -3272,7 +3255,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-std" name = "nu-std"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"log", "log",
"miette", "miette",
@ -3283,7 +3266,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-system" name = "nu-system"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"chrono", "chrono",
"itertools 0.12.1", "itertools 0.12.1",
@ -3301,7 +3284,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-table" name = "nu-table"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"fancy-regex", "fancy-regex",
"nu-ansi-term", "nu-ansi-term",
@ -3315,7 +3298,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-term-grid" name = "nu-term-grid"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"nu-utils", "nu-utils",
"unicode-width", "unicode-width",
@ -3323,7 +3306,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-test-support" name = "nu-test-support"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"nu-glob", "nu-glob",
"nu-path", "nu-path",
@ -3335,7 +3318,7 @@ dependencies = [
[[package]] [[package]]
name = "nu-utils" name = "nu-utils"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"crossterm_winapi", "crossterm_winapi",
"log", "log",
@ -3361,7 +3344,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_example" name = "nu_plugin_example"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"nu-cmd-lang", "nu-cmd-lang",
"nu-plugin", "nu-plugin",
@ -3371,7 +3354,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_formats" name = "nu_plugin_formats"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"eml-parser", "eml-parser",
"ical", "ical",
@ -3384,7 +3367,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_gstat" name = "nu_plugin_gstat"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"git2", "git2",
"nu-plugin", "nu-plugin",
@ -3393,7 +3376,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_inc" name = "nu_plugin_inc"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"nu-plugin", "nu-plugin",
"nu-protocol", "nu-protocol",
@ -3402,7 +3385,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_polars" name = "nu_plugin_polars"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"chrono", "chrono",
"chrono-tz 0.9.0", "chrono-tz 0.9.0",
@ -3425,7 +3408,7 @@ dependencies = [
"polars-plan", "polars-plan",
"polars-utils", "polars-utils",
"serde", "serde",
"sqlparser 0.45.0", "sqlparser 0.47.0",
"tempfile", "tempfile",
"typetag", "typetag",
"uuid", "uuid",
@ -3433,7 +3416,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_query" name = "nu_plugin_query"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"gjson", "gjson",
"nu-plugin", "nu-plugin",
@ -3445,7 +3428,7 @@ dependencies = [
[[package]] [[package]]
name = "nu_plugin_stress_internals" name = "nu_plugin_stress_internals"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"interprocess", "interprocess",
"serde", "serde",
@ -3571,7 +3554,7 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
[[package]] [[package]]
name = "nuon" name = "nuon"
version = "0.94.2" version = "0.94.3"
dependencies = [ dependencies = [
"chrono", "chrono",
"fancy-regex", "fancy-regex",
@ -4035,9 +4018,9 @@ dependencies = [
[[package]] [[package]]
name = "polars" name = "polars"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea21b858b16b9c0e17a12db2800d11aa5b4bd182be6b3022eb537bbfc1f2db5" checksum = "e148396dca5496566880fa19374f3f789a29db94e3eb458afac1497b4bac5442"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"polars-arrow", "polars-arrow",
@ -4055,9 +4038,9 @@ dependencies = [
[[package]] [[package]]
name = "polars-arrow" name = "polars-arrow"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "725b09f2b5ef31279b66e27bbab63c58d49d8f6696b66b1f46c7eaab95e80f75" checksum = "1cb5e11cd0752ae022fa6ca3afa50a14b0301b7ce53c0135828fbb0f4fa8303e"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"atoi", "atoi",
@ -4103,9 +4086,9 @@ dependencies = [
[[package]] [[package]]
name = "polars-compute" name = "polars-compute"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a796945b14b14fbb79b91ef0406e6fddca2be636e889f81ea5d6ee7d36efb4fe" checksum = "89fc4578f826234cdecb782952aa9c479dc49373f81694a7b439c70b6f609ba0"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"either", "either",
@ -4119,9 +4102,9 @@ dependencies = [
[[package]] [[package]]
name = "polars-core" name = "polars-core"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "465f70d3e96b6d0b1a43c358ba451286b8c8bd56696feff020d65702aa33e35c" checksum = "e490c6bace1366a558feea33d1846f749a8ca90bd72a6748752bc65bb4710b2a"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"bitflags 2.5.0", "bitflags 2.5.0",
@ -4153,9 +4136,9 @@ dependencies = [
[[package]] [[package]]
name = "polars-error" name = "polars-error"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5224d5d05e6b8a6f78b75951ae1b5f82c8ab1979e11ffaf5fd41941e3d5b0757" checksum = "08888f58e61599b00f5ea0c2ccdc796b54b9859559cc0d4582733509451fa01a"
dependencies = [ dependencies = [
"avro-schema", "avro-schema",
"polars-arrow-format", "polars-arrow-format",
@ -4165,10 +4148,30 @@ dependencies = [
] ]
[[package]] [[package]]
name = "polars-io" name = "polars-expr"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2c8589e418cbe4a48228d64b2a8a40284a82ec3c98817c0c2bcc0267701338b" checksum = "4173591920fe56ad55af025f92eb0d08421ca85705c326a640c43856094e3484"
dependencies = [
"ahash 0.8.11",
"bitflags 2.5.0",
"once_cell",
"polars-arrow",
"polars-core",
"polars-io",
"polars-ops",
"polars-plan",
"polars-time",
"polars-utils",
"rayon",
"smartstring",
]
[[package]]
name = "polars-io"
version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5842896aea46d975b425d63f156f412aed3cfde4c257b64fb1f43ceea288074e"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"async-trait", "async-trait",
@ -4207,9 +4210,9 @@ dependencies = [
[[package]] [[package]]
name = "polars-json" name = "polars-json"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81224492a649a12b668480c0cf219d703f432509765d2717e72fe32ad16fc701" checksum = "160cbad0145b93ac6a88639aadfa6f7d7c769d05a8674f9b7e895b398cae9901"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"chrono", "chrono",
@ -4228,9 +4231,9 @@ dependencies = [
[[package]] [[package]]
name = "polars-lazy" name = "polars-lazy"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89b2632b1af668e2058d5f8f916d8fbde3cac63d03ae29a705f598e41dcfeb7f" checksum = "e805ea2ebbc6b7749b0afb31b7fc5d32b42b57ba29b984549d43d3a16114c4a5"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"bitflags 2.5.0", "bitflags 2.5.0",
@ -4238,6 +4241,7 @@ dependencies = [
"once_cell", "once_cell",
"polars-arrow", "polars-arrow",
"polars-core", "polars-core",
"polars-expr",
"polars-io", "polars-io",
"polars-json", "polars-json",
"polars-ops", "polars-ops",
@ -4252,13 +4256,13 @@ dependencies = [
[[package]] [[package]]
name = "polars-ops" name = "polars-ops"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efdbdb4d9a92109bc2e0ce8e17af5ae8ab643bb5b7ee9d1d74f0aeffd1fbc95f" checksum = "7b0aed7e169c81b98457641cf82b251f52239a668916c2e683abd1f38df00d58"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"argminmax", "argminmax",
"base64 0.21.7", "base64 0.22.1",
"bytemuck", "bytemuck",
"chrono", "chrono",
"chrono-tz 0.8.6", "chrono-tz 0.8.6",
@ -4288,14 +4292,14 @@ dependencies = [
[[package]] [[package]]
name = "polars-parquet" name = "polars-parquet"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b421d2196f786fdfe162db614c8485f8308fe41575d4de634a39bbe460d1eb6a" checksum = "c70670a9e51cac66d0e77fd20b5cc957dbcf9f2660d410633862bb72f846d5b8"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"async-stream", "async-stream",
"base64 0.21.7", "base64 0.22.1",
"brotli 3.5.0", "brotli",
"ethnum", "ethnum",
"flate2", "flate2",
"futures", "futures",
@ -4314,9 +4318,9 @@ dependencies = [
[[package]] [[package]]
name = "polars-pipe" name = "polars-pipe"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48700f1d5bd56a15451e581f465c09541492750360f18637b196f995470a015c" checksum = "0a40ae1b3c74ee07e2d1f7cbf56c5d6e15969e45d9b6f0903bd2acaf783ba436"
dependencies = [ dependencies = [
"crossbeam-channel", "crossbeam-channel",
"crossbeam-queue", "crossbeam-queue",
@ -4326,6 +4330,7 @@ dependencies = [
"polars-arrow", "polars-arrow",
"polars-compute", "polars-compute",
"polars-core", "polars-core",
"polars-expr",
"polars-io", "polars-io",
"polars-ops", "polars-ops",
"polars-plan", "polars-plan",
@ -4339,13 +4344,14 @@ dependencies = [
[[package]] [[package]]
name = "polars-plan" name = "polars-plan"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fb8e2302e20c44defd5be8cad9c96e75face63c3a5f609aced8c4ec3b3ac97d" checksum = "8daa3541ae7e9af311a4389bc2b21f83349c34c723cc67fa524cdefdaa172d90"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"bytemuck", "bytemuck",
"chrono-tz 0.8.6", "chrono-tz 0.8.6",
"either",
"hashbrown 0.14.5", "hashbrown 0.14.5",
"once_cell", "once_cell",
"percent-encoding", "percent-encoding",
@ -4362,15 +4368,15 @@ dependencies = [
"regex", "regex",
"serde", "serde",
"smartstring", "smartstring",
"strum_macros 0.25.3", "strum_macros",
"version_check", "version_check",
] ]
[[package]] [[package]]
name = "polars-row" name = "polars-row"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a515bdc68c2ae3702e3de70d89601f3b71ca8137e282a226dddb53ee4bacfa2e" checksum = "deb285f2f3a65b00dd06bef16bb9f712dbb5478f941dab5cf74f9f016d382e40"
dependencies = [ dependencies = [
"bytemuck", "bytemuck",
"polars-arrow", "polars-arrow",
@ -4380,11 +4386,12 @@ dependencies = [
[[package]] [[package]]
name = "polars-sql" name = "polars-sql"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b4bb7cc1c04c3023d1953b2f1dec50515e8fd8169a5a2bf4967b3b082232db7" checksum = "a724f699d194cb02c25124d3832f7d4d77f387f1a89ee42f6b9e88ec561d4ad9"
dependencies = [ dependencies = [
"hex", "hex",
"once_cell",
"polars-arrow", "polars-arrow",
"polars-core", "polars-core",
"polars-error", "polars-error",
@ -4398,11 +4405,12 @@ dependencies = [
[[package]] [[package]]
name = "polars-time" name = "polars-time"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efc18e3ad92eec55db89d88f16c22d436559ba7030cf76f86f6ed7a754b673f1" checksum = "87ebec238d8b6200d9f0c3ce411c8441e950bd5a7df7806b8172d06c1d5a4b97"
dependencies = [ dependencies = [
"atoi", "atoi",
"bytemuck",
"chrono", "chrono",
"chrono-tz 0.8.6", "chrono-tz 0.8.6",
"now", "now",
@ -4419,9 +4427,9 @@ dependencies = [
[[package]] [[package]]
name = "polars-utils" name = "polars-utils"
version = "0.39.2" version = "0.40.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c760b6c698cfe2fbbbd93d6cfb408db14ececfe1d92445dae2229ce1b5b21ae8" checksum = "34e1a907c63abf71e5f21467e2e4ff748896c28196746f631c6c25512ec6102c"
dependencies = [ dependencies = [
"ahash 0.8.11", "ahash 0.8.11",
"bytemuck", "bytemuck",
@ -4856,7 +4864,7 @@ dependencies = [
"serde_json", "serde_json",
"strip-ansi-escapes", "strip-ansi-escapes",
"strum", "strum",
"strum_macros 0.26.2", "strum_macros",
"thiserror", "thiserror",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
@ -5584,9 +5592,9 @@ dependencies = [
[[package]] [[package]]
name = "sqlparser" name = "sqlparser"
version = "0.45.0" version = "0.47.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7bbffee862a796d67959a89859d6b1046bb5016d63e23835ad0da182777bbe0" checksum = "295e9930cd7a97e58ca2a070541a3ca502b17f5d1fa7157376d0fabd85324f25"
dependencies = [ dependencies = [
"log", "log",
] ]
@ -5700,20 +5708,7 @@ version = "0.26.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29"
dependencies = [ dependencies = [
"strum_macros 0.26.2", "strum_macros",
]
[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.60",
] ]
[[package]] [[package]]
@ -6090,6 +6085,7 @@ version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
dependencies = [ dependencies = [
"indexmap",
"serde", "serde",
"serde_spanned", "serde_spanned",
"toml_datetime", "toml_datetime",

View File

@ -11,7 +11,7 @@ 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.77.2"
version = "0.94.2" version = "0.94.3"
# 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
@ -180,22 +180,22 @@ windows = "0.54"
winreg = "0.52" winreg = "0.52"
[dependencies] [dependencies]
nu-cli = { path = "./crates/nu-cli", version = "0.94.2" } nu-cli = { path = "./crates/nu-cli", version = "0.94.3" }
nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.94.2" } nu-cmd-base = { path = "./crates/nu-cmd-base", version = "0.94.3" }
nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.94.2" } nu-cmd-lang = { path = "./crates/nu-cmd-lang", version = "0.94.3" }
nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.94.2", optional = true } nu-cmd-plugin = { path = "./crates/nu-cmd-plugin", version = "0.94.3", optional = true }
nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.94.2" } nu-cmd-extra = { path = "./crates/nu-cmd-extra", version = "0.94.3" }
nu-command = { path = "./crates/nu-command", version = "0.94.2" } nu-command = { path = "./crates/nu-command", version = "0.94.3" }
nu-engine = { path = "./crates/nu-engine", version = "0.94.2" } nu-engine = { path = "./crates/nu-engine", version = "0.94.3" }
nu-explore = { path = "./crates/nu-explore", version = "0.94.2" } nu-explore = { path = "./crates/nu-explore", version = "0.94.3" }
nu-lsp = { path = "./crates/nu-lsp/", version = "0.94.2" } nu-lsp = { path = "./crates/nu-lsp/", version = "0.94.3" }
nu-parser = { path = "./crates/nu-parser", version = "0.94.2" } nu-parser = { path = "./crates/nu-parser", version = "0.94.3" }
nu-path = { path = "./crates/nu-path", version = "0.94.2" } nu-path = { path = "./crates/nu-path", version = "0.94.3" }
nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.94.2" } nu-plugin-engine = { path = "./crates/nu-plugin-engine", optional = true, version = "0.94.3" }
nu-protocol = { path = "./crates/nu-protocol", version = "0.94.2" } nu-protocol = { path = "./crates/nu-protocol", version = "0.94.3" }
nu-std = { path = "./crates/nu-std", version = "0.94.2" } nu-std = { path = "./crates/nu-std", version = "0.94.3" }
nu-system = { path = "./crates/nu-system", version = "0.94.2" } nu-system = { path = "./crates/nu-system", version = "0.94.3" }
nu-utils = { path = "./crates/nu-utils", version = "0.94.2" } nu-utils = { path = "./crates/nu-utils", version = "0.94.3" }
reedline = { workspace = true, features = ["bashisms", "sqlite"] } reedline = { workspace = true, features = ["bashisms", "sqlite"] }
@ -224,9 +224,9 @@ nix = { workspace = true, default-features = false, features = [
] } ] }
[dev-dependencies] [dev-dependencies]
nu-test-support = { path = "./crates/nu-test-support", version = "0.94.2" } nu-test-support = { path = "./crates/nu-test-support", version = "0.94.3" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.94.2" } nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.94.3" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.94.2" } nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.94.3" }
assert_cmd = "2.0" assert_cmd = "2.0"
dirs-next = { workspace = true } dirs-next = { workspace = true }
tango-bench = "0.5" tango-bench = "0.5"
@ -311,4 +311,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

View File

@ -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.94.2" version = "0.94.3"
[lib] [lib]
bench = false bench = false
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.2" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.3" }
nu-command = { path = "../nu-command", version = "0.94.2" } nu-command = { path = "../nu-command", version = "0.94.3" }
nu-test-support = { path = "../nu-test-support", version = "0.94.2" } nu-test-support = { path = "../nu-test-support", version = "0.94.3" }
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.94.2" } nu-cmd-base = { path = "../nu-cmd-base", version = "0.94.3" }
nu-engine = { path = "../nu-engine", version = "0.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-path = { path = "../nu-path", version = "0.94.2" } nu-path = { path = "../nu-path", version = "0.94.3" }
nu-parser = { path = "../nu-parser", version = "0.94.2" } nu-parser = { path = "../nu-parser", version = "0.94.3" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.94.2", optional = true } nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.94.3", optional = true }
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
nu-utils = { path = "../nu-utils", version = "0.94.2" } nu-utils = { path = "../nu-utils", version = "0.94.3" }
nu-color-config = { path = "../nu-color-config", version = "0.94.2" } nu-color-config = { path = "../nu-color-config", version = "0.94.3" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
reedline = { workspace = true, features = ["bashisms", "sqlite"] } reedline = { workspace = true, features = ["bashisms", "sqlite"] }
@ -46,4 +46,4 @@ which = { workspace = true }
[features] [features]
plugin = ["nu-plugin-engine"] plugin = ["nu-plugin-engine"]
system-clipboard = ["reedline/system_clipboard"] system-clipboard = ["reedline/system_clipboard"]

View File

@ -52,18 +52,16 @@ impl Completer for CustomCompletion {
decl_id: self.decl_id, decl_id: self.decl_id,
head: span, head: span,
arguments: vec![ arguments: vec![
Argument::Positional(Expression { Argument::Positional(Expression::new_unknown(
span: Span::unknown(), Expr::String(self.line.clone()),
ty: Type::String, Span::unknown(),
expr: Expr::String(self.line.clone()), Type::String,
custom_completion: None, )),
}), Argument::Positional(Expression::new_unknown(
Argument::Positional(Expression { Expr::Int(line_pos as i64),
span: Span::unknown(), Span::unknown(),
ty: Type::Int, Type::Int,
expr: Expr::Int(line_pos as i64), )),
custom_completion: None,
}),
], ],
parser_info: HashMap::new(), parser_info: HashMap::new(),
}, },

View File

@ -1,6 +1,5 @@
use crate::{menus::NuMenuCompleter, NuHelpCompleter}; use crate::{menus::NuMenuCompleter, NuHelpCompleter};
use crossterm::event::{KeyCode, KeyModifiers}; use crossterm::event::{KeyCode, KeyModifiers};
use log::trace;
use nu_ansi_term::Style; use nu_ansi_term::Style;
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style}; use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
use nu_engine::eval_block; use nu_engine::eval_block;
@ -80,7 +79,7 @@ pub(crate) fn add_menus(
stack: &Stack, stack: &Stack,
config: &Config, config: &Config,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
trace!("add_menus: config: {:#?}", &config); //log::trace!("add_menus: config: {:#?}", &config);
line_editor = line_editor.clear_menus(); line_editor = line_editor.clear_menus();
for menu in &config.menus { for menu in &config.menus {

View File

@ -5,17 +5,17 @@ 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.94.2" version = "0.94.3"
# 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.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-parser = { path = "../nu-parser", version = "0.94.2" } nu-parser = { path = "../nu-parser", version = "0.94.3" }
nu-path = { path = "../nu-path", version = "0.94.2" } nu-path = { path = "../nu-path", version = "0.94.3" }
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
indexmap = { workspace = true } indexmap = { workspace = true }
miette = { workspace = true } miette = { workspace = true }
[dev-dependencies] [dev-dependencies]

View File

@ -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.94.2" version = "0.94.3"
# 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.94.2"
bench = false bench = false
[dependencies] [dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.94.2" } nu-cmd-base = { path = "../nu-cmd-base", version = "0.94.3" }
nu-engine = { path = "../nu-engine", version = "0.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-json = { version = "0.94.2", path = "../nu-json" } nu-json = { version = "0.94.3", path = "../nu-json" }
nu-parser = { path = "../nu-parser", version = "0.94.2" } nu-parser = { path = "../nu-parser", version = "0.94.3" }
nu-pretty-hex = { version = "0.94.2", path = "../nu-pretty-hex" } nu-pretty-hex = { version = "0.94.3", path = "../nu-pretty-hex" }
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
nu-utils = { path = "../nu-utils", version = "0.94.2" } nu-utils = { path = "../nu-utils", version = "0.94.3" }
# Potential dependencies for extras # Potential dependencies for extras
heck = { workspace = true } heck = { workspace = true }
@ -37,6 +37,6 @@ extra = ["default"]
default = [] default = []
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.2" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.3" }
nu-command = { path = "../nu-command", version = "0.94.2" } nu-command = { path = "../nu-command", version = "0.94.3" }
nu-test-support = { path = "../nu-test-support", version = "0.94.2" } nu-test-support = { path = "../nu-test-support", version = "0.94.3" }

View File

@ -6,16 +6,16 @@ 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.94.2" version = "0.94.3"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-parser = { path = "../nu-parser", version = "0.94.2" } nu-parser = { path = "../nu-parser", version = "0.94.3" }
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
nu-utils = { path = "../nu-utils", version = "0.94.2" } nu-utils = { path = "../nu-utils", version = "0.94.3" }
itertools = { workspace = true } itertools = { workspace = true }
shadow-rs = { version = "0.28", default-features = false } shadow-rs = { version = "0.28", default-features = false }
@ -29,4 +29,4 @@ which-support = []
trash-support = [] trash-support = []
sqlite = [] sqlite = []
static-link-openssl = [] static-link-openssl = []
system-clipboard = [] system-clipboard = []

View File

@ -60,6 +60,11 @@ impl Command for Def {
example: r#"def --env foo [] { $env.BAR = "BAZ" }; foo; $env.BAR"#, example: r#"def --env foo [] { $env.BAR = "BAZ" }; foo; $env.BAR"#,
result: Some(Value::test_string("BAZ")), result: Some(Value::test_string("BAZ")),
}, },
Example {
description: "cd affects the environment, so '--env' is required to change directory from within a command",
example: r#"def --env gohome [] { cd ~ }; gohome; $env.PWD == ('~' | path expand)"#,
result: Some(Value::test_string("true")),
},
Example { Example {
description: "Define a custom wrapper for an external command", description: "Define a custom wrapper for an external command",
example: r#"def --wrapped my-echo [...rest] { echo $rest }; my-echo spam"#, example: r#"def --wrapped my-echo [...rest] { echo $rest }; my-echo spam"#,

View File

@ -5,16 +5,16 @@ 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.94.2" version = "0.94.3"
# 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.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-path = { path = "../nu-path", version = "0.94.2" } nu-path = { path = "../nu-path", version = "0.94.3" }
nu-protocol = { path = "../nu-protocol", version = "0.94.2", features = ["plugin"] } nu-protocol = { path = "../nu-protocol", version = "0.94.3", features = ["plugin"] }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.94.2" } nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.94.3" }
itertools = { workspace = true } itertools = { workspace = true }
[dev-dependencies] [dev-dependencies]

View File

@ -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.94.2" version = "0.94.3"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
nu-engine = { path = "../nu-engine", version = "0.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-json = { path = "../nu-json", version = "0.94.2" } nu-json = { path = "../nu-json", version = "0.94.3" }
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.94.2" } nu-test-support = { path = "../nu-test-support", version = "0.94.3" }

View File

@ -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.94.2" version = "0.94.3"
# 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.94.2"
bench = false bench = false
[dependencies] [dependencies]
nu-cmd-base = { path = "../nu-cmd-base", version = "0.94.2" } nu-cmd-base = { path = "../nu-cmd-base", version = "0.94.3" }
nu-color-config = { path = "../nu-color-config", version = "0.94.2" } nu-color-config = { path = "../nu-color-config", version = "0.94.3" }
nu-engine = { path = "../nu-engine", version = "0.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-glob = { path = "../nu-glob", version = "0.94.2" } nu-glob = { path = "../nu-glob", version = "0.94.3" }
nu-json = { path = "../nu-json", version = "0.94.2" } nu-json = { path = "../nu-json", version = "0.94.3" }
nu-parser = { path = "../nu-parser", version = "0.94.2" } nu-parser = { path = "../nu-parser", version = "0.94.3" }
nu-path = { path = "../nu-path", version = "0.94.2" } nu-path = { path = "../nu-path", version = "0.94.3" }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.94.2" } nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.94.3" }
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
nu-system = { path = "../nu-system", version = "0.94.2" } nu-system = { path = "../nu-system", version = "0.94.3" }
nu-table = { path = "../nu-table", version = "0.94.2" } nu-table = { path = "../nu-table", version = "0.94.3" }
nu-term-grid = { path = "../nu-term-grid", version = "0.94.2" } nu-term-grid = { path = "../nu-term-grid", version = "0.94.3" }
nu-utils = { path = "../nu-utils", version = "0.94.2" } nu-utils = { path = "../nu-utils", version = "0.94.3" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
nuon = { path = "../nuon", version = "0.94.2" } nuon = { path = "../nuon", version = "0.94.3" }
alphanumeric-sort = { workspace = true } alphanumeric-sort = { workspace = true }
base64 = { workspace = true } base64 = { workspace = true }
@ -86,7 +86,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 } 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 @@ trash-support = ["trash"]
which-support = [] which-support = []
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.2" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.3" }
nu-test-support = { path = "../nu-test-support", version = "0.94.2" } nu-test-support = { path = "../nu-test-support", version = "0.94.3" }
dirs-next = { workspace = true } dirs-next = { workspace = true }
mockito = { workspace = true, default-features = false } mockito = { workspace = true, default-features = false }
@ -146,4 +146,4 @@ quickcheck = { workspace = true }
quickcheck_macros = { workspace = true } quickcheck_macros = { workspace = true }
rstest = { workspace = true, default-features = false } rstest = { workspace = true, default-features = false }
pretty_assertions = { workspace = true } pretty_assertions = { workspace = true }
tempfile = { workspace = true } tempfile = { workspace = true }

View File

@ -138,6 +138,7 @@ pub fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {
), ),
} }
} }
fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> { fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> {
// Get the Locale so we know what the thousands separator is // Get the Locale so we know what the thousands separator is
let locale = get_system_locale(); let locale = get_system_locale();
@ -148,29 +149,35 @@ fn int_from_string(a_string: &str, span: Span) -> Result<i64, ShellError> {
let clean_string = no_comma_string.trim(); let clean_string = no_comma_string.trim();
// Hadle negative file size // Hadle negative file size
if let Some(stripped_string) = clean_string.strip_prefix('-') { if let Some(stripped_negative_string) = clean_string.strip_prefix('-') {
match stripped_string.parse::<bytesize::ByteSize>() { match stripped_negative_string.parse::<bytesize::ByteSize>() {
Ok(n) => Ok(-(n.as_u64() as i64)), Ok(n) => Ok(-(n.as_u64() as i64)),
Err(_) => Err(ShellError::CantConvert { Err(_) => Err(string_convert_error(span)),
to_type: "int".into(), }
from_type: "string".into(), } else if let Some(stripped_positive_string) = clean_string.strip_prefix('+') {
span, match stripped_positive_string.parse::<bytesize::ByteSize>() {
help: None, Ok(n) if stripped_positive_string.starts_with(|c: char| c.is_ascii_digit()) => {
}), Ok(n.as_u64() as i64)
}
_ => Err(string_convert_error(span)),
} }
} else { } else {
match clean_string.parse::<bytesize::ByteSize>() { match clean_string.parse::<bytesize::ByteSize>() {
Ok(n) => Ok(n.0 as i64), Ok(n) => Ok(n.as_u64() as i64),
Err(_) => Err(ShellError::CantConvert { Err(_) => Err(string_convert_error(span)),
to_type: "int".into(),
from_type: "string".into(),
span,
help: None,
}),
} }
} }
} }
fn string_convert_error(span: Span) -> ShellError {
ShellError::CantConvert {
to_type: "filesize".into(),
from_type: "string".into(),
span,
help: None,
}
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@ -135,6 +135,11 @@ impl Command for Cd {
example: r#"cd -"#, example: r#"cd -"#,
result: None, result: None,
}, },
Example {
description: "Changing directory with a custom command requires 'def --env'",
example: r#"def --env gohome [] { cd ~ }"#,
result: None,
},
] ]
} }
} }

View File

@ -5,7 +5,7 @@ use nu_engine::command_prelude::*;
use super::msgpack::write_value; use super::msgpack::write_value;
const BUFFER_SIZE: usize = 65536; const BUFFER_SIZE: usize = 65536;
const DEFAULT_QUALITY: u32 = 1; const DEFAULT_QUALITY: u32 = 3; // 1 can be very bad
const DEFAULT_WINDOW_SIZE: u32 = 20; const DEFAULT_WINDOW_SIZE: u32 = 20;
#[derive(Clone)] #[derive(Clone)]
@ -22,7 +22,7 @@ impl Command for ToMsgpackz {
.named( .named(
"quality", "quality",
SyntaxShape::Int, SyntaxShape::Int,
"Quality of brotli compression (default 1)", "Quality of brotli compression (default 3)",
Some('q'), Some('q'),
) )
.named( .named(

View File

@ -24,7 +24,7 @@ impl Command for ToToml {
vec![Example { vec![Example {
description: "Outputs an TOML string representing the contents of this record", description: "Outputs an TOML string representing the contents of this record",
example: r#"{foo: 1 bar: 'qwe'} | to toml"#, example: r#"{foo: 1 bar: 'qwe'} | to toml"#,
result: Some(Value::test_string("bar = \"qwe\"\nfoo = 1\n")), result: Some(Value::test_string("foo = 1\nbar = \"qwe\"\n")),
}] }]
} }
@ -103,7 +103,7 @@ fn toml_into_pipeline_data(
value_type: Type, value_type: Type,
span: Span, span: Span,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
match toml::to_string(&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()),
_ => Ok(Value::error( _ => Ok(Value::error(
ShellError::CantConvert { ShellError::CantConvert {

View File

@ -12,14 +12,17 @@ impl Command for StorInsert {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("stor insert") Signature::build("stor insert")
.input_output_types(vec![(Type::Nothing, Type::table())]) .input_output_types(vec![
(Type::Nothing, Type::table()),
(Type::record(), Type::table()),
])
.required_named( .required_named(
"table-name", "table-name",
SyntaxShape::String, SyntaxShape::String,
"name of the table you want to insert into", "name of the table you want to insert into",
Some('t'), Some('t'),
) )
.required_named( .named(
"data-record", "data-record",
SyntaxShape::Record(vec![]), SyntaxShape::Record(vec![]),
"a record of column names and column values to insert into the specified table", "a record of column names and column values to insert into the specified table",
@ -39,10 +42,16 @@ impl Command for StorInsert {
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![Example {
description: "Insert data the in-memory sqlite database using a data-record of column-name and column-value pairs", description: "Insert data the in-memory sqlite database using a data-record of column-name and column-value pairs",
example: "stor insert --table-name nudb --data-record {bool1: true, int1: 5, float1: 1.1, str1: fdncred, datetime1: 2023-04-17}", example: "stor insert --table-name nudb --data-record {bool1: true, int1: 5, float1: 1.1, str1: fdncred, datetime1: 2023-04-17}",
result: None, result: None,
}] },
Example {
description: "Insert data through pipeline input as a record of column-name and column-value pairs",
example: "{bool1: true, int1: 5, float1: 1.1, str1: fdncred, datetime1: 2023-04-17} | stor insert --table-name nudb",
result: None,
},
]
} }
fn run( fn run(
@ -50,25 +59,79 @@ impl Command for StorInsert {
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let span = call.head; let span = call.head;
let table_name: Option<String> = call.get_flag(engine_state, stack, "table-name")?; let table_name: Option<String> = call.get_flag(engine_state, stack, "table-name")?;
let columns: Option<Record> = call.get_flag(engine_state, stack, "data-record")?; let data_record: Option<Record> = call.get_flag(engine_state, stack, "data-record")?;
// let config = engine_state.get_config(); // let config = engine_state.get_config();
let db = Box::new(SQLiteDatabase::new(std::path::Path::new(MEMORY_DB), None)); let db = Box::new(SQLiteDatabase::new(std::path::Path::new(MEMORY_DB), None));
// Check if the record is being passed as input or using the data record parameter
let columns = handle(span, data_record, input)?;
process(table_name, span, &db, columns)?; process(table_name, span, &db, columns)?;
Ok(Value::custom(db, span).into_pipeline_data()) Ok(Value::custom(db, span).into_pipeline_data())
} }
} }
fn handle(
span: Span,
data_record: Option<Record>,
input: PipelineData,
) -> Result<Record, ShellError> {
match input {
PipelineData::Empty => data_record.ok_or_else(|| ShellError::MissingParameter {
param_name: "requires a record".into(),
span,
}),
PipelineData::Value(value, ..) => {
// Since input is being used, check if the data record parameter is used too
if data_record.is_some() {
return Err(ShellError::GenericError {
error: "Pipeline and Flag both being used".into(),
msg: "Use either pipeline input or '--data-record' parameter".into(),
span: Some(span),
help: None,
inner: vec![],
});
}
match value {
Value::Record { val, .. } => Ok(val.into_owned()),
val => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "record".into(),
wrong_type: val.get_type().to_string(),
dst_span: Span::unknown(),
src_span: val.span(),
}),
}
}
_ => {
if data_record.is_some() {
return Err(ShellError::GenericError {
error: "Pipeline and Flag both being used".into(),
msg: "Use either pipeline input or '--data-record' parameter".into(),
span: Some(span),
help: None,
inner: vec![],
});
}
Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "record".into(),
wrong_type: "".into(),
dst_span: span,
src_span: span,
})
}
}
}
fn process( fn process(
table_name: Option<String>, table_name: Option<String>,
span: Span, span: Span,
db: &SQLiteDatabase, db: &SQLiteDatabase,
columns: Option<Record>, record: Record,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
if table_name.is_none() { if table_name.is_none() {
return Err(ShellError::MissingParameter { return Err(ShellError::MissingParameter {
@ -77,54 +140,45 @@ fn process(
}); });
} }
let new_table_name = table_name.unwrap_or("table".into()); let new_table_name = table_name.unwrap_or("table".into());
if let Ok(conn) = db.open_connection() { if let Ok(conn) = db.open_connection() {
match columns { let mut create_stmt = format!("INSERT INTO {} ( ", new_table_name);
Some(record) => { let cols = record.columns();
let mut create_stmt = format!("INSERT INTO {} ( ", new_table_name); cols.for_each(|col| {
let cols = record.columns(); create_stmt.push_str(&format!("{}, ", col));
cols.for_each(|col| { });
create_stmt.push_str(&format!("{}, ", col)); if create_stmt.ends_with(", ") {
}); create_stmt.pop();
if create_stmt.ends_with(", ") { create_stmt.pop();
create_stmt.pop(); }
create_stmt.pop();
}
// Values are set as placeholders. // Values are set as placeholders.
create_stmt.push_str(") VALUES ( "); create_stmt.push_str(") VALUES ( ");
for (index, _) in record.columns().enumerate() { for (index, _) in record.columns().enumerate() {
create_stmt.push_str(&format!("?{}, ", index + 1)); create_stmt.push_str(&format!("?{}, ", index + 1));
} }
if create_stmt.ends_with(", ") { if create_stmt.ends_with(", ") {
create_stmt.pop(); create_stmt.pop();
create_stmt.pop(); create_stmt.pop();
} }
create_stmt.push(')'); create_stmt.push(')');
// dbg!(&create_stmt); // dbg!(&create_stmt);
// Get the params from the passed values // Get the params from the passed values
let params = values_to_sql(record.values().cloned())?; let params = values_to_sql(record.values().cloned())?;
conn.execute(&create_stmt, params_from_iter(params)) conn.execute(&create_stmt, params_from_iter(params))
.map_err(|err| ShellError::GenericError { .map_err(|err| ShellError::GenericError {
error: "Failed to open SQLite connection in memory from insert".into(), error: "Failed to open SQLite connection in memory from insert".into(),
msg: err.to_string(), msg: err.to_string(),
span: Some(Span::test_data()), span: Some(Span::test_data()),
help: None, help: None,
inner: vec![], inner: vec![],
})?; })?;
} };
None => {
return Err(ShellError::MissingParameter {
param_name: "requires at least one column".into(),
span,
});
}
};
}
// dbg!(db.clone()); // dbg!(db.clone());
Ok(()) Ok(())
} }
@ -176,7 +230,7 @@ mod test {
), ),
); );
let result = process(table_name, span, &db, Some(columns)); let result = process(table_name, span, &db, columns);
assert!(result.is_ok()); assert!(result.is_ok());
} }
@ -201,7 +255,7 @@ mod test {
Value::test_string("String With Spaces".to_string()), Value::test_string("String With Spaces".to_string()),
); );
let result = process(table_name, span, &db, Some(columns)); let result = process(table_name, span, &db, columns);
assert!(result.is_ok()); assert!(result.is_ok());
} }
@ -226,7 +280,7 @@ mod test {
Value::test_string("ThisIsALongString".to_string()), Value::test_string("ThisIsALongString".to_string()),
); );
let result = process(table_name, span, &db, Some(columns)); let result = process(table_name, span, &db, columns);
// SQLite uses dynamic typing, making any length acceptable for a varchar column // SQLite uses dynamic typing, making any length acceptable for a varchar column
assert!(result.is_ok()); assert!(result.is_ok());
} }
@ -251,7 +305,7 @@ mod test {
Value::test_string("ThisIsTheWrongType".to_string()), Value::test_string("ThisIsTheWrongType".to_string()),
); );
let result = process(table_name, span, &db, Some(columns)); let result = process(table_name, span, &db, columns);
// SQLite uses dynamic typing, making any type acceptable for a column // SQLite uses dynamic typing, making any type acceptable for a column
assert!(result.is_ok()); assert!(result.is_ok());
} }
@ -276,7 +330,7 @@ mod test {
Value::test_string("ThisIsALongString".to_string()), Value::test_string("ThisIsALongString".to_string()),
); );
let result = process(table_name, span, &db, Some(columns)); let result = process(table_name, span, &db, columns);
assert!(result.is_err()); assert!(result.is_err());
} }
@ -293,7 +347,7 @@ mod test {
Value::test_string("ThisIsALongString".to_string()), Value::test_string("ThisIsALongString".to_string()),
); );
let result = process(table_name, span, &db, Some(columns)); let result = process(table_name, span, &db, columns);
assert!(result.is_err()); assert!(result.is_err());
} }

View File

@ -11,14 +11,17 @@ impl Command for StorUpdate {
fn signature(&self) -> Signature { fn signature(&self) -> Signature {
Signature::build("stor update") Signature::build("stor update")
.input_output_types(vec![(Type::Nothing, Type::table())]) .input_output_types(vec![
(Type::Nothing, Type::table()),
(Type::record(), Type::table()),
])
.required_named( .required_named(
"table-name", "table-name",
SyntaxShape::String, SyntaxShape::String,
"name of the table you want to insert into", "name of the table you want to insert into",
Some('t'), Some('t'),
) )
.required_named( .named(
"update-record", "update-record",
SyntaxShape::Record(vec![]), SyntaxShape::Record(vec![]),
"a record of column names and column values to update in the specified table", "a record of column names and column values to update in the specified table",
@ -54,6 +57,11 @@ impl Command for StorUpdate {
example: "stor update --table-name nudb --update-record {str1: nushell datetime1: 2020-04-17} --where-clause \"bool1 = 1\"", example: "stor update --table-name nudb --update-record {str1: nushell datetime1: 2020-04-17} --where-clause \"bool1 = 1\"",
result: None, result: None,
}, },
Example {
description: "Update the in-memory sqlite database through pipeline input",
example: "{str1: nushell datetime1: 2020-04-17} | stor update --table-name nudb",
result: None,
},
] ]
} }
@ -62,91 +70,147 @@ impl Command for StorUpdate {
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let span = call.head; let span = call.head;
let table_name: Option<String> = call.get_flag(engine_state, stack, "table-name")?; let table_name: Option<String> = call.get_flag(engine_state, stack, "table-name")?;
let columns: Option<Record> = call.get_flag(engine_state, stack, "update-record")?; let update_record: Option<Record> = call.get_flag(engine_state, stack, "update-record")?;
let where_clause_opt: Option<Spanned<String>> = let where_clause_opt: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "where-clause")?; call.get_flag(engine_state, stack, "where-clause")?;
// Open the in-mem database // Open the in-mem database
let db = Box::new(SQLiteDatabase::new(std::path::Path::new(MEMORY_DB), None)); let db = Box::new(SQLiteDatabase::new(std::path::Path::new(MEMORY_DB), None));
if table_name.is_none() { // Check if the record is being passed as input or using the update record parameter
return Err(ShellError::MissingParameter { let columns = handle(span, update_record, input)?;
param_name: "requires at table name".into(),
span,
});
}
let new_table_name = table_name.unwrap_or("table".into());
if let Ok(conn) = db.open_connection() {
match columns {
Some(record) => {
let mut update_stmt = format!("UPDATE {} ", new_table_name);
update_stmt.push_str("SET "); process(table_name, span, &db, columns, where_clause_opt)?;
let vals = record.iter();
vals.for_each(|(key, val)| match val {
Value::Int { val, .. } => {
update_stmt.push_str(&format!("{} = {}, ", key, val));
}
Value::Float { val, .. } => {
update_stmt.push_str(&format!("{} = {}, ", key, val));
}
Value::String { val, .. } => {
update_stmt.push_str(&format!("{} = '{}', ", key, val));
}
Value::Date { val, .. } => {
update_stmt.push_str(&format!("{} = '{}', ", key, val));
}
Value::Bool { val, .. } => {
update_stmt.push_str(&format!("{} = {}, ", key, val));
}
_ => {
// return Err(ShellError::UnsupportedInput {
// msg: format!("{} is not a valid datepart, expected one of year, month, day, hour, minute, second, millisecond, microsecond, nanosecond", part.item),
// input: "value originates from here".to_string(),
// msg_span: span,
// input_span: val.span(),
// });
}
});
if update_stmt.ends_with(", ") {
update_stmt.pop();
update_stmt.pop();
}
// Yup, this is a bit janky, but I'm not sure a better way to do this without having
// --and and --or flags as well as supporting ==, !=, <>, is null, is not null, etc.
// and other sql syntax. So, for now, just type a sql where clause as a string.
if let Some(where_clause) = where_clause_opt {
update_stmt.push_str(&format!(" WHERE {}", where_clause.item));
}
// dbg!(&update_stmt);
conn.execute(&update_stmt, [])
.map_err(|err| ShellError::GenericError {
error: "Failed to open SQLite connection in memory from update".into(),
msg: err.to_string(),
span: Some(Span::test_data()),
help: None,
inner: vec![],
})?;
}
None => {
return Err(ShellError::MissingParameter {
param_name: "requires at least one column".into(),
span: call.head,
});
}
};
}
// dbg!(db.clone());
Ok(Value::custom(db, span).into_pipeline_data()) Ok(Value::custom(db, span).into_pipeline_data())
} }
} }
fn handle(
span: Span,
update_record: Option<Record>,
input: PipelineData,
) -> Result<Record, ShellError> {
match input {
PipelineData::Empty => update_record.ok_or_else(|| ShellError::MissingParameter {
param_name: "requires a record".into(),
span,
}),
PipelineData::Value(value, ..) => {
// Since input is being used, check if the data record parameter is used too
if update_record.is_some() {
return Err(ShellError::GenericError {
error: "Pipeline and Flag both being used".into(),
msg: "Use either pipeline input or '--update-record' parameter".into(),
span: Some(span),
help: None,
inner: vec![],
});
}
match value {
Value::Record { val, .. } => Ok(val.into_owned()),
val => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "record".into(),
wrong_type: val.get_type().to_string(),
dst_span: Span::unknown(),
src_span: val.span(),
}),
}
}
_ => {
if update_record.is_some() {
return Err(ShellError::GenericError {
error: "Pipeline and Flag both being used".into(),
msg: "Use either pipeline input or '--update-record' parameter".into(),
span: Some(span),
help: None,
inner: vec![],
});
}
Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "record".into(),
wrong_type: "".into(),
dst_span: span,
src_span: span,
})
}
}
}
fn process(
table_name: Option<String>,
span: Span,
db: &SQLiteDatabase,
record: Record,
where_clause_opt: Option<Spanned<String>>,
) -> Result<(), ShellError> {
if table_name.is_none() {
return Err(ShellError::MissingParameter {
param_name: "requires at table name".into(),
span,
});
}
let new_table_name = table_name.unwrap_or("table".into());
if let Ok(conn) = db.open_connection() {
let mut update_stmt = format!("UPDATE {} ", new_table_name);
update_stmt.push_str("SET ");
let vals = record.iter();
vals.for_each(|(key, val)| match val {
Value::Int { val, .. } => {
update_stmt.push_str(&format!("{} = {}, ", key, val));
}
Value::Float { val, .. } => {
update_stmt.push_str(&format!("{} = {}, ", key, val));
}
Value::String { val, .. } => {
update_stmt.push_str(&format!("{} = '{}', ", key, val));
}
Value::Date { val, .. } => {
update_stmt.push_str(&format!("{} = '{}', ", key, val));
}
Value::Bool { val, .. } => {
update_stmt.push_str(&format!("{} = {}, ", key, val));
}
_ => {
// return Err(ShellError::UnsupportedInput {
// msg: format!("{} is not a valid datepart, expected one of year, month, day, hour, minute, second, millisecond, microsecond, nanosecond", part.item),
// input: "value originates from here".to_string(),
// msg_span: span,
// input_span: val.span(),
// });
}
});
if update_stmt.ends_with(", ") {
update_stmt.pop();
update_stmt.pop();
}
// Yup, this is a bit janky, but I'm not sure a better way to do this without having
// --and and --or flags as well as supporting ==, !=, <>, is null, is not null, etc.
// and other sql syntax. So, for now, just type a sql where clause as a string.
if let Some(where_clause) = where_clause_opt {
update_stmt.push_str(&format!(" WHERE {}", where_clause.item));
}
// dbg!(&update_stmt);
conn.execute(&update_stmt, [])
.map_err(|err| ShellError::GenericError {
error: "Failed to open SQLite connection in memory from update".into(),
msg: err.to_string(),
span: Some(Span::test_data()),
help: None,
inner: vec![],
})?;
}
// dbg!(db.clone());
Ok(())
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View File

@ -1,6 +1,6 @@
use indexmap::{indexmap, IndexMap}; use indexmap::{indexmap, IndexMap};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::engine::StateWorkingSet;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::sync::{atomic::AtomicBool, Arc}; use std::sync::{atomic::AtomicBool, Arc};

View File

@ -45,20 +45,6 @@ impl Command for DetectColumns {
vec!["split", "tabular"] vec!["split", "tabular"]
} }
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
if call.has_flag(engine_state, stack, "guess")? {
guess_width(engine_state, stack, call, input)
} else {
detect_columns(engine_state, stack, call, input)
}
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
@ -109,33 +95,87 @@ none 8150224 4 8150220 1% /mnt/c' | detect columns --gue
}, },
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let num_rows_to_skip: Option<usize> = call.get_flag(engine_state, stack, "skip")?;
let noheader = call.has_flag(engine_state, stack, "no-headers")?;
let range: Option<Range> = call.get_flag(engine_state, stack, "combine-columns")?;
let args = Arguments {
noheader,
num_rows_to_skip,
range,
};
if call.has_flag(engine_state, stack, "guess")? {
guess_width(engine_state, call, input, args)
} else {
detect_columns(engine_state, call, input, args)
}
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let num_rows_to_skip: Option<usize> = call.get_flag_const(working_set, "skip")?;
let noheader = call.has_flag_const(working_set, "no-headers")?;
let range: Option<Range> = call.get_flag_const(working_set, "combine-columns")?;
let args = Arguments {
noheader,
num_rows_to_skip,
range,
};
if call.has_flag_const(working_set, "guess")? {
guess_width(working_set.permanent(), call, input, args)
} else {
detect_columns(working_set.permanent(), call, input, args)
}
}
}
struct Arguments {
num_rows_to_skip: Option<usize>,
noheader: bool,
range: Option<Range>,
} }
fn guess_width( fn guess_width(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
args: Arguments,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
use super::guess_width::GuessWidth; use super::guess_width::GuessWidth;
let input_span = input.span().unwrap_or(call.head); let input_span = input.span().unwrap_or(call.head);
let mut input = input.collect_string("", engine_state.get_config())?; let mut input = input.collect_string("", engine_state.get_config())?;
let num_rows_to_skip: Option<usize> = call.get_flag(engine_state, stack, "skip")?; if let Some(rows) = args.num_rows_to_skip {
if let Some(rows) = num_rows_to_skip {
input = input.lines().skip(rows).map(|x| x.to_string()).join("\n"); input = input.lines().skip(rows).map(|x| x.to_string()).join("\n");
} }
let mut guess_width = GuessWidth::new_reader(Box::new(Cursor::new(input))); let mut guess_width = GuessWidth::new_reader(Box::new(Cursor::new(input)));
let noheader = call.has_flag(engine_state, stack, "no-headers")?;
let result = guess_width.read_all(); let result = guess_width.read_all();
if result.is_empty() { if result.is_empty() {
return Ok(Value::nothing(input_span).into_pipeline_data()); return Ok(Value::nothing(input_span).into_pipeline_data());
} }
let range: Option<Range> = call.get_flag(engine_state, stack, "combine-columns")?; if !args.noheader {
if !noheader {
let columns = result[0].clone(); let columns = result[0].clone();
Ok(result Ok(result
.into_iter() .into_iter()
@ -152,7 +192,7 @@ fn guess_width(
let record = let record =
Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span); Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span);
match record { match record {
Ok(r) => match &range { Ok(r) => match &args.range {
Some(range) => merge_record(r, range, input_span), Some(range) => merge_record(r, range, input_span),
None => Value::record(r, input_span), None => Value::record(r, input_span),
}, },
@ -177,7 +217,7 @@ fn guess_width(
let record = let record =
Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span); Record::from_raw_cols_vals(columns.clone(), values, input_span, input_span);
match record { match record {
Ok(r) => match &range { Ok(r) => match &args.range {
Some(range) => merge_record(r, range, input_span), Some(range) => merge_record(r, range, input_span),
None => Value::record(r, input_span), None => Value::record(r, input_span),
}, },
@ -190,21 +230,18 @@ fn guess_width(
fn detect_columns( fn detect_columns(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
args: Arguments,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let name_span = call.head; let name_span = call.head;
let num_rows_to_skip: Option<usize> = call.get_flag(engine_state, stack, "skip")?;
let noheader = call.has_flag(engine_state, stack, "no-headers")?;
let range: Option<Range> = call.get_flag(engine_state, stack, "combine-columns")?;
let ctrlc = engine_state.ctrlc.clone(); let ctrlc = engine_state.ctrlc.clone();
let config = engine_state.get_config(); let config = engine_state.get_config();
let input = input.collect_string("", config)?; let input = input.collect_string("", config)?;
let input: Vec<_> = input let input: Vec<_> = input
.lines() .lines()
.skip(num_rows_to_skip.unwrap_or_default()) .skip(args.num_rows_to_skip.unwrap_or_default())
.map(|x| x.to_string()) .map(|x| x.to_string())
.collect(); .collect();
@ -214,13 +251,14 @@ fn detect_columns(
if let Some(orig_headers) = headers { if let Some(orig_headers) = headers {
let mut headers = find_columns(&orig_headers); let mut headers = find_columns(&orig_headers);
if noheader { if args.noheader {
for header in headers.iter_mut().enumerate() { for header in headers.iter_mut().enumerate() {
header.1.item = format!("column{}", header.0); header.1.item = format!("column{}", header.0);
} }
} }
Ok(noheader Ok(args
.noheader
.then_some(orig_headers) .then_some(orig_headers)
.into_iter() .into_iter()
.chain(input) .chain(input)
@ -273,7 +311,7 @@ fn detect_columns(
} }
} }
match &range { match &args.range {
Some(range) => merge_record(record, range, name_span), Some(range) => merge_record(record, range, name_span),
None => Value::record(record, name_span), None => Value::record(record, name_span),
} }

View File

@ -7,10 +7,9 @@ use base64::{
Engine, Engine,
}; };
use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument}; use nu_cmd_base::input_handler::{operate as general_operate, CmdArgument};
use nu_engine::CallExt;
use nu_protocol::{ use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{EngineState, Stack}, engine::EngineState,
PipelineData, ShellError, Span, Spanned, Value, PipelineData, ShellError, Span, Spanned, Value,
}; };
@ -42,22 +41,24 @@ impl CmdArgument for Arguments {
} }
} }
pub(super) struct Base64CommandArguments {
pub(super) character_set: Option<Spanned<String>>,
pub(super) action_type: ActionType,
pub(super) binary: bool,
}
pub fn operate( pub fn operate(
action_type: ActionType,
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
cell_paths: Vec<CellPath>,
args: Base64CommandArguments,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
let character_set: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "character-set")?;
let binary = call.has_flag(engine_state, stack, "binary")?;
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths); let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
// Default the character set to standard if the argument is not specified. // Default the character set to standard if the argument is not specified.
let character_set = match character_set { let character_set = match args.character_set {
Some(inner_tag) => inner_tag, Some(inner_tag) => inner_tag,
None => Spanned { None => Spanned {
item: "standard".to_string(), item: "standard".to_string(),
@ -68,9 +69,9 @@ pub fn operate(
let args = Arguments { let args = Arguments {
encoding_config: Base64Config { encoding_config: Base64Config {
character_set, character_set,
action_type, action_type: args.action_type,
}, },
binary, binary: args.binary,
cell_paths, cell_paths,
}; };

View File

@ -46,6 +46,10 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -53,49 +57,67 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head;
let encoding: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?; let encoding: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
run(call, input, encoding)
}
match input { fn run_const(
PipelineData::ByteStream(stream, ..) => { &self,
let span = stream.span(); working_set: &StateWorkingSet,
let bytes = stream.into_bytes()?; call: &Call,
match encoding { input: PipelineData,
) -> Result<PipelineData, ShellError> {
let encoding: Option<Spanned<String>> = call.opt_const(working_set, 0)?;
run(call, input, encoding)
}
}
fn run(
call: &Call,
input: PipelineData,
encoding: Option<Spanned<String>>,
) -> Result<PipelineData, ShellError> {
let head = call.head;
match input {
PipelineData::ByteStream(stream, ..) => {
let span = stream.span();
let bytes = stream.into_bytes()?;
match encoding {
Some(encoding_name) => super::encoding::decode(head, encoding_name, &bytes),
None => super::encoding::detect_encoding_name(head, span, &bytes)
.map(|encoding| encoding.decode(&bytes).0.into_owned())
.map(|s| Value::string(s, head)),
}
.map(|val| val.into_pipeline_data())
}
PipelineData::Value(v, ..) => {
let input_span = v.span();
match v {
Value::Binary { val: bytes, .. } => match encoding {
Some(encoding_name) => super::encoding::decode(head, encoding_name, &bytes), Some(encoding_name) => super::encoding::decode(head, encoding_name, &bytes),
None => super::encoding::detect_encoding_name(head, span, &bytes) None => super::encoding::detect_encoding_name(head, input_span, &bytes)
.map(|encoding| encoding.decode(&bytes).0.into_owned()) .map(|encoding| encoding.decode(&bytes).0.into_owned())
.map(|s| Value::string(s, head)), .map(|s| Value::string(s, head)),
} }
.map(|val| val.into_pipeline_data()) .map(|val| val.into_pipeline_data()),
Value::Error { error, .. } => Err(*error),
_ => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: v.get_type().to_string(),
dst_span: head,
src_span: v.span(),
}),
} }
PipelineData::Value(v, ..) => {
let input_span = v.span();
match v {
Value::Binary { val: bytes, .. } => match encoding {
Some(encoding_name) => super::encoding::decode(head, encoding_name, &bytes),
None => super::encoding::detect_encoding_name(head, input_span, &bytes)
.map(|encoding| encoding.decode(&bytes).0.into_owned())
.map(|s| Value::string(s, head)),
}
.map(|val| val.into_pipeline_data()),
Value::Error { error, .. } => Err(*error),
_ => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "binary".into(),
wrong_type: v.get_type().to_string(),
dst_span: head,
src_span: v.span(),
}),
}
}
// This should be more precise, but due to difficulties in getting spans
// from PipelineData::ListData, this is as it is.
_ => Err(ShellError::UnsupportedInput {
msg: "non-binary input".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: input.span().unwrap_or(head),
}),
} }
// This should be more precise, but due to difficulties in getting spans
// from PipelineData::ListData, this is as it is.
_ => Err(ShellError::UnsupportedInput {
msg: "non-binary input".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: input.span().unwrap_or(head),
}),
} }
} }

View File

@ -1,4 +1,4 @@
use super::base64::{operate, ActionType, CHARACTER_SET_DESC}; use super::base64::{operate, ActionType, Base64CommandArguments, CHARACTER_SET_DESC};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
@ -66,6 +66,10 @@ impl Command for DecodeBase64 {
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -73,7 +77,34 @@ impl Command for DecodeBase64 {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(ActionType::Decode, engine_state, stack, call, input) let character_set: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "character-set")?;
let binary = call.has_flag(engine_state, stack, "binary")?;
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = Base64CommandArguments {
action_type: ActionType::Decode,
binary,
character_set,
};
operate(engine_state, call, input, cell_paths, args)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let character_set: Option<Spanned<String>> =
call.get_flag_const(working_set, "character-set")?;
let binary = call.has_flag_const(working_set, "binary")?;
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
let args = Base64CommandArguments {
action_type: ActionType::Decode,
binary,
character_set,
};
operate(working_set.permanent(), call, input, cell_paths, args)
} }
} }

View File

@ -69,6 +69,10 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -76,42 +80,62 @@ documentation link at https://docs.rs/encoding_rs/latest/encoding_rs/#statics"#
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head;
let encoding: Spanned<String> = call.req(engine_state, stack, 0)?; let encoding: Spanned<String> = call.req(engine_state, stack, 0)?;
let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?; let ignore_errors = call.has_flag(engine_state, stack, "ignore-errors")?;
run(call, input, encoding, ignore_errors)
}
match input { fn run_const(
PipelineData::ByteStream(stream, ..) => { &self,
let span = stream.span(); working_set: &StateWorkingSet,
let s = stream.into_string()?; call: &Call,
super::encoding::encode(head, encoding, &s, span, ignore_errors) input: PipelineData,
.map(|val| val.into_pipeline_data()) ) -> Result<PipelineData, ShellError> {
} let encoding: Spanned<String> = call.req_const(working_set, 0)?;
PipelineData::Value(v, ..) => { let ignore_errors = call.has_flag_const(working_set, "ignore-errors")?;
let span = v.span(); run(call, input, encoding, ignore_errors)
match v { }
Value::String { val: s, .. } => { }
super::encoding::encode(head, encoding, &s, span, ignore_errors)
.map(|val| val.into_pipeline_data()) fn run(
} call: &Call,
Value::Error { error, .. } => Err(*error), input: PipelineData,
_ => Err(ShellError::OnlySupportsThisInputType { encoding: Spanned<String>,
exp_input_type: "string".into(), ignore_errors: bool,
wrong_type: v.get_type().to_string(), ) -> Result<PipelineData, ShellError> {
dst_span: head, let head = call.head;
src_span: v.span(),
}), match input {
} PipelineData::ByteStream(stream, ..) => {
} let span = stream.span();
// This should be more precise, but due to difficulties in getting spans let s = stream.into_string()?;
// from PipelineData::ListStream, this is as it is. super::encoding::encode(head, encoding, &s, span, ignore_errors)
_ => Err(ShellError::UnsupportedInput { .map(|val| val.into_pipeline_data())
msg: "non-string input".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: input.span().unwrap_or(head),
}),
} }
PipelineData::Value(v, ..) => {
let span = v.span();
match v {
Value::String { val: s, .. } => {
super::encoding::encode(head, encoding, &s, span, ignore_errors)
.map(|val| val.into_pipeline_data())
}
Value::Error { error, .. } => Err(*error),
_ => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "string".into(),
wrong_type: v.get_type().to_string(),
dst_span: head,
src_span: v.span(),
}),
}
}
// This should be more precise, but due to difficulties in getting spans
// from PipelineData::ListStream, this is as it is.
_ => Err(ShellError::UnsupportedInput {
msg: "non-string input".into(),
input: "value originates from here".into(),
msg_span: head,
input_span: input.span().unwrap_or(head),
}),
} }
} }

View File

@ -1,4 +1,4 @@
use super::base64::{operate, ActionType, CHARACTER_SET_DESC}; use super::base64::{operate, ActionType, Base64CommandArguments, CHARACTER_SET_DESC};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
#[derive(Clone)] #[derive(Clone)]
@ -70,6 +70,10 @@ impl Command for EncodeBase64 {
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -77,7 +81,34 @@ impl Command for EncodeBase64 {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(ActionType::Encode, engine_state, stack, call, input) let character_set: Option<Spanned<String>> =
call.get_flag(engine_state, stack, "character-set")?;
let binary = call.has_flag(engine_state, stack, "binary")?;
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = Base64CommandArguments {
action_type: ActionType::Encode,
binary,
character_set,
};
operate(engine_state, call, input, cell_paths, args)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let character_set: Option<Spanned<String>> =
call.get_flag_const(working_set, "character-set")?;
let binary = call.has_flag_const(working_set, "binary")?;
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
let args = Base64CommandArguments {
action_type: ActionType::Encode,
binary,
character_set,
};
operate(working_set.permanent(), call, input, cell_paths, args)
} }
} }

View File

@ -38,36 +38,6 @@ impl Command for FormatDate {
vec!["fmt", "strftime"] vec!["fmt", "strftime"]
} }
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
if call.has_flag(engine_state, stack, "list")? {
return Ok(PipelineData::Value(
generate_strftime_list(head, false),
None,
));
}
let format = call.opt::<Spanned<String>>(engine_state, stack, 0)?;
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| match &format {
Some(format) => format_helper(value, format.item.as_str(), format.span, head),
None => format_helper_rfc2822(value, head),
},
engine_state.ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
@ -104,6 +74,61 @@ impl Command for FormatDate {
}, },
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let list = call.has_flag(engine_state, stack, "list")?;
let format = call.opt::<Spanned<String>>(engine_state, stack, 0)?;
run(engine_state, call, input, list, format)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let list = call.has_flag_const(working_set, "list")?;
let format = call.opt_const::<Spanned<String>>(working_set, 0)?;
run(working_set.permanent(), call, input, list, format)
}
}
fn run(
engine_state: &EngineState,
call: &Call,
input: PipelineData,
list: bool,
format: Option<Spanned<String>>,
) -> Result<PipelineData, ShellError> {
let head = call.head;
if list {
return Ok(PipelineData::Value(
generate_strftime_list(head, false),
None,
));
}
// This doesn't match explicit nulls
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| match &format {
Some(format) => format_helper(value, format.item.as_str(), format.span, head),
None => format_helper_rfc2822(value, head),
},
engine_state.ctrlc.clone(),
)
} }
fn format_from<Tz: TimeZone>(date_time: DateTime<Tz>, formatter: &str, span: Span) -> Value fn format_from<Tz: TimeZone>(date_time: DateTime<Tz>, formatter: &str, span: Span) -> Value

View File

@ -53,6 +53,10 @@ impl Command for FormatDuration {
vec!["convert", "display", "pattern", "human readable"] vec!["convert", "display", "pattern", "human readable"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -81,6 +85,33 @@ impl Command for FormatDuration {
) )
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let format_value = call
.req_const::<Value>(working_set, 0)?
.coerce_into_string()?
.to_ascii_lowercase();
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let float_precision = working_set.permanent().config.float_precision as usize;
let arg = Arguments {
format_value,
float_precision,
cell_paths,
};
operate(
format_value_impl,
arg,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -1,6 +1,6 @@
use nu_cmd_base::input_handler::{operate, CmdArgument}; use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::format_filesize; use nu_protocol::{engine::StateWorkingSet, format_filesize};
struct Arguments { struct Arguments {
format_value: String, format_value: String,
@ -50,6 +50,10 @@ impl Command for FormatFilesize {
vec!["convert", "display", "pattern", "human readable"] vec!["convert", "display", "pattern", "human readable"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -76,6 +80,31 @@ impl Command for FormatFilesize {
) )
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let format_value = call
.req_const::<Value>(working_set, 0)?
.coerce_into_string()?
.to_ascii_lowercase();
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let arg = Arguments {
format_value,
cell_paths,
};
operate(
format_value_impl,
arg,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -1,6 +1,6 @@
use fancy_regex::{Captures, Regex}; use fancy_regex::{Captures, Regex};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::ListStream; use nu_protocol::{engine::StateWorkingSet, ListStream};
use std::{ use std::{
collections::VecDeque, collections::VecDeque,
sync::{atomic::AtomicBool, Arc}, sync::{atomic::AtomicBool, Arc},
@ -99,6 +99,10 @@ impl Command for Parse {
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -106,19 +110,31 @@ impl Command for Parse {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
let regex: bool = call.has_flag(engine_state, stack, "regex")?;
operate(engine_state, pattern, regex, call, input)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let pattern: Spanned<String> = call.req_const(working_set, 0)?;
let regex: bool = call.has_flag_const(working_set, "regex")?;
operate(working_set.permanent(), pattern, regex, call, input)
} }
} }
fn operate( fn operate(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack, pattern: Spanned<String>,
regex: bool,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
let pattern: Spanned<String> = call.req(engine_state, stack, 0)?;
let regex: bool = call.has_flag(engine_state, stack, "regex")?;
let pattern_item = pattern.item; let pattern_item = pattern.item;
let pattern_span = pattern.span; let pattern_span = pattern.span;

View File

@ -1,5 +1,6 @@
use crate::grapheme_flags; use crate::{grapheme_flags, grapheme_flags_const};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
#[derive(Clone)] #[derive(Clone)]
@ -88,6 +89,10 @@ impl Command for SubCommand {
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -95,19 +100,28 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
split_chars(engine_state, stack, call, input) let graphemes = grapheme_flags(engine_state, stack, call)?;
split_chars(engine_state, call, input, graphemes)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let graphemes = grapheme_flags_const(working_set, call)?;
split_chars(working_set.permanent(), call, input, graphemes)
} }
} }
fn split_chars( fn split_chars(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
graphemes: bool,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let span = call.head; let span = call.head;
let graphemes = grapheme_flags(engine_state, stack, call)?;
input.map( input.map(
move |x| split_chars_helper(&x, span, graphemes), move |x| split_chars_helper(&x, span, graphemes),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),

View File

@ -43,16 +43,6 @@ impl Command for SubCommand {
vec!["separate", "divide", "regex"] vec!["separate", "divide", "regex"]
} }
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
split_column(engine_state, stack, call, input)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
@ -103,35 +93,83 @@ impl Command for SubCommand {
}, },
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?;
let collapse_empty = call.has_flag(engine_state, stack, "collapse-empty")?;
let has_regex = call.has_flag(engine_state, stack, "regex")?;
let args = Arguments {
separator,
rest,
collapse_empty,
has_regex,
};
split_column(engine_state, call, input, args)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let separator: Spanned<String> = call.req_const(working_set, 0)?;
let rest: Vec<Spanned<String>> = call.rest_const(working_set, 1)?;
let collapse_empty = call.has_flag_const(working_set, "collapse-empty")?;
let has_regex = call.has_flag_const(working_set, "regex")?;
let args = Arguments {
separator,
rest,
collapse_empty,
has_regex,
};
split_column(working_set.permanent(), call, input, args)
}
}
struct Arguments {
separator: Spanned<String>,
rest: Vec<Spanned<String>>,
collapse_empty: bool,
has_regex: bool,
} }
fn split_column( fn split_column(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
args: Arguments,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let name_span = call.head; let name_span = call.head;
let separator: Spanned<String> = call.req(engine_state, stack, 0)?; let regex = if args.has_regex {
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 1)?; Regex::new(&args.separator.item)
let collapse_empty = call.has_flag(engine_state, stack, "collapse-empty")?;
let regex = if call.has_flag(engine_state, stack, "regex")? {
Regex::new(&separator.item)
} else { } else {
let escaped = regex::escape(&separator.item); let escaped = regex::escape(&args.separator.item);
Regex::new(&escaped) Regex::new(&escaped)
} }
.map_err(|e| ShellError::GenericError { .map_err(|e| ShellError::GenericError {
error: "Error with regular expression".into(), error: "Error with regular expression".into(),
msg: e.to_string(), msg: e.to_string(),
span: Some(separator.span), span: Some(args.separator.span),
help: None, help: None,
inner: vec![], inner: vec![],
})?; })?;
input.flat_map( input.flat_map(
move |x| split_column_helper(&x, &regex, &rest, collapse_empty, name_span), move |x| split_column_helper(&x, &regex, &args.rest, args.collapse_empty, name_span),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
) )
} }

View File

@ -36,16 +36,6 @@ impl Command for SubCommand {
vec!["separate", "divide", "regex"] vec!["separate", "divide", "regex"]
} }
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
split_list(engine_state, stack, call, input)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
@ -145,6 +135,33 @@ impl Command for SubCommand {
}, },
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let has_regex = call.has_flag(engine_state, stack, "regex")?;
let separator: Value = call.req(engine_state, stack, 0)?;
split_list(engine_state, call, input, has_regex, separator)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let has_regex = call.has_flag_const(working_set, "regex")?;
let separator: Value = call.req_const(working_set, 0)?;
split_list(working_set.permanent(), call, input, has_regex, separator)
}
} }
enum Matcher { enum Matcher {
@ -188,15 +205,15 @@ impl Matcher {
fn split_list( fn split_list(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
has_regex: bool,
separator: Value,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let separator: Value = call.req(engine_state, stack, 0)?;
let mut temp_list = Vec::new(); let mut temp_list = Vec::new();
let mut returned_list = Vec::new(); let mut returned_list = Vec::new();
let matcher = Matcher::new(call.has_flag(engine_state, stack, "regex")?, separator)?; let matcher = Matcher::new(has_regex, separator)?;
for val in input { for val in input {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) { if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
break; break;

View File

@ -43,16 +43,6 @@ impl Command for SubCommand {
vec!["separate", "divide", "regex"] vec!["separate", "divide", "regex"]
} }
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
split_row(engine_state, stack, call, input)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {
@ -109,32 +99,77 @@ impl Command for SubCommand {
}, },
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run(
&self,
engine_state: &EngineState,
stack: &mut Stack,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let separator: Spanned<String> = call.req(engine_state, stack, 0)?;
let max_split: Option<usize> = call.get_flag(engine_state, stack, "number")?;
let has_regex = call.has_flag(engine_state, stack, "regex")?;
let args = Arguments {
separator,
max_split,
has_regex,
};
split_row(engine_state, call, input, args)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let separator: Spanned<String> = call.req_const(working_set, 0)?;
let max_split: Option<usize> = call.get_flag_const(working_set, "number")?;
let has_regex = call.has_flag_const(working_set, "regex")?;
let args = Arguments {
separator,
max_split,
has_regex,
};
split_row(working_set.permanent(), call, input, args)
}
}
struct Arguments {
has_regex: bool,
separator: Spanned<String>,
max_split: Option<usize>,
} }
fn split_row( fn split_row(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
args: Arguments,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let name_span = call.head; let name_span = call.head;
let separator: Spanned<String> = call.req(engine_state, stack, 0)?; let regex = if args.has_regex {
let regex = if call.has_flag(engine_state, stack, "regex")? { Regex::new(&args.separator.item)
Regex::new(&separator.item)
} else { } else {
let escaped = regex::escape(&separator.item); let escaped = regex::escape(&args.separator.item);
Regex::new(&escaped) Regex::new(&escaped)
} }
.map_err(|e| ShellError::GenericError { .map_err(|e| ShellError::GenericError {
error: "Error with regular expression".into(), error: "Error with regular expression".into(),
msg: e.to_string(), msg: e.to_string(),
span: Some(separator.span), span: Some(args.separator.span),
help: None, help: None,
inner: vec![], inner: vec![],
})?; })?;
let max_split: Option<usize> = call.get_flag(engine_state, stack, "number")?;
input.flat_map( input.flat_map(
move |x| split_row_helper(&x, &regex, max_split, name_span), move |x| split_row_helper(&x, &regex, args.max_split, name_span),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
) )
} }

View File

@ -1,4 +1,4 @@
use crate::grapheme_flags; use crate::{grapheme_flags, grapheme_flags_const};
use fancy_regex::Regex; use fancy_regex::Regex;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
@ -96,6 +96,10 @@ impl Command for SubCommand {
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -103,40 +107,76 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
split_words(engine_state, stack, call, input) let word_length: Option<usize> = call.get_flag(engine_state, stack, "min-word-length")?;
let has_grapheme = call.has_flag(engine_state, stack, "grapheme-clusters")?;
let has_utf8 = call.has_flag(engine_state, stack, "utf-8-bytes")?;
let graphemes = grapheme_flags(engine_state, stack, call)?;
let args = Arguments {
word_length,
has_grapheme,
has_utf8,
graphemes,
};
split_words(engine_state, call, input, args)
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let word_length: Option<usize> = call.get_flag_const(working_set, "min-word-length")?;
let has_grapheme = call.has_flag_const(working_set, "grapheme-clusters")?;
let has_utf8 = call.has_flag_const(working_set, "utf-8-bytes")?;
let graphemes = grapheme_flags_const(working_set, call)?;
let args = Arguments {
word_length,
has_grapheme,
has_utf8,
graphemes,
};
split_words(working_set.permanent(), call, input, args)
}
}
struct Arguments {
word_length: Option<usize>,
has_grapheme: bool,
has_utf8: bool,
graphemes: bool,
} }
fn split_words( fn split_words(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
args: Arguments,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let span = call.head; let span = call.head;
// let ignore_hyphenated = call.has_flag(engine_state, stack, "ignore-hyphenated")?; // let ignore_hyphenated = call.has_flag(engine_state, stack, "ignore-hyphenated")?;
// let ignore_apostrophes = call.has_flag(engine_state, stack, "ignore-apostrophes")?; // let ignore_apostrophes = call.has_flag(engine_state, stack, "ignore-apostrophes")?;
// let ignore_punctuation = call.has_flag(engine_state, stack, "ignore-punctuation")?; // let ignore_punctuation = call.has_flag(engine_state, stack, "ignore-punctuation")?;
let word_length: Option<usize> = call.get_flag(engine_state, stack, "min-word-length")?;
if word_length.is_none() { if args.word_length.is_none() {
if call.has_flag(engine_state, stack, "grapheme-clusters")? { if args.has_grapheme {
return Err(ShellError::IncompatibleParametersSingle { return Err(ShellError::IncompatibleParametersSingle {
msg: "--grapheme-clusters (-g) requires --min-word-length (-l)".to_string(), msg: "--grapheme-clusters (-g) requires --min-word-length (-l)".to_string(),
span, span,
}); });
} }
if call.has_flag(engine_state, stack, "utf-8-bytes")? { if args.has_utf8 {
return Err(ShellError::IncompatibleParametersSingle { return Err(ShellError::IncompatibleParametersSingle {
msg: "--utf-8-bytes (-b) requires --min-word-length (-l)".to_string(), msg: "--utf-8-bytes (-b) requires --min-word-length (-l)".to_string(),
span, span,
}); });
} }
} }
let graphemes = grapheme_flags(engine_state, stack, call)?;
input.map( input.map(
move |x| split_words_helper(&x, word_length, span, graphemes), move |x| split_words_helper(&x, args.word_length, span, args.graphemes),
engine_state.ctrlc.clone(), engine_state.ctrlc.clone(),
) )
} }

View File

@ -36,6 +36,10 @@ impl Command for SubCommand {
vec!["convert", "style", "caps", "upper"] vec!["convert", "style", "caps", "upper"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -43,7 +47,18 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
operate(engine_state, call, input, column_paths)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
operate(working_set.permanent(), call, input, column_paths)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -72,12 +87,11 @@ impl Command for SubCommand {
fn operate( fn operate(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
column_paths: Vec<CellPath>,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map( input.map(
move |v| { move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {

View File

@ -36,6 +36,10 @@ impl Command for SubCommand {
vec!["lower case", "lowercase"] vec!["lower case", "lowercase"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -43,7 +47,18 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
operate(engine_state, call, input, column_paths)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
operate(working_set.permanent(), call, input, column_paths)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -80,12 +95,11 @@ impl Command for SubCommand {
fn operate( fn operate(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
column_paths: Vec<CellPath>,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map( input.map(
move |v| { move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {

View File

@ -36,6 +36,10 @@ impl Command for SubCommand {
vec!["uppercase", "upper case"] vec!["uppercase", "upper case"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -43,7 +47,18 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
operate(engine_state, stack, call, input) let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
operate(engine_state, call, input, column_paths)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let column_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
operate(working_set.permanent(), call, input, column_paths)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -57,12 +72,11 @@ impl Command for SubCommand {
fn operate( fn operate(
engine_state: &EngineState, engine_state: &EngineState,
stack: &mut Stack,
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
column_paths: Vec<CellPath>,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
let column_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
input.map( input.map(
move |v| { move |v| {
if column_paths.is_empty() { if column_paths.is_empty() {

View File

@ -52,6 +52,10 @@ impl Command for SubCommand {
vec!["substring", "match", "find", "search"] vec!["substring", "match", "find", "search"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -84,6 +88,43 @@ impl Command for SubCommand {
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
if call.has_flag_const(working_set, "not")? {
nu_protocol::report_error_new(
working_set.permanent(),
&ShellError::GenericError {
error: "Deprecated option".into(),
msg: "`str contains --not {string}` is deprecated and will be removed in 0.95."
.into(),
span: Some(call.head),
help: Some("Please use the `not` operator instead.".into()),
inner: vec![],
},
);
}
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let args = Arguments {
substring: call.req_const::<String>(working_set, 0)?,
cell_paths,
case_insensitive: call.has_flag_const(working_set, "ignore-case")?,
not_contain: call.has_flag_const(working_set, "not")?,
};
operate(
action,
args,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -1,6 +1,6 @@
use nu_cmd_base::input_handler::{operate, CmdArgument}; use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::levenshtein_distance; use nu_protocol::{engine::StateWorkingSet, levenshtein_distance};
#[derive(Clone)] #[derive(Clone)]
pub struct SubCommand; pub struct SubCommand;
@ -49,6 +49,10 @@ impl Command for SubCommand {
vec!["edit", "levenshtein"] vec!["edit", "levenshtein"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -66,6 +70,28 @@ impl Command for SubCommand {
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let compare_string: String = call.req_const(working_set, 0)?;
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let args = Arguments {
compare_string,
cell_paths,
};
operate(
action,
args,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![Example { vec![Example {
description: "get the edit distance between two strings", description: "get the edit distance between two strings",

View File

@ -50,6 +50,10 @@ impl Command for SubCommand {
vec!["suffix", "match", "find", "search"] vec!["suffix", "match", "find", "search"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -67,6 +71,28 @@ impl Command for SubCommand {
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let args = Arguments {
substring: call.req_const::<String>(working_set, 0)?,
cell_paths,
case_insensitive: call.has_flag_const(working_set, "ignore-case")?,
};
operate(
action,
args,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -179,6 +179,10 @@ impl Command for SubCommand {
] ]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -186,32 +190,51 @@ impl Command for SubCommand {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let span = call.head;
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: span });
}
let is_path = call.has_flag(engine_state, stack, "path")?; let is_path = call.has_flag(engine_state, stack, "path")?;
input.map( run(call, input, is_path, engine_state)
move |v| {
let value_span = v.span();
match v.coerce_into_string() {
Ok(s) => {
let contents = if is_path { s.replace('\\', "\\\\") } else { s };
str_expand(&contents, span, value_span)
}
Err(_) => Value::error(
ShellError::PipelineMismatch {
exp_input_type: "string".into(),
dst_span: span,
src_span: value_span,
},
span,
),
}
},
engine_state.ctrlc.clone(),
)
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let is_path = call.has_flag_const(working_set, "path")?;
run(call, input, is_path, working_set.permanent())
}
}
fn run(
call: &Call,
input: PipelineData,
is_path: bool,
engine_state: &EngineState,
) -> Result<PipelineData, ShellError> {
let span = call.head;
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: span });
}
input.map(
move |v| {
let value_span = v.span();
match v.coerce_into_string() {
Ok(s) => {
let contents = if is_path { s.replace('\\', "\\\\") } else { s };
str_expand(&contents, span, value_span)
}
Err(_) => Value::error(
ShellError::PipelineMismatch {
exp_input_type: "string".into(),
dst_span: span,
src_span: value_span,
},
span,
),
}
},
engine_state.ctrlc.clone(),
)
} }
fn str_expand(contents: &str, span: Span, value_span: Span) -> Value { fn str_expand(contents: &str, span: Span, value_span: Span) -> Value {

View File

@ -1,10 +1,10 @@
use crate::grapheme_flags; use crate::{grapheme_flags, grapheme_flags_const};
use nu_cmd_base::{ use nu_cmd_base::{
input_handler::{operate, CmdArgument}, input_handler::{operate, CmdArgument},
util, util,
}; };
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::Range; use nu_protocol::{engine::StateWorkingSet, Range};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
struct Arguments { struct Arguments {
@ -72,6 +72,10 @@ impl Command for SubCommand {
vec!["match", "find", "search"] vec!["match", "find", "search"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -92,6 +96,31 @@ impl Command for SubCommand {
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let substring: Spanned<String> = call.req_const(working_set, 0)?;
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let args = Arguments {
substring: substring.item,
range: call.get_flag_const(working_set, "range")?,
end: call.has_flag_const(working_set, "end")?,
cell_paths,
graphemes: grapheme_flags_const(working_set, call)?,
};
operate(
action,
args,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -1,4 +1,5 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use std::io::Write; use std::io::Write;
#[derive(Clone)] #[derive(Clone)]
@ -32,6 +33,10 @@ impl Command for StrJoin {
vec!["collect", "concatenate"] vec!["collect", "concatenate"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -40,41 +45,17 @@ impl Command for StrJoin {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let separator: Option<String> = call.opt(engine_state, stack, 0)?; let separator: Option<String> = call.opt(engine_state, stack, 0)?;
run(engine_state, call, input, separator)
}
let config = engine_state.config.clone(); fn run_const(
&self,
let span = call.head; working_set: &StateWorkingSet,
call: &Call,
let metadata = input.metadata(); input: PipelineData,
let mut iter = input.into_iter(); ) -> Result<PipelineData, ShellError> {
let mut first = true; let separator: Option<String> = call.opt_const(working_set, 0)?;
run(working_set.permanent(), call, input, separator)
let output = ByteStream::from_fn(span, None, ByteStreamType::String, move |buffer| {
// Write each input to the buffer
if let Some(value) = iter.next() {
// Write the separator if this is not the first
if first {
first = false;
} else if let Some(separator) = &separator {
write!(buffer, "{}", separator)?;
}
match value {
Value::Error { error, .. } => {
return Err(*error);
}
// Hmm, not sure what we actually want.
// `to_expanded_string` formats dates as human readable which feels funny.
Value::Date { val, .. } => write!(buffer, "{val:?}")?,
value => write!(buffer, "{}", value.to_expanded_string("\n", &config))?,
}
Ok(true)
} else {
Ok(false)
}
});
Ok(PipelineData::ByteStream(output, metadata))
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -93,6 +74,48 @@ impl Command for StrJoin {
} }
} }
fn run(
engine_state: &EngineState,
call: &Call,
input: PipelineData,
separator: Option<String>,
) -> Result<PipelineData, ShellError> {
let config = engine_state.config.clone();
let span = call.head;
let metadata = input.metadata();
let mut iter = input.into_iter();
let mut first = true;
let output = ByteStream::from_fn(span, None, ByteStreamType::String, move |buffer| {
// Write each input to the buffer
if let Some(value) = iter.next() {
// Write the separator if this is not the first
if first {
first = false;
} else if let Some(separator) = &separator {
write!(buffer, "{}", separator)?;
}
match value {
Value::Error { error, .. } => {
return Err(*error);
}
// Hmm, not sure what we actually want.
// `to_expanded_string` formats dates as human readable which feels funny.
Value::Date { val, .. } => write!(buffer, "{val:?}")?,
value => write!(buffer, "{}", value.to_expanded_string("\n", &config))?,
}
Ok(true)
} else {
Ok(false)
}
});
Ok(PipelineData::ByteStream(output, metadata))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -1,7 +1,7 @@
use crate::{grapheme_flags, grapheme_flags_const}; use crate::{grapheme_flags, grapheme_flags_const};
use nu_cmd_base::input_handler::{operate, CmdArgument}; use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::engine::StateWorkingSet;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
struct Arguments { struct Arguments {

View File

@ -73,6 +73,10 @@ impl Command for SubCommand {
vec!["search", "shift", "switch", "regex"] vec!["search", "shift", "switch", "regex"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -101,6 +105,39 @@ impl Command for SubCommand {
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let find: Spanned<String> = call.req_const(working_set, 0)?;
let replace: Spanned<String> = call.req_const(working_set, 1)?;
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 2)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let literal_replace = call.has_flag_const(working_set, "no-expand")?;
let no_regex = !call.has_flag_const(working_set, "regex")?
&& !call.has_flag_const(working_set, "multiline")?;
let multiline = call.has_flag_const(working_set, "multiline")?;
let args = Arguments {
all: call.has_flag_const(working_set, "all")?,
find,
replace,
cell_paths,
literal_replace,
no_regex,
multiline,
};
operate(
action,
args,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -37,6 +37,10 @@ impl Command for SubCommand {
vec!["convert", "inverse", "flip"] vec!["convert", "inverse", "flip"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -49,6 +53,23 @@ impl Command for SubCommand {
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(
action,
args,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -51,6 +51,10 @@ impl Command for SubCommand {
vec!["prefix", "match", "find", "search"] vec!["prefix", "match", "find", "search"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -69,6 +73,29 @@ impl Command for SubCommand {
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let substring: Spanned<String> = call.req_const(working_set, 0)?;
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let args = Arguments {
substring: substring.item,
cell_paths,
case_insensitive: call.has_flag_const(working_set, "ignore-case")?,
};
operate(
action,
args,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -1,5 +1,6 @@
use fancy_regex::Regex; use fancy_regex::Regex;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::{fmt, str}; use std::{fmt, str};
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
@ -29,6 +30,10 @@ impl Command for SubCommand {
vec!["count", "word", "character", "unicode", "wc"] vec!["count", "word", "character", "unicode", "wc"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -39,6 +44,15 @@ impl Command for SubCommand {
stats(engine_state, call, input) stats(engine_state, call, input)
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
stats(working_set.permanent(), call, input)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -1,10 +1,10 @@
use crate::grapheme_flags; use crate::{grapheme_flags, grapheme_flags_const};
use nu_cmd_base::{ use nu_cmd_base::{
input_handler::{operate, CmdArgument}, input_handler::{operate, CmdArgument},
util, util,
}; };
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::Range; use nu_protocol::{engine::StateWorkingSet, Range};
use std::cmp::Ordering; use std::cmp::Ordering;
use unicode_segmentation::UnicodeSegmentation; use unicode_segmentation::UnicodeSegmentation;
@ -77,6 +77,10 @@ impl Command for SubCommand {
vec!["slice"] vec!["slice"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -103,6 +107,37 @@ impl Command for SubCommand {
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.ctrlc.clone())
} }
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let range: Range = call.req_const(working_set, 0)?;
let indexes = match util::process_range(&range) {
Ok(idxs) => idxs.into(),
Err(processing_error) => {
return Err(processing_error("could not perform substring", call.head))
}
};
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 1)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let args = Arguments {
indexes,
cell_paths,
graphemes: grapheme_flags_const(working_set, call)?,
};
operate(
action,
args,
input,
call.head,
working_set.permanent().ctrlc.clone(),
)
}
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
vec![ vec![
Example { Example {

View File

@ -71,6 +71,10 @@ impl Command for SubCommand {
vec!["whitespace", "strip", "lstrip", "rstrip"] vec!["whitespace", "strip", "lstrip", "rstrip"]
} }
fn is_const(&self) -> bool {
true
}
fn run( fn run(
&self, &self,
engine_state: &EngineState, engine_state: &EngineState,
@ -79,44 +83,37 @@ impl Command for SubCommand {
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let character = call.get_flag::<Spanned<String>>(engine_state, stack, "char")?; let character = call.get_flag::<Spanned<String>>(engine_state, stack, "char")?;
let to_trim = match character.as_ref() {
Some(v) => {
if v.item.chars().count() > 1 {
return Err(ShellError::GenericError {
error: "Trim only works with single character".into(),
msg: "needs single character".into(),
span: Some(v.span),
help: None,
inner: vec![],
});
}
v.item.chars().next()
}
None => None,
};
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?; let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let mode = match cell_paths {
None => ActionMode::Global,
Some(_) => ActionMode::Local,
};
let left = call.has_flag(engine_state, stack, "left")?; let left = call.has_flag(engine_state, stack, "left")?;
let right = call.has_flag(engine_state, stack, "right")?; let right = call.has_flag(engine_state, stack, "right")?;
let trim_side = match (left, right) { run(
(true, true) => TrimSide::Both, character,
(true, false) => TrimSide::Left,
(false, true) => TrimSide::Right,
(false, false) => TrimSide::Both,
};
let args = Arguments {
to_trim,
trim_side,
cell_paths, cell_paths,
mode, (left, right),
}; call,
operate(action, args, input, call.head, engine_state.ctrlc.clone()) input,
engine_state,
)
}
fn run_const(
&self,
working_set: &StateWorkingSet,
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let character = call.get_flag_const::<Spanned<String>>(working_set, "char")?;
let cell_paths: Vec<CellPath> = call.rest_const(working_set, 0)?;
let left = call.has_flag_const(working_set, "left")?;
let right = call.has_flag_const(working_set, "right")?;
run(
character,
cell_paths,
(left, right),
call,
input,
working_set.permanent(),
)
} }
fn examples(&self) -> Vec<Example> { fn examples(&self) -> Vec<Example> {
@ -150,6 +147,52 @@ impl Command for SubCommand {
} }
} }
fn run(
character: Option<Spanned<String>>,
cell_paths: Vec<CellPath>,
(left, right): (bool, bool),
call: &Call,
input: PipelineData,
engine_state: &EngineState,
) -> Result<PipelineData, ShellError> {
let to_trim = match character.as_ref() {
Some(v) => {
if v.item.chars().count() > 1 {
return Err(ShellError::GenericError {
error: "Trim only works with single character".into(),
msg: "needs single character".into(),
span: Some(v.span),
help: None,
inner: vec![],
});
}
v.item.chars().next()
}
None => None,
};
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
let mode = match cell_paths {
None => ActionMode::Global,
Some(_) => ActionMode::Local,
};
let trim_side = match (left, right) {
(true, true) => TrimSide::Both,
(true, false) => TrimSide::Left,
(false, true) => TrimSide::Right,
(false, false) => TrimSide::Both,
};
let args = Arguments {
to_trim,
trim_side,
cell_paths,
mode,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
}
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ActionMode { pub enum ActionMode {
Local, Local,

View File

@ -218,14 +218,16 @@ impl Command for External {
/// Removes surrounding quotes from a string. Doesn't remove quotes from raw /// Removes surrounding quotes from a string. Doesn't remove quotes from raw
/// strings. Returns the original string if it doesn't have matching quotes. /// strings. Returns the original string if it doesn't have matching quotes.
fn remove_quotes(s: &str) -> &str { fn remove_quotes(s: &str) -> Cow<'_, str> {
let quoted_by_double_quotes = s.len() >= 2 && s.starts_with('"') && s.ends_with('"'); let quoted_by_double_quotes = s.len() >= 2 && s.starts_with('"') && s.ends_with('"');
let quoted_by_single_quotes = s.len() >= 2 && s.starts_with('\'') && s.ends_with('\''); let quoted_by_single_quotes = s.len() >= 2 && s.starts_with('\'') && s.ends_with('\'');
let quoted_by_backticks = s.len() >= 2 && s.starts_with('`') && s.ends_with('`'); let quoted_by_backticks = s.len() >= 2 && s.starts_with('`') && s.ends_with('`');
if quoted_by_double_quotes || quoted_by_single_quotes || quoted_by_backticks { if quoted_by_double_quotes {
&s[1..s.len() - 1] Cow::Owned(s[1..s.len() - 1].to_string().replace(r#"\""#, "\""))
} else if quoted_by_single_quotes || quoted_by_backticks {
Cow::Borrowed(&s[1..s.len() - 1])
} else { } else {
s Cow::Borrowed(s)
} }
} }
@ -400,10 +402,6 @@ fn expand_glob(
/// with double quotes, single quotes, or backticks. Only removes the outermost /// with double quotes, single quotes, or backticks. Only removes the outermost
/// pair of quotes after the equal sign. /// pair of quotes after the equal sign.
fn remove_inner_quotes(arg: &str) -> Cow<'_, str> { fn remove_inner_quotes(arg: &str) -> Cow<'_, str> {
// Check that `arg` is a long option.
if !arg.starts_with("--") {
return Cow::Borrowed(arg);
}
// Split `arg` on the first `=`. // Split `arg` on the first `=`.
let Some((option, value)) = arg.split_once('=') else { let Some((option, value)) = arg.split_once('=') else {
return Cow::Borrowed(arg); return Cow::Borrowed(arg);
@ -660,17 +658,13 @@ mod test {
assert_eq!(remove_quotes(r#"`foo '"' bar`"#), r#"foo '"' bar"#); assert_eq!(remove_quotes(r#"`foo '"' bar`"#), r#"foo '"' bar"#);
assert_eq!(remove_quotes(r#"'foo' bar"#), r#"'foo' bar"#); assert_eq!(remove_quotes(r#"'foo' bar"#), r#"'foo' bar"#);
assert_eq!(remove_quotes(r#"r#'foo'#"#), r#"r#'foo'#"#); assert_eq!(remove_quotes(r#"r#'foo'#"#), r#"r#'foo'#"#);
assert_eq!(remove_quotes(r#""foo\" bar""#), r#"foo" bar"#);
} }
#[test] #[test]
fn test_eval_argument() { fn test_eval_argument() {
fn expression(expr: Expr) -> Expression { fn expression(expr: Expr) -> Expression {
Expression { Expression::new_unknown(expr, Span::unknown(), Type::Any)
expr,
span: Span::unknown(),
ty: Type::Any,
custom_completion: None,
}
} }
fn eval(expr: Expr, spread: bool) -> Result<Vec<String>, ShellError> { fn eval(expr: Expr, spread: bool) -> Result<Vec<String>, ShellError> {
@ -763,6 +757,18 @@ mod test {
let actual = remove_inner_quotes(r#"--option "value""#); let actual = remove_inner_quotes(r#"--option "value""#);
let expected = r#"--option "value""#; let expected = r#"--option "value""#;
assert_eq!(actual, expected); assert_eq!(actual, expected);
let actual = remove_inner_quotes(r#"-option="value""#);
let expected = r#"-option=value"#;
assert_eq!(actual, expected);
let actual = remove_inner_quotes(r#"option="value""#);
let expected = r#"option=value"#;
assert_eq!(actual, expected);
let actual = remove_inner_quotes(r#"option="v\"value""#);
let expected = r#"option=v"value"#;
assert_eq!(actual, expected);
} }
#[test] #[test]

View File

@ -63,8 +63,57 @@ fn into_filesize_negative_filesize() {
} }
#[test] #[test]
fn into_negative_filesize() { fn into_filesize_negative_str_filesize() {
let actual = nu!("'-3kib' | into filesize");
assert!(actual.out.contains("-3.0 KiB"));
}
#[test]
fn into_filesize_wrong_negative_str_filesize() {
let actual = nu!("'--3kib' | into filesize");
assert!(actual.err.contains("can't convert string to filesize"));
}
#[test]
fn into_filesize_negative_str() {
let actual = nu!("'-1' | into filesize"); let actual = nu!("'-1' | into filesize");
assert!(actual.out.contains("-1 B")); assert!(actual.out.contains("-1 B"));
} }
#[test]
fn into_filesize_wrong_negative_str() {
let actual = nu!("'--1' | into filesize");
assert!(actual.err.contains("can't convert string to filesize"));
}
#[test]
fn into_filesize_positive_str_filesize() {
let actual = nu!("'+1Kib' | into filesize");
assert!(actual.out.contains("1.0 KiB"));
}
#[test]
fn into_filesize_wrong_positive_str_filesize() {
let actual = nu!("'++1Kib' | into filesize");
assert!(actual.err.contains("can't convert string to filesize"));
}
#[test]
fn into_filesize_positive_str() {
let actual = nu!("'+1' | into filesize");
assert!(actual.out.contains("1 B"));
}
#[test]
fn into_filesize_wrong_positive_str() {
let actual = nu!("'++1' | into filesize");
assert!(actual.err.contains("can't convert string to filesize"));
}

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@ edition = "2021"
license = "MIT" license = "MIT"
name = "nu-derive-value" name = "nu-derive-value"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-derive-value" repository = "https://github.com/nushell/nushell/tree/main/crates/nu-derive-value"
version = "0.94.2" version = "0.94.3"
[lib] [lib]
proc-macro = true proc-macro = true

View File

@ -5,16 +5,16 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-engine"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-engine" name = "nu-engine"
version = "0.94.2" version = "0.94.3"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-protocol = { path = "../nu-protocol", features = ["plugin"], version = "0.94.2" } nu-protocol = { path = "../nu-protocol", features = ["plugin"], version = "0.94.3" }
nu-path = { path = "../nu-path", version = "0.94.2" } nu-path = { path = "../nu-path", version = "0.94.3" }
nu-glob = { path = "../nu-glob", version = "0.94.2" } nu-glob = { path = "../nu-glob", version = "0.94.3" }
nu-utils = { path = "../nu-utils", version = "0.94.2" } nu-utils = { path = "../nu-utils", version = "0.94.3" }
[features] [features]
plugin = [] plugin = []

View File

@ -1,7 +1,7 @@
pub use crate::CallExt; pub use crate::CallExt;
pub use nu_protocol::{ pub use nu_protocol::{
ast::{Call, CellPath}, ast::{Call, CellPath},
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack, StateWorkingSet},
record, ByteStream, ByteStreamType, Category, ErrSpan, Example, IntoInterruptiblePipelineData, record, ByteStream, ByteStreamType, Category, ErrSpan, Example, IntoInterruptiblePipelineData,
IntoPipelineData, IntoSpanned, PipelineData, Record, ShellError, Signature, Span, Spanned, IntoPipelineData, IntoSpanned, PipelineData, Record, ShellError, Signature, Span, Spanned,
SyntaxShape, Type, Value, SyntaxShape, Type, Value,

View File

@ -2,9 +2,9 @@ use crate::eval_call;
use nu_protocol::{ use nu_protocol::{
ast::{Argument, Call, Expr, Expression, RecordItem}, ast::{Argument, Call, Expr, Expression, RecordItem},
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{Command, EngineState, Stack}, engine::{Command, EngineState, Stack, UNKNOWN_SPAN_ID},
record, Category, Example, IntoPipelineData, PipelineData, Signature, Span, SyntaxShape, Type, record, Category, Example, IntoPipelineData, PipelineData, Signature, Span, SpanId,
Value, SyntaxShape, Type, Value,
}; };
use std::{collections::HashMap, fmt::Write}; use std::{collections::HashMap, fmt::Write};
@ -339,8 +339,9 @@ fn get_ansi_color_for_component_or_default(
if let Some(color) = &engine_state.get_config().color_config.get(theme_component) { if let Some(color) = &engine_state.get_config().color_config.get(theme_component) {
let caller_stack = &mut Stack::new().capture(); let caller_stack = &mut Stack::new().capture();
let span = Span::unknown(); let span = Span::unknown();
let span_id = UNKNOWN_SPAN_ID;
let argument_opt = get_argument_for_color_value(engine_state, color, span); let argument_opt = get_argument_for_color_value(engine_state, color, span, span_id);
// Call ansi command using argument // Call ansi command using argument
if let Some(argument) = argument_opt { if let Some(argument) = argument_opt {
@ -371,6 +372,7 @@ fn get_argument_for_color_value(
engine_state: &EngineState, engine_state: &EngineState,
color: &&Value, color: &&Value,
span: Span, span: Span,
span_id: SpanId,
) -> Option<Argument> { ) -> Option<Argument> {
match color { match color {
Value::Record { val, .. } => { Value::Record { val, .. } => {
@ -378,43 +380,43 @@ fn get_argument_for_color_value(
.iter() .iter()
.map(|(k, v)| { .map(|(k, v)| {
RecordItem::Pair( RecordItem::Pair(
Expression { Expression::new_existing(
expr: Expr::String(k.clone()), Expr::String(k.clone()),
span, span,
ty: Type::String, span_id,
custom_completion: None, Type::String,
}, ),
Expression { Expression::new_existing(
expr: Expr::String( Expr::String(
v.clone().to_expanded_string("", engine_state.get_config()), v.clone().to_expanded_string("", engine_state.get_config()),
), ),
span, span,
ty: Type::String, span_id,
custom_completion: None, Type::String,
}, ),
) )
}) })
.collect(); .collect();
Some(Argument::Positional(Expression { Some(Argument::Positional(Expression::new_existing(
span: Span::unknown(), Expr::Record(record_exp),
ty: Type::Record( Span::unknown(),
UNKNOWN_SPAN_ID,
Type::Record(
[ [
("fg".to_string(), Type::String), ("fg".to_string(), Type::String),
("attr".to_string(), Type::String), ("attr".to_string(), Type::String),
] ]
.into(), .into(),
), ),
expr: Expr::Record(record_exp), )))
custom_completion: None,
}))
} }
Value::String { val, .. } => Some(Argument::Positional(Expression { Value::String { val, .. } => Some(Argument::Positional(Expression::new_existing(
span: Span::unknown(), Expr::String(val.clone()),
ty: Type::String, Span::unknown(),
expr: Expr::String(val.clone()), UNKNOWN_SPAN_ID,
custom_completion: None, Type::String,
})), ))),
_ => None, _ => None,
} }
} }

View File

@ -5,21 +5,21 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-explore"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-explore" name = "nu-explore"
version = "0.94.2" version = "0.94.3"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
nu-parser = { path = "../nu-parser", version = "0.94.2" } nu-parser = { path = "../nu-parser", version = "0.94.3" }
nu-color-config = { path = "../nu-color-config", version = "0.94.2" } nu-color-config = { path = "../nu-color-config", version = "0.94.3" }
nu-engine = { path = "../nu-engine", version = "0.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-table = { path = "../nu-table", version = "0.94.2" } nu-table = { path = "../nu-table", version = "0.94.3" }
nu-json = { path = "../nu-json", version = "0.94.2" } nu-json = { path = "../nu-json", version = "0.94.3" }
nu-utils = { path = "../nu-utils", version = "0.94.2" } nu-utils = { path = "../nu-utils", version = "0.94.3" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.94.2" } nu-pretty-hex = { path = "../nu-pretty-hex", version = "0.94.3" }
anyhow = { workspace = true } anyhow = { workspace = true }
log = { workspace = true } log = { workspace = true }
@ -32,4 +32,4 @@ ansi-str = { workspace = true }
unicode-width = { workspace = true } unicode-width = { workspace = true }
lscolors = { workspace = true, default-features = false, features = [ lscolors = { workspace = true, default-features = false, features = [
"nu-ansi-term", "nu-ansi-term",
] } ] }

View File

@ -4,7 +4,6 @@ use crate::{
views::{Orientation, RecordView}, views::{Orientation, RecordView},
}; };
use anyhow::Result; use anyhow::Result;
use nu_ansi_term::Style;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
Value, Value,
@ -19,12 +18,6 @@ pub struct TableCmd {
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
struct TableSettings { struct TableSettings {
orientation: Option<Orientation>, orientation: Option<Orientation>,
split_line_s: Option<Style>,
selected_cell_s: Option<Style>,
selected_row_s: Option<Style>,
selected_column_s: Option<Style>,
padding_column_left: Option<usize>,
padding_column_right: Option<usize>,
turn_on_cursor_mode: bool, turn_on_cursor_mode: bool,
} }
@ -64,8 +57,6 @@ impl ViewCommand for TableCmd {
let mut view = RecordView::new(columns, data); let mut view = RecordView::new(columns, data);
// todo: use setup instead ????
if is_record { if is_record {
view.set_orientation_current(Orientation::Left); view.set_orientation_current(Orientation::Left);
} }
@ -74,32 +65,6 @@ impl ViewCommand for TableCmd {
view.set_orientation_current(o); view.set_orientation_current(o);
} }
if let Some(style) = self.settings.selected_cell_s {
view.set_style_selected_cell(style);
}
if let Some(style) = self.settings.selected_column_s {
view.set_style_selected_column(style);
}
if let Some(style) = self.settings.selected_row_s {
view.set_style_selected_row(style);
}
if let Some(style) = self.settings.split_line_s {
view.set_style_split_line(style);
}
if let Some(p) = self.settings.padding_column_left {
let c = view.get_padding_column();
view.set_padding_column((p, c.1))
}
if let Some(p) = self.settings.padding_column_right {
let c = view.get_padding_column();
view.set_padding_column((c.0, p))
}
if self.settings.turn_on_cursor_mode { if self.settings.turn_on_cursor_mode {
view.set_cursor_mode(); view.set_cursor_mode();
} }

View File

@ -1,5 +1,5 @@
use super::ViewCommand; use super::ViewCommand;
use crate::views::InteractiveView; use crate::views::TryView;
use anyhow::Result; use anyhow::Result;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
@ -22,7 +22,7 @@ impl TryCmd {
} }
impl ViewCommand for TryCmd { impl ViewCommand for TryCmd {
type View = InteractiveView<'static>; type View = TryView<'static>;
fn name(&self) -> &'static str { fn name(&self) -> &'static str {
Self::NAME Self::NAME
@ -45,7 +45,7 @@ impl ViewCommand for TryCmd {
value: Option<Value>, value: Option<Value>,
) -> Result<Self::View> { ) -> Result<Self::View> {
let value = value.unwrap_or_default(); let value = value.unwrap_or_default();
let mut view = InteractiveView::new(value); let mut view = TryView::new(value);
view.init(self.command.clone()); view.init(self.command.clone());
view.try_run(engine_state, stack)?; view.try_run(engine_state, stack)?;

View File

@ -1,13 +1,12 @@
use crate::{ use crate::{
run_pager, run_pager,
util::{create_lscolors, create_map, map_into_value}, util::{create_lscolors, create_map},
PagerConfig, StyleConfig, PagerConfig,
}; };
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use nu_color_config::{get_color_map, StyleComputer}; use nu_color_config::{get_color_map, StyleComputer};
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::Config;
use std::collections::HashMap;
/// A `less` like program to render a [`Value`] as a table. /// A `less` like program to render a [`Value`] as a table.
#[derive(Clone)] #[derive(Clone)]
@ -68,19 +67,21 @@ impl Command for Explore {
let nu_config = engine_state.get_config(); let nu_config = engine_state.get_config();
let style_computer = StyleComputer::from_config(engine_state, stack); let style_computer = StyleComputer::from_config(engine_state, stack);
let mut config = nu_config.explore.clone(); let mut explore_config = ExploreConfig::from_nu_config(nu_config);
include_nu_config(&mut config, &style_computer); explore_config.table.show_header = show_head;
update_config(&mut config, show_index, show_head); explore_config.table.show_index = show_index;
prepare_default_config(&mut config); explore_config.table.separator_style = lookup_color(&style_computer, "separator");
let style = style_from_config(&config);
let lscolors = create_lscolors(engine_state, stack); let lscolors = create_lscolors(engine_state, stack);
let mut config = PagerConfig::new(nu_config, &style_computer, &lscolors, config); let config = PagerConfig::new(
config.style = style; nu_config,
config.peek_value = peek_value; &explore_config,
config.tail = tail; &style_computer,
&lscolors,
peek_value,
tail,
);
let result = run_pager(engine_state, &mut stack.clone(), ctrlc, input, config); let result = run_pager(engine_state, &mut stack.clone(), ctrlc, input, config);
@ -134,153 +135,118 @@ impl Command for Explore {
} }
} }
fn update_config(config: &mut HashMap<String, Value>, show_index: bool, show_head: bool) { #[derive(Debug, Clone)]
let mut hm = config.get("table").and_then(create_map).unwrap_or_default(); pub struct ExploreConfig {
if show_index { pub table: TableConfig,
insert_bool(&mut hm, "show_index", show_index); pub selected_cell: Style,
} pub status_info: Style,
pub status_success: Style,
if show_head { pub status_warn: Style,
insert_bool(&mut hm, "show_head", show_head); pub status_error: Style,
} pub status_bar_background: Style,
pub status_bar_text: Style,
config.insert(String::from("table"), map_into_value(hm)); pub cmd_bar_text: Style,
pub cmd_bar_background: Style,
pub highlight: Style,
/// if true, the explore view will immediately try to run the command as it is typed
pub try_reactive: bool,
} }
fn style_from_config(config: &HashMap<String, Value>) -> StyleConfig { impl Default for ExploreConfig {
let mut style = StyleConfig::default(); fn default() -> Self {
Self {
let colors = get_color_map(config); table: TableConfig::default(),
selected_cell: color(None, Some(Color::LightBlue)),
if let Some(s) = colors.get("status_bar_text") { status_info: color(None, None),
style.status_bar_text = *s; status_success: color(Some(Color::Black), Some(Color::Green)),
} status_warn: color(None, None),
status_error: color(Some(Color::White), Some(Color::Red)),
if let Some(s) = colors.get("status_bar_background") { status_bar_background: color(
style.status_bar_background = *s; Some(Color::Rgb(29, 31, 33)),
} Some(Color::Rgb(196, 201, 198)),
),
if let Some(s) = colors.get("command_bar_text") { status_bar_text: color(None, None),
style.cmd_bar_text = *s; cmd_bar_text: color(Some(Color::Rgb(196, 201, 198)), None),
} cmd_bar_background: color(None, None),
highlight: color(Some(Color::Black), Some(Color::Yellow)),
if let Some(s) = colors.get("command_bar_background") { try_reactive: false,
style.cmd_bar_background = *s;
}
if let Some(hm) = config.get("status").and_then(create_map) {
let colors = get_color_map(&hm);
if let Some(s) = colors.get("info") {
style.status_info = *s;
}
if let Some(s) = colors.get("success") {
style.status_success = *s;
}
if let Some(s) = colors.get("warn") {
style.status_warn = *s;
}
if let Some(s) = colors.get("error") {
style.status_error = *s;
} }
} }
style
} }
impl ExploreConfig {
/// take the default explore config and update it with relevant values from the nu config
pub fn from_nu_config(config: &Config) -> Self {
let mut ret = Self::default();
fn prepare_default_config(config: &mut HashMap<String, Value>) { ret.table.column_padding_left = config.table_indent.left;
const STATUS_BAR: Style = color( ret.table.column_padding_right = config.table_indent.right;
Some(Color::Rgb(29, 31, 33)),
Some(Color::Rgb(196, 201, 198)),
);
const INPUT_BAR: Style = color(Some(Color::Rgb(196, 201, 198)), None);
const HIGHLIGHT: Style = color(Some(Color::Black), Some(Color::Yellow)); let explore_cfg_hash_map = config.explore.clone();
let colors = get_color_map(&explore_cfg_hash_map);
const STATUS_ERROR: Style = color(Some(Color::White), Some(Color::Red)); if let Some(s) = colors.get("status_bar_text") {
const STATUS_INFO: Style = color(None, None); ret.status_bar_text = *s;
const STATUS_SUCCESS: Style = color(Some(Color::Black), Some(Color::Green)); }
const STATUS_WARN: Style = color(None, None);
const TABLE_SPLIT_LINE: Style = color(Some(Color::Rgb(64, 64, 64)), None); if let Some(s) = colors.get("status_bar_background") {
const TABLE_SELECT_CELL: Style = color(None, None); ret.status_bar_background = *s;
const TABLE_SELECT_ROW: Style = color(None, None); }
const TABLE_SELECT_COLUMN: Style = color(None, None);
const HEXDUMP_INDEX: Style = color(Some(Color::Cyan), None); if let Some(s) = colors.get("command_bar_text") {
const HEXDUMP_SEGMENT: Style = color(Some(Color::Cyan), None).bold(); ret.cmd_bar_text = *s;
const HEXDUMP_SEGMENT_ZERO: Style = color(Some(Color::Purple), None).bold(); }
const HEXDUMP_SEGMENT_UNKNOWN: Style = color(Some(Color::Green), None).bold();
const HEXDUMP_ASCII: Style = color(Some(Color::Cyan), None).bold();
const HEXDUMP_ASCII_ZERO: Style = color(Some(Color::Purple), None).bold();
const HEXDUMP_ASCII_UNKNOWN: Style = color(Some(Color::Green), None).bold();
insert_style(config, "status_bar_background", STATUS_BAR); if let Some(s) = colors.get("command_bar_background") {
insert_style(config, "command_bar_text", INPUT_BAR); ret.cmd_bar_background = *s;
insert_style(config, "highlight", HIGHLIGHT); }
// because how config works we need to parse a string into Value::Record if let Some(s) = colors.get("command_bar_background") {
ret.cmd_bar_background = *s;
}
{ if let Some(s) = colors.get("selected_cell") {
let mut hm = config ret.selected_cell = *s;
.get("status") }
.and_then(parse_hash_map)
.unwrap_or_default();
insert_style(&mut hm, "info", STATUS_INFO); if let Some(hm) = explore_cfg_hash_map.get("status").and_then(create_map) {
insert_style(&mut hm, "success", STATUS_SUCCESS); let colors = get_color_map(&hm);
insert_style(&mut hm, "warn", STATUS_WARN);
insert_style(&mut hm, "error", STATUS_ERROR);
config.insert(String::from("status"), map_into_value(hm)); if let Some(s) = colors.get("info") {
} ret.status_info = *s;
}
{ if let Some(s) = colors.get("success") {
let mut hm = config ret.status_success = *s;
.get("table") }
.and_then(parse_hash_map)
.unwrap_or_default();
insert_style(&mut hm, "split_line", TABLE_SPLIT_LINE); if let Some(s) = colors.get("warn") {
insert_style(&mut hm, "selected_cell", TABLE_SELECT_CELL); ret.status_warn = *s;
insert_style(&mut hm, "selected_row", TABLE_SELECT_ROW); }
insert_style(&mut hm, "selected_column", TABLE_SELECT_COLUMN);
config.insert(String::from("table"), map_into_value(hm)); if let Some(s) = colors.get("error") {
} ret.status_error = *s;
}
}
{ if let Some(hm) = explore_cfg_hash_map.get("try").and_then(create_map) {
let mut hm = config if let Some(reactive) = hm.get("reactive") {
.get("hex-dump") if let Ok(b) = reactive.as_bool() {
.and_then(create_map) ret.try_reactive = b;
.unwrap_or_default(); }
}
}
insert_style(&mut hm, "color_index", HEXDUMP_INDEX); ret
insert_style(&mut hm, "color_segment", HEXDUMP_SEGMENT);
insert_style(&mut hm, "color_segment_zero", HEXDUMP_SEGMENT_ZERO);
insert_style(&mut hm, "color_segment_unknown", HEXDUMP_SEGMENT_UNKNOWN);
insert_style(&mut hm, "color_ascii", HEXDUMP_ASCII);
insert_style(&mut hm, "color_ascii_zero", HEXDUMP_ASCII_ZERO);
insert_style(&mut hm, "color_ascii_unknown", HEXDUMP_ASCII_UNKNOWN);
insert_int(&mut hm, "segment_size", 2);
insert_int(&mut hm, "count_segments", 8);
insert_bool(&mut hm, "split", true);
config.insert(String::from("hex-dump"), map_into_value(hm));
} }
} }
fn parse_hash_map(value: &Value) -> Option<HashMap<String, Value>> { #[derive(Debug, Default, Clone, Copy)]
value.as_record().ok().map(|val| { pub struct TableConfig {
val.iter() pub separator_style: Style,
.map(|(col, val)| (col.clone(), val.clone())) pub show_index: bool,
.collect::<HashMap<_, _>>() pub show_header: bool,
}) pub column_padding_left: usize,
pub column_padding_right: usize,
} }
const fn color(foreground: Option<Color>, background: Option<Color>) -> Style { const fn color(foreground: Option<Color>, background: Option<Color>) -> Style {
@ -299,49 +265,6 @@ const fn color(foreground: Option<Color>, background: Option<Color>) -> Style {
} }
} }
fn insert_style(map: &mut HashMap<String, Value>, key: &str, value: Style) {
if map.contains_key(key) {
return;
}
if value == Style::default() {
return;
}
let value = nu_color_config::NuStyle::from(value);
if let Ok(val) = nu_json::to_string_raw(&value) {
map.insert(String::from(key), Value::string(val, Span::unknown()));
}
}
fn insert_bool(map: &mut HashMap<String, Value>, key: &str, value: bool) {
if map.contains_key(key) {
return;
}
map.insert(String::from(key), Value::bool(value, Span::unknown()));
}
fn insert_int(map: &mut HashMap<String, Value>, key: &str, value: i64) {
if map.contains_key(key) {
return;
}
map.insert(String::from(key), Value::int(value, Span::unknown()));
}
fn include_nu_config(config: &mut HashMap<String, Value>, style_computer: &StyleComputer) {
let line_color = lookup_color(style_computer, "separator");
if line_color != nu_ansi_term::Style::default() {
let mut map = config
.get("table")
.and_then(parse_hash_map)
.unwrap_or_default();
insert_style(&mut map, "split_line", line_color);
config.insert(String::from("table"), map_into_value(map));
}
}
fn lookup_color(style_computer: &StyleComputer, key: &str) -> nu_ansi_term::Style { fn lookup_color(style_computer: &StyleComputer, key: &str) -> nu_ansi_term::Style {
style_computer.compute(key, &Value::nothing(Span::unknown())) style_computer.compute(key, &Value::nothing(Span::unknown()))
} }

View File

@ -15,13 +15,13 @@ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
PipelineData, Value, PipelineData, Value,
}; };
use pager::{Page, Pager, PagerConfig, StyleConfig}; use pager::{Page, Pager, PagerConfig};
use registry::CommandRegistry; use registry::CommandRegistry;
use terminal_size::{Height, Width}; use terminal_size::{Height, Width};
use views::{BinaryView, Orientation, Preview, RecordView}; use views::{BinaryView, Orientation, Preview, RecordView};
mod util { mod util {
pub use super::nu_common::{create_lscolors, create_map, map_into_value}; pub use super::nu_common::{create_lscolors, create_map};
} }
fn run_pager( fn run_pager(

View File

@ -4,7 +4,6 @@ use nu_ansi_term::{Color, Style};
use nu_engine::env_to_string; use nu_engine::env_to_string;
use nu_protocol::engine::{EngineState, Stack}; use nu_protocol::engine::{EngineState, Stack};
use nu_utils::get_ls_colors; use nu_utils::get_ls_colors;
use std::fs::symlink_metadata;
pub fn create_lscolors(engine_state: &EngineState, stack: &Stack) -> LsColors { pub fn create_lscolors(engine_state: &EngineState, stack: &Stack) -> LsColors {
let colors = stack let colors = stack
@ -14,6 +13,7 @@ pub fn create_lscolors(engine_state: &EngineState, stack: &Stack) -> LsColors {
get_ls_colors(colors) get_ls_colors(colors)
} }
/// Colorizes any columns named "name" in the table using LS_COLORS
pub fn lscolorize(header: &[String], data: &mut [Vec<NuText>], lscolors: &LsColors) { pub fn lscolorize(header: &[String], data: &mut [Vec<NuText>], lscolors: &LsColors) {
for (col, col_name) in header.iter().enumerate() { for (col, col_name) in header.iter().enumerate() {
if col_name != "name" { if col_name != "name" {
@ -33,14 +33,7 @@ pub fn lscolorize(header: &[String], data: &mut [Vec<NuText>], lscolors: &LsColo
fn get_path_style(path: &str, ls_colors: &LsColors) -> Option<Style> { fn get_path_style(path: &str, ls_colors: &LsColors) -> Option<Style> {
let stripped_path = nu_utils::strip_ansi_unlikely(path); let stripped_path = nu_utils::strip_ansi_unlikely(path);
let style = ls_colors.style_for_str(stripped_path.as_ref());
let style = match symlink_metadata(stripped_path.as_ref()) {
Ok(metadata) => {
ls_colors.style_for_path_with_metadata(stripped_path.as_ref(), Some(&metadata))
}
Err(_) => ls_colors.style_for_path(stripped_path.as_ref()),
};
style.map(lsstyle_to_nu_style) style.map(lsstyle_to_nu_style)
} }

View File

@ -18,7 +18,7 @@ pub use command::run_command_with_value;
pub use lscolor::{create_lscolors, lscolorize}; pub use lscolor::{create_lscolors, lscolorize};
pub use string::{string_width, truncate_str}; pub use string::{string_width, truncate_str};
pub use table::try_build_table; pub use table::try_build_table;
pub use value::{collect_input, collect_pipeline, create_map, map_into_value}; pub use value::{collect_input, collect_pipeline, create_map};
pub fn has_simple_value(data: &[Vec<Value>]) -> Option<&Value> { pub fn has_simple_value(data: &[Vec<Value>]) -> Option<&Value> {
if data.len() == 1 if data.len() == 1

View File

@ -176,10 +176,6 @@ pub fn create_map(value: &Value) -> Option<HashMap<String, Value>> {
) )
} }
pub fn map_into_value(hm: HashMap<String, Value>) -> Value {
Value::record(hm.into_iter().collect(), NuSpan::unknown())
}
fn unknown_error_value() -> Value { fn unknown_error_value() -> Value {
Value::string(String::from(""), NuSpan::unknown()) Value::string(String::from(""), NuSpan::unknown())
} }

View File

@ -10,9 +10,9 @@ use self::{
}; };
use super::views::{Layout, View}; use super::views::{Layout, View};
use crate::{ use crate::{
nu_common::{CtrlC, NuColor, NuConfig, NuSpan, NuStyle}, explore::ExploreConfig,
nu_common::{CtrlC, NuColor, NuConfig, NuStyle},
registry::{Command, CommandRegistry}, registry::{Command, CommandRegistry},
util::map_into_value,
views::{util::nu_style_to_tui, ViewConfig}, views::{util::nu_style_to_tui, ViewConfig},
}; };
use anyhow::Result; use anyhow::Result;
@ -26,15 +26,14 @@ use crossterm::{
}; };
use events::UIEvents; use events::UIEvents;
use lscolors::LsColors; use lscolors::LsColors;
use nu_color_config::{lookup_ansi_color_style, StyleComputer}; use nu_color_config::StyleComputer;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
Record, Value, Value,
}; };
use ratatui::{backend::CrosstermBackend, layout::Rect, widgets::Block}; use ratatui::{backend::CrosstermBackend, layout::Rect, widgets::Block};
use std::{ use std::{
cmp::min, cmp::min,
collections::HashMap,
io::{self, Stdout}, io::{self, Stdout},
result, result,
sync::atomic::Ordering, sync::atomic::Ordering,
@ -42,7 +41,6 @@ use std::{
pub type Frame<'a> = ratatui::Frame<'a>; pub type Frame<'a> = ratatui::Frame<'a>;
pub type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>; pub type Terminal = ratatui::Terminal<CrosstermBackend<Stdout>>;
pub type ConfigMap = HashMap<String, Value>;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Pager<'a> { pub struct Pager<'a> {
@ -73,19 +71,6 @@ struct CommandBuf {
cmd_exec_info: Option<String>, cmd_exec_info: Option<String>,
} }
#[derive(Debug, Default, Clone)]
pub struct StyleConfig {
pub status_info: NuStyle,
pub status_success: NuStyle,
pub status_warn: NuStyle,
pub status_error: NuStyle,
pub status_bar_background: NuStyle,
pub status_bar_text: NuStyle,
pub cmd_bar_text: NuStyle,
pub cmd_bar_background: NuStyle,
pub highlight: NuStyle,
}
impl<'a> Pager<'a> { impl<'a> Pager<'a> {
pub fn new(config: PagerConfig<'a>) -> Self { pub fn new(config: PagerConfig<'a>) -> Self {
Self { Self {
@ -100,27 +85,6 @@ impl<'a> Pager<'a> {
self.message = Some(text.into()); self.message = Some(text.into());
} }
pub fn set_config(&mut self, path: &[String], value: Value) -> bool {
let path = path.iter().map(|s| s.as_str()).collect::<Vec<_>>();
match &path[..] {
["status_bar_text"] => value_as_style(&mut self.config.style.status_bar_text, &value),
["status_bar_background"] => {
value_as_style(&mut self.config.style.status_bar_background, &value)
}
["command_bar_text"] => value_as_style(&mut self.config.style.cmd_bar_text, &value),
["command_bar_background"] => {
value_as_style(&mut self.config.style.cmd_bar_background, &value)
}
["highlight"] => value_as_style(&mut self.config.style.highlight, &value),
["status", "info"] => value_as_style(&mut self.config.style.status_info, &value),
["status", "success"] => value_as_style(&mut self.config.style.status_success, &value),
["status", "warn"] => value_as_style(&mut self.config.style.status_warn, &value),
["status", "error"] => value_as_style(&mut self.config.style.status_error, &value),
path => set_config(&mut self.config.config, path, value),
}
}
pub fn run( pub fn run(
&mut self, &mut self,
engine_state: &EngineState, engine_state: &EngineState,
@ -132,8 +96,8 @@ impl<'a> Pager<'a> {
if let Some(page) = &mut view { if let Some(page) = &mut view {
page.view.setup(ViewConfig::new( page.view.setup(ViewConfig::new(
self.config.nu_config, self.config.nu_config,
self.config.explore_config,
self.config.style_computer, self.config.style_computer,
&self.config.config,
self.config.lscolors, self.config.lscolors,
)) ))
} }
@ -153,10 +117,9 @@ pub enum Transition {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PagerConfig<'a> { pub struct PagerConfig<'a> {
pub nu_config: &'a NuConfig, pub nu_config: &'a NuConfig,
pub explore_config: &'a ExploreConfig,
pub style_computer: &'a StyleComputer<'a>, pub style_computer: &'a StyleComputer<'a>,
pub lscolors: &'a LsColors, pub lscolors: &'a LsColors,
pub config: ConfigMap,
pub style: StyleConfig,
// If true, when quitting output the value of the cell the cursor was on // If true, when quitting output the value of the cell the cursor was on
pub peek_value: bool, pub peek_value: bool,
pub tail: bool, pub tail: bool,
@ -165,18 +128,19 @@ pub struct PagerConfig<'a> {
impl<'a> PagerConfig<'a> { impl<'a> PagerConfig<'a> {
pub fn new( pub fn new(
nu_config: &'a NuConfig, nu_config: &'a NuConfig,
explore_config: &'a ExploreConfig,
style_computer: &'a StyleComputer, style_computer: &'a StyleComputer,
lscolors: &'a LsColors, lscolors: &'a LsColors,
config: ConfigMap, peek_value: bool,
tail: bool,
) -> Self { ) -> Self {
Self { Self {
nu_config, nu_config,
explore_config,
style_computer, style_computer,
config,
lscolors, lscolors,
peek_value: false, peek_value,
tail: false, tail,
style: StyleConfig::default(),
} }
} }
} }
@ -401,7 +365,7 @@ fn draw_frame(
draw_info(f, pager, info); draw_info(f, pager, info);
highlight_search_results(f, pager, layout, pager.config.style.highlight); highlight_search_results(f, pager, layout, pager.config.explore_config.highlight);
set_cursor_cmd_bar(f, area, pager); set_cursor_cmd_bar(f, area, pager);
} }
@ -411,19 +375,24 @@ fn draw_info(f: &mut Frame, pager: &mut Pager<'_>, info: ViewInfo) {
if let Some(report) = info.status { if let Some(report) = info.status {
let last_2nd_line = area.bottom().saturating_sub(2); let last_2nd_line = area.bottom().saturating_sub(2);
let area = Rect::new(area.left(), last_2nd_line, area.width, 1); let area = Rect::new(area.left(), last_2nd_line, area.width, 1);
render_status_bar(f, area, report, &pager.config.style); render_status_bar(f, area, report, pager.config.explore_config);
} }
{ {
let last_line = area.bottom().saturating_sub(1); let last_line = area.bottom().saturating_sub(1);
let area = Rect::new(area.left(), last_line, area.width, 1); let area = Rect::new(area.left(), last_line, area.width, 1);
render_cmd_bar(f, area, pager, info.report, &pager.config.style); render_cmd_bar(f, area, pager, info.report, pager.config.explore_config);
} }
} }
fn create_view_config<'a>(pager: &'a Pager<'_>) -> ViewConfig<'a> { fn create_view_config<'a>(pager: &'a Pager<'_>) -> ViewConfig<'a> {
let cfg = &pager.config; let cfg = &pager.config;
ViewConfig::new(cfg.nu_config, cfg.style_computer, &cfg.config, cfg.lscolors) ViewConfig::new(
cfg.nu_config,
cfg.explore_config,
cfg.style_computer,
cfg.lscolors,
)
} }
fn pager_run_command( fn pager_run_command(
@ -463,16 +432,7 @@ fn run_command(
let value = view_stack.curr_view.as_mut().and_then(|p| p.view.exit()); let value = view_stack.curr_view.as_mut().and_then(|p| p.view.exit());
let transition = command.react(engine_state, stack, pager, value)?; let transition = command.react(engine_state, stack, pager, value)?;
match transition { match transition {
Transition::Ok => { Transition::Ok => Ok(CmdResult::new(false, false, String::new())),
// so we basically allow a change of a config inside a command,
// and cause of this we wanna update all of our views.
//
// THOUGH: MOST LIKELY IT WON'T BE CHANGED AND WE DO A WASTE.......
update_view_stack_setup(view_stack, &pager.config);
Ok(CmdResult::new(false, false, String::new()))
}
Transition::Exit => Ok(CmdResult::new(true, false, String::new())), Transition::Exit => Ok(CmdResult::new(true, false, String::new())),
Transition::Cmd { .. } => todo!("not used so far"), Transition::Cmd { .. } => todo!("not used so far"),
} }
@ -487,7 +447,8 @@ fn run_command(
} }
} }
update_view_setup(&mut new_view, &pager.config); setup_view(&mut new_view, &pager.config);
view_stack.curr_view = Some(Page::raw(new_view, stackable)); view_stack.curr_view = Some(Page::raw(new_view, stackable));
Ok(CmdResult::new(false, true, cmd.name().to_owned())) Ok(CmdResult::new(false, true, cmd.name().to_owned()))
@ -495,18 +456,13 @@ fn run_command(
} }
} }
fn update_view_stack_setup(view_stack: &mut ViewStack, cfg: &PagerConfig<'_>) { fn setup_view(view: &mut Box<dyn View>, cfg: &PagerConfig<'_>) {
if let Some(page) = view_stack.curr_view.as_mut() { let cfg = ViewConfig::new(
update_view_setup(&mut page.view, cfg); cfg.nu_config,
} cfg.explore_config,
cfg.style_computer,
for page in &mut view_stack.stack { cfg.lscolors,
update_view_setup(&mut page.view, cfg); );
}
}
fn update_view_setup(view: &mut Box<dyn View>, cfg: &PagerConfig<'_>) {
let cfg = ViewConfig::new(cfg.nu_config, cfg.style_computer, &cfg.config, cfg.lscolors);
view.setup(cfg); view.setup(cfg);
} }
@ -528,7 +484,7 @@ fn set_cursor_cmd_bar(f: &mut Frame, area: Rect, pager: &Pager) {
} }
} }
fn render_status_bar(f: &mut Frame, area: Rect, report: Report, theme: &StyleConfig) { fn render_status_bar(f: &mut Frame, area: Rect, report: Report, theme: &ExploreConfig) {
let msg_style = report_msg_style(&report, theme, theme.status_bar_text); let msg_style = report_msg_style(&report, theme, theme.status_bar_text);
let mut status_bar = create_status_bar(report); let mut status_bar = create_status_bar(report);
status_bar.set_background_style(theme.status_bar_background); status_bar.set_background_style(theme.status_bar_background);
@ -549,11 +505,11 @@ fn create_status_bar(report: Report) -> StatusBar {
) )
} }
fn report_msg_style(report: &Report, theme: &StyleConfig, style: NuStyle) -> NuStyle { fn report_msg_style(report: &Report, config: &ExploreConfig, style: NuStyle) -> NuStyle {
if matches!(report.level, Severity::Info) { if matches!(report.level, Severity::Info) {
style style
} else { } else {
report_level_style(report.level, theme) report_level_style(report.level, config)
} }
} }
@ -562,15 +518,15 @@ fn render_cmd_bar(
area: Rect, area: Rect,
pager: &Pager, pager: &Pager,
report: Option<Report>, report: Option<Report>,
theme: &StyleConfig, config: &ExploreConfig,
) { ) {
if let Some(report) = report { if let Some(report) = report {
let style = report_msg_style(&report, theme, theme.cmd_bar_text); let style = report_msg_style(&report, config, config.cmd_bar_text);
let bar = CommandBar::new( let bar = CommandBar::new(
&report.message, &report.message,
&report.context1, &report.context1,
style, style,
theme.cmd_bar_background, config.cmd_bar_background,
); );
f.render_widget(bar, area); f.render_widget(bar, area);
@ -578,16 +534,16 @@ fn render_cmd_bar(
} }
if pager.cmd_buf.is_cmd_input { if pager.cmd_buf.is_cmd_input {
render_cmd_bar_cmd(f, area, pager, theme); render_cmd_bar_cmd(f, area, pager, config);
return; return;
} }
if pager.search_buf.is_search_input || !pager.search_buf.buf_cmd_input.is_empty() { if pager.search_buf.is_search_input || !pager.search_buf.buf_cmd_input.is_empty() {
render_cmd_bar_search(f, area, pager, theme); render_cmd_bar_search(f, area, pager, config);
} }
} }
fn render_cmd_bar_search(f: &mut Frame, area: Rect, pager: &Pager<'_>, theme: &StyleConfig) { fn render_cmd_bar_search(f: &mut Frame, area: Rect, pager: &Pager<'_>, config: &ExploreConfig) {
if pager.search_buf.search_results.is_empty() && !pager.search_buf.is_search_input { if pager.search_buf.search_results.is_empty() && !pager.search_buf.is_search_input {
let message = format!("Pattern not found: {}", pager.search_buf.buf_cmd_input); let message = format!("Pattern not found: {}", pager.search_buf.buf_cmd_input);
let style = NuStyle { let style = NuStyle {
@ -596,7 +552,7 @@ fn render_cmd_bar_search(f: &mut Frame, area: Rect, pager: &Pager<'_>, theme: &S
..Default::default() ..Default::default()
}; };
let bar = CommandBar::new(&message, "", style, theme.cmd_bar_background); let bar = CommandBar::new(&message, "", style, config.cmd_bar_background);
f.render_widget(bar, area); f.render_widget(bar, area);
return; return;
} }
@ -615,11 +571,11 @@ fn render_cmd_bar_search(f: &mut Frame, area: Rect, pager: &Pager<'_>, theme: &S
format!("[{index}/{total}]") format!("[{index}/{total}]")
}; };
let bar = CommandBar::new(&text, &info, theme.cmd_bar_text, theme.cmd_bar_background); let bar = CommandBar::new(&text, &info, config.cmd_bar_text, config.cmd_bar_background);
f.render_widget(bar, area); f.render_widget(bar, area);
} }
fn render_cmd_bar_cmd(f: &mut Frame, area: Rect, pager: &Pager, theme: &StyleConfig) { fn render_cmd_bar_cmd(f: &mut Frame, area: Rect, pager: &Pager, config: &ExploreConfig) {
let mut input = pager.cmd_buf.buf_cmd2.as_str(); let mut input = pager.cmd_buf.buf_cmd2.as_str();
if input.len() > area.width as usize + 1 { if input.len() > area.width as usize + 1 {
// in such case we take last max_cmd_len chars // in such case we take last max_cmd_len chars
@ -637,7 +593,7 @@ fn render_cmd_bar_cmd(f: &mut Frame, area: Rect, pager: &Pager, theme: &StyleCon
let prefix = ':'; let prefix = ':';
let text = format!("{prefix}{input}"); let text = format!("{prefix}{input}");
let bar = CommandBar::new(&text, "", theme.cmd_bar_text, theme.cmd_bar_background); let bar = CommandBar::new(&text, "", config.cmd_bar_text, config.cmd_bar_background);
f.render_widget(bar, area); f.render_widget(bar, area);
} }
@ -998,72 +954,12 @@ fn cmd_input_key_event(buf: &mut CommandBuf, key: &KeyEvent) -> bool {
} }
} }
fn value_as_style(style: &mut nu_ansi_term::Style, value: &Value) -> bool { fn report_level_style(level: Severity, config: &ExploreConfig) -> NuStyle {
match value.coerce_str() {
Ok(s) => {
*style = lookup_ansi_color_style(&s);
true
}
Err(_) => false,
}
}
fn set_config(hm: &mut HashMap<String, Value>, path: &[&str], value: Value) -> bool {
if path.is_empty() {
return false;
}
let key = path[0];
if !hm.contains_key(key) {
hm.insert(
key.to_string(),
Value::record(Record::new(), NuSpan::unknown()),
);
}
let val = hm.get_mut(key).expect("...");
if path.len() == 1 {
*val = value;
return true;
}
match val {
Value::Record { val: record, .. } => {
if path.len() == 2 {
let key = path[1];
record.to_mut().insert(key, value);
} else {
let mut hm2: HashMap<String, Value> = HashMap::new();
for (k, v) in record.iter() {
hm2.insert(k.to_string(), v.clone());
}
let result = set_config(&mut hm2, &path[1..], value);
if !result {
*val = map_into_value(hm2);
}
if path.len() == 2 {
} else {
return false;
}
}
true
}
_ => false,
}
}
fn report_level_style(level: Severity, theme: &StyleConfig) -> NuStyle {
match level { match level {
Severity::Info => theme.status_info, Severity::Info => config.status_info,
Severity::Success => theme.status_success, Severity::Success => config.status_success,
Severity::Warn => theme.status_warn, Severity::Warn => config.status_warn,
Severity::Err => theme.status_error, Severity::Err => config.status_error,
} }
} }

View File

@ -3,7 +3,6 @@
mod binary_widget; mod binary_widget;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use nu_color_config::get_color_map;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
Value, Value,
@ -11,22 +10,26 @@ use nu_protocol::{
use ratatui::layout::Rect; use ratatui::layout::Rect;
use crate::{ use crate::{
explore::ExploreConfig,
nu_common::NuText, nu_common::NuText,
pager::{ pager::{
report::{Report, Severity}, report::{Report, Severity},
ConfigMap, Frame, Transition, ViewInfo, Frame, Transition, ViewInfo,
}, },
util::create_map, views::cursor::Position,
}; };
use self::binary_widget::{BinarySettings, BinaryStyle, BinaryWidget}; use self::binary_widget::{BinarySettings, BinaryStyle, BinaryWidget};
use super::{cursor::XYCursor, Layout, View, ViewConfig}; use super::{cursor::WindowCursor2D, Layout, View, ViewConfig};
/// An interactive view that displays binary data in a hex dump format.
/// Not finished; many aspects are still WIP.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct BinaryView { pub struct BinaryView {
data: Vec<u8>, data: Vec<u8>,
cursor: XYCursor, // HACK: we are only using the vertical dimension of the cursor, should we use a plain old WindowCursor?
cursor: WindowCursor2D,
settings: Settings, settings: Settings,
} }
@ -40,7 +43,7 @@ impl BinaryView {
pub fn new(data: Vec<u8>) -> Self { pub fn new(data: Vec<u8>) -> Self {
Self { Self {
data, data,
cursor: XYCursor::default(), cursor: WindowCursor2D::default(),
settings: Settings::default(), settings: Settings::default(),
} }
} }
@ -86,21 +89,17 @@ impl View for BinaryView {
} }
fn setup(&mut self, cfg: ViewConfig<'_>) { fn setup(&mut self, cfg: ViewConfig<'_>) {
let hm = match cfg.config.get("hex-dump").and_then(create_map) { self.settings = settings_from_config(cfg.explore_config);
Some(hm) => hm,
None => return,
};
self.settings = settings_from_config(&hm);
let count_rows = let count_rows =
BinaryWidget::new(&self.data, self.settings.opts, Default::default()).count_lines(); BinaryWidget::new(&self.data, self.settings.opts, Default::default()).count_lines();
self.cursor = XYCursor::new(count_rows, 0); // TODO: refactor View so setup() is fallible and we don't have to panic here
self.cursor = WindowCursor2D::new(count_rows, 1).expect("Failed to create XYCursor");
} }
} }
fn create_binary_widget(v: &BinaryView) -> BinaryWidget<'_> { fn create_binary_widget(v: &BinaryView) -> BinaryWidget<'_> {
let start_line = v.cursor.row_starts_at(); let start_line = v.cursor.window_origin().row;
let count_elements = let count_elements =
BinaryWidget::new(&[], v.settings.opts, Default::default()).count_elements(); BinaryWidget::new(&[], v.settings.opts, Default::default()).count_elements();
let index = start_line * count_elements; let index = start_line * count_elements;
@ -179,31 +178,19 @@ fn handle_event_view_mode(view: &mut BinaryView, key: &KeyEvent) -> Option<Trans
} }
} }
fn settings_from_config(config: &ConfigMap) -> Settings { fn settings_from_config(config: &ExploreConfig) -> Settings {
let colors = get_color_map(config); // Most of this is hardcoded for now, add it to the config later if needed
Settings { Settings {
opts: BinarySettings::new( opts: BinarySettings::new(2, 8),
config_get_usize(config, "segment_size", 2),
config_get_usize(config, "count_segments", 8),
),
style: BinaryStyle::new( style: BinaryStyle::new(
colors.get("color_index").cloned(), None,
config_get_usize(config, "column_padding_left", 1) as u16, config.table.column_padding_left as u16,
config_get_usize(config, "column_padding_right", 1) as u16, config.table.column_padding_right as u16,
), ),
} }
} }
fn config_get_usize(config: &ConfigMap, key: &str, default: usize) -> usize { fn create_report(cursor: WindowCursor2D) -> Report {
config
.get(key)
.and_then(|v| v.coerce_str().ok())
.and_then(|s| s.parse::<usize>().ok())
.unwrap_or(default)
}
fn create_report(cursor: XYCursor) -> Report {
let covered_percent = report_row_position(cursor); let covered_percent = report_row_position(cursor);
let cursor = report_cursor_position(cursor); let cursor = report_cursor_position(cursor);
let mode = report_mode_name(); let mode = report_mode_name();
@ -216,8 +203,8 @@ fn report_mode_name() -> String {
String::from("VIEW") String::from("VIEW")
} }
fn report_row_position(cursor: XYCursor) -> String { fn report_row_position(cursor: WindowCursor2D) -> String {
if cursor.row_starts_at() == 0 { if cursor.window_origin().row == 0 {
return String::from("Top"); return String::from("Top");
} }
@ -233,10 +220,9 @@ fn report_row_position(cursor: XYCursor) -> String {
} }
} }
fn report_cursor_position(cursor: XYCursor) -> String { fn report_cursor_position(cursor: WindowCursor2D) -> String {
let rows_seen = cursor.row_starts_at(); let Position { row, column } = cursor.window_origin();
let columns_seen = cursor.column_starts_at(); format!("{row},{column}")
format!("{rows_seen},{columns_seen}")
} }
fn get_percentage(value: usize, max: usize) -> usize { fn get_percentage(value: usize, max: usize) -> usize {

View File

@ -1,71 +1,124 @@
mod windowcursor; mod window_cursor;
mod xycursor; mod window_cursor_2d;
pub use windowcursor::WindowCursor; use anyhow::{bail, Result};
pub use xycursor::XYCursor; pub use window_cursor::WindowCursor;
pub use window_cursor_2d::{Position, WindowCursor2D};
/// A 1-dimensional cursor to track a position from 0 to N
///
/// Say we have a cursor with size=9, at position 3:
/// 0 1 2 3 4 5 6 7 8 9
/// | | | C | | | | | |
///
/// After moving forward by 2 steps:
/// 0 1 2 3 4 5 6 7 8 9
/// | | | | | C | | | |
///
/// After moving backward by 6 steps (clamped to 0):
/// 0 1 2 3 4 5 6 7 8 9
/// C | | | | | | | | |
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Cursor { struct Cursor {
index: usize, /// The current position of the cursor
limit: usize, position: usize,
/// The number of distinct positions the cursor can be at
size: usize,
} }
impl Cursor { impl Cursor {
pub fn new(limit: usize) -> Self { /// Constructor to create a new Cursor
Self { index: 0, limit } pub fn new(size: usize) -> Self {
} // In theory we should not be able to create a cursor with size 0, but in practice
// it's easier to allow that for empty lists etc. instead of propagating errors
#[allow(dead_code)] Cursor { position: 0, size }
pub fn get_index(&self) -> usize {
self.index
}
pub fn cap(&self) -> usize {
self.limit - self.index
}
pub fn set(&mut self, i: usize) -> bool {
if i >= self.limit {
return false;
}
self.index = i;
true
}
pub fn limit(&mut self, i: usize) -> bool {
if self.index > self.limit {
self.index = self.limit.saturating_sub(1);
return false;
}
self.limit = i;
if self.index >= self.limit {
self.index = self.limit.saturating_sub(1);
}
true
} }
/// The max position the cursor can be at
pub fn end(&self) -> usize { pub fn end(&self) -> usize {
self.limit self.size - 1
} }
pub fn next(&mut self, i: usize) -> bool { /// Set the position to a specific value within the bounds [0, end]
if self.index + i == self.limit { pub fn set_position(&mut self, pos: usize) {
return false; if pos <= self.end() {
self.position = pos;
} else {
// Clamp the position to end if out of bounds
self.position = self.end();
} }
self.index += i;
true
} }
pub fn prev(&mut self, i: usize) -> bool { /// Set the size of the cursor. The position is clamped if it exceeds the new size
if self.index < i { pub fn set_size(&mut self, size: usize) -> Result<()> {
return false; if size == 0 {
bail!("Size cannot be zero");
} }
self.size = size;
if self.position > self.end() {
self.position = self.end();
}
Ok(())
}
self.index -= i; /// Move the cursor forward by a specified number of steps
true pub fn move_forward(&mut self, steps: usize) {
if self.position + steps <= self.end() {
self.position += steps;
} else {
self.position = self.end();
}
}
/// Move the cursor backward by a specified number of steps
pub fn move_backward(&mut self, steps: usize) {
if self.position >= steps {
self.position -= steps;
} else {
self.position = 0;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cursor_set_position() {
// from 0 to 9
let mut cursor = Cursor::new(10);
cursor.set_position(5);
assert_eq!(cursor.position, 5);
cursor.set_position(15);
assert_eq!(cursor.position, 9);
}
#[test]
fn test_cursor_move_forward() {
// from 0 to 9
let mut cursor = Cursor::new(10);
assert_eq!(cursor.position, 0);
cursor.move_forward(3);
assert_eq!(cursor.position, 3);
cursor.move_forward(10);
assert_eq!(cursor.position, 9);
}
#[test]
fn test_cursor_move_backward() {
// from 0 to 9
let mut cursor = Cursor::new(10);
cursor.move_backward(3);
assert_eq!(cursor.position, 0);
cursor.move_forward(5);
assert_eq!(cursor.position, 5);
cursor.move_backward(3);
assert_eq!(cursor.position, 2);
cursor.move_backward(3);
assert_eq!(cursor.position, 0);
} }
} }

View File

@ -0,0 +1,146 @@
use std::cmp::min;
use super::Cursor;
use anyhow::{bail, Ok, Result};
/// WindowCursor provides a mechanism to navigate through a 1-dimensional range
/// using a smaller movable window within the view.
///
/// View: The larger context or total allowable range for navigation.
/// Window: The smaller, focused subset of the view.
///
/// Example:
/// ```plaintext
/// 1. Initial view of size 20 with a window of size 5. The absolute cursor position starts at 0.
/// View :
/// |--------------------|
/// Window :
/// |X====|
///
/// 2. After advancing the window by 3, the absolute cursor position becomes 3.
/// View :
/// |--------------------|
/// Window :
/// |X====|
///
/// 3. After advancing the cursor inside the window by 2, the absolute cursor position becomes 5.
/// View :
/// |--------------------|
/// Window :
/// |==X==|
/// ```
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct WindowCursor {
view: Cursor,
window: Cursor,
}
impl WindowCursor {
pub fn new(view_size: usize, window_size: usize) -> Result<Self> {
if window_size > view_size {
bail!("Window size cannot be greater than view size");
}
Ok(Self {
view: Cursor::new(view_size),
window: Cursor::new(window_size),
})
}
pub fn absolute_position(&self) -> usize {
self.window_starts_at() + self.window.position
}
pub fn window_relative_position(&self) -> usize {
self.window.position
}
pub fn window_starts_at(&self) -> usize {
self.view.position
}
pub fn window_size(&self) -> usize {
self.window.size
}
pub fn end(&self) -> usize {
self.view.end()
}
pub fn set_window_start_position(&mut self, i: usize) {
self.view.set_position(i)
}
pub fn move_window_to_end(&mut self) {
self.view.set_position(self.end() - self.window_size() + 1);
}
pub fn set_window_size(&mut self, new_size: usize) -> Result<()> {
if new_size > self.view.size {
// TODO: should we return an error here or clamp? the Ok is copying existing behavior
return Ok(());
}
self.window.set_size(new_size)?;
Ok(())
}
pub fn next_n(&mut self, n: usize) {
for _ in 0..n {
self.next();
}
}
pub fn next(&mut self) {
if self.absolute_position() >= self.end() {
return;
}
if self.window_relative_position() == self.window.end() {
self.view.move_forward(1);
} else {
self.window.move_forward(1);
}
}
pub fn next_window(&mut self) {
self.move_cursor_to_end_of_window();
// move window forward by window size, or less if that would send it off the end of the view
let window_end = self.window_starts_at() + self.window_size() - 1;
let distance_from_window_end_to_view_end = self.end() - window_end;
self.view.move_forward(min(
distance_from_window_end_to_view_end,
self.window_size(),
));
}
pub fn prev_n(&mut self, n: usize) {
for _ in 0..n {
self.prev();
}
}
pub fn prev(&mut self) {
if self.window_relative_position() == 0 {
self.view.move_backward(1);
} else {
self.window.move_backward(1);
}
}
pub fn prev_window(&mut self) {
self.move_cursor_to_start_of_window();
// move the whole window back
self.view.move_backward(self.window_size());
}
pub fn move_cursor_to_start_of_window(&mut self) {
self.window.move_backward(self.window_size());
}
pub fn move_cursor_to_end_of_window(&mut self) {
self.window.move_forward(self.window_size());
}
}

View File

@ -0,0 +1,172 @@
use super::WindowCursor;
use anyhow::Result;
/// `WindowCursor2D` manages a 2-dimensional "window" onto a grid of cells, with a cursor that can point to a specific cell.
/// For example, consider a 3x3 grid of cells:
///
/// +---+---+---+
/// | a | b | c |
/// |---|---|---|
/// | d | e | f |
/// |---|---|---|
/// | g | h | i |
/// +---+---+---+
///
/// A `WindowCursor2D` can be used to track the currently visible section of this grid.
/// For example, a 2x2 window onto this grid could initially show the top left 2x2 section:
///
/// +---+---+
/// | a | b |
/// |---|---|
/// | d | e |
/// +---+---+
///
/// Moving the window down 1 row:
///
/// +---+---+
/// | d | e |
/// |---|---|
/// | g | h |
/// +---+---+
///
/// Inside the window, the cursor can point to a specific cell.
#[derive(Debug, Default, Clone, Copy)]
pub struct WindowCursor2D {
x: WindowCursor,
y: WindowCursor,
}
pub struct Position {
pub row: usize,
pub column: usize,
}
impl WindowCursor2D {
pub fn new(count_rows: usize, count_columns: usize) -> Result<Self> {
Ok(Self {
x: WindowCursor::new(count_columns, count_columns)?,
y: WindowCursor::new(count_rows, count_rows)?,
})
}
pub fn set_window_size(&mut self, count_rows: usize, count_columns: usize) -> Result<()> {
self.x.set_window_size(count_columns)?;
self.y.set_window_size(count_rows)?;
Ok(())
}
pub fn set_window_start_position(&mut self, row: usize, col: usize) {
self.x.set_window_start_position(col);
self.y.set_window_start_position(row);
}
/// The absolute position of the cursor in the grid (0-indexed, row only)
pub fn row(&self) -> usize {
self.y.absolute_position()
}
/// The absolute position of the cursor in the grid (0-indexed, column only)
pub fn column(&self) -> usize {
self.x.absolute_position()
}
/// The absolute position of the cursor in the grid (0-indexed)
pub fn position(&self) -> Position {
Position {
row: self.row(),
column: self.column(),
}
}
pub fn row_limit(&self) -> usize {
self.y.end()
}
pub fn window_origin(&self) -> Position {
Position {
row: self.y.window_starts_at(),
column: self.x.window_starts_at(),
}
}
pub fn window_relative_position(&self) -> Position {
Position {
row: self.y.window_relative_position(),
column: self.x.window_relative_position(),
}
}
pub fn window_width_in_columns(&self) -> usize {
self.x.window_size()
}
pub fn next_row(&mut self) {
self.y.next_n(1)
}
pub fn next_row_page(&mut self) {
self.y.next_window()
}
pub fn row_move_to_end(&mut self) {
self.y.move_window_to_end();
self.y.move_cursor_to_end_of_window();
}
pub fn row_move_to_start(&mut self) {
self.y.move_cursor_to_start_of_window();
self.y.set_window_start_position(0);
}
pub fn prev_row(&mut self) {
self.y.prev()
}
pub fn prev_row_page(&mut self) {
self.y.prev_window()
}
pub fn next_column(&mut self) {
self.x.next()
}
pub fn next_column_by(&mut self, i: usize) {
self.x.next_n(i)
}
pub fn prev_column(&mut self) {
self.x.prev()
}
pub fn prev_column_by(&mut self, i: usize) {
self.x.prev_n(i)
}
pub fn next_column_i(&mut self) {
self.x
.set_window_start_position(self.x.window_starts_at() + 1)
}
pub fn prev_column_i(&mut self) {
if self.x.window_starts_at() == 0 {
return;
}
self.x
.set_window_start_position(self.x.window_starts_at() - 1)
}
pub fn next_row_i(&mut self) {
self.y
.set_window_start_position(self.y.window_starts_at() + 1)
}
pub fn prev_row_i(&mut self) {
if self.y.window_starts_at() == 0 {
return;
}
self.y
.set_window_start_position(self.y.window_starts_at() - 1)
}
}

View File

@ -1,106 +0,0 @@
use super::Cursor;
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct WindowCursor {
view: Cursor,
window: Cursor,
}
impl WindowCursor {
pub fn new(limit: usize, window: usize) -> Option<Self> {
if window > limit {
return None;
}
Some(Self {
view: Cursor::new(limit),
window: Cursor::new(window),
})
}
pub fn index(&self) -> usize {
self.view.index + self.window.index
}
pub fn offset(&self) -> usize {
self.window.index
}
pub fn starts_at(&self) -> usize {
self.view.index
}
pub fn cap(&self) -> usize {
self.view.cap()
}
pub fn window(&self) -> usize {
self.window.end()
}
pub fn end(&self) -> usize {
self.view.end()
}
pub fn set_window_at(&mut self, i: usize) -> bool {
self.view.set(i)
}
pub fn set_window(&mut self, i: usize) -> bool {
if i > self.view.end() {
return false;
}
self.window.limit(i)
}
pub fn next(&mut self, i: usize) -> bool {
if i > self.cap() {
return false;
}
let mut rest = 0;
for y in 0..i {
if !self.window.next(1) {
rest = i - y;
break;
}
}
for _ in 0..rest {
if self.index() + 1 == self.end() {
return rest != i;
}
self.view.next(1);
}
true
}
pub fn next_window(&mut self) -> bool {
let end_cursor = self.window() - self.offset();
self.next(end_cursor);
let mut index_move = self.window();
if index_move + self.starts_at() >= self.end() {
index_move = self.end() - self.starts_at();
}
self.next(index_move)
}
pub fn prev(&mut self, i: usize) -> bool {
for _ in 0..i {
if !self.window.prev(1) {
self.view.prev(1);
}
}
true
}
pub fn prev_window(&mut self) -> bool {
self.prev(self.window() + self.offset())
}
}

View File

@ -1,122 +0,0 @@
use super::WindowCursor;
#[derive(Debug, Default, Clone, Copy)]
pub struct XYCursor {
x: WindowCursor,
y: WindowCursor,
}
impl XYCursor {
pub fn new(count_rows: usize, count_columns: usize) -> Self {
Self {
x: WindowCursor::new(count_columns, count_columns).expect("..."),
y: WindowCursor::new(count_rows, count_rows).expect("..."),
}
}
pub fn set_window(&mut self, count_rows: usize, count_columns: usize) {
self.x.set_window(count_columns);
self.y.set_window(count_rows);
}
pub fn set_position(&mut self, row: usize, col: usize) {
self.x.set_window_at(col);
self.y.set_window_at(row);
}
pub fn row(&self) -> usize {
self.y.index()
}
pub fn column(&self) -> usize {
self.x.index()
}
pub fn row_limit(&self) -> usize {
self.y.end()
}
pub fn row_starts_at(&self) -> usize {
self.y.starts_at()
}
pub fn column_starts_at(&self) -> usize {
self.x.starts_at()
}
pub fn row_window(&self) -> usize {
self.y.offset()
}
pub fn column_window(&self) -> usize {
self.x.offset()
}
pub fn column_window_size(&self) -> usize {
self.x.window()
}
pub fn next_row(&mut self) -> bool {
self.y.next(1)
}
pub fn next_row_page(&mut self) -> bool {
self.y.next_window()
}
pub fn row_move_to_end(&mut self) -> bool {
self.y.next(self.y.cap())
}
pub fn row_move_to_start(&mut self) -> bool {
self.y.prev(self.y.index())
}
pub fn prev_row(&mut self) -> bool {
self.y.prev(1)
}
pub fn prev_row_page(&mut self) -> bool {
self.y.prev_window()
}
pub fn next_column(&mut self) -> bool {
self.x.next(1)
}
pub fn next_column_by(&mut self, i: usize) -> bool {
self.x.next(i)
}
pub fn prev_column(&mut self) -> bool {
self.x.prev(1)
}
pub fn prev_column_by(&mut self, i: usize) -> bool {
self.x.prev(i)
}
pub fn next_column_i(&mut self) -> bool {
self.x.set_window_at(self.x.starts_at() + 1)
}
pub fn prev_column_i(&mut self) -> bool {
if self.x.starts_at() == 0 {
return false;
}
self.x.set_window_at(self.x.starts_at() - 1)
}
pub fn next_row_i(&mut self) -> bool {
self.y.set_window_at(self.y.starts_at() + 1)
}
pub fn prev_row_i(&mut self) -> bool {
if self.y.starts_at() == 0 {
return false;
}
self.y.set_window_at(self.y.starts_at() - 1)
}
}

View File

@ -1,16 +1,16 @@
mod binary; mod binary;
mod colored_text_widget; mod colored_text_widget;
mod cursor; mod cursor;
mod interactive;
mod preview; mod preview;
mod record; mod record;
mod r#try;
pub mod util; pub mod util;
use super::{ use super::{
nu_common::NuText, nu_common::NuText,
pager::{Frame, Transition, ViewInfo}, pager::{Frame, Transition, ViewInfo},
}; };
use crate::{nu_common::NuConfig, pager::ConfigMap}; use crate::{explore::ExploreConfig, nu_common::NuConfig};
use crossterm::event::KeyEvent; use crossterm::event::KeyEvent;
use lscolors::LsColors; use lscolors::LsColors;
use nu_color_config::StyleComputer; use nu_color_config::StyleComputer;
@ -21,8 +21,8 @@ use nu_protocol::{
use ratatui::layout::Rect; use ratatui::layout::Rect;
pub use binary::BinaryView; pub use binary::BinaryView;
pub use interactive::InteractiveView;
pub use preview::Preview; pub use preview::Preview;
pub use r#try::TryView;
pub use record::{Orientation, RecordView}; pub use record::{Orientation, RecordView};
#[derive(Debug, Default)] #[derive(Debug, Default)]
@ -55,22 +55,22 @@ impl ElementInfo {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct ViewConfig<'a> { pub struct ViewConfig<'a> {
pub nu_config: &'a NuConfig, pub nu_config: &'a NuConfig,
pub explore_config: &'a ExploreConfig,
pub style_computer: &'a StyleComputer<'a>, pub style_computer: &'a StyleComputer<'a>,
pub config: &'a ConfigMap,
pub lscolors: &'a LsColors, pub lscolors: &'a LsColors,
} }
impl<'a> ViewConfig<'a> { impl<'a> ViewConfig<'a> {
pub fn new( pub fn new(
nu_config: &'a NuConfig, nu_config: &'a NuConfig,
explore_config: &'a ExploreConfig,
style_computer: &'a StyleComputer<'a>, style_computer: &'a StyleComputer<'a>,
config: &'a ConfigMap,
lscolors: &'a LsColors, lscolors: &'a LsColors,
) -> Self { ) -> Self {
Self { Self {
nu_config, nu_config,
explore_config,
style_computer, style_computer,
config,
lscolors, lscolors,
} }
} }

View File

@ -1,4 +1,6 @@
use super::{colored_text_widget::ColoredTextWidget, cursor::XYCursor, Layout, View, ViewConfig}; use super::{
colored_text_widget::ColoredTextWidget, cursor::WindowCursor2D, Layout, View, ViewConfig,
};
use crate::{ use crate::{
nu_common::{NuSpan, NuText}, nu_common::{NuSpan, NuText},
pager::{report::Report, Frame, Transition, ViewInfo}, pager::{report::Report, Frame, Transition, ViewInfo},
@ -17,7 +19,7 @@ use std::cmp::max;
pub struct Preview { pub struct Preview {
underlying_value: Option<Value>, underlying_value: Option<Value>,
lines: Vec<String>, lines: Vec<String>,
cursor: XYCursor, cursor: WindowCursor2D,
} }
impl Preview { impl Preview {
@ -26,8 +28,9 @@ impl Preview {
.lines() .lines()
.map(|line| line.replace('\t', " ")) // tui: doesn't support TAB .map(|line| line.replace('\t', " ")) // tui: doesn't support TAB
.collect(); .collect();
let cursor = XYCursor::new(lines.len(), usize::MAX);
// TODO: refactor so this is fallible and returns a Result instead of panicking
let cursor = WindowCursor2D::new(lines.len(), usize::MAX).expect("Failed to create cursor");
Self { Self {
lines, lines,
cursor, cursor,
@ -38,10 +41,11 @@ impl Preview {
impl View for Preview { impl View for Preview {
fn draw(&mut self, f: &mut Frame, area: Rect, _: ViewConfig<'_>, layout: &mut Layout) { fn draw(&mut self, f: &mut Frame, area: Rect, _: ViewConfig<'_>, layout: &mut Layout) {
self.cursor let _ = self
.set_window(area.height as usize, area.width as usize); .cursor
.set_window_size(area.height as usize, area.width as usize);
let lines = &self.lines[self.cursor.row_starts_at()..]; let lines = &self.lines[self.cursor.window_origin().row..];
for (i, line) in lines.iter().enumerate().take(area.height as usize) { for (i, line) in lines.iter().enumerate().take(area.height as usize) {
let text_widget = ColoredTextWidget::new(line, self.cursor.column()); let text_widget = ColoredTextWidget::new(line, self.cursor.column());
let plain_text = text_widget.get_plain_text(area.width as usize); let plain_text = text_widget.get_plain_text(area.width as usize);
@ -65,13 +69,13 @@ impl View for Preview {
match key.code { match key.code {
KeyCode::Left => { KeyCode::Left => {
self.cursor self.cursor
.prev_column_by(max(1, self.cursor.column_window_size() / 2)); .prev_column_by(max(1, self.cursor.window_width_in_columns() / 2));
Some(Transition::Ok) Some(Transition::Ok)
} }
KeyCode::Right => { KeyCode::Right => {
self.cursor self.cursor
.next_column_by(max(1, self.cursor.column_window_size() / 2)); .next_column_by(max(1, self.cursor.window_width_in_columns() / 2));
Some(Transition::Ok) Some(Transition::Ok)
} }
@ -128,7 +132,7 @@ impl View for Preview {
// //
// todo: improve somehow? // todo: improve somehow?
self.cursor.set_position(row, 0); self.cursor.set_window_start_position(row, 0);
true true
} }
@ -152,7 +156,7 @@ fn set_status_end(view: &Preview, info: &mut ViewInfo) {
} }
fn set_status_top(view: &Preview, info: &mut ViewInfo) { fn set_status_top(view: &Preview, info: &mut ViewInfo) {
if view.cursor.row_starts_at() == 0 { if view.cursor.window_origin().row == 0 {
info.status = Some(Report::info("TOP")); info.status = Some(Report::info("TOP"));
} else { } else {
info.status = Some(Report::default()); info.status = Some(Report::default());

View File

@ -1,26 +1,26 @@
mod table_widget; mod table_widget;
use self::table_widget::{TableStyle, TableWidget, TableWidgetState}; use self::table_widget::{TableWidget, TableWidgetState};
use super::{ use super::{
cursor::XYCursor, cursor::{Position, WindowCursor2D},
util::{make_styled_string, nu_style_to_tui}, util::{make_styled_string, nu_style_to_tui},
Layout, View, ViewConfig, Layout, View, ViewConfig,
}; };
use crate::{ use crate::{
nu_common::{collect_input, lscolorize, NuConfig, NuSpan, NuStyle, NuText}, explore::ExploreConfig,
nu_common::{collect_input, lscolorize, NuSpan, NuText},
pager::{ pager::{
report::{Report, Severity}, report::{Report, Severity},
ConfigMap, Frame, Transition, ViewInfo, Frame, Transition, ViewInfo,
}, },
util::create_map,
views::ElementInfo, views::ElementInfo,
}; };
use anyhow::Result; use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use nu_color_config::{get_color_map, StyleComputer}; use nu_color_config::StyleComputer;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
Record, Span, Value, Config, Record, Span, Value,
}; };
use ratatui::{layout::Rect, widgets::Block}; use ratatui::{layout::Rect, widgets::Block};
use std::{borrow::Cow, collections::HashMap}; use std::{borrow::Cow, collections::HashMap};
@ -32,7 +32,7 @@ pub struct RecordView<'a> {
layer_stack: Vec<RecordLayer<'a>>, layer_stack: Vec<RecordLayer<'a>>,
mode: UIMode, mode: UIMode,
orientation: Orientation, orientation: Orientation,
theme: TableTheme, cfg: ExploreConfig,
} }
impl<'a> RecordView<'a> { impl<'a> RecordView<'a> {
@ -44,52 +44,18 @@ impl<'a> RecordView<'a> {
layer_stack: vec![RecordLayer::new(columns, records)], layer_stack: vec![RecordLayer::new(columns, records)],
mode: UIMode::View, mode: UIMode::View,
orientation: Orientation::Top, orientation: Orientation::Top,
theme: TableTheme::default(), // TODO: It's kind of gross how this temporarily has an incorrect/default config.
// See if we can pass correct config in through the constructor
cfg: ExploreConfig::default(),
} }
} }
pub fn tail(&mut self, width: u16, height: u16) { pub fn tail(&mut self, width: u16, height: u16) {
let page_size = let page_size =
estimate_page_size(Rect::new(0, 0, width, height), self.theme.table.show_header); estimate_page_size(Rect::new(0, 0, width, height), self.cfg.table.show_header);
tail_data(self, page_size as usize); tail_data(self, page_size as usize);
} }
pub fn set_style_split_line(&mut self, style: NuStyle) {
self.theme.table.splitline_style = style
}
pub fn set_style_selected_cell(&mut self, style: NuStyle) {
self.theme.cursor.selected_cell = Some(style)
}
pub fn set_style_selected_row(&mut self, style: NuStyle) {
self.theme.cursor.selected_row = Some(style)
}
pub fn set_style_selected_column(&mut self, style: NuStyle) {
self.theme.cursor.selected_column = Some(style)
}
pub fn set_padding_column(&mut self, (left, right): (usize, usize)) {
self.theme.table.column_padding_left = left;
self.theme.table.column_padding_right = right;
}
pub fn get_padding_column(&self) -> (usize, usize) {
(
self.theme.table.column_padding_left,
self.theme.table.column_padding_right,
)
}
pub fn get_theme(&self) -> &TableTheme {
&self.theme
}
pub fn set_theme(&mut self, theme: TableTheme) {
self.theme = theme;
}
pub fn transpose(&mut self) { pub fn transpose(&mut self) {
let layer = self.get_layer_last_mut(); let layer = self.get_layer_last_mut();
transpose_table(layer); transpose_table(layer);
@ -133,22 +99,22 @@ impl<'a> RecordView<'a> {
layer.reset_cursor(); layer.reset_cursor();
} }
pub fn get_current_position(&self) -> (usize, usize) { /// Get the current position of the cursor in the table as a whole
pub fn get_cursor_position(&self) -> Position {
let layer = self.get_layer_last(); let layer = self.get_layer_last();
(layer.cursor.row(), layer.cursor.column()) layer.cursor.position()
} }
pub fn get_current_window(&self) -> (usize, usize) { /// Get the current position of the cursor in the window being shown
pub fn get_cursor_position_in_window(&self) -> Position {
let layer = self.get_layer_last(); let layer = self.get_layer_last();
(layer.cursor.row_window(), layer.cursor.column_window()) layer.cursor.window_relative_position()
} }
pub fn get_current_offset(&self) -> (usize, usize) { /// Get the origin of the window being shown. (0,0), top left corner.
pub fn get_window_origin(&self) -> Position {
let layer = self.get_layer_last(); let layer = self.get_layer_last();
( layer.cursor.window_origin()
layer.cursor.row_starts_at(),
layer.cursor.column_starts_at(),
)
} }
pub fn set_cursor_mode(&mut self) { pub fn set_cursor_mode(&mut self) {
@ -160,7 +126,7 @@ impl<'a> RecordView<'a> {
} }
pub fn get_current_value(&self) -> Value { pub fn get_current_value(&self) -> Value {
let (row, column) = self.get_current_position(); let Position { row, column } = self.get_cursor_position();
let layer = self.get_layer_last(); let layer = self.get_layer_last();
let (row, column) = match layer.orientation { let (row, column) = match layer.orientation {
@ -175,15 +141,17 @@ impl<'a> RecordView<'a> {
} }
} }
fn create_tablew(&'a self, cfg: ViewConfig<'a>) -> TableWidget<'a> { /// Create a table widget.
/// WARNING: this is currently really slow on large data sets.
/// It creates a string representation of every cell in the table and looks at every row for lscolorize.
fn create_table_widget(&'a self, cfg: ViewConfig<'a>) -> TableWidget<'a> {
let layer = self.get_layer_last(); let layer = self.get_layer_last();
let mut data = convert_records_to_string(&layer.records, cfg.nu_config, cfg.style_computer); let mut data = convert_records_to_string(&layer.records, cfg.nu_config, cfg.style_computer);
lscolorize(&layer.columns, &mut data, cfg.lscolors); lscolorize(&layer.columns, &mut data, cfg.lscolors);
let headers = layer.columns.as_ref(); let headers = layer.columns.as_ref();
let style_computer = cfg.style_computer; let style_computer = cfg.style_computer;
let (row, column) = self.get_current_offset(); let Position { row, column } = self.get_window_origin();
TableWidget::new( TableWidget::new(
headers, headers,
@ -191,7 +159,7 @@ impl<'a> RecordView<'a> {
style_computer, style_computer,
row, row,
column, column,
self.theme.table, self.cfg.table,
layer.orientation, layer.orientation,
) )
} }
@ -199,11 +167,17 @@ impl<'a> RecordView<'a> {
fn update_cursors(&mut self, rows: usize, columns: usize) { fn update_cursors(&mut self, rows: usize, columns: usize) {
match self.get_layer_last().orientation { match self.get_layer_last().orientation {
Orientation::Top => { Orientation::Top => {
self.get_layer_last_mut().cursor.set_window(rows, columns); let _ = self
.get_layer_last_mut()
.cursor
.set_window_size(rows, columns);
} }
Orientation::Left => { Orientation::Left => {
self.get_layer_last_mut().cursor.set_window(rows, columns); let _ = self
.get_layer_last_mut()
.cursor
.set_window_size(rows, columns);
} }
} }
} }
@ -226,7 +200,10 @@ impl<'a> RecordView<'a> {
impl View for RecordView<'_> { impl View for RecordView<'_> {
fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) { fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) {
let mut table_layout = TableWidgetState::default(); let mut table_layout = TableWidgetState::default();
let table = self.create_tablew(cfg); // TODO: creating the table widget is O(N) where N is the number of cells in the grid.
// Way too slow to do on every draw call!
// To make explore work for larger data sets, this needs to be improved.
let table = self.create_table_widget(cfg);
f.render_stateful_widget(table, area, &mut table_layout); f.render_stateful_widget(table, area, &mut table_layout);
*layout = table_layout.layout; *layout = table_layout.layout;
@ -234,18 +211,18 @@ impl View for RecordView<'_> {
self.update_cursors(table_layout.count_rows, table_layout.count_columns); self.update_cursors(table_layout.count_rows, table_layout.count_columns);
if self.mode == UIMode::Cursor { if self.mode == UIMode::Cursor {
let (row, column) = self.get_current_window(); let Position { row, column } = self.get_cursor_position_in_window();
let info = get_element_info( let info = get_element_info(
layout, layout,
row, row,
column, column,
table_layout.count_rows, table_layout.count_rows,
self.get_layer_last().orientation, self.get_layer_last().orientation,
self.theme.table.show_header, self.cfg.table.show_header,
); );
if let Some(info) = info { if let Some(info) = info {
highlight_cell(f, area, info.clone(), &self.theme.cursor); highlight_selected_cell(f, info.clone(), &self.cfg);
} }
} }
} }
@ -289,7 +266,7 @@ impl View for RecordView<'_> {
let data = convert_records_to_string( let data = convert_records_to_string(
&self.get_layer_last().records, &self.get_layer_last().records,
&NuConfig::default(), &nu_protocol::Config::default(),
&style_computer, &style_computer,
); );
@ -308,7 +285,9 @@ impl View for RecordView<'_> {
for (column, _) in cells.iter().enumerate() { for (column, _) in cells.iter().enumerate() {
if i == pos { if i == pos {
self.get_layer_last_mut().cursor.set_position(row, column); self.get_layer_last_mut()
.cursor
.set_window_start_position(row, column);
return true; return true;
} }
@ -325,22 +304,7 @@ impl View for RecordView<'_> {
// todo: move the method to Command? // todo: move the method to Command?
fn setup(&mut self, cfg: ViewConfig<'_>) { fn setup(&mut self, cfg: ViewConfig<'_>) {
if let Some(hm) = cfg.config.get("table").and_then(create_map) { self.cfg = cfg.explore_config.clone();
self.theme = theme_from_config(&hm);
if let Some(orientation) = hm.get("orientation").and_then(|v| v.coerce_str().ok()) {
let orientation = match orientation.as_ref() {
"left" => Some(Orientation::Left),
"top" => Some(Orientation::Top),
_ => None,
};
if let Some(orientation) = orientation {
self.set_orientation(orientation);
self.set_orientation_current(orientation);
}
}
}
} }
} }
@ -374,7 +338,7 @@ pub struct RecordLayer<'a> {
orientation: Orientation, orientation: Orientation,
name: Option<String>, name: Option<String>,
was_transposed: bool, was_transposed: bool,
cursor: XYCursor, cursor: WindowCursor2D,
} }
impl<'a> RecordLayer<'a> { impl<'a> RecordLayer<'a> {
@ -384,7 +348,10 @@ impl<'a> RecordLayer<'a> {
) -> Self { ) -> Self {
let columns = columns.into(); let columns = columns.into();
let records = records.into(); let records = records.into();
let cursor = XYCursor::new(records.len(), columns.len());
// TODO: refactor so this is fallible and returns a Result instead of panicking
let cursor =
WindowCursor2D::new(records.len(), columns.len()).expect("Failed to create cursor");
Self { Self {
columns, columns,
@ -420,7 +387,9 @@ impl<'a> RecordLayer<'a> {
} }
fn reset_cursor(&mut self) { fn reset_cursor(&mut self) {
self.cursor = XYCursor::new(self.count_rows(), self.count_columns()); // TODO: refactor so this is fallible and returns a Result instead of panicking
self.cursor = WindowCursor2D::new(self.count_rows(), self.count_columns())
.expect("Failed to create cursor");
} }
} }
@ -634,13 +603,15 @@ fn tail_data(state: &mut RecordView<'_>, page_size: usize) {
let layer = state.get_layer_last_mut(); let layer = state.get_layer_last_mut();
let count_rows = layer.records.len(); let count_rows = layer.records.len();
if count_rows > page_size { if count_rows > page_size {
layer.cursor.set_position(count_rows - page_size, 0); layer
.cursor
.set_window_start_position(count_rows - page_size, 0);
} }
} }
fn convert_records_to_string( fn convert_records_to_string(
records: &[Vec<Value>], records: &[Vec<Value>],
cfg: &NuConfig, cfg: &Config,
style_computer: &StyleComputer, style_computer: &StyleComputer,
) -> Vec<Vec<NuText>> { ) -> Vec<Vec<NuText>> {
records records
@ -658,31 +629,8 @@ fn convert_records_to_string(
.collect::<Vec<_>>() .collect::<Vec<_>>()
} }
fn highlight_cell(f: &mut Frame, area: Rect, info: ElementInfo, theme: &CursorStyle) { fn highlight_selected_cell(f: &mut Frame, info: ElementInfo, cfg: &ExploreConfig) {
// highlight selected column let cell_style = cfg.selected_cell;
if let Some(style) = theme.selected_column {
let highlight_block = Block::default().style(nu_style_to_tui(style));
let area = Rect::new(info.area.x, area.y, info.area.width, area.height);
f.render_widget(highlight_block.clone(), area);
}
// highlight selected row
if let Some(style) = theme.selected_row {
let highlight_block = Block::default().style(nu_style_to_tui(style));
let area = Rect::new(area.x, info.area.y, area.width, 1);
f.render_widget(highlight_block.clone(), area);
}
// highlight selected cell
let cell_style = match theme.selected_cell {
Some(s) => s,
None => {
let mut style = nu_ansi_term::Style::new();
// light blue chosen somewhat arbitrarily, looks OK but I'm not set on it
style.background = Some(nu_ansi_term::Color::LightBlue);
style
}
};
let highlight_block = Block::default().style(nu_style_to_tui(cell_style)); let highlight_block = Block::default().style(nu_style_to_tui(cell_style));
let area = Rect::new(info.area.x, info.area.y, info.area.width, 1); let area = Rect::new(info.area.x, info.area.y, info.area.width, 1);
f.render_widget(highlight_block.clone(), area) f.render_widget(highlight_block.clone(), area)
@ -731,20 +679,18 @@ fn build_table_as_record(v: &RecordView) -> Value {
Value::record(record, NuSpan::unknown()) Value::record(record, NuSpan::unknown())
} }
fn report_cursor_position(mode: UIMode, cursor: XYCursor) -> String { fn report_cursor_position(mode: UIMode, cursor: WindowCursor2D) -> String {
if mode == UIMode::Cursor { if mode == UIMode::Cursor {
let row = cursor.row(); let Position { row, column } = cursor.position();
let column = cursor.column();
format!("{row},{column}") format!("{row},{column}")
} else { } else {
let rows_seen = cursor.row_starts_at(); let Position { row, column } = cursor.window_origin();
let columns_seen = cursor.column_starts_at(); format!("{row},{column}")
format!("{rows_seen},{columns_seen}")
} }
} }
fn report_row_position(cursor: XYCursor) -> String { fn report_row_position(cursor: WindowCursor2D) -> String {
if cursor.row_starts_at() == 0 { if cursor.window_origin().row == 0 {
String::from("Top") String::from("Top")
} else { } else {
let percent_rows = get_percentage(cursor.row(), cursor.row_limit()); let percent_rows = get_percentage(cursor.row(), cursor.row_limit());
@ -824,53 +770,3 @@ fn _transpose_table(
data data
} }
fn theme_from_config(config: &ConfigMap) -> TableTheme {
let mut theme = TableTheme::default();
let colors = get_color_map(config);
if let Some(s) = colors.get("split_line") {
theme.table.splitline_style = *s;
}
theme.cursor.selected_cell = colors.get("selected_cell").cloned();
theme.cursor.selected_row = colors.get("selected_row").cloned();
theme.cursor.selected_column = colors.get("selected_column").cloned();
theme.table.show_header = config_get_bool(config, "show_head", true);
theme.table.show_index = config_get_bool(config, "show_index", false);
theme.table.column_padding_left = config_get_usize(config, "column_padding_left", 1);
theme.table.column_padding_right = config_get_usize(config, "column_padding_right", 1);
theme
}
fn config_get_bool(config: &ConfigMap, key: &str, default: bool) -> bool {
config
.get(key)
.and_then(|v| v.as_bool().ok())
.unwrap_or(default)
}
fn config_get_usize(config: &ConfigMap, key: &str, default: usize) -> usize {
config
.get(key)
.and_then(|v| v.coerce_str().ok())
.and_then(|s| s.parse::<usize>().ok())
.unwrap_or(default)
}
#[derive(Debug, Default, Clone)]
pub struct TableTheme {
table: TableStyle,
cursor: CursorStyle,
}
#[derive(Debug, Default, Clone)]
struct CursorStyle {
selected_cell: Option<NuStyle>,
selected_column: Option<NuStyle>,
selected_row: Option<NuStyle>,
}

View File

@ -1,5 +1,6 @@
use super::Layout; use super::Layout;
use crate::{ use crate::{
explore::TableConfig,
nu_common::{truncate_str, NuStyle, NuText}, nu_common::{truncate_str, NuStyle, NuText},
views::util::{nu_style_to_tui, text_style_to_tui_style}, views::util::{nu_style_to_tui, text_style_to_tui_style},
}; };
@ -23,7 +24,7 @@ pub struct TableWidget<'a> {
data: Cow<'a, [Vec<NuText>]>, data: Cow<'a, [Vec<NuText>]>,
index_row: usize, index_row: usize,
index_column: usize, index_column: usize,
style: TableStyle, config: TableConfig,
header_position: Orientation, header_position: Orientation,
style_computer: &'a StyleComputer<'a>, style_computer: &'a StyleComputer<'a>,
} }
@ -35,15 +36,6 @@ pub enum Orientation {
Left, Left,
} }
#[derive(Debug, Default, Clone, Copy)]
pub struct TableStyle {
pub splitline_style: NuStyle,
pub show_index: bool,
pub show_header: bool,
pub column_padding_left: usize,
pub column_padding_right: usize,
}
impl<'a> TableWidget<'a> { impl<'a> TableWidget<'a> {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn new( pub fn new(
@ -52,7 +44,7 @@ impl<'a> TableWidget<'a> {
style_computer: &'a StyleComputer<'a>, style_computer: &'a StyleComputer<'a>,
index_row: usize, index_row: usize,
index_column: usize, index_column: usize,
style: TableStyle, config: TableConfig,
header_position: Orientation, header_position: Orientation,
) -> Self { ) -> Self {
Self { Self {
@ -61,7 +53,7 @@ impl<'a> TableWidget<'a> {
style_computer, style_computer,
index_row, index_row,
index_column, index_column,
style, config,
header_position, header_position,
} }
} }
@ -100,13 +92,13 @@ impl StatefulWidget for TableWidget<'_> {
// todo: refactoring these to methods as they have quite a bit in common. // todo: refactoring these to methods as they have quite a bit in common.
impl<'a> TableWidget<'a> { impl<'a> TableWidget<'a> {
fn render_table_horizontal(self, area: Rect, buf: &mut Buffer, state: &mut TableWidgetState) { fn render_table_horizontal(self, area: Rect, buf: &mut Buffer, state: &mut TableWidgetState) {
let padding_l = self.style.column_padding_left as u16; let padding_l = self.config.column_padding_left as u16;
let padding_r = self.style.column_padding_right as u16; let padding_r = self.config.column_padding_right as u16;
let show_index = self.style.show_index; let show_index = self.config.show_index;
let show_head = self.style.show_header; let show_head = self.config.show_header;
let splitline_s = self.style.splitline_style; let separator_s = self.config.separator_style;
let mut data_height = area.height; let mut data_height = area.height;
let mut data_y = area.y; let mut data_y = area.y;
@ -137,7 +129,7 @@ impl<'a> TableWidget<'a> {
} }
if show_head { if show_head {
render_header_borders(buf, area, 1, splitline_s); render_header_borders(buf, area, 1, separator_s);
} }
if show_index { if show_index {
@ -158,7 +150,7 @@ impl<'a> TableWidget<'a> {
data_height, data_height,
show_head, show_head,
false, false,
splitline_s, separator_s,
); );
} }
@ -250,7 +242,7 @@ impl<'a> TableWidget<'a> {
data_height, data_height,
show_head, show_head,
false, false,
splitline_s, separator_s,
); );
} }
@ -268,12 +260,12 @@ impl<'a> TableWidget<'a> {
return; return;
} }
let padding_l = self.style.column_padding_left as u16; let padding_l = self.config.column_padding_left as u16;
let padding_r = self.style.column_padding_right as u16; let padding_r = self.config.column_padding_right as u16;
let show_index = self.style.show_index; let show_index = self.config.show_index;
let show_head = self.style.show_header; let show_head = self.config.show_header;
let splitline_s = self.style.splitline_style; let separator_s = self.config.separator_style;
let mut left_w = 0; let mut left_w = 0;
@ -295,7 +287,7 @@ impl<'a> TableWidget<'a> {
area.height, area.height,
false, false,
false, false,
splitline_s, separator_s,
); );
} }
@ -327,7 +319,7 @@ impl<'a> TableWidget<'a> {
area.height, area.height,
false, false,
false, false,
splitline_s, separator_s,
); );
} }
@ -352,7 +344,7 @@ impl<'a> TableWidget<'a> {
area.height, area.height,
false, false,
false, false,
splitline_s, separator_s,
); );
} }

View File

@ -1,16 +1,10 @@
use super::{ use super::{record::RecordView, util::nu_style_to_tui, Layout, Orientation, View, ViewConfig};
record::{RecordView, TableTheme},
util::{lookup_tui_color, nu_style_to_tui},
Layout, Orientation, View, ViewConfig,
};
use crate::{ use crate::{
nu_common::{collect_pipeline, run_command_with_value}, nu_common::{collect_pipeline, run_command_with_value},
pager::{report::Report, Frame, Transition, ViewInfo}, pager::{report::Report, Frame, Transition, ViewInfo},
util::create_map,
}; };
use anyhow::Result; use anyhow::Result;
use crossterm::event::{KeyCode, KeyEvent}; use crossterm::event::{KeyCode, KeyEvent};
use nu_color_config::get_color_map;
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
PipelineData, Value, PipelineData, Value,
@ -22,26 +16,22 @@ use ratatui::{
}; };
use std::cmp::min; use std::cmp::min;
pub struct InteractiveView<'a> { pub struct TryView<'a> {
input: Value, input: Value,
command: String, command: String,
immediate: bool, reactive: bool,
table: Option<RecordView<'a>>, table: Option<RecordView<'a>>,
table_theme: TableTheme,
view_mode: bool, view_mode: bool,
border_color: Style, border_color: Style,
highlighted_color: Style,
} }
impl<'a> InteractiveView<'a> { impl<'a> TryView<'a> {
pub fn new(input: Value) -> Self { pub fn new(input: Value) -> Self {
Self { Self {
input, input,
table: None, table: None,
immediate: false, reactive: false,
table_theme: TableTheme::default(),
border_color: Style::default(), border_color: Style::default(),
highlighted_color: Style::default(),
view_mode: false, view_mode: false,
command: String::new(), command: String::new(),
} }
@ -52,18 +42,15 @@ impl<'a> InteractiveView<'a> {
} }
pub fn try_run(&mut self, engine_state: &EngineState, stack: &mut Stack) -> Result<()> { pub fn try_run(&mut self, engine_state: &EngineState, stack: &mut Stack) -> Result<()> {
let mut view = run_command(&self.command, &self.input, engine_state, stack)?; let view = run_command(&self.command, &self.input, engine_state, stack)?;
view.set_theme(self.table_theme.clone());
self.table = Some(view); self.table = Some(view);
Ok(()) Ok(())
} }
} }
impl View for InteractiveView<'_> { impl View for TryView<'_> {
fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) { fn draw(&mut self, f: &mut Frame, area: Rect, cfg: ViewConfig<'_>, layout: &mut Layout) {
let border_color = self.border_color; let border_color = self.border_color;
let highlighted_color = self.highlighted_color;
let cmd_block = ratatui::widgets::Block::default() let cmd_block = ratatui::widgets::Block::default()
.borders(Borders::ALL) .borders(Borders::ALL)
@ -77,7 +64,7 @@ impl View for InteractiveView<'_> {
cmd_block cmd_block
.border_style(Style::default().add_modifier(Modifier::BOLD)) .border_style(Style::default().add_modifier(Modifier::BOLD))
.border_type(BorderType::Double) .border_type(BorderType::Double)
.border_style(highlighted_color) .border_style(border_color)
}; };
f.render_widget(cmd_block, cmd_area); f.render_widget(cmd_block, cmd_area);
@ -127,7 +114,7 @@ impl View for InteractiveView<'_> {
table_block table_block
.border_style(Style::default().add_modifier(Modifier::BOLD)) .border_style(Style::default().add_modifier(Modifier::BOLD))
.border_type(BorderType::Double) .border_type(BorderType::Double)
.border_style(highlighted_color) .border_style(border_color)
} else { } else {
table_block table_block
}; };
@ -135,6 +122,7 @@ impl View for InteractiveView<'_> {
f.render_widget(table_block, table_area); f.render_widget(table_block, table_area);
if let Some(table) = &mut self.table { if let Some(table) = &mut self.table {
table.setup(cfg);
let area = Rect::new( let area = Rect::new(
area.x + 2, area.x + 2,
area.y + 4, area.y + 4,
@ -160,7 +148,7 @@ impl View for InteractiveView<'_> {
.as_mut() .as_mut()
.expect("we know that we have a table cause of a flag"); .expect("we know that we have a table cause of a flag");
let was_at_the_top = table.get_current_position().0 == 0; let was_at_the_top = table.get_cursor_position().row == 0;
if was_at_the_top && matches!(key.code, KeyCode::Up | KeyCode::PageUp) { if was_at_the_top && matches!(key.code, KeyCode::Up | KeyCode::PageUp) {
self.view_mode = false; self.view_mode = false;
@ -190,7 +178,7 @@ impl View for InteractiveView<'_> {
if !self.command.is_empty() { if !self.command.is_empty() {
self.command.pop(); self.command.pop();
if self.immediate { if self.reactive {
match self.try_run(engine_state, stack) { match self.try_run(engine_state, stack) {
Ok(_) => info.report = Some(Report::default()), Ok(_) => info.report = Some(Report::default()),
Err(err) => info.report = Some(Report::error(format!("Error: {err}"))), Err(err) => info.report = Some(Report::error(format!("Error: {err}"))),
@ -203,7 +191,7 @@ impl View for InteractiveView<'_> {
KeyCode::Char(c) => { KeyCode::Char(c) => {
self.command.push(*c); self.command.push(*c);
if self.immediate { if self.reactive {
match self.try_run(engine_state, stack) { match self.try_run(engine_state, stack) {
Ok(_) => info.report = Some(Report::default()), Ok(_) => info.report = Some(Report::default()),
Err(err) => info.report = Some(Report::error(format!("Error: {err}"))), Err(err) => info.report = Some(Report::error(format!("Error: {err}"))),
@ -246,31 +234,14 @@ impl View for InteractiveView<'_> {
} }
fn setup(&mut self, config: ViewConfig<'_>) { fn setup(&mut self, config: ViewConfig<'_>) {
self.border_color = lookup_tui_color(config.style_computer, "separator"); self.border_color = nu_style_to_tui(config.explore_config.table.separator_style);
self.reactive = config.explore_config.try_reactive;
if let Some(hm) = config.config.get("try").and_then(create_map) {
let colors = get_color_map(&hm);
if let Some(color) = colors.get("highlighted_color").copied() {
self.highlighted_color = nu_style_to_tui(color);
}
if self.border_color != Style::default() && self.highlighted_color == Style::default() {
self.highlighted_color = self.border_color;
}
if let Some(val) = hm.get("reactive").and_then(|v| v.as_bool().ok()) {
self.immediate = val;
}
}
let mut r = RecordView::new(vec![], vec![]); let mut r = RecordView::new(vec![], vec![]);
r.setup(config); r.setup(config);
self.table_theme = r.get_theme().clone();
if let Some(view) = &mut self.table { if let Some(view) = &mut self.table {
view.set_theme(self.table_theme.clone()); view.setup(config);
view.set_orientation(r.get_orientation_current()); view.set_orientation(r.get_orientation_current());
view.set_orientation_current(r.get_orientation_current()); view.set_orientation_current(r.get_orientation_current());
} }

View File

@ -31,11 +31,6 @@ pub fn set_span(
text_width as u16 text_width as u16
} }
pub fn lookup_tui_color(style_computer: &StyleComputer, key: &str) -> Style {
let nu_style = style_computer.compute(key, &Value::nothing(nu_protocol::Span::unknown()));
nu_style_to_tui(nu_style)
}
pub fn nu_style_to_tui(style: NuStyle) -> ratatui::style::Style { pub fn nu_style_to_tui(style: NuStyle) -> ratatui::style::Style {
let mut out = ratatui::style::Style::default(); let mut out = ratatui::style::Style::default();
if let Some(clr) = style.background { if let Some(clr) = style.background {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "nu-glob" name = "nu-glob"
version = "0.94.2" version = "0.94.3"
authors = ["The Nushell Project Developers", "The Rust Project Developers"] authors = ["The Nushell Project Developers", "The Rust Project Developers"]
license = "MIT/Apache-2.0" license = "MIT/Apache-2.0"
description = """ description = """
@ -14,4 +14,4 @@ categories = ["filesystem"]
bench = false bench = false
[dev-dependencies] [dev-dependencies]
doc-comment = "0.3" doc-comment = "0.3"

View File

@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-json"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-json" name = "nu-json"
version = "0.94.2" version = "0.94.3"
# 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
@ -23,5 +23,5 @@ serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
[dev-dependencies] [dev-dependencies]
# nu-path = { path="../nu-path", version = "0.94.2" } # nu-path = { path="../nu-path", version = "0.94.3" }
# serde_json = "1.0" # serde_json = "1.0"

View File

@ -3,14 +3,14 @@ authors = ["The Nushell Project Developers"]
description = "Nushell's integrated LSP server" description = "Nushell's integrated LSP server"
repository = "https://github.com/nushell/nushell/tree/main/crates/nu-lsp" repository = "https://github.com/nushell/nushell/tree/main/crates/nu-lsp"
name = "nu-lsp" name = "nu-lsp"
version = "0.94.2" version = "0.94.3"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
[dependencies] [dependencies]
nu-cli = { path = "../nu-cli", version = "0.94.2" } nu-cli = { path = "../nu-cli", version = "0.94.3" }
nu-parser = { path = "../nu-parser", version = "0.94.2" } nu-parser = { path = "../nu-parser", version = "0.94.3" }
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
reedline = { workspace = true } reedline = { workspace = true }
@ -23,8 +23,8 @@ serde = { workspace = true }
serde_json = { workspace = true } serde_json = { workspace = true }
[dev-dependencies] [dev-dependencies]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.2" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.3" }
nu-command = { path = "../nu-command", version = "0.94.2" } nu-command = { path = "../nu-command", version = "0.94.3" }
nu-test-support = { path = "../nu-test-support", version = "0.94.2" } nu-test-support = { path = "../nu-test-support", version = "0.94.3" }
assert-json-diff = "2.0" assert-json-diff = "2.0"

View File

@ -5,17 +5,17 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-parser"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-parser" name = "nu-parser"
version = "0.94.2" version = "0.94.3"
exclude = ["/fuzz"] exclude = ["/fuzz"]
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-path = { path = "../nu-path", version = "0.94.2" } nu-path = { path = "../nu-path", version = "0.94.3" }
nu-plugin-engine = { path = "../nu-plugin-engine", optional = true, version = "0.94.2" } nu-plugin-engine = { path = "../nu-plugin-engine", optional = true, version = "0.94.3" }
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
bytesize = { workspace = true } bytesize = { workspace = true }
chrono = { default-features = false, features = ['std'], workspace = true } chrono = { default-features = false, features = ['std'], workspace = true }
@ -27,4 +27,4 @@ serde_json = { workspace = true }
rstest = { workspace = true, default-features = false } rstest = { workspace = true, default-features = false }
[features] [features]
plugin = ["nu-plugin-engine"] plugin = ["nu-plugin-engine"]

View File

@ -1,7 +1,7 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::{ use nu_protocol::{
ast::{Argument, Expr, Expression}, ast::{Argument, Expr, Expression},
engine::CommandType, engine::{CommandType, UNKNOWN_SPAN_ID},
}; };
#[derive(Clone)] #[derive(Clone)]
@ -56,43 +56,49 @@ impl Command for KnownExternal {
}; };
let extern_name: Vec<_> = extern_name.split(' ').collect(); let extern_name: Vec<_> = extern_name.split(' ').collect();
let call_head_id = engine_state
.find_span_id(call.head)
.unwrap_or(UNKNOWN_SPAN_ID);
let arg_extern_name = Expression { let arg_extern_name = Expression::new_existing(
expr: Expr::String(extern_name[0].to_string()), Expr::String(extern_name[0].to_string()),
span: call.head, call.head,
ty: Type::String, call_head_id,
custom_completion: None, Type::String,
}; );
extern_call.add_positional(arg_extern_name); extern_call.add_positional(arg_extern_name);
for subcommand in extern_name.into_iter().skip(1) { for subcommand in extern_name.into_iter().skip(1) {
extern_call.add_positional(Expression { extern_call.add_positional(Expression::new_existing(
expr: Expr::String(subcommand.to_string()), Expr::String(subcommand.to_string()),
span: call.head, call.head,
ty: Type::String, call_head_id,
custom_completion: None, Type::String,
}); ));
} }
for arg in &call.arguments { for arg in &call.arguments {
match arg { match arg {
Argument::Positional(positional) => extern_call.add_positional(positional.clone()), Argument::Positional(positional) => extern_call.add_positional(positional.clone()),
Argument::Named(named) => { Argument::Named(named) => {
let named_span_id = engine_state
.find_span_id(named.0.span)
.unwrap_or(UNKNOWN_SPAN_ID);
if let Some(short) = &named.1 { if let Some(short) = &named.1 {
extern_call.add_positional(Expression { extern_call.add_positional(Expression::new_existing(
expr: Expr::String(format!("-{}", short.item)), Expr::String(format!("-{}", short.item)),
span: named.0.span, named.0.span,
ty: Type::String, named_span_id,
custom_completion: None, Type::String,
}); ));
} else { } else {
extern_call.add_positional(Expression { extern_call.add_positional(Expression::new_existing(
expr: Expr::String(format!("--{}", named.0.item)), Expr::String(format!("--{}", named.0.item)),
span: named.0.span, named.0.span,
ty: Type::String, named_span_id,
custom_completion: None, Type::String,
}); ));
} }
if let Some(arg) = &named.2 { if let Some(arg) = &named.2 {
extern_call.add_positional(arg.clone()); extern_call.add_positional(arg.clone());

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -68,7 +68,7 @@ pub fn type_compatible(lhs: &Type, rhs: &Type) -> bool {
} }
pub fn math_result_type( pub fn math_result_type(
_working_set: &StateWorkingSet, working_set: &mut StateWorkingSet,
lhs: &mut Expression, lhs: &mut Expression,
op: &mut Expression, op: &mut Expression,
rhs: &mut Expression, rhs: &mut Expression,
@ -104,7 +104,7 @@ pub fn math_result_type(
| Type::Filesize, | Type::Filesize,
_, _,
) => { ) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -118,7 +118,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -130,7 +130,7 @@ pub fn math_result_type(
) )
} }
}, },
Operator::Math(Math::Append) => check_append(lhs, rhs, op), Operator::Math(Math::Append) => check_append(working_set, lhs, rhs, op),
Operator::Math(Math::Minus) => match (&lhs.ty, &rhs.ty) { Operator::Math(Math::Minus) => match (&lhs.ty, &rhs.ty) {
(Type::Int, Type::Int) => (Type::Int, None), (Type::Int, Type::Int) => (Type::Int, None),
(Type::Float, Type::Int) => (Type::Float, None), (Type::Float, Type::Int) => (Type::Float, None),
@ -152,7 +152,7 @@ pub fn math_result_type(
(Type::Any, _) => (Type::Any, None), (Type::Any, _) => (Type::Any, None),
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
(Type::Int | Type::Float | Type::Date | Type::Duration | Type::Filesize, _) => { (Type::Int | Type::Float | Type::Date | Type::Duration | Type::Filesize, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -166,7 +166,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -209,7 +209,7 @@ pub fn math_result_type(
| (Type::Duration, _) | (Type::Duration, _)
| (Type::Filesize, _) | (Type::Filesize, _)
| (Type::List(_), _) => { | (Type::List(_), _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -223,7 +223,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -252,7 +252,7 @@ pub fn math_result_type(
(Type::Any, _) => (Type::Any, None), (Type::Any, _) => (Type::Any, None),
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
(Type::Int | Type::Float, _) => { (Type::Int | Type::Float, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -266,7 +266,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -302,7 +302,7 @@ pub fn math_result_type(
(Type::Any, _) => (Type::Any, None), (Type::Any, _) => (Type::Any, None),
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
(Type::Int | Type::Float | Type::Filesize | Type::Duration, _) => { (Type::Int | Type::Float | Type::Filesize | Type::Duration, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -316,7 +316,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -348,7 +348,7 @@ pub fn math_result_type(
(Type::Any, _) => (Type::Any, None), (Type::Any, _) => (Type::Any, None),
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
(Type::Int | Type::Float | Type::Filesize | Type::Duration, _) => { (Type::Int | Type::Float | Type::Filesize | Type::Duration, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -362,7 +362,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -390,7 +390,7 @@ pub fn math_result_type(
// definitions. As soon as that syntax is added this should be removed // definitions. As soon as that syntax is added this should be removed
(a, b) if a == b => (Type::Bool, None), (a, b) if a == b => (Type::Bool, None),
(Type::Bool, _) => { (Type::Bool, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -404,7 +404,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -441,7 +441,7 @@ pub fn math_result_type(
(Type::Any, _) => (Type::Bool, None), (Type::Any, _) => (Type::Bool, None),
(_, Type::Any) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None),
(Type::Int | Type::Float | Type::Duration | Type::Filesize, _) => { (Type::Int | Type::Float | Type::Duration | Type::Filesize, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -455,7 +455,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -491,7 +491,7 @@ pub fn math_result_type(
(Type::Any, _) => (Type::Bool, None), (Type::Any, _) => (Type::Bool, None),
(_, Type::Any) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None),
(Type::Int | Type::Float | Type::Duration | Type::Filesize, _) => { (Type::Int | Type::Float | Type::Duration | Type::Filesize, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -505,7 +505,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -541,7 +541,7 @@ pub fn math_result_type(
(Type::Nothing, _) => (Type::Nothing, None), (Type::Nothing, _) => (Type::Nothing, None),
(_, Type::Nothing) => (Type::Nothing, None), (_, Type::Nothing) => (Type::Nothing, None),
(Type::Int | Type::Float | Type::Duration | Type::Filesize, _) => { (Type::Int | Type::Float | Type::Duration | Type::Filesize, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -555,7 +555,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -591,7 +591,7 @@ pub fn math_result_type(
(Type::Nothing, _) => (Type::Nothing, None), (Type::Nothing, _) => (Type::Nothing, None),
(_, Type::Nothing) => (Type::Nothing, None), (_, Type::Nothing) => (Type::Nothing, None),
(Type::Int | Type::Float | Type::Duration | Type::Filesize, _) => { (Type::Int | Type::Float | Type::Duration | Type::Filesize, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -605,7 +605,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -638,7 +638,7 @@ pub fn math_result_type(
(Type::Custom(a), _) => (Type::Custom(a.clone()), None), (Type::Custom(a), _) => (Type::Custom(a.clone()), None),
(Type::String, _) => { (Type::String, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -652,7 +652,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -673,7 +673,7 @@ pub fn math_result_type(
(Type::Custom(a), _) => (Type::Custom(a.clone()), None), (Type::Custom(a), _) => (Type::Custom(a.clone()), None),
(Type::String, _) => { (Type::String, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -687,7 +687,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -708,7 +708,7 @@ pub fn math_result_type(
(Type::Custom(a), _) => (Type::Custom(a.clone()), None), (Type::Custom(a), _) => (Type::Custom(a.clone()), None),
(Type::String, _) => { (Type::String, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -722,7 +722,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -743,7 +743,7 @@ pub fn math_result_type(
(Type::Custom(a), _) => (Type::Custom(a.clone()), None), (Type::Custom(a), _) => (Type::Custom(a.clone()), None),
(Type::String, _) => { (Type::String, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -757,7 +757,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -781,7 +781,7 @@ pub fn math_result_type(
(Type::Any, _) => (Type::Bool, None), (Type::Any, _) => (Type::Bool, None),
(_, Type::Any) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None),
(Type::Int | Type::Float | Type::String, _) => { (Type::Int | Type::Float | Type::String, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -795,7 +795,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -819,7 +819,7 @@ pub fn math_result_type(
(Type::Any, _) => (Type::Bool, None), (Type::Any, _) => (Type::Bool, None),
(_, Type::Any) => (Type::Bool, None), (_, Type::Any) => (Type::Bool, None),
(Type::Int | Type::Float | Type::String, _) => { (Type::Int | Type::Float | Type::String, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -833,7 +833,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -855,7 +855,7 @@ pub fn math_result_type(
(Type::Any, _) => (Type::Any, None), (Type::Any, _) => (Type::Any, None),
(_, Type::Any) => (Type::Any, None), (_, Type::Any) => (Type::Any, None),
(Type::Int, _) => { (Type::Int, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -869,7 +869,7 @@ pub fn math_result_type(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(
@ -881,7 +881,9 @@ pub fn math_result_type(
) )
} }
}, },
Operator::Assignment(Assignment::AppendAssign) => check_append(lhs, rhs, op), Operator::Assignment(Assignment::AppendAssign) => {
check_append(working_set, lhs, rhs, op)
}
Operator::Assignment(_) => match (&lhs.ty, &rhs.ty) { Operator::Assignment(_) => match (&lhs.ty, &rhs.ty) {
(x, y) if x == y => (Type::Nothing, None), (x, y) if x == y => (Type::Nothing, None),
(Type::Any, _) => (Type::Nothing, None), (Type::Any, _) => (Type::Nothing, None),
@ -894,7 +896,7 @@ pub fn math_result_type(
}, },
}, },
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
@ -1026,6 +1028,7 @@ pub fn check_block_input_output(working_set: &StateWorkingSet, block: &Block) ->
} }
fn check_append( fn check_append(
working_set: &mut StateWorkingSet,
lhs: &Expression, lhs: &Expression,
rhs: &Expression, rhs: &Expression,
op: &mut Expression, op: &mut Expression,
@ -1050,7 +1053,7 @@ fn check_append(
(Type::Binary, Type::Binary) => (Type::Binary, None), (Type::Binary, Type::Binary) => (Type::Binary, None),
(Type::Any, _) | (_, Type::Any) => (Type::Any, None), (Type::Any, _) | (_, Type::Any) => (Type::Any, None),
(Type::Table(_) | Type::String | Type::Binary, _) => { (Type::Table(_) | Type::String | Type::Binary, _) => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationRHS( Some(ParseError::UnsupportedOperationRHS(
@ -1064,7 +1067,7 @@ fn check_append(
) )
} }
_ => { _ => {
*op = Expression::garbage(op.span); *op = Expression::garbage(working_set, op.span);
( (
Type::Any, Type::Any,
Some(ParseError::UnsupportedOperationLHS( Some(ParseError::UnsupportedOperationLHS(

View File

@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-path"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-path" name = "nu-path"
version = "0.94.2" version = "0.94.3"
exclude = ["/fuzz"] exclude = ["/fuzz"]
[lib] [lib]
@ -18,4 +18,4 @@ dirs-next = { workspace = true }
omnipath = { workspace = true } omnipath = { workspace = true }
[target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))'.dependencies] [target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))'.dependencies]
pwd = { workspace = true } pwd = { workspace = true }

View File

@ -5,14 +5,14 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-plugin-core
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-plugin-core" name = "nu-plugin-core"
version = "0.94.2" version = "0.94.3"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.94.2", default-features = false } nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.94.3", default-features = false }
rmp-serde = { workspace = true } rmp-serde = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
@ -25,4 +25,4 @@ default = ["local-socket"]
local-socket = ["interprocess", "nu-plugin-protocol/local-socket"] local-socket = ["interprocess", "nu-plugin-protocol/local-socket"]
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
windows = { workspace = true } windows = { workspace = true }

View File

@ -5,17 +5,17 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-plugin-engi
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-plugin-engine" name = "nu-plugin-engine"
version = "0.94.2" version = "0.94.3"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
nu-system = { path = "../nu-system", version = "0.94.2" } nu-system = { path = "../nu-system", version = "0.94.3" }
nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.94.2" } nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.94.3" }
nu-plugin-core = { path = "../nu-plugin-core", version = "0.94.2", default-features = false } nu-plugin-core = { path = "../nu-plugin-core", version = "0.94.3", default-features = false }
serde = { workspace = true } serde = { workspace = true }
log = { workspace = true } log = { workspace = true }
@ -31,4 +31,4 @@ local-socket = ["nu-plugin-core/local-socket"]
windows = { workspace = true, features = [ windows = { workspace = true, features = [
# For setting process creation flags # For setting process creation flags
"Win32_System_Threading", "Win32_System_Threading",
] } ] }

View File

@ -5,14 +5,14 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-plugin-prot
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-plugin-protocol" name = "nu-plugin-protocol"
version = "0.94.2" version = "0.94.3"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-protocol = { path = "../nu-protocol", version = "0.94.2", features = ["plugin"] } nu-protocol = { path = "../nu-protocol", version = "0.94.3", features = ["plugin"] }
nu-utils = { path = "../nu-utils", version = "0.94.2" } nu-utils = { path = "../nu-utils", version = "0.94.3" }
bincode = "1.3" bincode = "1.3"
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
@ -21,4 +21,4 @@ typetag = "0.2"
[features] [features]
default = ["local-socket"] default = ["local-socket"]
local-socket = [] local-socket = []

View File

@ -1,6 +1,6 @@
[package] [package]
name = "nu-plugin-test-support" name = "nu-plugin-test-support"
version = "0.94.2" version = "0.94.3"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
description = "Testing support for Nushell plugins" description = "Testing support for Nushell plugins"
@ -12,17 +12,17 @@ bench = false
# 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.94.2", features = ["plugin"] } nu-engine = { path = "../nu-engine", version = "0.94.3", features = ["plugin"] }
nu-protocol = { path = "../nu-protocol", version = "0.94.2", features = ["plugin"] } nu-protocol = { path = "../nu-protocol", version = "0.94.3", features = ["plugin"] }
nu-parser = { path = "../nu-parser", version = "0.94.2", features = ["plugin"] } nu-parser = { path = "../nu-parser", version = "0.94.3", features = ["plugin"] }
nu-plugin = { path = "../nu-plugin", version = "0.94.2" } nu-plugin = { path = "../nu-plugin", version = "0.94.3" }
nu-plugin-core = { path = "../nu-plugin-core", version = "0.94.2" } nu-plugin-core = { path = "../nu-plugin-core", version = "0.94.3" }
nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.94.2" } nu-plugin-engine = { path = "../nu-plugin-engine", version = "0.94.3" }
nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.94.2" } nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.94.3" }
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.2" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.94.3" }
nu-ansi-term = { workspace = true } nu-ansi-term = { workspace = true }
similar = "2.5" similar = "2.5"
[dev-dependencies] [dev-dependencies]
typetag = "0.2" typetag = "0.2"
serde = "1.0" serde = "1.0"

View File

@ -5,16 +5,16 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-plugin"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-plugin" name = "nu-plugin"
version = "0.94.2" version = "0.94.3"
[lib] [lib]
bench = false bench = false
[dependencies] [dependencies]
nu-engine = { path = "../nu-engine", version = "0.94.2" } nu-engine = { path = "../nu-engine", version = "0.94.3" }
nu-protocol = { path = "../nu-protocol", version = "0.94.2" } nu-protocol = { path = "../nu-protocol", version = "0.94.3" }
nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.94.2" } nu-plugin-protocol = { path = "../nu-plugin-protocol", version = "0.94.3" }
nu-plugin-core = { path = "../nu-plugin-core", version = "0.94.2", default-features = false } nu-plugin-core = { path = "../nu-plugin-core", version = "0.94.3", default-features = false }
log = { workspace = true } log = { workspace = true }
thiserror = "1.0" thiserror = "1.0"
@ -29,4 +29,4 @@ local-socket = ["nu-plugin-core/local-socket"]
[target.'cfg(target_family = "unix")'.dependencies] [target.'cfg(target_family = "unix")'.dependencies]
# For setting the process group ID (EnterForeground / LeaveForeground) # For setting the process group ID (EnterForeground / LeaveForeground)
nix = { workspace = true, default-features = false, features = ["process"] } nix = { workspace = true, default-features = false, features = ["process"] }

View File

@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-pretty-hex"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-pretty-hex" name = "nu-pretty-hex"
version = "0.94.2" version = "0.94.3"
[lib] [lib]
doctest = false doctest = false
@ -18,4 +18,4 @@ nu-ansi-term = { workspace = true }
[dev-dependencies] [dev-dependencies]
heapless = { version = "0.8", default-features = false } heapless = { version = "0.8", default-features = false }
rand = "0.8" rand = "0.8"

View File

@ -5,7 +5,7 @@ repository = "https://github.com/nushell/nushell/tree/main/crates/nu-protocol"
edition = "2021" edition = "2021"
license = "MIT" license = "MIT"
name = "nu-protocol" name = "nu-protocol"
version = "0.94.2" version = "0.94.3"
# 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,10 +13,10 @@ version = "0.94.2"
bench = false bench = false
[dependencies] [dependencies]
nu-utils = { path = "../nu-utils", version = "0.94.2" } nu-utils = { path = "../nu-utils", version = "0.94.3" }
nu-path = { path = "../nu-path", version = "0.94.2" } nu-path = { path = "../nu-path", version = "0.94.3" }
nu-system = { path = "../nu-system", version = "0.94.2" } nu-system = { path = "../nu-system", version = "0.94.3" }
nu-derive-value = { path = "../nu-derive-value", version = "0.94.2" } nu-derive-value = { path = "../nu-derive-value", version = "0.94.3" }
brotli = { workspace = true, optional = true } brotli = { workspace = true, optional = true }
byte-unit = { version = "5.1", features = [ "serde" ] } byte-unit = { version = "5.1", features = [ "serde" ] }
@ -46,11 +46,11 @@ plugin = [
serde_json = { workspace = true } serde_json = { workspace = true }
strum = "0.26" strum = "0.26"
strum_macros = "0.26" strum_macros = "0.26"
nu-test-support = { path = "../nu-test-support", version = "0.94.2" } nu-test-support = { path = "../nu-test-support", version = "0.94.3" }
pretty_assertions = { workspace = true } pretty_assertions = { workspace = true }
rstest = { workspace = true } rstest = { workspace = true }
tempfile = { workspace = true } tempfile = { workspace = true }
os_pipe = { workspace = true } os_pipe = { workspace = true }
[package.metadata.docs.rs] [package.metadata.docs.rs]
all-features = true all-features = true

View File

@ -338,9 +338,13 @@ impl Call {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::engine::EngineState;
#[test] #[test]
fn argument_span_named() { fn argument_span_named() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let named = Spanned { let named = Spanned {
item: "named".to_string(), item: "named".to_string(),
span: Span::new(2, 3), span: Span::new(2, 3),
@ -349,7 +353,7 @@ mod test {
item: "short".to_string(), item: "short".to_string(),
span: Span::new(5, 7), span: Span::new(5, 7),
}; };
let expr = Expression::garbage(Span::new(11, 13)); let expr = Expression::garbage(&mut working_set, Span::new(11, 13));
let arg = Argument::Named((named.clone(), None, None)); let arg = Argument::Named((named.clone(), None, None));
@ -370,8 +374,11 @@ mod test {
#[test] #[test]
fn argument_span_positional() { fn argument_span_positional() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let span = Span::new(2, 3); let span = Span::new(2, 3);
let expr = Expression::garbage(span); let expr = Expression::garbage(&mut working_set, span);
let arg = Argument::Positional(expr); let arg = Argument::Positional(expr);
assert_eq!(span, arg.span()); assert_eq!(span, arg.span());
@ -379,8 +386,11 @@ mod test {
#[test] #[test]
fn argument_span_unknown() { fn argument_span_unknown() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let span = Span::new(2, 3); let span = Span::new(2, 3);
let expr = Expression::garbage(span); let expr = Expression::garbage(&mut working_set, span);
let arg = Argument::Unknown(expr); let arg = Argument::Unknown(expr);
assert_eq!(span, arg.span()); assert_eq!(span, arg.span());
@ -388,9 +398,12 @@ mod test {
#[test] #[test]
fn call_arguments_span() { fn call_arguments_span() {
let engine_state = EngineState::new();
let mut working_set = StateWorkingSet::new(&engine_state);
let mut call = Call::new(Span::new(0, 1)); let mut call = Call::new(Span::new(0, 1));
call.add_positional(Expression::garbage(Span::new(2, 3))); call.add_positional(Expression::garbage(&mut working_set, Span::new(2, 3)));
call.add_positional(Expression::garbage(Span::new(5, 7))); call.add_positional(Expression::garbage(&mut working_set, Span::new(5, 7)));
assert_eq!(Span::new(2, 7), call.arguments_span()); assert_eq!(Span::new(2, 7), call.arguments_span());
} }

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
ast::{Argument, Block, Expr, ExternalArgument, ImportPattern, MatchPattern, RecordItem}, ast::{Argument, Block, Expr, ExternalArgument, ImportPattern, MatchPattern, RecordItem},
engine::StateWorkingSet, engine::{EngineState, StateWorkingSet},
BlockId, DeclId, Signature, Span, Type, VarId, IN_VARIABLE_ID, BlockId, DeclId, Signature, Span, SpanId, Type, VarId, IN_VARIABLE_ID,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::sync::Arc; use std::sync::Arc;
@ -10,15 +10,18 @@ use std::sync::Arc;
pub struct Expression { pub struct Expression {
pub expr: Expr, pub expr: Expr,
pub span: Span, pub span: Span,
pub span_id: SpanId,
pub ty: Type, pub ty: Type,
pub custom_completion: Option<DeclId>, pub custom_completion: Option<DeclId>,
} }
impl Expression { impl Expression {
pub fn garbage(span: Span) -> Expression { pub fn garbage(working_set: &mut StateWorkingSet, span: Span) -> Expression {
let span_id = working_set.add_span(span);
Expression { Expression {
expr: Expr::Garbage, expr: Expr::Garbage,
span, span,
span_id,
ty: Type::Any, ty: Type::Any,
custom_completion: None, custom_completion: None,
} }
@ -471,4 +474,49 @@ impl Expression {
Expr::VarDecl(_) => {} Expr::VarDecl(_) => {}
} }
} }
pub fn new(working_set: &mut StateWorkingSet, expr: Expr, span: Span, ty: Type) -> Expression {
let span_id = working_set.add_span(span);
Expression {
expr,
span,
span_id,
ty,
custom_completion: None,
}
}
pub fn new_existing(expr: Expr, span: Span, span_id: SpanId, ty: Type) -> Expression {
Expression {
expr,
span,
span_id,
ty,
custom_completion: None,
}
}
pub fn new_unknown(expr: Expr, span: Span, ty: Type) -> Expression {
Expression {
expr,
span,
span_id: SpanId(0),
ty,
custom_completion: None,
}
}
pub fn with_span_id(self, span_id: SpanId) -> Expression {
Expression {
expr: self.expr,
span: self.span,
span_id,
ty: self.ty,
custom_completion: self.custom_completion,
}
}
pub fn span(&self, engine_state: &EngineState) -> Span {
engine_state.get_span(self.span_id)
}
} }

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