Merge branch 'main' into ir

This commit is contained in:
Devyn Cairns 2024-07-09 18:39:23 -07:00
commit 9e86e04af0
393 changed files with 5542 additions and 3796 deletions

View File

@ -36,10 +36,10 @@ jobs:
token: ${{ secrets.WORKFLOW_TOKEN }}
- name: Setup Nushell
uses: hustcer/setup-nu@v3.11
uses: hustcer/setup-nu@v3.12
if: github.repository == 'nushell/nightly'
with:
version: 0.93.0
version: 0.95.0
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
- name: Prepare for Nightly Release
@ -99,13 +99,13 @@ jobs:
extra: msi
os: windows-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-22.04
- target: x86_64-unknown-linux-musl
os: ubuntu-20.04
os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-22.04
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04
os: ubuntu-22.04
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest
@ -128,9 +128,9 @@ jobs:
rustflags: ''
- name: Setup Nushell
uses: hustcer/setup-nu@v3.11
uses: hustcer/setup-nu@v3.12
with:
version: 0.93.0
version: 0.95.0
- name: Release Nu Binary
id: nu
@ -161,7 +161,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release
# Create a release only in nushell/nightly repo
- name: Publish Archive
uses: softprops/action-gh-release@v2.0.5
uses: softprops/action-gh-release@v2.0.6
if: ${{ startsWith(github.repository, 'nushell/nightly') }}
with:
prerelease: true
@ -186,9 +186,9 @@ jobs:
ref: main
- name: Setup Nushell
uses: hustcer/setup-nu@v3.11
uses: hustcer/setup-nu@v3.12
with:
version: 0.93.0
version: 0.95.0
# Keep the last a few releases
- name: Delete Older Releases

View File

@ -49,13 +49,13 @@ jobs:
extra: msi
os: windows-latest
- target: x86_64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-22.04
- target: x86_64-unknown-linux-musl
os: ubuntu-20.04
os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu
os: ubuntu-20.04
os: ubuntu-22.04
- target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04
os: ubuntu-22.04
- target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest
@ -76,9 +76,9 @@ jobs:
rustflags: ''
- name: Setup Nushell
uses: hustcer/setup-nu@v3.11
uses: hustcer/setup-nu@v3.12
with:
version: 0.93.0
version: 0.95.0
- name: Release Nu Binary
id: nu
@ -91,7 +91,7 @@ jobs:
# REF: https://github.com/marketplace/actions/gh-release
- name: Publish Archive
uses: softprops/action-gh-release@v2.0.5
uses: softprops/action-gh-release@v2.0.6
if: ${{ startsWith(github.ref, 'refs/tags/') }}
with:
draft: true

View File

@ -10,4 +10,4 @@ jobs:
uses: actions/checkout@v4.1.7
- name: Check spelling
uses: crate-ci/typos@v1.22.7
uses: crate-ci/typos@v1.23.1

320
Cargo.lock generated
View File

@ -1117,6 +1117,36 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "curl"
version = "0.4.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6"
dependencies = [
"curl-sys",
"libc",
"openssl-probe",
"openssl-sys",
"schannel",
"socket2",
"windows-sys 0.52.0",
]
[[package]]
name = "curl-sys"
version = "0.4.73+curl-8.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "450ab250ecf17227c39afb9a2dd9261dc0035cb80f2612472fc0c4aac2dcb84d"
dependencies = [
"cc",
"libc",
"libz-sys",
"openssl-sys",
"pkg-config",
"vcpkg",
"windows-sys 0.52.0",
]
[[package]]
name = "deranged"
version = "0.3.11"
@ -1148,6 +1178,12 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "deunicode"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
[[package]]
name = "dialoguer"
version = "0.11.0"
@ -1330,6 +1366,16 @@ dependencies = [
"syn 2.0.60",
]
[[package]]
name = "env_filter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea"
dependencies = [
"log",
"regex",
]
[[package]]
name = "env_logger"
version = "0.8.4"
@ -1340,6 +1386,19 @@ dependencies = [
"regex",
]
[[package]]
name = "env_logger"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9"
dependencies = [
"anstream",
"anstyle",
"env_filter",
"humantime",
"log",
]
[[package]]
name = "equivalent"
version = "1.0.1"
@ -1854,12 +1913,26 @@ checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
dependencies = [
"log",
"mac",
"markup5ever",
"markup5ever 0.11.0",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "html5ever"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4"
dependencies = [
"log",
"mac",
"markup5ever 0.12.1",
"proc-macro2",
"quote",
"syn 2.0.60",
]
[[package]]
name = "http"
version = "0.2.12"
@ -1906,6 +1979,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "hyper"
version = "0.14.28"
@ -1995,12 +2074,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "indoc"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]]
name = "inotify"
version = "0.9.6"
@ -2309,9 +2382,9 @@ dependencies = [
[[package]]
name = "libloading"
version = "0.8.3"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [
"cfg-if",
"windows-targets 0.52.5",
@ -2523,6 +2596,32 @@ dependencies = [
"tendril",
]
[[package]]
name = "markup5ever"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45"
dependencies = [
"log",
"phf 0.11.2",
"phf_codegen 0.11.2",
"string_cache",
"string_cache_codegen",
"tendril",
]
[[package]]
name = "markup5ever_rcdom"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edaa21ab3701bfee5099ade5f7e1f84553fd19228cf332f13cd6e964bf59be18"
dependencies = [
"html5ever 0.27.0",
"markup5ever 0.12.1",
"tendril",
"xml5ever",
]
[[package]]
name = "md-5"
version = "0.10.6"
@ -2945,6 +3044,7 @@ dependencies = [
"chrono-tz 0.8.6",
"crossterm",
"csv",
"deunicode",
"dialoguer",
"digest",
"dirs-next",
@ -3400,8 +3500,10 @@ version = "0.95.1"
dependencies = [
"chrono",
"chrono-tz 0.9.0",
"env_logger 0.11.3",
"fancy-regex",
"indexmap",
"log",
"mimalloc",
"nu-cmd-lang",
"nu-command",
@ -3411,6 +3513,7 @@ dependencies = [
"nu-plugin",
"nu-plugin-test-support",
"nu-protocol",
"nu-utils",
"num",
"polars",
"polars-arrow",
@ -3419,7 +3522,7 @@ dependencies = [
"polars-plan",
"polars-utils",
"serde",
"sqlparser 0.47.0",
"sqlparser",
"tempfile",
"typetag",
"uuid",
@ -3433,8 +3536,11 @@ dependencies = [
"nu-plugin",
"nu-protocol",
"scraper",
"serde",
"serde_json",
"sxd-document",
"sxd-xpath",
"webpage",
]
[[package]]
@ -3662,9 +3768,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "open"
version = "5.1.2"
version = "5.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "449f0ff855d85ddbf1edd5b646d65249ead3f5e422aaa86b7d2d0b049b103e32"
checksum = "9d2c909a3fce3bd80efef4cd1c6c056bd9376a8fe06fcfdbebaf32cb485a7e37"
dependencies = [
"is-wsl",
"libc",
@ -4029,9 +4135,9 @@ dependencies = [
[[package]]
name = "polars"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e148396dca5496566880fa19374f3f789a29db94e3eb458afac1497b4bac5442"
checksum = "ce49e10a756f68eb99c102c6b2a0cbc0c583a0fa7263536ad0913d94be878d2d"
dependencies = [
"getrandom",
"polars-arrow",
@ -4049,9 +4155,9 @@ dependencies = [
[[package]]
name = "polars-arrow"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cb5e11cd0752ae022fa6ca3afa50a14b0301b7ce53c0135828fbb0f4fa8303e"
checksum = "b436f83f62e864f0d91871e26528f2c5552c7cf07c8d77547f1b8e3fde22bd27"
dependencies = [
"ahash 0.8.11",
"atoi",
@ -4097,9 +4203,9 @@ dependencies = [
[[package]]
name = "polars-compute"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89fc4578f826234cdecb782952aa9c479dc49373f81694a7b439c70b6f609ba0"
checksum = "f6758f834f07e622a2f859bebb542b2b7f8879b8704dbb2b2bbab460ddcdca4b"
dependencies = [
"bytemuck",
"either",
@ -4113,9 +4219,9 @@ dependencies = [
[[package]]
name = "polars-core"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e490c6bace1366a558feea33d1846f749a8ca90bd72a6748752bc65bb4710b2a"
checksum = "7ed262e9bdda15a12a9bfcfc9200bec5253335633dbd86cf5b94fda0194244b3"
dependencies = [
"ahash 0.8.11",
"bitflags 2.5.0",
@ -4147,9 +4253,9 @@ dependencies = [
[[package]]
name = "polars-error"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08888f58e61599b00f5ea0c2ccdc796b54b9859559cc0d4582733509451fa01a"
checksum = "53e1707a17475ba5e74c349154b415e3148a1a275e395965427971b5e53ad621"
dependencies = [
"avro-schema",
"polars-arrow-format",
@ -4160,9 +4266,9 @@ dependencies = [
[[package]]
name = "polars-expr"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4173591920fe56ad55af025f92eb0d08421ca85705c326a640c43856094e3484"
checksum = "31a9688d5842e7a7fbad88e67a174778794a91d97d3bba1b3c09dd1656fee3b2"
dependencies = [
"ahash 0.8.11",
"bitflags 2.5.0",
@ -4180,9 +4286,9 @@ dependencies = [
[[package]]
name = "polars-io"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5842896aea46d975b425d63f156f412aed3cfde4c257b64fb1f43ceea288074e"
checksum = "18798dacd94fb9263f65f63f0feab0908675422646d6f7fc37043b85ff6dca35"
dependencies = [
"ahash 0.8.11",
"async-trait",
@ -4221,9 +4327,9 @@ dependencies = [
[[package]]
name = "polars-json"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "160cbad0145b93ac6a88639aadfa6f7d7c769d05a8674f9b7e895b398cae9901"
checksum = "044ea319f667efbf8007c4c38171c2956e0e7f9b078eb66e31e82f80d1e14b51"
dependencies = [
"ahash 0.8.11",
"chrono",
@ -4242,19 +4348,21 @@ dependencies = [
[[package]]
name = "polars-lazy"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e805ea2ebbc6b7749b0afb31b7fc5d32b42b57ba29b984549d43d3a16114c4a5"
checksum = "74a11994c2211f2e99d9ac31776fd7c2c0607d5fe62d5b5db9e396f7d663f3d5"
dependencies = [
"ahash 0.8.11",
"bitflags 2.5.0",
"glob",
"memchr",
"once_cell",
"polars-arrow",
"polars-core",
"polars-expr",
"polars-io",
"polars-json",
"polars-mem-engine",
"polars-ops",
"polars-pipe",
"polars-plan",
@ -4266,10 +4374,29 @@ dependencies = [
]
[[package]]
name = "polars-ops"
version = "0.40.0"
name = "polars-mem-engine"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b0aed7e169c81b98457641cf82b251f52239a668916c2e683abd1f38df00d58"
checksum = "5acd5fde6fadaddfcae3227ec5b64121007928f8e68870c80653438e20c1c587"
dependencies = [
"polars-arrow",
"polars-core",
"polars-error",
"polars-expr",
"polars-io",
"polars-json",
"polars-ops",
"polars-plan",
"polars-time",
"polars-utils",
"rayon",
]
[[package]]
name = "polars-ops"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4170c59e974727941edfb722f6d430ed623be9e7f30581ee00832c907f1b9fd"
dependencies = [
"ahash 0.8.11",
"argminmax",
@ -4303,9 +4430,9 @@ dependencies = [
[[package]]
name = "polars-parquet"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c70670a9e51cac66d0e77fd20b5cc957dbcf9f2660d410633862bb72f846d5b8"
checksum = "c684638c36c60c691d707d414249fe8af4a19a35a39d418464b140fe23732e5d"
dependencies = [
"ahash 0.8.11",
"async-stream",
@ -4318,9 +4445,11 @@ dependencies = [
"num-traits",
"parquet-format-safe",
"polars-arrow",
"polars-compute",
"polars-error",
"polars-utils",
"seq-macro",
"serde",
"simdutf8",
"snap",
"streaming-decompression",
@ -4329,9 +4458,9 @@ dependencies = [
[[package]]
name = "polars-pipe"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a40ae1b3c74ee07e2d1f7cbf56c5d6e15969e45d9b6f0903bd2acaf783ba436"
checksum = "832af9fbebc4c074d95fb19e1ef9e1bf37c343641238c2476febff296a7028ea"
dependencies = [
"crossbeam-channel",
"crossbeam-queue",
@ -4355,9 +4484,9 @@ dependencies = [
[[package]]
name = "polars-plan"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8daa3541ae7e9af311a4389bc2b21f83349c34c723cc67fa524cdefdaa172d90"
checksum = "801390ea815c05c9cf8337f3148090c9c10c9595a839fa0706b77cc2405b4466"
dependencies = [
"ahash 0.8.11",
"bytemuck",
@ -4385,9 +4514,9 @@ dependencies = [
[[package]]
name = "polars-row"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "deb285f2f3a65b00dd06bef16bb9f712dbb5478f941dab5cf74f9f016d382e40"
checksum = "dee955e91b605fc91db4d0a8ea02609d3a09ff79256d905214a2a6f758cd6f7b"
dependencies = [
"bytemuck",
"polars-arrow",
@ -4397,9 +4526,9 @@ dependencies = [
[[package]]
name = "polars-sql"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a724f699d194cb02c25124d3832f7d4d77f387f1a89ee42f6b9e88ec561d4ad9"
checksum = "d89c00a4b399501d5bd478e8e8022b9391047fe8570324ecba20c4e4833c0e87"
dependencies = [
"hex",
"once_cell",
@ -4407,18 +4536,20 @@ dependencies = [
"polars-core",
"polars-error",
"polars-lazy",
"polars-ops",
"polars-plan",
"polars-time",
"rand",
"serde",
"serde_json",
"sqlparser 0.39.0",
"sqlparser",
]
[[package]]
name = "polars-time"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87ebec238d8b6200d9f0c3ce411c8441e950bd5a7df7806b8172d06c1d5a4b97"
checksum = "9689b3aff99d64befe300495528bdc44c36d2656c3a8b242a790d4f43df027fc"
dependencies = [
"atoi",
"bytemuck",
@ -4438,9 +4569,9 @@ dependencies = [
[[package]]
name = "polars-utils"
version = "0.40.0"
version = "0.41.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34e1a907c63abf71e5f21467e2e4ff748896c28196746f631c6c25512ec6102c"
checksum = "12081e346983a91e26f395597e1d53dea1b4ecd694653aee1cc402d2fae01f04"
dependencies = [
"ahash 0.8.11",
"bytemuck",
@ -4676,7 +4807,7 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
dependencies = [
"env_logger",
"env_logger 0.8.4",
"log",
"rand",
]
@ -4755,21 +4886,21 @@ dependencies = [
[[package]]
name = "ratatui"
version = "0.26.2"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a564a852040e82671dc50a37d88f3aa83bbc690dfc6844cfe7a2591620206a80"
checksum = "f44c9e68fd46eda15c646fbb85e1040b657a58cdc8c98db1d97a55930d991eef"
dependencies = [
"bitflags 2.5.0",
"cassowary",
"compact_str",
"crossterm",
"indoc",
"itertools 0.12.1",
"lru",
"paste",
"stability",
"strum",
"unicode-segmentation",
"unicode-truncate",
"unicode-width",
]
@ -4860,8 +4991,7 @@ dependencies = [
[[package]]
name = "reedline"
version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abf59e4c97b5049ba96b052cdb652368305a2eddcbce9bf1c16f9d003139eeea"
source = "git+https://github.com/nushell/reedline?branch=main#480059a3f52cf919341cda88e8c544edd846bc73"
dependencies = [
"arboard",
"chrono",
@ -5228,7 +5358,7 @@ dependencies = [
"ahash 0.8.11",
"cssparser",
"ego-tree",
"html5ever",
"html5ever 0.26.0",
"once_cell",
"selectors",
"tendril",
@ -5444,9 +5574,9 @@ dependencies = [
[[package]]
name = "shadow-rs"
version = "0.28.0"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d75516bdaee8f640543ad1f6e292448c23ce57143f812c3736ab4b0874383df"
checksum = "0a600f795d0894cda22235b44eea4b85c2a35b405f65523645ac8e35b306817a"
dependencies = [
"const_format",
"is_debug",
@ -5591,15 +5721,6 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "sqlparser"
version = "0.39.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "743b4dc2cbde11890ccb254a8fc9d537fa41b36da00de2a1c5e9848c9bc42bd7"
dependencies = [
"log",
]
[[package]]
name = "sqlparser"
version = "0.47.0"
@ -6317,6 +6438,16 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202"
[[package]]
name = "unicode-truncate"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5fbabedabe362c618c714dbefda9927b5afc8e2a8102f47f081089a9019226"
dependencies = [
"itertools 0.12.1",
"unicode-width",
]
[[package]]
name = "unicode-width"
version = "0.1.12"
@ -6384,9 +6515,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uu_cp"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c31fc5c95f7668999e129464a29e9080f69ba01ccf7a0ae43ff2cfdb15baa340"
checksum = "6fb99d355ccb02e8c514e4a1d93e4aa4eedea9837de24635dfd24c165971444e"
dependencies = [
"clap",
"filetime",
@ -6400,9 +6531,9 @@ dependencies = [
[[package]]
name = "uu_mkdir"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "496d95e0e3121e4d424ba62019eb84a6f1102213ca8ca16c0a2f8c652c7236c3"
checksum = "219588fbc146f18188781208ac4034616c51cf151677b4e1f9caf63ca8a7f2cf"
dependencies = [
"clap",
"uucore",
@ -6410,9 +6541,9 @@ dependencies = [
[[package]]
name = "uu_mktemp"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28a0d9744bdc28ceaf13f70b959bacded91aedfd008402d72fa1e3224158653"
checksum = "b1e79ad2c5911908fce23a6069c52ca82e1997e2ed4bf6abf2d867c79c3dc73f"
dependencies = [
"clap",
"rand",
@ -6422,9 +6553,9 @@ dependencies = [
[[package]]
name = "uu_mv"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53680908b01c5ac3cc0ee8a376de3e51a36dde2c5a5227a115a3d0977cc4539b"
checksum = "cd57c8d02f8a99ed56ed9f6fddab403ee0e2bf9e8f3a5ca8f0f9e4d6e3e392a0"
dependencies = [
"clap",
"fs_extra",
@ -6434,9 +6565,9 @@ dependencies = [
[[package]]
name = "uu_uname"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7f4125fb4f286313bca8f222abaefe39db54d65179ea788c91ebd3162345f4e"
checksum = "ad1ca90f9b292bccaad0de70e6feccac5182c6713a5e1ca72d97bf3555b608b4"
dependencies = [
"clap",
"platform-info",
@ -6445,9 +6576,9 @@ dependencies = [
[[package]]
name = "uu_whoami"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f7b313901a15cfde2d88f434fcd077903d690f73cc36d1cec20f47906960aec"
checksum = "bc7c52e42e0425710461700adc1063f468f2ba8a8ff83ee69ba661095ab7b77a"
dependencies = [
"clap",
"libc",
@ -6457,9 +6588,9 @@ dependencies = [
[[package]]
name = "uucore"
version = "0.0.26"
version = "0.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb2ea2f77699e5ff5c7e001af588ceb34cae8b5f9af5496bea5a6476aaa8e780"
checksum = "7b54aad02cf7e96f5fafabb6b836efa73eef934783b17530095a29ffd4fdc154"
dependencies = [
"clap",
"dunce",
@ -6496,9 +6627,9 @@ checksum = "425a23c7b7145bc7620c9c445817c37b1f78b6790aee9f208133f3c028975b60"
[[package]]
name = "uuid"
version = "1.8.0"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439"
dependencies = [
"getrandom",
"serde",
@ -6741,6 +6872,20 @@ dependencies = [
"pkg-config",
]
[[package]]
name = "webpage"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70862efc041d46e6bbaa82bb9c34ae0596d090e86cbd14bd9e93b36ee6802eac"
dependencies = [
"curl",
"html5ever 0.27.0",
"markup5ever_rcdom",
"serde",
"serde_json",
"url",
]
[[package]]
name = "which"
version = "6.0.1"
@ -7153,6 +7298,17 @@ dependencies = [
"rustix",
]
[[package]]
name = "xml5ever"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bbb26405d8e919bc1547a5aa9abc95cbfa438f04844f5fdd9dc7596b748bf69"
dependencies = [
"log",
"mac",
"markup5ever 0.12.1",
]
[[package]]
name = "xxhash-rust"
version = "0.8.10"

View File

@ -80,6 +80,7 @@ crossbeam-channel = "0.5.8"
crossterm = "0.27"
csv = "1.3"
ctrlc = "3.4"
deunicode = "1.6.0"
dialoguer = { default-features = false, version = "0.11" }
digest = { default-features = false, version = "0.10" }
dirs-next = "2.0"
@ -119,7 +120,7 @@ num-format = "0.4"
num-traits = "0.2"
omnipath = "0.1"
once_cell = "1.18"
open = "5.1"
open = "5.2"
os_pipe = { version = "1.2", features = ["io_safety"] }
pathdiff = "0.2"
percent-encoding = "2"
@ -165,14 +166,14 @@ unicode-segmentation = "1.11"
unicode-width = "0.1"
ureq = { version = "2.9", default-features = false }
url = "2.2"
uu_cp = "0.0.26"
uu_mkdir = "0.0.26"
uu_mktemp = "0.0.26"
uu_mv = "0.0.26"
uu_whoami = "0.0.26"
uu_uname = "0.0.26"
uucore = "0.0.26"
uuid = "1.8.0"
uu_cp = "0.0.27"
uu_mkdir = "0.0.27"
uu_mktemp = "0.0.27"
uu_mv = "0.0.27"
uu_whoami = "0.0.27"
uu_uname = "0.0.27"
uucore = "0.0.27"
uuid = "1.9.1"
v_htmlescape = "0.15.0"
wax = "0.6"
which = "6.0.0"
@ -196,7 +197,6 @@ nu-protocol = { path = "./crates/nu-protocol", version = "0.95.1" }
nu-std = { path = "./crates/nu-std", version = "0.95.1" }
nu-system = { path = "./crates/nu-system", version = "0.95.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.95.1" }
reedline = { workspace = true, features = ["bashisms", "sqlite"] }
crossterm = { workspace = true }
@ -304,7 +304,7 @@ bench = false
# To use a development version of a dependency please use a global override here
# changing versions in each sub-crate of the workspace is tedious
[patch.crates-io]
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
# Run all benchmarks with `cargo bench`

View File

@ -4,11 +4,14 @@ use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
use nu_protocol::{
engine::{EngineState, Stack},
PipelineData, Span, Spanned, Value,
PipelineData, Signals, Span, Spanned, Value,
};
use nu_std::load_standard_library;
use nu_utils::{get_default_config, get_default_env};
use std::rc::Rc;
use std::{
rc::Rc,
sync::{atomic::AtomicBool, Arc},
};
use std::hint::black_box;
@ -248,14 +251,12 @@ fn bench_eval_interleave(n: i32) -> impl IntoBenchmarks {
)
}
fn bench_eval_interleave_with_ctrlc(n: i32) -> impl IntoBenchmarks {
fn bench_eval_interleave_with_interrupt(n: i32) -> impl IntoBenchmarks {
let mut engine = setup_engine();
engine.ctrlc = Some(std::sync::Arc::new(std::sync::atomic::AtomicBool::new(
false,
)));
engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
let stack = Stack::new();
bench_command(
&format!("eval_interleave_with_ctrlc_{n}"),
&format!("eval_interleave_with_interrupt_{n}"),
&format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
stack,
engine,
@ -443,9 +444,9 @@ tango_benchmarks!(
bench_eval_interleave(100),
bench_eval_interleave(1_000),
bench_eval_interleave(10_000),
bench_eval_interleave_with_ctrlc(100),
bench_eval_interleave_with_ctrlc(1_000),
bench_eval_interleave_with_ctrlc(10_000),
bench_eval_interleave_with_interrupt(100),
bench_eval_interleave_with_interrupt(1_000),
bench_eval_interleave_with_interrupt(10_000),
// For
bench_eval_for(1),
bench_eval_for(10),

View File

@ -47,7 +47,7 @@ impl Command for History {
if let Some(config_path) = nu_path::config_dir() {
let clear = call.has_flag(engine_state, stack, "clear")?;
let long = call.has_flag(engine_state, stack, "long")?;
let ctrlc = engine_state.ctrlc.clone();
let signals = engine_state.signals().clone();
let mut history_path = config_path;
history_path.push("nushell");
@ -107,7 +107,7 @@ impl Command for History {
file: history_path.display().to_string(),
span: head,
})?
.into_pipeline_data(head, ctrlc)),
.into_pipeline_data(head, signals)),
HistoryFileFormat::Sqlite => Ok(history_reader
.and_then(|h| {
h.search(SearchQuery::everything(SearchDirection::Forward, None))
@ -122,7 +122,7 @@ impl Command for History {
file: history_path.display().to_string(),
span: head,
})?
.into_pipeline_data(head, ctrlc)),
.into_pipeline_data(head, signals)),
}
}
} else {

View File

@ -1,13 +1,12 @@
use crate::completions::{CompletionOptions, SortBy};
use crate::completions::CompletionOptions;
use nu_protocol::{
engine::{Stack, StateWorkingSet},
levenshtein_distance, Span,
Span,
};
use reedline::Suggestion;
// Completer trait represents the three stages of the completion
// fetch, filter and sort
pub trait Completer {
/// Fetch, filter, and sort completions
#[allow(clippy::too_many_arguments)]
fn fetch(
&mut self,
@ -19,32 +18,6 @@ pub trait Completer {
pos: usize,
options: &CompletionOptions,
) -> Vec<SemanticSuggestion>;
fn get_sort_by(&self) -> SortBy {
SortBy::Ascending
}
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
let mut filtered_items = items;
// Sort items
match self.get_sort_by() {
SortBy::LevenshteinDistance => {
filtered_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
a_distance.cmp(&b_distance)
});
}
SortBy::Ascending => {
filtered_items.sort_by(|a, b| a.suggestion.value.cmp(&b.suggestion.value));
}
SortBy::None => {}
};
filtered_items
}
}
#[derive(Debug, Default, PartialEq)]

View File

@ -9,7 +9,7 @@ use nu_protocol::{
};
use reedline::Suggestion;
use super::SemanticSuggestion;
use super::{completion_common::sort_suggestions, SemanticSuggestion};
pub struct CommandCompletion {
flattened: Vec<(Span, FlatShape)>,
@ -161,7 +161,7 @@ impl Completer for CommandCompletion {
&mut self,
working_set: &StateWorkingSet,
_stack: &Stack,
_prefix: Vec<u8>,
prefix: Vec<u8>,
span: Span,
offset: usize,
pos: usize,
@ -198,7 +198,11 @@ impl Completer for CommandCompletion {
};
if !subcommands.is_empty() {
return subcommands;
return sort_suggestions(
&String::from_utf8_lossy(&prefix),
subcommands,
SortBy::LevenshteinDistance,
);
}
let config = working_set.get_config();
@ -223,11 +227,11 @@ impl Completer for CommandCompletion {
vec![]
};
subcommands.into_iter().chain(commands).collect::<Vec<_>>()
}
fn get_sort_by(&self) -> SortBy {
SortBy::LevenshteinDistance
sort_suggestions(
&String::from_utf8_lossy(&prefix),
commands,
SortBy::LevenshteinDistance,
)
}
}

View File

@ -51,8 +51,7 @@ impl NuCompleter {
..Default::default()
};
// Fetch
let mut suggestions = completer.fetch(
completer.fetch(
working_set,
&self.stack,
prefix.clone(),
@ -60,12 +59,7 @@ impl NuCompleter {
offset,
pos,
&options,
);
// Sort
suggestions = completer.sort(suggestions, prefix);
suggestions
)
}
fn external_completion(

View File

@ -1,16 +1,21 @@
use crate::completions::{matches, CompletionOptions};
use crate::{
completions::{matches, CompletionOptions},
SemanticSuggestion,
};
use nu_ansi_term::Style;
use nu_engine::env_to_string;
use nu_path::{expand_to_real_path, home_dir};
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
Span,
levenshtein_distance, Span,
};
use nu_utils::get_ls_colors;
use std::path::{
is_separator, Component, Path, PathBuf, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR,
};
use super::SortBy;
#[derive(Clone, Default)]
pub struct PathBuiltFromString {
parts: Vec<String>,
@ -45,6 +50,7 @@ fn complete_rec(
return completions;
};
let mut entries = Vec::new();
for entry in result.filter_map(|e| e.ok()) {
let entry_name = entry.file_name().to_string_lossy().into_owned();
let entry_isdir = entry.path().is_dir();
@ -53,20 +59,26 @@ fn complete_rec(
built.isdir = entry_isdir;
if !dir || entry_isdir {
match partial.split_first() {
Some((base, rest)) => {
if matches(base, &entry_name, options) {
if !rest.is_empty() || isdir {
completions
.extend(complete_rec(rest, &built, cwd, options, dir, isdir));
} else {
completions.push(built);
}
entries.push((entry_name, built));
}
}
let prefix = partial.first().unwrap_or(&"");
let sorted_entries = sort_completions(prefix, entries, SortBy::Ascending, |(entry, _)| entry);
for (entry_name, built) in sorted_entries {
match partial.split_first() {
Some((base, rest)) => {
if matches(base, &entry_name, options) {
if !rest.is_empty() || isdir {
completions.extend(complete_rec(rest, &built, cwd, options, dir, isdir));
} else {
completions.push(built);
}
}
None => {
completions.push(built);
}
}
None => {
completions.push(built);
}
}
}
@ -256,3 +268,38 @@ pub fn adjust_if_intermediate(
readjusted,
}
}
/// Convenience function to sort suggestions using [`sort_completions`]
pub fn sort_suggestions(
prefix: &str,
items: Vec<SemanticSuggestion>,
sort_by: SortBy,
) -> Vec<SemanticSuggestion> {
sort_completions(prefix, items, sort_by, |it| &it.suggestion.value)
}
/// # Arguments
/// * `prefix` - What the user's typed, for sorting by Levenshtein distance
pub fn sort_completions<T>(
prefix: &str,
mut items: Vec<T>,
sort_by: SortBy,
get_value: fn(&T) -> &str,
) -> Vec<T> {
// Sort items
match sort_by {
SortBy::LevenshteinDistance => {
items.sort_by(|a, b| {
let a_distance = levenshtein_distance(prefix, get_value(a));
let b_distance = levenshtein_distance(prefix, get_value(b));
a_distance.cmp(&b_distance)
});
}
SortBy::Ascending => {
items.sort_by(|a, b| get_value(a).cmp(get_value(b)));
}
SortBy::None => {}
};
items
}

View File

@ -12,6 +12,8 @@ use nu_protocol::{
use nu_utils::IgnoreCaseExt;
use std::collections::HashMap;
use super::completion_common::sort_suggestions;
pub struct CustomCompletion {
stack: Stack,
decl_id: usize,
@ -122,15 +124,12 @@ impl Completer for CustomCompletion {
})
.unwrap_or_default();
if let Some(custom_completion_options) = custom_completion_options {
let suggestions = if let Some(custom_completion_options) = custom_completion_options {
filter(&prefix, suggestions, &custom_completion_options)
} else {
filter(&prefix, suggestions, completion_options)
}
}
fn get_sort_by(&self) -> SortBy {
self.sort_by
};
sort_suggestions(&String::from_utf8_lossy(&prefix), suggestions, self.sort_by)
}
}

View File

@ -1,14 +1,14 @@
use crate::completions::{
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
Completer, CompletionOptions, SortBy,
Completer, CompletionOptions,
};
use nu_ansi_term::Style;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
levenshtein_distance, Span,
Span,
};
use reedline::Suggestion;
use std::path::{Path, MAIN_SEPARATOR as SEP};
use std::path::Path;
use super::SemanticSuggestion;
@ -36,7 +36,7 @@ impl Completer for DirectoryCompletion {
// Filter only the folders
#[allow(deprecated)]
let output: Vec<_> = directory_completion(
let items: Vec<_> = directory_completion(
span,
&prefix,
&working_set.permanent_state.current_work_dir(),
@ -62,41 +62,11 @@ impl Completer for DirectoryCompletion {
})
.collect();
output
}
// Sort results prioritizing the non hidden folders
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
// Sort items
let mut sorted_items = items;
match self.get_sort_by() {
SortBy::Ascending => {
sorted_items.sort_by(|a, b| {
// Ignore trailing slashes in folder names when sorting
a.suggestion
.value
.trim_end_matches(SEP)
.cmp(b.suggestion.value.trim_end_matches(SEP))
});
}
SortBy::LevenshteinDistance => {
sorted_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
a_distance.cmp(&b_distance)
});
}
_ => (),
}
// Separate the results between hidden and non hidden
let mut hidden: Vec<SemanticSuggestion> = vec![];
let mut non_hidden: Vec<SemanticSuggestion> = vec![];
for item in sorted_items.into_iter() {
for item in items.into_iter() {
let item_path = Path::new(&item.suggestion.value);
if let Some(value) = item_path.file_name() {

View File

@ -1,4 +1,4 @@
use crate::completions::{file_path_completion, Completer, CompletionOptions, SortBy};
use crate::completions::{file_path_completion, Completer, CompletionOptions};
use nu_protocol::{
engine::{Stack, StateWorkingSet},
Span,
@ -6,7 +6,7 @@ use nu_protocol::{
use reedline::Suggestion;
use std::path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR};
use super::SemanticSuggestion;
use super::{completion_common::sort_suggestions, SemanticSuggestion, SortBy};
#[derive(Clone, Default)]
pub struct DotNuCompletion {}
@ -131,10 +131,6 @@ impl Completer for DotNuCompletion {
})
.collect();
output
}
fn get_sort_by(&self) -> SortBy {
SortBy::LevenshteinDistance
sort_suggestions(&prefix_str, output, SortBy::Ascending)
}
}

View File

@ -1,15 +1,15 @@
use crate::completions::{
completion_common::{adjust_if_intermediate, complete_item, AdjustView},
Completer, CompletionOptions, SortBy,
Completer, CompletionOptions,
};
use nu_ansi_term::Style;
use nu_protocol::{
engine::{EngineState, Stack, StateWorkingSet},
levenshtein_distance, Span,
Span,
};
use nu_utils::IgnoreCaseExt;
use reedline::Suggestion;
use std::path::{Path, MAIN_SEPARATOR as SEP};
use std::path::Path;
use super::SemanticSuggestion;
@ -40,7 +40,7 @@ impl Completer for FileCompletion {
} = adjust_if_intermediate(&prefix, working_set, span);
#[allow(deprecated)]
let output: Vec<_> = complete_item(
let items: Vec<_> = complete_item(
readjusted,
span,
&prefix,
@ -67,41 +67,13 @@ impl Completer for FileCompletion {
})
.collect();
output
}
// Sort results prioritizing the non hidden folders
fn sort(&self, items: Vec<SemanticSuggestion>, prefix: Vec<u8>) -> Vec<SemanticSuggestion> {
let prefix_str = String::from_utf8_lossy(&prefix).to_string();
// Sort items
let mut sorted_items = items;
match self.get_sort_by() {
SortBy::Ascending => {
sorted_items.sort_by(|a, b| {
// Ignore trailing slashes in folder names when sorting
a.suggestion
.value
.trim_end_matches(SEP)
.cmp(b.suggestion.value.trim_end_matches(SEP))
});
}
SortBy::LevenshteinDistance => {
sorted_items.sort_by(|a, b| {
let a_distance = levenshtein_distance(&prefix_str, &a.suggestion.value);
let b_distance = levenshtein_distance(&prefix_str, &b.suggestion.value);
a_distance.cmp(&b_distance)
});
}
_ => (),
}
// Sort results prioritizing the non hidden folders
// Separate the results between hidden and non hidden
let mut hidden: Vec<SemanticSuggestion> = vec![];
let mut non_hidden: Vec<SemanticSuggestion> = vec![];
for item in sorted_items.into_iter() {
for item in items.into_iter() {
let item_path = Path::new(&item.suggestion.value);
if let Some(value) = item_path.file_name() {

View File

@ -1,4 +1,6 @@
use crate::completions::{Completer, CompletionOptions};
use crate::completions::{
completion_common::sort_suggestions, Completer, CompletionOptions, SortBy,
};
use nu_protocol::{
ast::{Expr, Expression},
engine::{Stack, StateWorkingSet},
@ -90,7 +92,7 @@ impl Completer for FlagCompletion {
}
}
return output;
return sort_suggestions(&String::from_utf8_lossy(&prefix), output, SortBy::Ascending);
}
vec![]

View File

@ -9,6 +9,8 @@ use nu_protocol::{
use reedline::Suggestion;
use std::str;
use super::{completion_common::sort_suggestions, SortBy};
#[derive(Clone)]
pub struct VariableCompletion {
var_context: (Vec<u8>, Vec<Vec<u8>>), // tuple with $var and the sublevels (.b.c.d)
@ -40,6 +42,7 @@ impl Completer for VariableCompletion {
end: span.end - offset,
};
let sublevels_count = self.var_context.1.len();
let prefix_str = String::from_utf8_lossy(&prefix);
// Completions for the given variable
if !var_str.is_empty() {
@ -69,7 +72,7 @@ impl Completer for VariableCompletion {
}
}
return output;
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
}
} else {
// No nesting provided, return all env vars
@ -93,7 +96,7 @@ impl Completer for VariableCompletion {
}
}
return output;
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
}
}
@ -117,7 +120,7 @@ impl Completer for VariableCompletion {
}
}
return output;
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
}
}
@ -139,7 +142,7 @@ impl Completer for VariableCompletion {
}
}
return output;
return sort_suggestions(&prefix_str, output, SortBy::Ascending);
}
}
}
@ -226,6 +229,8 @@ impl Completer for VariableCompletion {
}
}
output = sort_suggestions(&prefix_str, output, SortBy::Ascending);
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
output

View File

@ -8,7 +8,7 @@ use nu_protocol::{
report_error_new, HistoryFileFormat, PipelineData,
};
#[cfg(feature = "plugin")]
use nu_utils::utils::perf;
use nu_utils::perf;
use std::path::PathBuf;
#[cfg(feature = "plugin")]
@ -53,13 +53,10 @@ pub fn read_plugin_file(
// Reading signatures from plugin registry file
// The plugin.msgpackz file stores the parsed signature collected from each registered plugin
add_plugin_file(engine_state, plugin_file.clone(), storage_path);
perf(
perf!(
"add plugin file to engine_state",
start_time,
file!(),
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
engine_state.get_config().use_ansi_coloring
);
start_time = std::time::Instant::now();
@ -137,13 +134,10 @@ pub fn read_plugin_file(
}
};
perf(
perf!(
&format!("read plugin file {}", plugin_path.display()),
start_time,
file!(),
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
engine_state.get_config().use_ansi_coloring
);
start_time = std::time::Instant::now();
@ -156,13 +150,10 @@ pub fn read_plugin_file(
return;
}
perf(
perf!(
&format!("load plugin file {}", plugin_path.display()),
start_time,
file!(),
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
engine_state.get_config().use_ansi_coloring
);
}
}
@ -381,13 +372,10 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState, storage_path: &str) -
);
}
perf(
perf!(
"migrate old plugin file",
start_time,
file!(),
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
engine_state.get_config().use_ansi_coloring
);
true
}

View File

@ -32,7 +32,7 @@ impl Command for NuHighlight {
) -> Result<PipelineData, ShellError> {
let head = call.head;
let ctrlc = engine_state.ctrlc.clone();
let signals = engine_state.signals();
let engine_state = std::sync::Arc::new(engine_state.clone());
let config = engine_state.get_config().clone();
@ -50,7 +50,7 @@ impl Command for NuHighlight {
}
Err(err) => Value::error(err, head),
},
ctrlc,
signals,
)
}

View File

@ -31,7 +31,7 @@ use nu_protocol::{
};
use nu_utils::{
filesystem::{have_permission, PermissionResult},
utils::perf,
perf,
};
use reedline::{
CursorConfig, CwdAwareHinter, DefaultCompleter, EditCommand, Emacs, FileBackedHistory,
@ -43,7 +43,7 @@ use std::{
io::{self, IsTerminal, Write},
panic::{catch_unwind, AssertUnwindSafe},
path::{Path, PathBuf},
sync::{atomic::Ordering, Arc},
sync::Arc,
time::{Duration, Instant},
};
use sysinfo::System;
@ -89,14 +89,7 @@ pub fn evaluate_repl(
if let Err(e) = convert_env_values(engine_state, &unique_stack) {
report_error_new(engine_state, &e);
}
perf(
"translate env vars",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("translate env vars", start_time, use_color);
// seed env vars
unique_stack.add_env_var(
@ -225,28 +218,14 @@ fn get_line_editor(
// Now that reedline is created, get the history session id and store it in engine_state
store_history_id_in_engine(engine_state, &line_editor);
perf(
"setup reedline",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("setup reedline", start_time, use_color);
if let Some(history) = engine_state.history_config() {
start_time = std::time::Instant::now();
line_editor = setup_history(nushell_path, engine_state, line_editor, history)?;
perf(
"setup history",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("setup history", start_time, use_color);
}
Ok(line_editor)
}
@ -289,28 +268,11 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
if let Err(err) = engine_state.merge_env(&mut stack, cwd) {
report_error_new(engine_state, &err);
}
perf(
"merge env",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("merge env", start_time, use_color);
start_time = std::time::Instant::now();
// Reset the ctrl-c handler
if let Some(ctrlc) = &mut engine_state.ctrlc {
ctrlc.store(false, Ordering::SeqCst);
}
perf(
"reset ctrlc",
start_time,
file!(),
line!(),
column!(),
use_color,
);
engine_state.reset_signals();
perf!("reset signals", start_time, use_color);
start_time = std::time::Instant::now();
// Right before we start our prompt and take input from the user,
@ -320,14 +282,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
report_error_new(engine_state, &err);
}
}
perf(
"pre-prompt hook",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("pre-prompt hook", start_time, use_color);
start_time = std::time::Instant::now();
// Next, check all the environment variables they ask for
@ -336,14 +291,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
if let Err(error) = hook::eval_env_change_hook(env_change, engine_state, &mut stack) {
report_error_new(engine_state, &error)
}
perf(
"env-change hook",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("env-change hook", start_time, use_color);
let engine_reference = Arc::new(engine_state.clone());
let config = engine_state.get_config();
@ -355,14 +303,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
vi_normal: map_nucursorshape_to_cursorshape(config.cursor_shape_vi_normal),
emacs: map_nucursorshape_to_cursorshape(config.cursor_shape_emacs),
};
perf(
"get config/cursor config",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("get config/cursor config", start_time, use_color);
start_time = std::time::Instant::now();
// at this line we have cloned the state for the completer and the transient prompt
@ -392,16 +333,17 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
.with_quick_completions(config.quick_completions)
.with_partial_completions(config.partial_completions)
.with_ansi_colors(config.use_ansi_coloring)
.with_cwd(Some(
engine_state
.cwd(None)
.map(|cwd| cwd.into_std_path_buf())
.unwrap_or_default()
.to_string_lossy()
.to_string(),
))
.with_cursor_config(cursor_config);
perf(
"reedline builder",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("reedline builder", start_time, use_color);
let style_computer = StyleComputer::from_config(engine_state, &stack_arc);
@ -416,14 +358,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
line_editor.disable_hints()
};
perf(
"reedline coloring/style_computer",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("reedline coloring/style_computer", start_time, use_color);
start_time = std::time::Instant::now();
trace!("adding menus");
@ -433,14 +368,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
Reedline::create()
});
perf(
"reedline adding menus",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("reedline adding menus", start_time, use_color);
start_time = std::time::Instant::now();
let buffer_editor = get_editor(engine_state, &stack_arc, Span::unknown());
@ -457,14 +385,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
line_editor
};
perf(
"reedline buffer_editor",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("reedline buffer_editor", start_time, use_color);
if let Some(history) = engine_state.history_config() {
start_time = std::time::Instant::now();
@ -474,28 +395,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
}
}
perf(
"sync_history",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("sync_history", start_time, use_color);
}
start_time = std::time::Instant::now();
// Changing the line editor based on the found keybindings
line_editor = setup_keybindings(engine_state, line_editor);
perf(
"keybindings",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("keybindings", start_time, use_color);
start_time = std::time::Instant::now();
let config = &engine_state.get_config().clone();
@ -512,14 +419,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
nu_prompt,
);
perf(
"update_prompt",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("update_prompt", start_time, use_color);
*entry_num += 1;
@ -546,14 +446,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
// so we should avoid it or making stack cheaper to clone.
let mut stack = Arc::unwrap_or_clone(stack_arc);
perf(
"line_editor setup",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("line_editor setup", start_time, use_color);
let line_editor_input_time = std::time::Instant::now();
match input {
@ -590,14 +483,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
}
}
perf(
"pre_execution_hook",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("pre_execution_hook", start_time, use_color);
let mut repl = engine_state.repl_state.lock().expect("repl state mutex");
repl.cursor_pos = line_editor.current_insertion_point();
@ -612,26 +498,20 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
run_ansi_sequence(VSCODE_PRE_EXECUTION_MARKER);
perf(
perf!(
"pre_execute_marker (633;C) ansi escape sequence",
start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
} else if shell_integration_osc133 {
start_time = Instant::now();
run_ansi_sequence(PRE_EXECUTION_MARKER);
perf(
perf!(
"pre_execute_marker (133;C) ansi escape sequence",
start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
}
} else if shell_integration_osc133 {
@ -639,13 +519,10 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
run_ansi_sequence(PRE_EXECUTION_MARKER);
perf(
perf!(
"pre_execute_marker (133;C) ansi escape sequence",
start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
}
@ -769,22 +646,16 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
);
}
}
perf(
perf!(
"processing line editor input",
line_editor_input_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
perf(
perf!(
"time between prompts in line editor loop",
loop_start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
(true, stack, line_editor)
@ -800,13 +671,14 @@ fn prepare_history_metadata(
line_editor: &mut Reedline,
) {
if !s.is_empty() && line_editor.has_last_command_context() {
#[allow(deprecated)]
let result = line_editor
.update_last_command_context(&|mut c| {
c.start_timestamp = Some(chrono::Utc::now());
c.hostname = hostname.map(str::to_string);
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd());
c.cwd = engine_state
.cwd(None)
.ok()
.map(|path| path.to_string_lossy().to_string());
c
})
.into_diagnostic();
@ -1061,14 +933,7 @@ fn run_shell_integration_osc2(
// ESC]2;stringBEL -- Set window title to string
run_ansi_sequence(&format!("\x1b]2;{title}\x07"));
perf(
"set title with command osc2",
start_time,
file!(),
line!(),
column!(),
use_color,
);
perf!("set title with command osc2", start_time, use_color);
}
}
@ -1093,13 +958,10 @@ fn run_shell_integration_osc7(
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
));
perf(
perf!(
"communicate path to terminal with osc7",
start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
}
}
@ -1116,13 +978,10 @@ fn run_shell_integration_osc9_9(engine_state: &EngineState, stack: &mut Stack, u
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
));
perf(
perf!(
"communicate path to terminal with osc9;9",
start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
}
}
@ -1142,13 +1001,10 @@ fn run_shell_integration_osc633(engine_state: &EngineState, stack: &mut Stack, u
VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX
));
perf(
perf!(
"communicate path to terminal with osc633;P",
start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
}
}
@ -1371,13 +1227,10 @@ fn run_finaliziation_ansi_sequence(
shell_integration_osc133,
));
perf(
perf!(
"post_execute_marker (633;D) ansi escape sequences",
start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
} else if shell_integration_osc133 {
let start_time = Instant::now();
@ -1389,13 +1242,10 @@ fn run_finaliziation_ansi_sequence(
shell_integration_osc133,
));
perf(
perf!(
"post_execute_marker (133;D) ansi escape sequences",
start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
}
} else if shell_integration_osc133 {
@ -1408,13 +1258,10 @@ fn run_finaliziation_ansi_sequence(
shell_integration_osc133,
));
perf(
perf!(
"post_execute_marker (133;D) ansi escape sequences",
start_time,
file!(),
line!(),
column!(),
use_color,
use_color
);
}
}

View File

@ -8,7 +8,7 @@ use nu_protocol::{
};
#[cfg(windows)]
use nu_utils::enable_vt_processing;
use nu_utils::utils::perf;
use nu_utils::perf;
use std::path::Path;
// This will collect environment variables from std::env and adds them to a stack.
@ -228,13 +228,10 @@ pub fn eval_source(
let _ = enable_vt_processing();
}
perf(
perf!(
&format!("eval_source {}", &fname),
start_time,
file!(),
line!(),
column!(),
engine_state.get_config().use_ansi_coloring,
engine_state.get_config().use_ansi_coloring
);
exit_code

View File

@ -11,7 +11,7 @@ use std::{
sync::Arc,
};
use support::{
completions_helpers::{new_partial_engine, new_quote_engine},
completions_helpers::{new_dotnu_engine, new_partial_engine, new_quote_engine},
file, folder, match_suggestions, new_engine,
};
@ -85,8 +85,29 @@ fn custom_completer() -> NuCompleter {
NuCompleter::new(Arc::new(engine), Arc::new(stack))
}
#[fixture]
fn subcommand_completer() -> NuCompleter {
// Create a new engine
let (dir, _, mut engine, mut stack) = new_engine();
// Use fuzzy matching, because subcommands are sorted by Levenshtein distance,
// and that's not very useful with prefix matching
let commands = r#"
$env.config.completions.algorithm = "fuzzy"
def foo [] {}
def "foo bar" [] {}
def "foo abaz" [] {}
def "foo aabrr" [] {}
def food [] {}
"#;
assert!(support::merge_input(commands.as_bytes(), &mut engine, &mut stack, dir).is_ok());
// Instantiate a new completer
NuCompleter::new(Arc::new(engine), Arc::new(stack))
}
#[test]
fn variables_dollar_sign_with_varialblecompletion() {
fn variables_dollar_sign_with_variablecompletion() {
let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
@ -138,43 +159,42 @@ fn variables_customcompletion_subcommands_with_customcompletion_2(
#[test]
fn dotnu_completions() {
// Create a new engine
let (_, _, engine, stack) = new_engine();
let (_, _, engine, stack) = new_dotnu_engine();
// Instantiate a new completer
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
let expected = vec![
"asdf.nu".into(),
"bar.nu".into(),
"bat.nu".into(),
"baz.nu".into(),
#[cfg(windows)]
"dir_module\\".into(),
#[cfg(not(windows))]
"dir_module/".into(),
"foo.nu".into(),
"spam.nu".into(),
"xyzzy.nu".into(),
];
// Test source completion
let completion_str = "source-env ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
match_suggestions(expected.clone(), suggestions);
// Test use completion
let completion_str = "use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
match_suggestions(expected.clone(), suggestions);
// Test overlay use completion
let completion_str = "overlay use ".to_string();
let suggestions = completer.complete(&completion_str, completion_str.len());
assert_eq!(2, suggestions.len());
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
#[cfg(windows)]
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
#[cfg(not(windows))]
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
match_suggestions(expected, suggestions);
}
#[test]
@ -276,9 +296,10 @@ fn partial_completions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
folder(dir.join("partial_a")),
folder(dir.join("partial_b")),
folder(dir.join("partial_c")),
folder(dir.join("partial")),
folder(dir.join("partial-a")),
folder(dir.join("partial-b")),
folder(dir.join("partial-c")),
];
// Match the results
@ -292,13 +313,14 @@ fn partial_completions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")),
file(dir.join("partial_a").join("hello")),
file(dir.join("partial_a").join("hola")),
file(dir.join("partial_b").join("hello_b")),
file(dir.join("partial_b").join("hi_b")),
file(dir.join("partial_c").join("hello_c")),
file(dir.join("partial").join("hello.txt")),
file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial-a").join("have_ext.txt")),
file(dir.join("partial-a").join("hello")),
file(dir.join("partial-a").join("hola")),
file(dir.join("partial-b").join("hello_b")),
file(dir.join("partial-b").join("hi_b")),
file(dir.join("partial-c").join("hello_c")),
];
// Match the results
@ -311,14 +333,15 @@ fn partial_completions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("anotherfile")),
file(dir.join("partial_a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")),
file(dir.join("partial_a").join("hello")),
file(dir.join("partial_a").join("hola")),
file(dir.join("partial_b").join("hello_b")),
file(dir.join("partial_b").join("hi_b")),
file(dir.join("partial_c").join("hello_c")),
file(dir.join("partial").join("hello.txt")),
file(dir.join("partial-a").join("anotherfile")),
file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial-a").join("have_ext.txt")),
file(dir.join("partial-a").join("hello")),
file(dir.join("partial-a").join("hola")),
file(dir.join("partial-b").join("hello_b")),
file(dir.join("partial-b").join("hi_b")),
file(dir.join("partial-c").join("hello_c")),
];
// Match the results
@ -343,19 +366,25 @@ fn partial_completions() {
// Create the expected values
let expected_paths: Vec<String> = vec![
file(
dir.join("partial_a")
dir.join("partial")
.join("..")
.join("final_partial")
.join("somefile"),
),
file(
dir.join("partial_b")
dir.join("partial-a")
.join("..")
.join("final_partial")
.join("somefile"),
),
file(
dir.join("partial_c")
dir.join("partial-b")
.join("..")
.join("final_partial")
.join("somefile"),
),
file(
dir.join("partial-c")
.join("..")
.join("final_partial")
.join("somefile"),
@ -366,28 +395,28 @@ fn partial_completions() {
match_suggestions(expected_paths, suggestions);
// Test completion for all files under directories whose names begin with "pa"
let file_str = file(dir.join("partial_a").join("have"));
let file_str = file(dir.join("partial-a").join("have"));
let target_file = format!("rm {file_str}");
let suggestions = completer.complete(&target_file, target_file.len());
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")),
file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial-a").join("have_ext.txt")),
];
// Match the results
match_suggestions(expected_paths, suggestions);
// Test completion for all files under directories whose names begin with "pa"
let file_str = file(dir.join("partial_a").join("have_ext."));
let file_str = file(dir.join("partial-a").join("have_ext."));
let file_dir = format!("rm {file_str}");
let suggestions = completer.complete(&file_dir, file_dir.len());
// Create the expected values
let expected_paths: Vec<String> = vec![
file(dir.join("partial_a").join("have_ext.exe")),
file(dir.join("partial_a").join("have_ext.txt")),
file(dir.join("partial-a").join("have_ext.exe")),
file(dir.join("partial-a").join("have_ext.txt")),
];
// Match the results
@ -652,6 +681,27 @@ fn command_watch_with_filecompletion() {
match_suggestions(expected_paths, suggestions)
}
#[rstest]
fn subcommand_completions(mut subcommand_completer: NuCompleter) {
let prefix = "foo br";
let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions(
vec!["foo bar".to_string(), "foo aabrr".to_string()],
suggestions,
);
let prefix = "foo b";
let suggestions = subcommand_completer.complete(prefix, prefix.len());
match_suggestions(
vec![
"foo bar".to_string(),
"foo abaz".to_string(),
"foo aabrr".to_string(),
],
suggestions,
);
}
#[test]
fn file_completion_quoted() {
let (_, _, engine, stack) = new_quote_engine();
@ -662,11 +712,11 @@ fn file_completion_quoted() {
let suggestions = completer.complete(target_dir, target_dir.len());
let expected_paths: Vec<String> = vec![
"\'[a] bc.txt\'".to_string(),
"`--help`".to_string(),
"`-42`".to_string(),
"`-inf`".to_string(),
"`4.2`".to_string(),
"\'[a] bc.txt\'".to_string(),
"`te st.txt`".to_string(),
"`te#st.txt`".to_string(),
"`te'st.txt`".to_string(),
@ -857,6 +907,11 @@ fn variables_completions() {
// Match results
match_suggestions(expected, suggestions);
let suggestions = completer.complete("$", 1);
let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()];
match_suggestions(expected, suggestions);
}
#[test]

View File

@ -68,6 +68,52 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack)
}
// creates a new engine with the current path into the completions fixtures folder
pub fn new_dotnu_engine() -> (PathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("dotnu_completions");
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
let dir_span = nu_protocol::Span::new(0, dir_str.len());
// Create a new engine with default context
let mut engine_state = create_default_context();
// Add $nu
engine_state.generate_nu_constant();
// New stack
let mut stack = Stack::new();
// Add pwd as env var
stack.add_env_var("PWD".to_string(), Value::string(dir_str.clone(), dir_span));
stack.add_env_var(
"TEST".to_string(),
Value::string("NUSHELL".to_string(), dir_span),
);
stack.add_env_var(
"NU_LIB_DIRS".to_string(),
Value::List {
vals: vec![
Value::string(file(dir.join("lib-dir1")), dir_span),
Value::string(file(dir.join("lib-dir2")), dir_span),
Value::string(file(dir.join("lib-dir3")), dir_span),
],
internal_span: dir_span,
},
);
// Merge environment into the permanent state
let merge_result = engine_state.merge_env(&mut stack, &dir);
assert!(merge_result.is_ok());
(dir, dir_str, engine_state, stack)
}
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
// Target folder inside assets
let dir = fs::fixtures().join("quoted_completions");
@ -149,9 +195,13 @@ pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
Expected: {expected:#?}\n"
)
}
expected.iter().zip(suggestions).for_each(|it| {
assert_eq!(it.0, &it.1.value);
});
assert_eq!(
expected,
suggestions
.into_iter()
.map(|it| it.value)
.collect::<Vec<_>>()
);
}
// append the separator to the converted path

View File

@ -1,5 +1,5 @@
use nu_protocol::{ast::CellPath, PipelineData, ShellError, Span, Value};
use std::sync::{atomic::AtomicBool, Arc};
use nu_protocol::{ast::CellPath, PipelineData, ShellError, Signals, Span, Value};
use std::sync::Arc;
pub trait CmdArgument {
fn take_cell_paths(&mut self) -> Option<Vec<CellPath>>;
@ -40,7 +40,7 @@ pub fn operate<C, A>(
mut arg: A,
input: PipelineData,
span: Span,
ctrlc: Option<Arc<AtomicBool>>,
signals: &Signals,
) -> Result<PipelineData, ShellError>
where
A: CmdArgument + Send + Sync + 'static,
@ -55,7 +55,7 @@ where
_ => cmd(&v, &arg, span),
}
},
ctrlc,
signals,
),
Some(column_paths) => {
let arg = Arc::new(arg);
@ -79,7 +79,7 @@ where
}
v
},
ctrlc,
signals,
)
}
}

View File

@ -1,3 +1,4 @@
use nu_path::AbsolutePathBuf;
use nu_protocol::{
engine::{EngineState, Stack},
Range, ShellError, Span, Value,
@ -15,6 +16,7 @@ pub fn get_init_cwd() -> PathBuf {
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
engine_state
.cwd(Some(stack))
.map(AbsolutePathBuf::into_std_path_buf)
.unwrap_or(crate::util::get_init_cwd())
}

View File

@ -79,7 +79,7 @@ impl Command for BitsAnd {
input.map(
move |value| binary_op(&value, &target, little_endian, |(l, r)| l & r, head),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -1,6 +1,9 @@
use std::io::{self, Read, Write};
use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*;
use nu_protocol::Signals;
use num_traits::ToPrimitive;
pub struct Arguments {
@ -118,12 +121,43 @@ fn into_bits(
let cell_paths = call.rest(engine_state, stack, 0)?;
let cell_paths = (!cell_paths.is_empty()).then_some(cell_paths);
if let PipelineData::ByteStream(stream, ..) = input {
// TODO: in the future, we may want this to stream out, converting each to bytes
Ok(Value::binary(stream.into_bytes()?, head).into_pipeline_data())
if let PipelineData::ByteStream(stream, metadata) = input {
Ok(PipelineData::ByteStream(
byte_stream_to_bits(stream, head),
metadata,
))
} else {
let args = Arguments { cell_paths };
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
}
fn byte_stream_to_bits(stream: ByteStream, head: Span) -> ByteStream {
if let Some(mut reader) = stream.reader() {
let mut is_first = true;
ByteStream::from_fn(
head,
Signals::empty(),
ByteStreamType::String,
move |buffer| {
let mut byte = [0];
if reader.read(&mut byte[..]).err_span(head)? > 0 {
// Format the byte as bits
if is_first {
is_first = false;
} else {
buffer.push(b' ');
}
write!(buffer, "{:08b}", byte[0]).expect("format failed");
Ok(true)
} else {
// EOF
Ok(false)
}
},
)
} else {
ByteStream::read(io::empty(), head, Signals::empty(), ByteStreamType::String)
}
}

View File

@ -82,7 +82,7 @@ impl Command for BitsNot {
number_size,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -80,7 +80,7 @@ impl Command for BitsOr {
input.map(
move |value| binary_op(&value, &target, little_endian, |(l, r)| l | r, head),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -86,7 +86,7 @@ impl Command for BitsRol {
bits,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -86,7 +86,7 @@ impl Command for BitsRor {
bits,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -88,7 +88,7 @@ impl Command for BitsShl {
bits,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -88,7 +88,7 @@ impl Command for BitsShr {
bits,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -80,7 +80,7 @@ impl Command for BitsXor {
input.map(
move |value| binary_op(&value, &target, little_endian, |(l, r)| l ^ r, head),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -59,7 +59,7 @@ fn fmt(
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
fn action(input: &Value, _args: &CellPathOnlyArgs, span: Span) -> Value {

View File

@ -89,7 +89,7 @@ impl Command for EachWhile {
}
})
.fuse()
.into_pipeline_data(head, engine_state.ctrlc.clone()))
.into_pipeline_data(head, engine_state.signals().clone()))
}
PipelineData::ByteStream(stream, ..) => {
let span = stream.span();
@ -107,7 +107,7 @@ impl Command for EachWhile {
}
})
.fuse()
.into_pipeline_data(head, engine_state.ctrlc.clone()))
.into_pipeline_data(head, engine_state.signals().clone()))
} else {
Ok(PipelineData::Empty)
}

View File

@ -108,7 +108,7 @@ impl Command for UpdateCells {
columns,
span: head,
}
.into_pipeline_data(head, engine_state.ctrlc.clone())
.into_pipeline_data(head, engine_state.signals().clone())
.set_metadata(metadata))
}
}

View File

@ -368,6 +368,7 @@ fn theme_demo(span: Span) -> PipelineData {
.collect();
Value::list(result, span).into_pipeline_data_with_metadata(PipelineMetadata {
data_source: DataSource::HtmlThemes,
content_type: None,
})
}

View File

@ -45,7 +45,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -45,7 +45,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -45,7 +45,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -44,7 +44,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -44,7 +44,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -44,7 +44,7 @@ impl Command for SubCommand {
}
input.map(
move |value| operate(value, head, use_degrees),
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -41,10 +41,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(
move |value| operate(value, head),
engine_state.ctrlc.clone(),
)
input.map(move |value| operate(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -140,7 +140,7 @@ fn operate(
ret
}
},
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -88,7 +88,7 @@ pub fn operate(
cell_paths,
};
general_operate(action, args, input, call.head, engine_state.ctrlc.clone())
general_operate(action, args, input, call.head, engine_state.signals())
}
fn action(

View File

@ -220,7 +220,7 @@ fn format(
}
}
Ok(ListStream::new(list.into_iter(), head_span, engine_state.ctrlc.clone()).into())
Ok(ListStream::new(list.into_iter(), head_span, engine_state.signals().clone()).into())
}
// Unwrapping this ShellError is a bit unfortunate.
// Ideally, its Span would be preserved.

View File

@ -44,7 +44,7 @@ where
case_operation,
cell_paths,
};
general_operate(action, args, input, call.head, engine_state.ctrlc.clone())
general_operate(action, args, input, call.head, engine_state.signals())
}
fn action<F>(input: &Value, args: &Arguments<F>, head: Span) -> Value

View File

@ -0,0 +1,13 @@
use nu_test_support::nu;
#[test]
fn byte_stream_into_bits() {
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits");
assert_eq!("00000001 00000010 00000011", result.out);
}
#[test]
fn byte_stream_into_bits_is_stream() {
let result = nu!("[0x[01] 0x[02 03]] | bytes collect | into bits | describe");
assert_eq!("string (stream)", result.out);
}

View File

@ -0,0 +1 @@
mod into;

View File

@ -1 +1,2 @@
mod bits;
mod bytes;

View File

@ -18,10 +18,10 @@ nu-protocol = { path = "../nu-protocol", version = "0.95.1" }
nu-utils = { path = "../nu-utils", version = "0.95.1" }
itertools = { workspace = true }
shadow-rs = { version = "0.28", default-features = false }
shadow-rs = { version = "0.29", default-features = false }
[build-dependencies]
shadow-rs = { version = "0.28", default-features = false }
shadow-rs = { version = "0.29", default-features = false }
[features]
mimalloc = []

View File

@ -50,6 +50,7 @@ is particularly large, this can cause high memory usage."#
// check where some input came from.
Some(PipelineMetadata {
data_source: DataSource::FilePath(_),
content_type: None,
}) => None,
other => other,
};

View File

@ -23,11 +23,7 @@ impl Command for Do {
fn signature(&self) -> Signature {
Signature::build("do")
.required(
"closure",
SyntaxShape::OneOf(vec![SyntaxShape::Closure(None), SyntaxShape::Any]),
"The closure to run.",
)
.required("closure", SyntaxShape::Closure(None), "The closure to run.")
.input_output_types(vec![(Type::Any, Type::Any)])
.switch(
"ignore-errors",

View File

@ -56,16 +56,7 @@ impl Command for ErrorMake {
Example {
description: "Create a simple custom error",
example: r#"error make {msg: "my custom error message"}"#,
result: Some(Value::error(
ShellError::GenericError {
error: "my custom error message".into(),
msg: "".into(),
span: None,
help: None,
inner: vec![],
},
Span::unknown(),
)),
result: None,
},
Example {
description: "Create a more complex custom error",
@ -82,16 +73,7 @@ impl Command for ErrorMake {
}
help: "A help string, suggesting a fix to the user" # optional
}"#,
result: Some(Value::error(
ShellError::GenericError {
error: "my custom error message".into(),
msg: "my custom label text".into(),
span: Some(Span::new(123, 456)),
help: Some("A help string, suggesting a fix to the user".into()),
inner: vec![],
},
Span::unknown(),
)),
result: None,
},
Example {
description:

View File

@ -1,6 +1,5 @@
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
use nu_protocol::engine::CommandType;
use nu_protocol::ParseWarning;
use nu_protocol::{engine::CommandType, Signals};
#[derive(Clone)]
pub struct For;
@ -29,11 +28,6 @@ impl Command for For {
"Range of the loop.",
)
.required("block", SyntaxShape::Block, "The block to run.")
.switch(
"numbered",
"DEPRECATED: return a numbered item ($it.index and $it.item)",
Some('n'),
)
.creates_scope()
.category(Category::Core)
}
@ -79,23 +73,6 @@ impl Command for For {
let value = eval_expression(engine_state, stack, keyword_expr)?;
let numbered = call.has_flag(engine_state, stack, "numbered")?;
if numbered {
nu_protocol::report_error_new(
engine_state,
&ParseWarning::DeprecatedWarning {
old_command: "--numbered/-n".into(),
new_suggestion: "use `enumerate`".into(),
span: call
.get_named_arg("numbered")
.expect("`get_named_arg` found `--numbered` but still failed")
.span,
url: "See `help for` examples".into(),
},
);
}
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone();
let block = engine_state.get_block(block_id);
@ -104,29 +81,14 @@ impl Command for For {
let span = value.span();
match value {
Value::List { vals, .. } => {
for (idx, x) in vals.into_iter().enumerate() {
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
break;
}
for x in vals.into_iter() {
engine_state.signals().check(head)?;
// with_env() is used here to ensure that each iteration uses
// a different set of environment variables.
// Hence, a 'cd' in the first loop won't affect the next loop.
stack.add_var(
var_id,
if numbered {
Value::record(
record! {
"index" => Value::int(idx as i64, head),
"item" => x,
},
head,
)
} else {
x
},
);
stack.add_var(var_id, x);
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => {
@ -152,21 +114,9 @@ impl Command for For {
}
}
Value::Range { val, .. } => {
for (idx, x) in val.into_range_iter(span, ctrlc).enumerate() {
stack.add_var(
var_id,
if numbered {
Value::record(
record! {
"index" => Value::int(idx as i64, head),
"item" => x,
},
head,
)
} else {
x
},
);
for x in val.into_range_iter(span, Signals::empty()) {
engine_state.signals().check(head)?;
stack.add_var(var_id, x);
match eval_block(&engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => {

View File

@ -38,6 +38,7 @@ impl Command for Loop {
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; // FIXME
let head = call.head;
let block_id = call
.positional_nth(0)
.expect("checked through parser")
@ -50,9 +51,7 @@ impl Command for Loop {
let stack = &mut stack.push_redirection(None, None);
loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
break;
}
engine_state.signals().check(head)?;
match eval_block(engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => {

View File

@ -47,6 +47,7 @@ impl Command for While {
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let call = call.assert_ast_call()?; // FIXME
let head = call.head;
let cond = call.positional_nth(0).expect("checked through parser");
let block_id = call
.positional_nth(1)
@ -60,9 +61,7 @@ impl Command for While {
let stack = &mut stack.push_redirection(None, None);
loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) {
break;
}
engine_state.signals().check(head)?;
let result = eval_expression(engine_state, stack, cond)?;

View File

@ -1,5 +1,3 @@
mod plugin;
mod register;
pub use plugin::*;
pub use register::Register;

View File

@ -72,7 +72,7 @@ impl Command for PluginStop {
error: format!("Failed to stop the `{}` plugin", name.item),
msg: "couldn't find a plugin with this name".into(),
span: Some(name.span),
help: Some("you may need to `register` the plugin first".into()),
help: Some("you may need to `plugin add` the plugin first".into()),
inner: vec![],
})
}

View File

@ -1,80 +0,0 @@
use nu_engine::command_prelude::*;
use nu_protocol::engine::CommandType;
#[derive(Clone)]
pub struct Register;
impl Command for Register {
fn name(&self) -> &str {
"register"
}
fn usage(&self) -> &str {
"Register a plugin."
}
fn signature(&self) -> nu_protocol::Signature {
Signature::build("register")
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
.required(
"plugin",
SyntaxShape::Filepath,
"Path of executable for plugin.",
)
.optional(
"signature",
SyntaxShape::Any,
"Block with signature description as json object.",
)
.named(
"shell",
SyntaxShape::Filepath,
"path of shell used to run plugin (cmd, sh, python, etc)",
Some('s'),
)
.category(Category::Plugin)
}
fn extra_usage(&self) -> &str {
r#"
Deprecated in favor of `plugin add` and `plugin use`.
This command is a parser keyword. For details, check:
https://www.nushell.sh/book/thinking_in_nu.html
"#
.trim()
}
fn search_terms(&self) -> Vec<&str> {
vec!["add"]
}
fn command_type(&self) -> CommandType {
CommandType::Keyword
}
fn run(
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
Ok(PipelineData::empty())
}
fn examples(&self) -> Vec<Example> {
vec![
Example {
description: "Register `nu_plugin_query` plugin from ~/.cargo/bin/ dir",
example: r#"register ~/.cargo/bin/nu_plugin_query"#,
result: None,
},
Example {
description: "Register `nu_plugin_query` plugin from `nu -c` (writes/updates $nu.plugin-path)",
example: r#"let plugin = ((which nu).path.0 | path dirname | path join 'nu_plugin_query'); nu -c $'register ($plugin); version'"#,
result: None,
},
]
}
}

View File

@ -18,7 +18,6 @@ pub fn add_plugin_command_context(mut engine_state: EngineState) -> EngineState
PluginRm,
PluginStop,
PluginUse,
Register,
);
working_set.render()

View File

@ -31,11 +31,20 @@ pub(crate) fn modify_plugin_file(
})?
};
let file_span = custom_path.as_ref().map(|p| p.span).unwrap_or(span);
// Try to read the plugin file if it exists
let mut contents = if fs::metadata(&plugin_registry_file_path).is_ok_and(|m| m.len() > 0) {
PluginRegistryFile::read_from(
File::open(&plugin_registry_file_path).err_span(span)?,
Some(span),
File::open(&plugin_registry_file_path).map_err(|err| ShellError::IOErrorSpanned {
msg: format!(
"failed to read `{}`: {}",
plugin_registry_file_path.display(),
err
),
span: file_span,
})?,
Some(file_span),
)?
} else {
PluginRegistryFile::default()
@ -46,7 +55,14 @@ pub(crate) fn modify_plugin_file(
// Save the modified file on success
contents.write_to(
File::create(&plugin_registry_file_path).err_span(span)?,
File::create(&plugin_registry_file_path).map_err(|err| ShellError::IOErrorSpanned {
msg: format!(
"failed to create `{}`: {}",
plugin_registry_file_path.display(),
err
),
span: file_span,
})?,
Some(span),
)?;

View File

@ -42,6 +42,7 @@ chrono-humanize = { workspace = true }
chrono-tz = { workspace = true }
crossterm = { workspace = true }
csv = { workspace = true }
deunicode = { workspace = true }
dialoguer = { workspace = true, default-features = false, features = ["fuzzy-select"] }
digest = { workspace = true, default-features = false }
dtparse = { workspace = true }

View File

@ -78,7 +78,7 @@ impl Command for BytesAdd {
end,
cell_paths,
};
operate(add, arg, input, call.head, engine_state.ctrlc.clone())
operate(add, arg, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -83,7 +83,7 @@ impl Command for BytesAt {
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -60,7 +60,12 @@ impl Command for BytesCollect {
)
.flatten();
let output = ByteStream::from_result_iter(iter, span, None, ByteStreamType::Binary);
let output = ByteStream::from_result_iter(
iter,
span,
engine_state.signals().clone(),
ByteStreamType::Binary,
);
Ok(PipelineData::ByteStream(output, metadata))
}

View File

@ -102,7 +102,7 @@ impl Command for BytesEndsWith {
pattern,
cell_paths,
};
operate(ends_with, arg, input, head, engine_state.ctrlc.clone())
operate(ends_with, arg, input, head, engine_state.signals())
}
}

View File

@ -71,7 +71,7 @@ impl Command for BytesIndexOf {
all: call.has_flag(engine_state, stack, "all")?,
cell_paths,
};
operate(index_of, arg, input, call.head, engine_state.ctrlc.clone())
operate(index_of, arg, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -46,7 +46,7 @@ impl Command for BytesLen {
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 1)?;
let arg = CellPathOnlyArgs::from(cell_paths);
operate(length, arg, input, call.head, engine_state.ctrlc.clone())
operate(length, arg, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -73,7 +73,7 @@ impl Command for BytesRemove {
all: call.has_flag(engine_state, stack, "all")?,
};
operate(remove, arg, input, call.head, engine_state.ctrlc.clone())
operate(remove, arg, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -73,7 +73,7 @@ impl Command for BytesReplace {
all: call.has_flag(engine_state, stack, "all")?,
};
operate(replace, arg, input, call.head, engine_state.ctrlc.clone())
operate(replace, arg, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -42,7 +42,7 @@ impl Command for BytesReverse {
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let arg = CellPathOnlyArgs::from(cell_paths);
operate(reverse, arg, input, call.head, engine_state.ctrlc.clone())
operate(reverse, arg, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -79,7 +79,7 @@ impl Command for BytesStartsWith {
pattern,
cell_paths,
};
operate(starts_with, arg, input, head, engine_state.ctrlc.clone())
operate(starts_with, arg, input, head, engine_state.signals())
}
}

View File

@ -165,7 +165,7 @@ fn fill(
cell_paths,
};
operate(action, arg, input, call.head, engine_state.ctrlc.clone())
operate(action, arg, input, call.head, engine_state.signals())
}
fn action(input: &Value, args: &Arguments, span: Span) -> Value {

View File

@ -138,7 +138,7 @@ fn into_binary(
cell_paths,
compact: call.has_flag(engine_state, stack, "compact")?,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
}

View File

@ -107,7 +107,7 @@ fn into_bool(
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
fn string_to_boolean(s: &str, span: Span) -> Result<bool, ShellError> {

View File

@ -1,5 +1,5 @@
use crate::{generate_strftime_list, parse_date_from_string};
use chrono::{DateTime, FixedOffset, Local, NaiveTime, TimeZone, Utc};
use chrono::{DateTime, FixedOffset, Local, NaiveDateTime, NaiveTime, TimeZone, Utc};
use human_date_parser::{from_human_time, ParseResult};
use nu_cmd_base::input_handler::{operate, CmdArgument};
use nu_engine::command_prelude::*;
@ -141,7 +141,7 @@ impl Command for SubCommand {
zone_options,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
}
@ -162,24 +162,37 @@ impl Command for SubCommand {
};
vec![
Example {
description: "Convert any standard timestamp string to datetime",
description: "Convert timestamp string to datetime with timezone offset",
example: "'27.02.2021 1:55 pm +0000' | into datetime",
#[allow(clippy::inconsistent_digit_grouping)]
result: example_result_1(1614434100_000000000),
},
Example {
description: "Convert any standard timestamp string to datetime",
description: "Convert standard timestamp string to datetime with timezone offset",
example: "'2021-02-27T13:55:40.2246+00:00' | into datetime",
#[allow(clippy::inconsistent_digit_grouping)]
result: example_result_1(1614434140_224600000),
},
Example {
description:
"Convert non-standard timestamp string to datetime using a custom format",
"Convert non-standard timestamp string, with timezone offset, to datetime using a custom format",
example: "'20210227_135540+0000' | into datetime --format '%Y%m%d_%H%M%S%z'",
#[allow(clippy::inconsistent_digit_grouping)]
result: example_result_1(1614434140_000000000),
},
Example {
description: "Convert non-standard timestamp string, without timezone offset, to datetime with custom formatting",
example: "'16.11.1984 8:00 am' | into datetime --format '%d.%m.%Y %H:%M %P'",
#[allow(clippy::inconsistent_digit_grouping)]
result: Some(Value::date(
DateTime::from_naive_utc_and_offset(
NaiveDateTime::parse_from_str("16.11.1984 8:00 am", "%d.%m.%Y %H:%M %P")
.expect("date calculation should not fail in test"),
*Local::now().offset(),
),
Span::test_data(),
)),
},
Example {
description:
"Convert nanosecond-precision unix timestamp to a datetime with offset from UTC",
@ -372,10 +385,21 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
Some(dt) => match DateTime::parse_from_str(val, &dt.0) {
Ok(d) => Value::date ( d, head ),
Err(reason) => {
Value::error (
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
head,
)
match NaiveDateTime::parse_from_str(val, &dt.0) {
Ok(d) => Value::date (
DateTime::from_naive_utc_and_offset(
d,
*Local::now().offset(),
),
head,
),
Err(_) => {
Value::error (
ShellError::CantConvert { to_type: format!("could not parse as datetime using format '{}'", dt.0), from_type: reason.to_string(), span: head, help: Some("you can use `into datetime` without a format string to enable flexible parsing".to_string()) },
head,
)
}
}
}
},
@ -457,7 +481,7 @@ mod tests {
}
#[test]
fn takes_a_date_format() {
fn takes_a_date_format_with_timezone() {
let date_str = Value::test_string("16.11.1984 8:00 am +0000");
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P %z".to_string()));
let args = Arguments {
@ -473,6 +497,26 @@ mod tests {
assert_eq!(actual, expected)
}
#[test]
fn takes_a_date_format_without_timezone() {
let date_str = Value::test_string("16.11.1984 8:00 am");
let fmt_options = Some(DatetimeFormat("%d.%m.%Y %H:%M %P".to_string()));
let args = Arguments {
zone_options: None,
format_options: fmt_options,
cell_paths: None,
};
let actual = action(&date_str, &args, Span::test_data());
let expected = Value::date(
DateTime::from_naive_utc_and_offset(
NaiveDateTime::parse_from_str("16.11.1984 8:00 am", "%d.%m.%Y %H:%M %P").unwrap(),
*Local::now().offset(),
),
Span::test_data(),
);
assert_eq!(actual, expected)
}
#[test]
fn takes_iso8601_date_format() {
let date_str = Value::test_string("2020-08-04T16:39:18+00:00");

View File

@ -166,7 +166,7 @@ fn into_duration(
ret
}
},
engine_state.ctrlc.clone(),
engine_state.signals(),
)
}

View File

@ -68,7 +68,7 @@ impl Command for SubCommand {
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -49,7 +49,7 @@ impl Command for SubCommand {
) -> Result<PipelineData, ShellError> {
let cell_paths: Vec<CellPath> = call.rest(engine_state, stack, 0)?;
let args = CellPathOnlyArgs::from(cell_paths);
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -87,7 +87,7 @@ fn glob_helper(
Ok(Value::glob(stream.into_string()?, false, head).into_pipeline_data())
} else {
let args = Arguments { cell_paths };
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
}

View File

@ -158,7 +158,7 @@ impl Command for SubCommand {
signed,
cell_paths,
};
operate(action, args, input, call.head, engine_state.ctrlc.clone())
operate(action, args, input, call.head, engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -125,7 +125,7 @@ fn into_record(
),
},
Value::Range { val, .. } => Value::record(
val.into_range_iter(span, engine_state.ctrlc.clone())
val.into_range_iter(span, engine_state.signals().clone())
.enumerate()
.map(|(idx, val)| (format!("{idx}"), val))
.collect(),

View File

@ -180,7 +180,7 @@ fn string_helper(
cell_paths,
config,
};
operate(action, args, input, head, engine_state.ctrlc.clone())
operate(action, args, input, head, engine_state.signals())
}
}

View File

@ -57,14 +57,12 @@ impl Command for IntoValue {
call: &Call,
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let engine_state = engine_state.clone();
let metadata = input.metadata();
let ctrlc = engine_state.ctrlc.clone();
let span = call.head;
let display_as_filesizes = call.has_flag(&engine_state, stack, "prefer-filesizes")?;
let display_as_filesizes = call.has_flag(engine_state, stack, "prefer-filesizes")?;
// the columns to update
let columns: Option<Value> = call.get_flag(&engine_state, stack, "columns")?;
let columns: Option<Value> = call.get_flag(engine_state, stack, "columns")?;
let columns: Option<HashSet<String>> = match columns {
Some(val) => Some(
val.into_list()?
@ -81,7 +79,7 @@ impl Command for IntoValue {
display_as_filesizes,
span,
}
.into_pipeline_data(span, ctrlc)
.into_pipeline_data(span, engine_state.signals().clone())
.set_metadata(metadata))
}
}

View File

@ -2,13 +2,8 @@ use crate::database::values::sqlite::{open_sqlite_db, values_to_sql};
use nu_engine::command_prelude::*;
use itertools::Itertools;
use std::{
path::Path,
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use nu_protocol::Signals;
use std::path::Path;
pub const DEFAULT_TABLE_NAME: &str = "main";
@ -188,23 +183,18 @@ fn operate(
let file_name: Spanned<String> = call.req(engine_state, stack, 0)?;
let table_name: Option<Spanned<String>> = call.get_flag(engine_state, stack, "table-name")?;
let table = Table::new(&file_name, table_name)?;
let ctrl_c = engine_state.ctrlc.clone();
match action(input, table, span, ctrl_c) {
Ok(val) => Ok(val.into_pipeline_data()),
Err(e) => Err(e),
}
Ok(action(input, table, span, engine_state.signals())?.into_pipeline_data())
}
fn action(
input: PipelineData,
table: Table,
span: Span,
ctrl_c: Option<Arc<AtomicBool>>,
signals: &Signals,
) -> Result<Value, ShellError> {
match input {
PipelineData::ListStream(stream, _) => {
insert_in_transaction(stream.into_iter(), span, table, ctrl_c)
insert_in_transaction(stream.into_iter(), span, table, signals)
}
PipelineData::Value(
Value::List {
@ -212,9 +202,9 @@ fn action(
internal_span,
},
_,
) => insert_in_transaction(vals.into_iter(), internal_span, table, ctrl_c),
) => insert_in_transaction(vals.into_iter(), internal_span, table, signals),
PipelineData::Value(val, _) => {
insert_in_transaction(std::iter::once(val), span, table, ctrl_c)
insert_in_transaction(std::iter::once(val), span, table, signals)
}
_ => Err(ShellError::OnlySupportsThisInputType {
exp_input_type: "list".into(),
@ -229,7 +219,7 @@ fn insert_in_transaction(
stream: impl Iterator<Item = Value>,
span: Span,
mut table: Table,
ctrl_c: Option<Arc<AtomicBool>>,
signals: &Signals,
) -> Result<Value, ShellError> {
let mut stream = stream.peekable();
let first_val = match stream.peek() {
@ -251,17 +241,15 @@ fn insert_in_transaction(
let tx = table.try_init(&first_val)?;
for stream_value in stream {
if let Some(ref ctrlc) = ctrl_c {
if ctrlc.load(Ordering::Relaxed) {
tx.rollback().map_err(|e| ShellError::GenericError {
error: "Failed to rollback SQLite transaction".into(),
msg: e.to_string(),
span: None,
help: None,
inner: Vec::new(),
})?;
return Err(ShellError::InterruptedByUser { span: None });
}
if let Err(err) = signals.check(span) {
tx.rollback().map_err(|e| ShellError::GenericError {
error: "Failed to rollback SQLite transaction".into(),
msg: e.to_string(),
span: None,
help: None,
inner: Vec::new(),
})?;
return Err(err);
}
let val = stream_value.as_record()?;

View File

@ -2,7 +2,7 @@ use super::definitions::{
db_column::DbColumn, db_constraint::DbConstraint, db_foreignkey::DbForeignKey,
db_index::DbIndex, db_table::DbTable,
};
use nu_protocol::{CustomValue, PipelineData, Record, ShellError, Span, Spanned, Value};
use nu_protocol::{CustomValue, PipelineData, Record, ShellError, Signals, Span, Spanned, Value};
use rusqlite::{
types::ValueRef, Connection, DatabaseName, Error as SqliteError, OpenFlags, Row, Statement,
ToSql,
@ -12,7 +12,6 @@ use std::{
fs::File,
io::Read,
path::{Path, PathBuf},
sync::{atomic::AtomicBool, Arc},
};
const SQLITE_MAGIC_BYTES: &[u8] = "SQLite format 3\0".as_bytes();
@ -24,25 +23,21 @@ pub struct SQLiteDatabase {
// 1) YAGNI, 2) it's not obvious how cloning a connection could work, 3) state
// management gets tricky quick. Revisit this approach if we find a compelling use case.
pub path: PathBuf,
#[serde(skip)]
#[serde(skip, default = "Signals::empty")]
// this understandably can't be serialized. think that's OK, I'm not aware of a
// reason why a CustomValue would be serialized outside of a plugin
ctrlc: Option<Arc<AtomicBool>>,
signals: Signals,
}
impl SQLiteDatabase {
pub fn new(path: &Path, ctrlc: Option<Arc<AtomicBool>>) -> Self {
pub fn new(path: &Path, signals: Signals) -> Self {
Self {
path: PathBuf::from(path),
ctrlc,
signals,
}
}
pub fn try_from_path(
path: &Path,
span: Span,
ctrlc: Option<Arc<AtomicBool>>,
) -> Result<Self, ShellError> {
pub fn try_from_path(path: &Path, span: Span, signals: Signals) -> Result<Self, ShellError> {
let mut file = File::open(path).map_err(|e| ShellError::ReadingFile {
msg: e.to_string(),
span,
@ -56,7 +51,7 @@ impl SQLiteDatabase {
})
.and_then(|_| {
if buf == SQLITE_MAGIC_BYTES {
Ok(SQLiteDatabase::new(path, ctrlc))
Ok(SQLiteDatabase::new(path, signals))
} else {
Err(ShellError::ReadingFile {
msg: "Not a SQLite file".into(),
@ -72,7 +67,7 @@ impl SQLiteDatabase {
Value::Custom { val, .. } => match val.as_any().downcast_ref::<Self>() {
Some(db) => Ok(Self {
path: db.path.clone(),
ctrlc: db.ctrlc.clone(),
signals: db.signals.clone(),
}),
None => Err(ShellError::CantConvert {
to_type: "database".into(),
@ -107,16 +102,8 @@ impl SQLiteDatabase {
call_span: Span,
) -> Result<Value, ShellError> {
let conn = open_sqlite_db(&self.path, call_span)?;
let stream = run_sql_query(conn, sql, params, self.ctrlc.clone()).map_err(|e| {
ShellError::GenericError {
error: "Failed to query SQLite database".into(),
msg: e.to_string(),
span: Some(sql.span),
help: None,
inner: vec![],
}
})?;
let stream = run_sql_query(conn, sql, params, &self.signals)
.map_err(|e| e.into_shell_error(sql.span, "Failed to query SQLite database"))?;
Ok(stream)
}
@ -352,12 +339,7 @@ impl SQLiteDatabase {
impl CustomValue for SQLiteDatabase {
fn clone_value(&self, span: Span) -> Value {
let cloned = SQLiteDatabase {
path: self.path.clone(),
ctrlc: self.ctrlc.clone(),
};
Value::custom(Box::new(cloned), span)
Value::custom(Box::new(self.clone()), span)
}
fn type_name(&self) -> String {
@ -366,13 +348,8 @@ impl CustomValue for SQLiteDatabase {
fn to_base_value(&self, span: Span) -> Result<Value, ShellError> {
let db = open_sqlite_db(&self.path, span)?;
read_entire_sqlite_db(db, span, self.ctrlc.clone()).map_err(|e| ShellError::GenericError {
error: "Failed to read from SQLite database".into(),
msg: e.to_string(),
span: Some(span),
help: None,
inner: vec![],
})
read_entire_sqlite_db(db, span, &self.signals)
.map_err(|e| e.into_shell_error(span, "Failed to read from SQLite database"))
}
fn as_any(&self) -> &dyn std::any::Any {
@ -396,20 +373,12 @@ impl CustomValue for SQLiteDatabase {
fn follow_path_string(
&self,
_self_span: Span,
_column_name: String,
column_name: String,
path_span: Span,
) -> Result<Value, ShellError> {
let db = open_sqlite_db(&self.path, path_span)?;
read_single_table(db, _column_name, path_span, self.ctrlc.clone()).map_err(|e| {
ShellError::GenericError {
error: "Failed to read from SQLite database".into(),
msg: e.to_string(),
span: Some(path_span),
help: None,
inner: vec![],
}
})
read_single_table(db, column_name, path_span, &self.signals)
.map_err(|e| e.into_shell_error(path_span, "Failed to read from SQLite database"))
}
fn typetag_name(&self) -> &'static str {
@ -426,12 +395,12 @@ pub fn open_sqlite_db(path: &Path, call_span: Span) -> Result<Connection, ShellE
open_connection_in_memory_custom()
} else {
let path = path.to_string_lossy().to_string();
Connection::open(path).map_err(|e| ShellError::GenericError {
Connection::open(path).map_err(|err| ShellError::GenericError {
error: "Failed to open SQLite database".into(),
msg: e.to_string(),
msg: err.to_string(),
span: Some(call_span),
help: None,
inner: vec![],
inner: Vec::new(),
})
}
}
@ -440,11 +409,10 @@ fn run_sql_query(
conn: Connection,
sql: &Spanned<String>,
params: NuSqlParams,
ctrlc: Option<Arc<AtomicBool>>,
) -> Result<Value, SqliteError> {
signals: &Signals,
) -> Result<Value, SqliteOrShellError> {
let stmt = conn.prepare(&sql.item)?;
prepared_statement_to_nu_list(stmt, params, sql.span, ctrlc)
prepared_statement_to_nu_list(stmt, params, sql.span, signals)
}
// This is taken from to text local_into_string but tweaks it a bit so that certain formatting does not happen
@ -534,23 +502,56 @@ pub fn nu_value_to_params(value: Value) -> Result<NuSqlParams, ShellError> {
}
}
#[derive(Debug)]
enum SqliteOrShellError {
SqliteError(SqliteError),
ShellError(ShellError),
}
impl From<SqliteError> for SqliteOrShellError {
fn from(error: SqliteError) -> Self {
Self::SqliteError(error)
}
}
impl From<ShellError> for SqliteOrShellError {
fn from(error: ShellError) -> Self {
Self::ShellError(error)
}
}
impl SqliteOrShellError {
fn into_shell_error(self, span: Span, msg: &str) -> ShellError {
match self {
Self::SqliteError(err) => ShellError::GenericError {
error: msg.into(),
msg: err.to_string(),
span: Some(span),
help: None,
inner: Vec::new(),
},
Self::ShellError(err) => err,
}
}
}
fn read_single_table(
conn: Connection,
table_name: String,
call_span: Span,
ctrlc: Option<Arc<AtomicBool>>,
) -> Result<Value, SqliteError> {
signals: &Signals,
) -> Result<Value, SqliteOrShellError> {
// TODO: Should use params here?
let stmt = conn.prepare(&format!("SELECT * FROM [{table_name}]"))?;
prepared_statement_to_nu_list(stmt, NuSqlParams::default(), call_span, ctrlc)
prepared_statement_to_nu_list(stmt, NuSqlParams::default(), call_span, signals)
}
fn prepared_statement_to_nu_list(
mut stmt: Statement,
params: NuSqlParams,
call_span: Span,
ctrlc: Option<Arc<AtomicBool>>,
) -> Result<Value, SqliteError> {
signals: &Signals,
) -> Result<Value, SqliteOrShellError> {
let column_names = stmt
.column_names()
.into_iter()
@ -576,11 +577,7 @@ fn prepared_statement_to_nu_list(
let mut row_values = vec![];
for row_result in row_results {
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
// return whatever we have so far, let the caller decide whether to use it
return Ok(Value::list(row_values, call_span));
}
signals.check(call_span)?;
if let Ok(row_value) = row_result {
row_values.push(row_value);
}
@ -606,11 +603,7 @@ fn prepared_statement_to_nu_list(
let mut row_values = vec![];
for row_result in row_results {
if nu_utils::ctrl_c::was_pressed(&ctrlc) {
// return whatever we have so far, let the caller decide whether to use it
return Ok(Value::list(row_values, call_span));
}
signals.check(call_span)?;
if let Ok(row_value) = row_result {
row_values.push(row_value);
}
@ -626,8 +619,8 @@ fn prepared_statement_to_nu_list(
fn read_entire_sqlite_db(
conn: Connection,
call_span: Span,
ctrlc: Option<Arc<AtomicBool>>,
) -> Result<Value, SqliteError> {
signals: &Signals,
) -> Result<Value, SqliteOrShellError> {
let mut tables = Record::new();
let mut get_table_names =
@ -638,12 +631,8 @@ fn read_entire_sqlite_db(
let table_name: String = row?;
// TODO: Should use params here?
let table_stmt = conn.prepare(&format!("select * from [{table_name}]"))?;
let rows = prepared_statement_to_nu_list(
table_stmt,
NuSqlParams::default(),
call_span,
ctrlc.clone(),
)?;
let rows =
prepared_statement_to_nu_list(table_stmt, NuSqlParams::default(), call_span, signals)?;
tables.push(table_name, rows);
}
@ -710,7 +699,7 @@ mod test {
#[test]
fn can_read_empty_db() {
let db = open_connection_in_memory().unwrap();
let converted_db = read_entire_sqlite_db(db, Span::test_data(), None).unwrap();
let converted_db = read_entire_sqlite_db(db, Span::test_data(), &Signals::empty()).unwrap();
let expected = Value::test_record(Record::new());
@ -730,7 +719,7 @@ mod test {
[],
)
.unwrap();
let converted_db = read_entire_sqlite_db(db, Span::test_data(), None).unwrap();
let converted_db = read_entire_sqlite_db(db, Span::test_data(), &Signals::empty()).unwrap();
let expected = Value::test_record(record! {
"person" => Value::test_list(vec![]),
@ -759,7 +748,7 @@ mod test {
db.execute("INSERT INTO item (id, name) VALUES (456, 'foo bar')", [])
.unwrap();
let converted_db = read_entire_sqlite_db(db, span, None).unwrap();
let converted_db = read_entire_sqlite_db(db, span, &Signals::empty()).unwrap();
let expected = Value::test_record(record! {
"item" => Value::test_list(

View File

@ -50,7 +50,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(move |value| helper(value, head), engine_state.ctrlc.clone())
input.map(move |value| helper(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -40,7 +40,7 @@ impl Command for SubCommand {
head,
)
})
.into_pipeline_data(head, engine_state.ctrlc.clone()))
.into_pipeline_data(head, engine_state.signals().clone()))
}
fn examples(&self) -> Vec<Example> {

View File

@ -40,7 +40,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(move |value| helper(value, head), engine_state.ctrlc.clone())
input.map(move |value| helper(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

View File

@ -40,7 +40,7 @@ impl Command for SubCommand {
if matches!(input, PipelineData::Empty) {
return Err(ShellError::PipelineEmpty { dst_span: head });
}
input.map(move |value| helper(value, head), engine_state.ctrlc.clone())
input.map(move |value| helper(value, head), engine_state.signals())
}
fn examples(&self) -> Vec<Example> {

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