mirror of
https://github.com/nushell/nushell.git
synced 2025-05-31 15:18:23 +02:00
Merge branch 'main' into ecow-record
This commit is contained in:
commit
e67221edec
8
.github/workflows/nightly-build.yml
vendored
8
.github/workflows/nightly-build.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
|||||||
uses: hustcer/setup-nu@v3.10
|
uses: hustcer/setup-nu@v3.10
|
||||||
if: github.repository == 'nushell/nightly'
|
if: github.repository == 'nushell/nightly'
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.93.0
|
||||||
|
|
||||||
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
|
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
|
||||||
- name: Prepare for Nightly Release
|
- name: Prepare for Nightly Release
|
||||||
@ -141,7 +141,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.10
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.93.0
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
@ -253,7 +253,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.10
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.93.0
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
@ -317,7 +317,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.10
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.93.0
|
||||||
|
|
||||||
# Keep the last a few releases
|
# Keep the last a few releases
|
||||||
- name: Delete Older Releases
|
- name: Delete Older Releases
|
||||||
|
4
.github/workflows/release.yml
vendored
4
.github/workflows/release.yml
vendored
@ -89,7 +89,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.10
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.93.0
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
@ -179,7 +179,7 @@ jobs:
|
|||||||
- name: Setup Nushell
|
- name: Setup Nushell
|
||||||
uses: hustcer/setup-nu@v3.10
|
uses: hustcer/setup-nu@v3.10
|
||||||
with:
|
with:
|
||||||
version: 0.91.0
|
version: 0.93.0
|
||||||
|
|
||||||
- name: Release Nu Binary
|
- name: Release Nu Binary
|
||||||
id: nu
|
id: nu
|
||||||
|
182
Cargo.lock
generated
182
Cargo.lock
generated
@ -240,30 +240,6 @@ dependencies = [
|
|||||||
"wait-timeout",
|
"wait-timeout",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-channel"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f28243a43d821d11341ab73c80bed182dc015c514b951616cf79bd4af39af0c3"
|
|
||||||
dependencies = [
|
|
||||||
"concurrent-queue",
|
|
||||||
"event-listener 5.3.0",
|
|
||||||
"event-listener-strategy 0.5.1",
|
|
||||||
"futures-core",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-lock"
|
|
||||||
version = "3.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b"
|
|
||||||
dependencies = [
|
|
||||||
"event-listener 4.0.3",
|
|
||||||
"event-listener-strategy 0.4.0",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-stream"
|
name = "async-stream"
|
||||||
version = "0.3.5"
|
version = "0.3.5"
|
||||||
@ -286,12 +262,6 @@ dependencies = [
|
|||||||
"syn 2.0.58",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "async-task"
|
|
||||||
version = "4.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-trait"
|
name = "async-trait"
|
||||||
version = "0.1.79"
|
version = "0.1.79"
|
||||||
@ -318,12 +288,6 @@ version = "0.15.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9ae037714f313c1353189ead58ef9eec30a8e8dc101b2622d461418fd59e28a9"
|
checksum = "9ae037714f313c1353189ead58ef9eec30a8e8dc101b2622d461418fd59e28a9"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atomic-waker"
|
|
||||||
version = "1.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
@ -472,22 +436,6 @@ dependencies = [
|
|||||||
"generic-array",
|
"generic-array",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "blocking"
|
|
||||||
version = "1.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118"
|
|
||||||
dependencies = [
|
|
||||||
"async-channel",
|
|
||||||
"async-lock",
|
|
||||||
"async-task",
|
|
||||||
"fastrand",
|
|
||||||
"futures-io",
|
|
||||||
"futures-lite",
|
|
||||||
"piper",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "borsh"
|
name = "borsh"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@ -905,15 +853,6 @@ dependencies = [
|
|||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "concurrent-queue"
|
|
||||||
version = "2.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "condtype"
|
name = "condtype"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -1446,48 +1385,6 @@ version = "1.5.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c"
|
checksum = "b90ca2580b73ab6a1f724b76ca11ab632df820fd6040c336200d2c1df7b3c82c"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "event-listener"
|
|
||||||
version = "4.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e"
|
|
||||||
dependencies = [
|
|
||||||
"concurrent-queue",
|
|
||||||
"parking",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "event-listener"
|
|
||||||
version = "5.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24"
|
|
||||||
dependencies = [
|
|
||||||
"concurrent-queue",
|
|
||||||
"parking",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "event-listener-strategy"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3"
|
|
||||||
dependencies = [
|
|
||||||
"event-listener 4.0.3",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "event-listener-strategy"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3"
|
|
||||||
dependencies = [
|
|
||||||
"event-listener 5.3.0",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "fallible-iterator"
|
name = "fallible-iterator"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
@ -1704,16 +1601,6 @@ version = "0.3.30"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-lite"
|
|
||||||
version = "2.3.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-macro"
|
name = "futures-macro"
|
||||||
version = "0.3.30"
|
version = "0.3.30"
|
||||||
@ -2135,30 +2022,16 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "interprocess"
|
name = "interprocess"
|
||||||
version = "1.2.1"
|
version = "2.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb"
|
checksum = "6d5f0e3c218e7a86a6712fd3adc84672304f9e839402b866685b9117a077c37f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"blocking",
|
|
||||||
"cfg-if",
|
|
||||||
"futures-core",
|
|
||||||
"futures-io",
|
|
||||||
"intmap",
|
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"recvmsg",
|
||||||
"rustc_version",
|
"widestring",
|
||||||
"spinning",
|
"windows-sys 0.52.0",
|
||||||
"thiserror",
|
|
||||||
"to_method",
|
|
||||||
"winapi",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "intmap"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "inventory"
|
name = "inventory"
|
||||||
version = "0.3.15"
|
version = "0.3.15"
|
||||||
@ -3364,6 +3237,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros 0.26.2",
|
"strum_macros 0.26.2",
|
||||||
|
"tempfile",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"typetag",
|
"typetag",
|
||||||
]
|
]
|
||||||
@ -3840,12 +3714,6 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "parking"
|
|
||||||
version = "2.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
@ -4069,17 +3937,6 @@ version = "0.1.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "piper"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4"
|
|
||||||
dependencies = [
|
|
||||||
"atomic-waker",
|
|
||||||
"fastrand",
|
|
||||||
"futures-io",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.30"
|
version = "0.3.30"
|
||||||
@ -4875,6 +4732,12 @@ dependencies = [
|
|||||||
"syn 2.0.58",
|
"syn 2.0.58",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "recvmsg"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -5615,15 +5478,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spinning"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlparser"
|
name = "sqlparser"
|
||||||
version = "0.39.0"
|
version = "0.39.0"
|
||||||
@ -6061,12 +5915,6 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "to_method"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.37.0"
|
version = "1.37.0"
|
||||||
@ -6772,6 +6620,12 @@ dependencies = [
|
|||||||
"winsafe",
|
"winsafe",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "widestring"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wild"
|
name = "wild"
|
||||||
version = "2.2.1"
|
version = "2.2.1"
|
||||||
|
@ -94,7 +94,7 @@ heck = "0.5.0"
|
|||||||
human-date-parser = "0.1.1"
|
human-date-parser = "0.1.1"
|
||||||
indexmap = "2.2"
|
indexmap = "2.2"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
interprocess = "1.2.1"
|
interprocess = "2.0.0"
|
||||||
is_executable = "1.0"
|
is_executable = "1.0"
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
@ -21,6 +21,7 @@ fn load_bench_commands() -> EngineState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf {
|
fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf {
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = engine_state.current_work_dir();
|
let cwd = engine_state.current_work_dir();
|
||||||
|
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
|
@ -43,6 +43,7 @@ impl Completer for DirectoryCompletion {
|
|||||||
let AdjustView { prefix, span, .. } = adjust_if_intermediate(&prefix, working_set, span);
|
let AdjustView { prefix, span, .. } = adjust_if_intermediate(&prefix, working_set, span);
|
||||||
|
|
||||||
// Filter only the folders
|
// Filter only the folders
|
||||||
|
#[allow(deprecated)]
|
||||||
let output: Vec<_> = directory_completion(
|
let output: Vec<_> = directory_completion(
|
||||||
span,
|
span,
|
||||||
&prefix,
|
&prefix,
|
||||||
|
@ -84,6 +84,7 @@ impl Completer for DotNuCompletion {
|
|||||||
partial = base_dir_partial;
|
partial = base_dir_partial;
|
||||||
} else {
|
} else {
|
||||||
// Fetch the current folder
|
// Fetch the current folder
|
||||||
|
#[allow(deprecated)]
|
||||||
let current_folder = self.engine_state.current_work_dir();
|
let current_folder = self.engine_state.current_work_dir();
|
||||||
is_current_folder = true;
|
is_current_folder = true;
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ impl Completer for FileCompletion {
|
|||||||
readjusted,
|
readjusted,
|
||||||
} = adjust_if_intermediate(&prefix, working_set, span);
|
} = adjust_if_intermediate(&prefix, working_set, span);
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let output: Vec<_> = complete_item(
|
let output: Vec<_> = complete_item(
|
||||||
readjusted,
|
readjusted,
|
||||||
span,
|
span,
|
||||||
|
@ -267,24 +267,6 @@ fn nested_suggestions(
|
|||||||
|
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
// Add all the columns as completion
|
|
||||||
for column_name in val.column_names() {
|
|
||||||
output.push(SemanticSuggestion {
|
|
||||||
suggestion: Suggestion {
|
|
||||||
value: column_name.to_string(),
|
|
||||||
description: None,
|
|
||||||
style: None,
|
|
||||||
extra: None,
|
|
||||||
span: current_span,
|
|
||||||
append_whitespace: false,
|
|
||||||
},
|
|
||||||
kind: Some(kind.clone()),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
output
|
|
||||||
}
|
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
for column_name in get_columns(vals.as_slice()) {
|
for column_name in get_columns(vals.as_slice()) {
|
||||||
output.push(SemanticSuggestion {
|
output.push(SemanticSuggestion {
|
||||||
@ -321,17 +303,6 @@ fn recursive_value(val: &Value, sublevels: &[Vec<u8>]) -> Result<Value, Span> {
|
|||||||
Err(span)
|
Err(span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
for col in val.column_names() {
|
|
||||||
if col.as_bytes() == *sublevel {
|
|
||||||
let val = val.get_column_value(col).map_err(|_| span)?;
|
|
||||||
return recursive_value(&val, next_sublevels);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Current sublevel value not found
|
|
||||||
Err(span)
|
|
||||||
}
|
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
for col in get_columns(vals.as_slice()) {
|
for col in get_columns(vals.as_slice()) {
|
||||||
if col.as_bytes() == *sublevel {
|
if col.as_bytes() == *sublevel {
|
||||||
|
@ -177,6 +177,7 @@ pub fn add_plugin_file(
|
|||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
if let Some(plugin_file) = plugin_file {
|
if let Some(plugin_file) = plugin_file {
|
||||||
@ -235,6 +236,7 @@ pub fn eval_config_contents(
|
|||||||
engine_state.file = prev_file;
|
engine_state.file = prev_file;
|
||||||
|
|
||||||
// Merge the environment in case env vars changed in the config
|
// Merge the environment in case env vars changed in the config
|
||||||
|
#[allow(deprecated)]
|
||||||
match nu_engine::env::current_dir(engine_state, stack) {
|
match nu_engine::env::current_dir(engine_state, stack) {
|
||||||
Ok(cwd) => {
|
Ok(cwd) => {
|
||||||
if let Err(e) = engine_state.merge_env(stack, cwd) {
|
if let Err(e) = engine_state.merge_env(stack, cwd) {
|
||||||
@ -272,6 +274,7 @@ pub fn migrate_old_plugin_file(engine_state: &EngineState, storage_path: &str) -
|
|||||||
|
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = engine_state.current_work_dir();
|
let cwd = engine_state.current_work_dir();
|
||||||
|
|
||||||
let Some(config_dir) = nu_path::config_dir().and_then(|mut dir| {
|
let Some(config_dir) = nu_path::config_dir().and_then(|mut dir| {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::util::eval_source;
|
use crate::util::eval_source;
|
||||||
use log::{info, trace};
|
use log::{info, trace};
|
||||||
use miette::{IntoDiagnostic, Result};
|
use miette::{IntoDiagnostic, Result};
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{convert_env_values, current_dir, eval_block};
|
use nu_engine::{convert_env_values, current_dir, eval_block};
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
@ -29,6 +30,7 @@ pub fn evaluate_file(
|
|||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
let file_path = canonicalize_with(&path, cwd).unwrap_or_else(|e| {
|
let file_path = canonicalize_with(&path, cwd).unwrap_or_else(|e| {
|
||||||
|
@ -1,4 +1,10 @@
|
|||||||
use crate::prompt_update::{POST_PROMPT_MARKER, PRE_PROMPT_MARKER};
|
use crate::prompt_update::{
|
||||||
|
POST_PROMPT_MARKER, PRE_PROMPT_MARKER, VSCODE_POST_PROMPT_MARKER, VSCODE_PRE_PROMPT_MARKER,
|
||||||
|
};
|
||||||
|
use nu_protocol::{
|
||||||
|
engine::{EngineState, Stack},
|
||||||
|
Value,
|
||||||
|
};
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use nu_utils::enable_vt_processing;
|
use nu_utils::enable_vt_processing;
|
||||||
use reedline::{
|
use reedline::{
|
||||||
@ -10,7 +16,8 @@ use std::borrow::Cow;
|
|||||||
/// Nushell prompt definition
|
/// Nushell prompt definition
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct NushellPrompt {
|
pub struct NushellPrompt {
|
||||||
shell_integration: bool,
|
shell_integration_osc133: bool,
|
||||||
|
shell_integration_osc633: bool,
|
||||||
left_prompt_string: Option<String>,
|
left_prompt_string: Option<String>,
|
||||||
right_prompt_string: Option<String>,
|
right_prompt_string: Option<String>,
|
||||||
default_prompt_indicator: Option<String>,
|
default_prompt_indicator: Option<String>,
|
||||||
@ -18,12 +25,20 @@ pub struct NushellPrompt {
|
|||||||
default_vi_normal_prompt_indicator: Option<String>,
|
default_vi_normal_prompt_indicator: Option<String>,
|
||||||
default_multiline_indicator: Option<String>,
|
default_multiline_indicator: Option<String>,
|
||||||
render_right_prompt_on_last_line: bool,
|
render_right_prompt_on_last_line: bool,
|
||||||
|
engine_state: EngineState,
|
||||||
|
stack: Stack,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NushellPrompt {
|
impl NushellPrompt {
|
||||||
pub fn new(shell_integration: bool) -> NushellPrompt {
|
pub fn new(
|
||||||
|
shell_integration_osc133: bool,
|
||||||
|
shell_integration_osc633: bool,
|
||||||
|
engine_state: EngineState,
|
||||||
|
stack: Stack,
|
||||||
|
) -> NushellPrompt {
|
||||||
NushellPrompt {
|
NushellPrompt {
|
||||||
shell_integration,
|
shell_integration_osc133,
|
||||||
|
shell_integration_osc633,
|
||||||
left_prompt_string: None,
|
left_prompt_string: None,
|
||||||
right_prompt_string: None,
|
right_prompt_string: None,
|
||||||
default_prompt_indicator: None,
|
default_prompt_indicator: None,
|
||||||
@ -31,6 +46,8 @@ impl NushellPrompt {
|
|||||||
default_vi_normal_prompt_indicator: None,
|
default_vi_normal_prompt_indicator: None,
|
||||||
default_multiline_indicator: None,
|
default_multiline_indicator: None,
|
||||||
render_right_prompt_on_last_line: false,
|
render_right_prompt_on_last_line: false,
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +123,17 @@ impl Prompt for NushellPrompt {
|
|||||||
.to_string()
|
.to_string()
|
||||||
.replace('\n', "\r\n");
|
.replace('\n', "\r\n");
|
||||||
|
|
||||||
if self.shell_integration {
|
if self.shell_integration_osc633 {
|
||||||
|
if self.stack.get_env_var(&self.engine_state, "TERM_PROGRAM")
|
||||||
|
== Some(Value::test_string("vscode"))
|
||||||
|
{
|
||||||
|
// We're in vscode and we have osc633 enabled
|
||||||
|
format!("{VSCODE_PRE_PROMPT_MARKER}{prompt}{VSCODE_POST_PROMPT_MARKER}").into()
|
||||||
|
} else {
|
||||||
|
// If we're in VSCode but we don't find the env var, just return the regular markers
|
||||||
|
format!("{PRE_PROMPT_MARKER}{prompt}{POST_PROMPT_MARKER}").into()
|
||||||
|
}
|
||||||
|
} else if self.shell_integration_osc133 {
|
||||||
format!("{PRE_PROMPT_MARKER}{prompt}{POST_PROMPT_MARKER}").into()
|
format!("{PRE_PROMPT_MARKER}{prompt}{POST_PROMPT_MARKER}").into()
|
||||||
} else {
|
} else {
|
||||||
prompt.into()
|
prompt.into()
|
||||||
|
@ -23,10 +23,37 @@ pub(crate) const TRANSIENT_PROMPT_INDICATOR_VI_NORMAL: &str =
|
|||||||
"TRANSIENT_PROMPT_INDICATOR_VI_NORMAL";
|
"TRANSIENT_PROMPT_INDICATOR_VI_NORMAL";
|
||||||
pub(crate) const TRANSIENT_PROMPT_MULTILINE_INDICATOR: &str =
|
pub(crate) const TRANSIENT_PROMPT_MULTILINE_INDICATOR: &str =
|
||||||
"TRANSIENT_PROMPT_MULTILINE_INDICATOR";
|
"TRANSIENT_PROMPT_MULTILINE_INDICATOR";
|
||||||
|
|
||||||
|
// Store all these Ansi Escape Markers here so they can be reused easily
|
||||||
// According to Daniel Imms @Tyriar, we need to do these this way:
|
// According to Daniel Imms @Tyriar, we need to do these this way:
|
||||||
// <133 A><prompt><133 B><command><133 C><command output>
|
// <133 A><prompt><133 B><command><133 C><command output>
|
||||||
pub(crate) const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
pub(crate) const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
||||||
pub(crate) const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
|
pub(crate) const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
|
||||||
|
pub(crate) const PRE_EXECUTION_MARKER: &str = "\x1b]133;C\x1b\\";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const POST_EXECUTION_MARKER_PREFIX: &str = "\x1b]133;D;";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const POST_EXECUTION_MARKER_SUFFIX: &str = "\x1b\\";
|
||||||
|
|
||||||
|
// OSC633 is the same as OSC133 but specifically for VSCode
|
||||||
|
pub(crate) const VSCODE_PRE_PROMPT_MARKER: &str = "\x1b]633;A\x1b\\";
|
||||||
|
pub(crate) const VSCODE_POST_PROMPT_MARKER: &str = "\x1b]633;B\x1b\\";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const VSCODE_PRE_EXECUTION_MARKER: &str = "\x1b]633;C\x1b\\";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
//"\x1b]633;D;{}\x1b\\"
|
||||||
|
pub(crate) const VSCODE_POST_EXECUTION_MARKER_PREFIX: &str = "\x1b]633;D;";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const VSCODE_POST_EXECUTION_MARKER_SUFFIX: &str = "\x1b\\";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const VSCODE_COMMANDLINE_MARKER: &str = "\x1b]633;E\x1b\\";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
// "\x1b]633;P;Cwd={}\x1b\\"
|
||||||
|
pub(crate) const VSCODE_CWD_PROPERTY_MARKER_PREFIX: &str = "\x1b]633;P;Cwd=";
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub(crate) const VSCODE_CWD_PROPERTY_MARKER_SUFFIX: &str = "\x1b\\";
|
||||||
|
|
||||||
|
pub(crate) const RESET_APPLICATION_MODE: &str = "\x1b[?1l";
|
||||||
|
|
||||||
fn get_prompt_string(
|
fn get_prompt_string(
|
||||||
prompt: &str,
|
prompt: &str,
|
||||||
@ -85,16 +112,46 @@ pub(crate) fn update_prompt(
|
|||||||
|
|
||||||
// Now that we have the prompt string lets ansify it.
|
// Now that we have the prompt string lets ansify it.
|
||||||
// <133 A><prompt><133 B><command><133 C><command output>
|
// <133 A><prompt><133 B><command><133 C><command output>
|
||||||
let left_prompt_string = if config.shell_integration {
|
let left_prompt_string_133 = if config.shell_integration_osc133 {
|
||||||
if let Some(prompt_string) = left_prompt_string {
|
if let Some(prompt_string) = left_prompt_string.clone() {
|
||||||
Some(format!(
|
Some(format!(
|
||||||
"{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}"
|
"{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}"
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
left_prompt_string
|
left_prompt_string.clone()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
left_prompt_string
|
left_prompt_string.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let left_prompt_string_633 = if config.shell_integration_osc633 {
|
||||||
|
if let Some(prompt_string) = left_prompt_string.clone() {
|
||||||
|
if stack.get_env_var(engine_state, "TERM_PROGRAM") == Some(Value::test_string("vscode"))
|
||||||
|
{
|
||||||
|
// If the user enabled osc633 and we're in vscode, use the vscode markers
|
||||||
|
Some(format!(
|
||||||
|
"{VSCODE_PRE_PROMPT_MARKER}{prompt_string}{VSCODE_POST_PROMPT_MARKER}"
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
// otherwise, use the regular osc133 markers
|
||||||
|
Some(format!(
|
||||||
|
"{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}"
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
left_prompt_string.clone()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
left_prompt_string.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
let left_prompt_string = match (left_prompt_string_133, left_prompt_string_633) {
|
||||||
|
(None, None) => left_prompt_string,
|
||||||
|
(None, Some(l633)) => Some(l633),
|
||||||
|
(Some(l133), None) => Some(l133),
|
||||||
|
// If both are set, it means we're in vscode, so use the vscode markers
|
||||||
|
// and even if we're not actually in vscode atm, the regular 133 markers are used
|
||||||
|
(Some(_l133), Some(l633)) => Some(l633),
|
||||||
};
|
};
|
||||||
|
|
||||||
let right_prompt_string = get_prompt_string(PROMPT_COMMAND_RIGHT, config, engine_state, stack);
|
let right_prompt_string = get_prompt_string(PROMPT_COMMAND_RIGHT, config, engine_state, stack);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
|
||||||
use crossterm::event::{KeyCode, KeyModifiers};
|
use crossterm::event::{KeyCode, KeyModifiers};
|
||||||
use log::trace;
|
use log::trace;
|
||||||
|
use nu_ansi_term::Style;
|
||||||
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
|
||||||
use nu_engine::eval_block;
|
use nu_engine::eval_block;
|
||||||
use nu_parser::parse;
|
use nu_parser::parse;
|
||||||
@ -158,21 +159,14 @@ fn add_menu(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! add_style {
|
fn get_style(record: &Record, name: &str, span: Span) -> Option<Style> {
|
||||||
// first arm match add!(1,2), add!(2,3) etc
|
extract_value(name, record, span)
|
||||||
($name:expr, $record: expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
|
.ok()
|
||||||
$menu = match extract_value($name, $record, $span) {
|
.map(|text| match text {
|
||||||
Ok(text) => {
|
Value::String { val, .. } => lookup_ansi_color_style(val),
|
||||||
let style = match text {
|
Value::Record { .. } => color_record_to_nustyle(text),
|
||||||
Value::String { val, .. } => lookup_ansi_color_style(&val),
|
_ => lookup_ansi_color_style("green"),
|
||||||
Value::Record { .. } => color_record_to_nustyle(&text),
|
})
|
||||||
_ => lookup_ansi_color_style("green"),
|
|
||||||
};
|
|
||||||
$f($menu, style)
|
|
||||||
}
|
|
||||||
Err(_) => $menu,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds a columnar menu to the editor engine
|
// Adds a columnar menu to the editor engine
|
||||||
@ -215,46 +209,21 @@ pub(crate) fn add_columnar_menu(
|
|||||||
|
|
||||||
let span = menu.style.span();
|
let span = menu.style.span();
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
if let Value::Record { val, .. } = &menu.style {
|
||||||
add_style!(
|
if let Some(style) = get_style(val, "text", span) {
|
||||||
"text",
|
columnar_menu = columnar_menu.with_text_style(style);
|
||||||
val,
|
}
|
||||||
span,
|
if let Some(style) = get_style(val, "selected_text", span) {
|
||||||
config,
|
columnar_menu = columnar_menu.with_selected_text_style(style);
|
||||||
columnar_menu,
|
}
|
||||||
ColumnarMenu::with_text_style
|
if let Some(style) = get_style(val, "description_text", span) {
|
||||||
);
|
columnar_menu = columnar_menu.with_description_text_style(style);
|
||||||
add_style!(
|
}
|
||||||
"selected_text",
|
if let Some(style) = get_style(val, "match_text", span) {
|
||||||
val,
|
columnar_menu = columnar_menu.with_match_text_style(style);
|
||||||
span,
|
}
|
||||||
config,
|
if let Some(style) = get_style(val, "selected_match_text", span) {
|
||||||
columnar_menu,
|
columnar_menu = columnar_menu.with_selected_match_text_style(style);
|
||||||
ColumnarMenu::with_selected_text_style
|
}
|
||||||
);
|
|
||||||
add_style!(
|
|
||||||
"description_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
columnar_menu,
|
|
||||||
ColumnarMenu::with_description_text_style
|
|
||||||
);
|
|
||||||
add_style!(
|
|
||||||
"match_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
columnar_menu,
|
|
||||||
ColumnarMenu::with_match_text_style
|
|
||||||
);
|
|
||||||
add_style!(
|
|
||||||
"selected_match_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
columnar_menu,
|
|
||||||
ColumnarMenu::with_selected_match_text_style
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", config);
|
let marker = menu.marker.to_expanded_string("", config);
|
||||||
@ -313,30 +282,15 @@ pub(crate) fn add_list_menu(
|
|||||||
|
|
||||||
let span = menu.style.span();
|
let span = menu.style.span();
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
if let Value::Record { val, .. } = &menu.style {
|
||||||
add_style!(
|
if let Some(style) = get_style(val, "text", span) {
|
||||||
"text",
|
list_menu = list_menu.with_text_style(style);
|
||||||
val,
|
}
|
||||||
span,
|
if let Some(style) = get_style(val, "selected_text", span) {
|
||||||
config,
|
list_menu = list_menu.with_selected_text_style(style);
|
||||||
list_menu,
|
}
|
||||||
ListMenu::with_text_style
|
if let Some(style) = get_style(val, "description_text", span) {
|
||||||
);
|
list_menu = list_menu.with_description_text_style(style);
|
||||||
add_style!(
|
}
|
||||||
"selected_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
list_menu,
|
|
||||||
ListMenu::with_selected_text_style
|
|
||||||
);
|
|
||||||
add_style!(
|
|
||||||
"description_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
list_menu,
|
|
||||||
ListMenu::with_description_text_style
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", config);
|
let marker = menu.marker.to_expanded_string("", config);
|
||||||
@ -520,46 +474,21 @@ pub(crate) fn add_ide_menu(
|
|||||||
|
|
||||||
let span = menu.style.span();
|
let span = menu.style.span();
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
if let Value::Record { val, .. } = &menu.style {
|
||||||
add_style!(
|
if let Some(style) = get_style(val, "text", span) {
|
||||||
"text",
|
ide_menu = ide_menu.with_text_style(style);
|
||||||
val,
|
}
|
||||||
span,
|
if let Some(style) = get_style(val, "selected_text", span) {
|
||||||
config,
|
ide_menu = ide_menu.with_selected_text_style(style);
|
||||||
ide_menu,
|
}
|
||||||
IdeMenu::with_text_style
|
if let Some(style) = get_style(val, "description_text", span) {
|
||||||
);
|
ide_menu = ide_menu.with_description_text_style(style);
|
||||||
add_style!(
|
}
|
||||||
"selected_text",
|
if let Some(style) = get_style(val, "match_text", span) {
|
||||||
val,
|
ide_menu = ide_menu.with_match_text_style(style);
|
||||||
span,
|
}
|
||||||
config,
|
if let Some(style) = get_style(val, "selected_match_text", span) {
|
||||||
ide_menu,
|
ide_menu = ide_menu.with_selected_match_text_style(style);
|
||||||
IdeMenu::with_selected_text_style
|
}
|
||||||
);
|
|
||||||
add_style!(
|
|
||||||
"description_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
ide_menu,
|
|
||||||
IdeMenu::with_description_text_style
|
|
||||||
);
|
|
||||||
add_style!(
|
|
||||||
"match_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
ide_menu,
|
|
||||||
IdeMenu::with_match_text_style
|
|
||||||
);
|
|
||||||
add_style!(
|
|
||||||
"selected_match_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
ide_menu,
|
|
||||||
IdeMenu::with_selected_match_text_style
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", config);
|
let marker = menu.marker.to_expanded_string("", config);
|
||||||
@ -650,30 +579,15 @@ pub(crate) fn add_description_menu(
|
|||||||
|
|
||||||
let span = menu.style.span();
|
let span = menu.style.span();
|
||||||
if let Value::Record { val, .. } = &menu.style {
|
if let Value::Record { val, .. } = &menu.style {
|
||||||
add_style!(
|
if let Some(style) = get_style(val, "text", span) {
|
||||||
"text",
|
description_menu = description_menu.with_text_style(style);
|
||||||
val,
|
}
|
||||||
span,
|
if let Some(style) = get_style(val, "selected_text", span) {
|
||||||
config,
|
description_menu = description_menu.with_selected_text_style(style);
|
||||||
description_menu,
|
}
|
||||||
DescriptionMenu::with_text_style
|
if let Some(style) = get_style(val, "description_text", span) {
|
||||||
);
|
description_menu = description_menu.with_description_text_style(style);
|
||||||
add_style!(
|
}
|
||||||
"selected_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
description_menu,
|
|
||||||
DescriptionMenu::with_selected_text_style
|
|
||||||
);
|
|
||||||
add_style!(
|
|
||||||
"description_text",
|
|
||||||
val,
|
|
||||||
span,
|
|
||||||
config,
|
|
||||||
description_menu,
|
|
||||||
DescriptionMenu::with_description_text_style
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let marker = menu.marker.to_expanded_string("", config);
|
let marker = menu.marker.to_expanded_string("", config);
|
||||||
|
@ -1,3 +1,9 @@
|
|||||||
|
use crate::prompt_update::{
|
||||||
|
POST_EXECUTION_MARKER_PREFIX, POST_EXECUTION_MARKER_SUFFIX, PRE_EXECUTION_MARKER,
|
||||||
|
RESET_APPLICATION_MODE, VSCODE_CWD_PROPERTY_MARKER_PREFIX, VSCODE_CWD_PROPERTY_MARKER_SUFFIX,
|
||||||
|
VSCODE_POST_EXECUTION_MARKER_PREFIX, VSCODE_POST_EXECUTION_MARKER_SUFFIX,
|
||||||
|
VSCODE_PRE_EXECUTION_MARKER,
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
completions::NuCompleter,
|
completions::NuCompleter,
|
||||||
nu_highlight::NoOpHighlighter,
|
nu_highlight::NoOpHighlighter,
|
||||||
@ -14,7 +20,8 @@ use nu_cmd_base::{
|
|||||||
util::{get_editor, get_guaranteed_cwd},
|
util::{get_editor, get_guaranteed_cwd},
|
||||||
};
|
};
|
||||||
use nu_color_config::StyleComputer;
|
use nu_color_config::StyleComputer;
|
||||||
use nu_engine::{convert_env_values, env_to_strings};
|
#[allow(deprecated)]
|
||||||
|
use nu_engine::{convert_env_values, current_dir_str, env_to_strings};
|
||||||
use nu_parser::{lex, parse, trim_quotes_str};
|
use nu_parser::{lex, parse, trim_quotes_str};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
config::NuCursorShape,
|
config::NuCursorShape,
|
||||||
@ -42,16 +49,6 @@ use std::{
|
|||||||
};
|
};
|
||||||
use sysinfo::System;
|
use sysinfo::System;
|
||||||
|
|
||||||
// According to Daniel Imms @Tyriar, we need to do these this way:
|
|
||||||
// <133 A><prompt><133 B><command><133 C><command output>
|
|
||||||
// These first two have been moved to prompt_update to get as close as possible to the prompt.
|
|
||||||
// const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
|
||||||
// const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
|
|
||||||
const PRE_EXECUTE_MARKER: &str = "\x1b]133;C\x1b\\";
|
|
||||||
// This one is in get_command_finished_marker() now so we can capture the exit codes properly.
|
|
||||||
// const CMD_FINISHED_MARKER: &str = "\x1b]133;D;{}\x1b\\";
|
|
||||||
const RESET_APPLICATION_MODE: &str = "\x1b[?1l";
|
|
||||||
|
|
||||||
/// The main REPL loop, including spinning up the prompt itself.
|
/// The main REPL loop, including spinning up the prompt itself.
|
||||||
pub fn evaluate_repl(
|
pub fn evaluate_repl(
|
||||||
engine_state: &mut EngineState,
|
engine_state: &mut EngineState,
|
||||||
@ -66,7 +63,7 @@ pub fn evaluate_repl(
|
|||||||
// so that it may be read by various reedline plugins. During this, we
|
// so that it may be read by various reedline plugins. During this, we
|
||||||
// can't modify the stack, but at the end of the loop we take back ownership
|
// can't modify the stack, but at the end of the loop we take back ownership
|
||||||
// from the Arc. This lets us avoid copying stack variables needlessly
|
// from the Arc. This lets us avoid copying stack variables needlessly
|
||||||
let mut unique_stack = stack;
|
let mut unique_stack = stack.clone();
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
let use_color = config.use_ansi_coloring;
|
let use_color = config.use_ansi_coloring;
|
||||||
|
|
||||||
@ -74,8 +71,19 @@ pub fn evaluate_repl(
|
|||||||
|
|
||||||
let mut entry_num = 0;
|
let mut entry_num = 0;
|
||||||
|
|
||||||
let shell_integration = config.shell_integration;
|
// Let's grab the shell_integration configs
|
||||||
let nu_prompt = NushellPrompt::new(shell_integration);
|
let shell_integration_osc2 = config.shell_integration_osc2;
|
||||||
|
let shell_integration_osc7 = config.shell_integration_osc7;
|
||||||
|
let shell_integration_osc9_9 = config.shell_integration_osc9_9;
|
||||||
|
let shell_integration_osc133 = config.shell_integration_osc133;
|
||||||
|
let shell_integration_osc633 = config.shell_integration_osc633;
|
||||||
|
|
||||||
|
let nu_prompt = NushellPrompt::new(
|
||||||
|
shell_integration_osc133,
|
||||||
|
shell_integration_osc633,
|
||||||
|
engine_state.clone(),
|
||||||
|
stack.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
let start_time = std::time::Instant::now();
|
let start_time = std::time::Instant::now();
|
||||||
// Translate environment variables from Strings to Values
|
// Translate environment variables from Strings to Values
|
||||||
@ -116,8 +124,22 @@ pub fn evaluate_repl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let hostname = System::host_name();
|
let hostname = System::host_name();
|
||||||
if shell_integration {
|
if shell_integration_osc2 {
|
||||||
shell_integration_osc_7_633_2(hostname.as_deref(), engine_state, &mut unique_stack);
|
run_shell_integration_osc2(None, engine_state, &mut unique_stack, use_color);
|
||||||
|
}
|
||||||
|
if shell_integration_osc7 {
|
||||||
|
run_shell_integration_osc7(
|
||||||
|
hostname.as_deref(),
|
||||||
|
engine_state,
|
||||||
|
&mut unique_stack,
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if shell_integration_osc9_9 {
|
||||||
|
run_shell_integration_osc9_9(engine_state, &mut unique_stack, use_color);
|
||||||
|
}
|
||||||
|
if shell_integration_osc633 {
|
||||||
|
run_shell_integration_osc633(engine_state, &mut unique_stack, use_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
|
engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64);
|
||||||
@ -513,7 +535,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
.with_highlighter(Box::<NoOpHighlighter>::default())
|
.with_highlighter(Box::<NoOpHighlighter>::default())
|
||||||
// CLEAR STACK-REFERENCE 2
|
// CLEAR STACK-REFERENCE 2
|
||||||
.with_completer(Box::<DefaultCompleter>::default());
|
.with_completer(Box::<DefaultCompleter>::default());
|
||||||
let shell_integration = config.shell_integration;
|
|
||||||
|
// Let's grab the shell_integration configs
|
||||||
|
let shell_integration_osc2 = config.shell_integration_osc2;
|
||||||
|
let shell_integration_osc7 = config.shell_integration_osc7;
|
||||||
|
let shell_integration_osc9_9 = config.shell_integration_osc9_9;
|
||||||
|
let shell_integration_osc133 = config.shell_integration_osc133;
|
||||||
|
let shell_integration_osc633 = config.shell_integration_osc633;
|
||||||
|
let shell_integration_reset_application_mode = config.shell_integration_reset_application_mode;
|
||||||
|
|
||||||
let mut stack = Stack::unwrap_unique(stack_arc);
|
let mut stack = Stack::unwrap_unique(stack_arc);
|
||||||
|
|
||||||
@ -575,10 +604,40 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
repl.buffer = line_editor.current_buffer_contents().to_string();
|
repl.buffer = line_editor.current_buffer_contents().to_string();
|
||||||
drop(repl);
|
drop(repl);
|
||||||
|
|
||||||
if shell_integration {
|
if shell_integration_osc633 {
|
||||||
|
if stack.get_env_var(engine_state, "TERM_PROGRAM")
|
||||||
|
== Some(Value::test_string("vscode"))
|
||||||
|
{
|
||||||
|
start_time = Instant::now();
|
||||||
|
|
||||||
|
run_ansi_sequence(VSCODE_PRE_EXECUTION_MARKER);
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"pre_execute_marker (633;C) ansi escape sequence",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
start_time = Instant::now();
|
||||||
|
|
||||||
|
run_ansi_sequence(PRE_EXECUTION_MARKER);
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"pre_execute_marker (133;C) ansi escape sequence",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if shell_integration_osc133 {
|
||||||
start_time = Instant::now();
|
start_time = Instant::now();
|
||||||
|
|
||||||
run_ansi_sequence(PRE_EXECUTE_MARKER);
|
run_ansi_sequence(PRE_EXECUTION_MARKER);
|
||||||
|
|
||||||
perf(
|
perf(
|
||||||
"pre_execute_marker (133;C) ansi escape sequence",
|
"pre_execute_marker (133;C) ansi escape sequence",
|
||||||
@ -598,20 +657,13 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
ReplOperation::AutoCd { cwd, target, span } => {
|
ReplOperation::AutoCd { cwd, target, span } => {
|
||||||
do_auto_cd(target, cwd, &mut stack, engine_state, span);
|
do_auto_cd(target, cwd, &mut stack, engine_state, span);
|
||||||
|
|
||||||
if shell_integration {
|
run_finaliziation_ansi_sequence(
|
||||||
start_time = Instant::now();
|
&stack,
|
||||||
|
engine_state,
|
||||||
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
shell_integration_osc633,
|
||||||
|
shell_integration_osc133,
|
||||||
perf(
|
use_color,
|
||||||
"post_execute_marker (133;D) ansi escape sequences",
|
);
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ReplOperation::RunCommand(cmd) => {
|
ReplOperation::RunCommand(cmd) => {
|
||||||
line_editor = do_run_cmd(
|
line_editor = do_run_cmd(
|
||||||
@ -619,25 +671,18 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
&mut stack,
|
&mut stack,
|
||||||
engine_state,
|
engine_state,
|
||||||
line_editor,
|
line_editor,
|
||||||
shell_integration,
|
shell_integration_osc2,
|
||||||
*entry_num,
|
*entry_num,
|
||||||
use_color,
|
use_color,
|
||||||
);
|
);
|
||||||
|
|
||||||
if shell_integration {
|
run_finaliziation_ansi_sequence(
|
||||||
start_time = Instant::now();
|
&stack,
|
||||||
|
engine_state,
|
||||||
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
shell_integration_osc633,
|
||||||
|
shell_integration_osc133,
|
||||||
perf(
|
use_color,
|
||||||
"post_execute_marker (133;D) ansi escape sequences",
|
);
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// as the name implies, we do nothing in this case
|
// as the name implies, we do nothing in this case
|
||||||
ReplOperation::DoNothing => {}
|
ReplOperation::DoNothing => {}
|
||||||
@ -663,56 +708,45 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if shell_integration {
|
if shell_integration_osc2 {
|
||||||
start_time = Instant::now();
|
run_shell_integration_osc2(None, engine_state, &mut stack, use_color);
|
||||||
|
}
|
||||||
shell_integration_osc_7_633_2(hostname, engine_state, &mut stack);
|
if shell_integration_osc7 {
|
||||||
|
run_shell_integration_osc7(hostname, engine_state, &mut stack, use_color);
|
||||||
perf(
|
}
|
||||||
"shell_integration_finalize ansi escape sequences",
|
if shell_integration_osc9_9 {
|
||||||
start_time,
|
run_shell_integration_osc9_9(engine_state, &mut stack, use_color);
|
||||||
file!(),
|
}
|
||||||
line!(),
|
if shell_integration_osc633 {
|
||||||
column!(),
|
run_shell_integration_osc633(engine_state, &mut stack, use_color);
|
||||||
use_color,
|
}
|
||||||
);
|
if shell_integration_reset_application_mode {
|
||||||
|
run_shell_integration_reset_application_mode();
|
||||||
}
|
}
|
||||||
|
|
||||||
flush_engine_state_repl_buffer(engine_state, &mut line_editor);
|
flush_engine_state_repl_buffer(engine_state, &mut line_editor);
|
||||||
}
|
}
|
||||||
Ok(Signal::CtrlC) => {
|
Ok(Signal::CtrlC) => {
|
||||||
// `Reedline` clears the line content. New prompt is shown
|
// `Reedline` clears the line content. New prompt is shown
|
||||||
if shell_integration {
|
run_finaliziation_ansi_sequence(
|
||||||
start_time = Instant::now();
|
&stack,
|
||||||
|
engine_state,
|
||||||
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
shell_integration_osc633,
|
||||||
|
shell_integration_osc133,
|
||||||
perf(
|
use_color,
|
||||||
"command_finished_marker ansi escape sequence",
|
);
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(Signal::CtrlD) => {
|
Ok(Signal::CtrlD) => {
|
||||||
// When exiting clear to a new line
|
// When exiting clear to a new line
|
||||||
if shell_integration {
|
|
||||||
start_time = Instant::now();
|
|
||||||
|
|
||||||
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
run_finaliziation_ansi_sequence(
|
||||||
|
&stack,
|
||||||
|
engine_state,
|
||||||
|
shell_integration_osc633,
|
||||||
|
shell_integration_osc133,
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
|
||||||
perf(
|
|
||||||
"command_finished_marker ansi escape sequence",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
println!();
|
println!();
|
||||||
return (false, stack, line_editor);
|
return (false, stack, line_editor);
|
||||||
}
|
}
|
||||||
@ -725,20 +759,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
|
|||||||
// e.g. https://github.com/nushell/nushell/issues/6452
|
// e.g. https://github.com/nushell/nushell/issues/6452
|
||||||
// Alternatively only allow that expected failures let the REPL loop
|
// Alternatively only allow that expected failures let the REPL loop
|
||||||
}
|
}
|
||||||
if shell_integration {
|
|
||||||
start_time = Instant::now();
|
|
||||||
|
|
||||||
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
|
run_finaliziation_ansi_sequence(
|
||||||
|
&stack,
|
||||||
perf(
|
engine_state,
|
||||||
"command_finished_marker ansi escape sequence",
|
shell_integration_osc633,
|
||||||
start_time,
|
shell_integration_osc133,
|
||||||
file!(),
|
use_color,
|
||||||
line!(),
|
);
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
perf(
|
perf(
|
||||||
@ -772,6 +800,7 @@ fn prepare_history_metadata(
|
|||||||
line_editor: &mut Reedline,
|
line_editor: &mut Reedline,
|
||||||
) {
|
) {
|
||||||
if !s.is_empty() && line_editor.has_last_command_context() {
|
if !s.is_empty() && line_editor.has_last_command_context() {
|
||||||
|
#[allow(deprecated)]
|
||||||
let result = line_editor
|
let result = line_editor
|
||||||
.update_last_command_context(&|mut c| {
|
.update_last_command_context(&|mut c| {
|
||||||
c.start_timestamp = Some(chrono::Utc::now());
|
c.start_timestamp = Some(chrono::Utc::now());
|
||||||
@ -842,6 +871,7 @@ fn parse_operation(
|
|||||||
) -> Result<ReplOperation, ErrReport> {
|
) -> Result<ReplOperation, ErrReport> {
|
||||||
let tokens = lex(s.as_bytes(), 0, &[], &[], false);
|
let tokens = lex(s.as_bytes(), 0, &[], &[], false);
|
||||||
// Check if this is a single call to a directory, if so auto-cd
|
// Check if this is a single call to a directory, if so auto-cd
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = nu_engine::env::current_dir_str(engine_state, stack)?;
|
let cwd = nu_engine::env::current_dir_str(engine_state, stack)?;
|
||||||
let mut orig = s.clone();
|
let mut orig = s.clone();
|
||||||
if orig.starts_with('`') {
|
if orig.starts_with('`') {
|
||||||
@ -882,8 +912,6 @@ fn do_auto_cd(
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let path = nu_path::canonicalize_with(path, &cwd)
|
|
||||||
.expect("internal error: cannot canonicalize known path");
|
|
||||||
path.to_string_lossy().to_string()
|
path.to_string_lossy().to_string()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -946,7 +974,7 @@ fn do_run_cmd(
|
|||||||
// we pass in the line editor so it can be dropped in the case of a process exit
|
// we pass in the line editor so it can be dropped in the case of a process exit
|
||||||
// (in the normal case we don't want to drop it so return it as-is otherwise)
|
// (in the normal case we don't want to drop it so return it as-is otherwise)
|
||||||
line_editor: Reedline,
|
line_editor: Reedline,
|
||||||
shell_integration: bool,
|
shell_integration_osc2: bool,
|
||||||
entry_num: usize,
|
entry_num: usize,
|
||||||
use_color: bool,
|
use_color: bool,
|
||||||
) -> Reedline {
|
) -> Reedline {
|
||||||
@ -973,39 +1001,8 @@ fn do_run_cmd(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if shell_integration {
|
if shell_integration_osc2 {
|
||||||
let start_time = Instant::now();
|
run_shell_integration_osc2(Some(s), engine_state, stack, use_color);
|
||||||
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
|
||||||
match cwd.coerce_into_string() {
|
|
||||||
Ok(path) => {
|
|
||||||
// Try to abbreviate string for windows title
|
|
||||||
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
|
|
||||||
path.replace(&p.as_path().display().to_string(), "~")
|
|
||||||
} else {
|
|
||||||
path
|
|
||||||
};
|
|
||||||
let binary_name = s.split_whitespace().next();
|
|
||||||
|
|
||||||
if let Some(binary_name) = binary_name {
|
|
||||||
run_ansi_sequence(&format!(
|
|
||||||
"\x1b]2;{maybe_abbrev_path}> {binary_name}\x07"
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
warn!("Could not coerce working directory to string {e}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
perf(
|
|
||||||
"set title with command ansi escape sequence",
|
|
||||||
start_time,
|
|
||||||
file!(),
|
|
||||||
line!(),
|
|
||||||
column!(),
|
|
||||||
use_color,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
eval_source(
|
eval_source(
|
||||||
@ -1025,54 +1022,136 @@ fn do_run_cmd(
|
|||||||
/// can have more information about what is going on (both on startup and after we have
|
/// can have more information about what is going on (both on startup and after we have
|
||||||
/// run a command)
|
/// run a command)
|
||||||
///
|
///
|
||||||
fn shell_integration_osc_7_633_2(
|
fn run_shell_integration_osc2(
|
||||||
|
command_name: Option<&str>,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
stack: &mut Stack,
|
||||||
|
use_color: bool,
|
||||||
|
) {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
if let Ok(path) = current_dir_str(engine_state, stack) {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
|
// Try to abbreviate string for windows title
|
||||||
|
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
|
||||||
|
path.replace(&p.as_path().display().to_string(), "~")
|
||||||
|
} else {
|
||||||
|
path
|
||||||
|
};
|
||||||
|
|
||||||
|
let title = match command_name {
|
||||||
|
Some(binary_name) => {
|
||||||
|
let split_binary_name = binary_name.split_whitespace().next();
|
||||||
|
if let Some(binary_name) = split_binary_name {
|
||||||
|
format!("{maybe_abbrev_path}> {binary_name}")
|
||||||
|
} else {
|
||||||
|
maybe_abbrev_path.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => maybe_abbrev_path.to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set window title too
|
||||||
|
// https://tldp.org/HOWTO/Xterm-Title-3.html
|
||||||
|
// ESC]0;stringBEL -- Set icon name and window title to string
|
||||||
|
// ESC]1;stringBEL -- Set icon name to string
|
||||||
|
// 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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_shell_integration_osc7(
|
||||||
hostname: Option<&str>,
|
hostname: Option<&str>,
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
|
use_color: bool,
|
||||||
) {
|
) {
|
||||||
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
|
#[allow(deprecated)]
|
||||||
match cwd.coerce_into_string() {
|
if let Ok(path) = current_dir_str(engine_state, stack) {
|
||||||
Ok(path) => {
|
let start_time = Instant::now();
|
||||||
// Supported escape sequences of Microsoft's Visual Studio Code (vscode)
|
|
||||||
// https://code.visualstudio.com/docs/terminal/shell-integration#_supported-escape-sequences
|
|
||||||
if stack.get_env_var(engine_state, "TERM_PROGRAM")
|
|
||||||
== Some(Value::test_string("vscode"))
|
|
||||||
{
|
|
||||||
// If we're in vscode, run their specific ansi escape sequence.
|
|
||||||
// This is helpful for ctrl+g to change directories in the terminal.
|
|
||||||
run_ansi_sequence(&format!("\x1b]633;P;Cwd={}\x1b\\", path));
|
|
||||||
} else {
|
|
||||||
// Otherwise, communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
|
|
||||||
run_ansi_sequence(&format!(
|
|
||||||
"\x1b]7;file://{}{}{}\x1b\\",
|
|
||||||
percent_encoding::utf8_percent_encode(
|
|
||||||
hostname.unwrap_or("localhost"),
|
|
||||||
percent_encoding::CONTROLS
|
|
||||||
),
|
|
||||||
if path.starts_with('/') { "" } else { "/" },
|
|
||||||
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to abbreviate string for windows title
|
// Otherwise, communicate the path as OSC 7 (often used for spawning new tabs in the same dir)
|
||||||
let maybe_abbrev_path = if let Some(p) = nu_path::home_dir() {
|
run_ansi_sequence(&format!(
|
||||||
path.replace(&p.as_path().display().to_string(), "~")
|
"\x1b]7;file://{}{}{}\x1b\\",
|
||||||
} else {
|
percent_encoding::utf8_percent_encode(
|
||||||
path
|
hostname.unwrap_or("localhost"),
|
||||||
};
|
percent_encoding::CONTROLS
|
||||||
|
),
|
||||||
|
if path.starts_with('/') { "" } else { "/" },
|
||||||
|
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
|
||||||
|
));
|
||||||
|
|
||||||
// Set window title too
|
perf(
|
||||||
// https://tldp.org/HOWTO/Xterm-Title-3.html
|
"communicate path to terminal with osc7",
|
||||||
// ESC]0;stringBEL -- Set icon name and window title to string
|
start_time,
|
||||||
// ESC]1;stringBEL -- Set icon name to string
|
file!(),
|
||||||
// ESC]2;stringBEL -- Set window title to string
|
line!(),
|
||||||
run_ansi_sequence(&format!("\x1b]2;{maybe_abbrev_path}\x07"));
|
column!(),
|
||||||
}
|
use_color,
|
||||||
Err(e) => {
|
);
|
||||||
warn!("Could not coerce working directory to string {e}");
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_shell_integration_osc9_9(engine_state: &EngineState, stack: &mut Stack, use_color: bool) {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
if let Ok(path) = current_dir_str(engine_state, stack) {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
|
// Otherwise, communicate the path as OSC 9;9 from ConEmu (often used for spawning new tabs in the same dir)
|
||||||
|
run_ansi_sequence(&format!(
|
||||||
|
"\x1b]9;9;{}{}\x1b\\",
|
||||||
|
if path.starts_with('/') { "" } else { "/" },
|
||||||
|
percent_encoding::utf8_percent_encode(&path, percent_encoding::CONTROLS)
|
||||||
|
));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"communicate path to terminal with osc9;9",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_shell_integration_osc633(engine_state: &EngineState, stack: &mut Stack, use_color: bool) {
|
||||||
|
#[allow(deprecated)]
|
||||||
|
if let Ok(path) = current_dir_str(engine_state, stack) {
|
||||||
|
// Supported escape sequences of Microsoft's Visual Studio Code (vscode)
|
||||||
|
// https://code.visualstudio.com/docs/terminal/shell-integration#_supported-escape-sequences
|
||||||
|
if stack.get_env_var(engine_state, "TERM_PROGRAM") == Some(Value::test_string("vscode")) {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
|
// If we're in vscode, run their specific ansi escape sequence.
|
||||||
|
// This is helpful for ctrl+g to change directories in the terminal.
|
||||||
|
run_ansi_sequence(&format!(
|
||||||
|
"{}{}{}",
|
||||||
|
VSCODE_CWD_PROPERTY_MARKER_PREFIX, path, VSCODE_CWD_PROPERTY_MARKER_SUFFIX
|
||||||
|
));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"communicate path to terminal with osc633;P",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_shell_integration_reset_application_mode() {
|
||||||
run_ansi_sequence(RESET_APPLICATION_MODE);
|
run_ansi_sequence(RESET_APPLICATION_MODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1219,12 +1298,28 @@ fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option<SetCursorSty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
|
fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState, vscode: bool) -> String {
|
||||||
let exit_code = stack
|
let exit_code = stack
|
||||||
.get_env_var(engine_state, "LAST_EXIT_CODE")
|
.get_env_var(engine_state, "LAST_EXIT_CODE")
|
||||||
.and_then(|e| e.as_i64().ok());
|
.and_then(|e| e.as_i64().ok());
|
||||||
|
|
||||||
format!("\x1b]133;D;{}\x1b\\", exit_code.unwrap_or(0))
|
if vscode {
|
||||||
|
// format!("\x1b]633;D;{}\x1b\\", exit_code.unwrap_or(0))
|
||||||
|
format!(
|
||||||
|
"{}{}{}",
|
||||||
|
VSCODE_POST_EXECUTION_MARKER_PREFIX,
|
||||||
|
exit_code.unwrap_or(0),
|
||||||
|
VSCODE_POST_EXECUTION_MARKER_SUFFIX
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// format!("\x1b]133;D;{}\x1b\\", exit_code.unwrap_or(0))
|
||||||
|
format!(
|
||||||
|
"{}{}{}",
|
||||||
|
POST_EXECUTION_MARKER_PREFIX,
|
||||||
|
exit_code.unwrap_or(0),
|
||||||
|
POST_EXECUTION_MARKER_SUFFIX
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_ansi_sequence(seq: &str) {
|
fn run_ansi_sequence(seq: &str) {
|
||||||
@ -1235,6 +1330,58 @@ fn run_ansi_sequence(seq: &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn run_finaliziation_ansi_sequence(
|
||||||
|
stack: &Stack,
|
||||||
|
engine_state: &EngineState,
|
||||||
|
use_color: bool,
|
||||||
|
shell_integration_osc633: bool,
|
||||||
|
shell_integration_osc133: bool,
|
||||||
|
) {
|
||||||
|
if shell_integration_osc633 {
|
||||||
|
// Only run osc633 if we are in vscode
|
||||||
|
if stack.get_env_var(engine_state, "TERM_PROGRAM") == Some(Value::test_string("vscode")) {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
|
run_ansi_sequence(&get_command_finished_marker(stack, engine_state, true));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"post_execute_marker (633;D) ansi escape sequences",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
|
run_ansi_sequence(&get_command_finished_marker(stack, engine_state, false));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"post_execute_marker (133;D) ansi escape sequences",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if shell_integration_osc133 {
|
||||||
|
let start_time = Instant::now();
|
||||||
|
|
||||||
|
run_ansi_sequence(&get_command_finished_marker(stack, engine_state, false));
|
||||||
|
|
||||||
|
perf(
|
||||||
|
"post_execute_marker (133;D) ansi escape sequences",
|
||||||
|
start_time,
|
||||||
|
file!(),
|
||||||
|
line!(),
|
||||||
|
column!(),
|
||||||
|
use_color,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Absolute paths with a drive letter, like 'C:', 'D:\', 'E:\foo'
|
// Absolute paths with a drive letter, like 'C:', 'D:\', 'E:\foo'
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
static DRIVE_PATH_REGEX: once_cell::sync::Lazy<fancy_regex::Regex> =
|
static DRIVE_PATH_REGEX: once_cell::sync::Lazy<fancy_regex::Regex> =
|
||||||
|
@ -4,7 +4,7 @@ use nu_color_config::{get_matching_brackets_style, get_shape_color};
|
|||||||
use nu_engine::env;
|
use nu_engine::env;
|
||||||
use nu_parser::{flatten_block, parse, FlatShape};
|
use nu_parser::{flatten_block, parse, FlatShape};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Argument, Block, Expr, Expression, PipelineRedirection, RecordItem},
|
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
|
||||||
engine::{EngineState, Stack, StateWorkingSet},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Config, Span,
|
Config, Span,
|
||||||
};
|
};
|
||||||
@ -37,6 +37,7 @@ impl Highlighter for NuHighlighter {
|
|||||||
|
|
||||||
let str_word = String::from_utf8_lossy(str_contents).to_string();
|
let str_word = String::from_utf8_lossy(str_contents).to_string();
|
||||||
let paths = env::path_str(&self.engine_state, &self.stack, *span).ok();
|
let paths = env::path_str(&self.engine_state, &self.stack, *span).ok();
|
||||||
|
#[allow(deprecated)]
|
||||||
let res = if let Ok(cwd) =
|
let res = if let Ok(cwd) =
|
||||||
env::current_dir_str(&self.engine_state, &self.stack)
|
env::current_dir_str(&self.engine_state, &self.stack)
|
||||||
{
|
{
|
||||||
@ -86,27 +87,6 @@ impl Highlighter for NuHighlighter {
|
|||||||
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
[(shape.0.start - global_span_offset)..(shape.0.end - global_span_offset)]
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
macro_rules! add_colored_token_with_bracket_highlight {
|
|
||||||
($shape:expr, $span:expr, $text:expr) => {{
|
|
||||||
let spans = split_span_by_highlight_positions(
|
|
||||||
line,
|
|
||||||
$span,
|
|
||||||
&matching_brackets_pos,
|
|
||||||
global_span_offset,
|
|
||||||
);
|
|
||||||
spans.iter().for_each(|(part, highlight)| {
|
|
||||||
let start = part.start - $span.start;
|
|
||||||
let end = part.end - $span.start;
|
|
||||||
let text = (&next_token[start..end]).to_string();
|
|
||||||
let mut style = get_shape_color($shape.to_string(), &self.config);
|
|
||||||
if *highlight {
|
|
||||||
style = get_matching_brackets_style(style, &self.config);
|
|
||||||
}
|
|
||||||
output.push((style, text));
|
|
||||||
});
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
let mut add_colored_token = |shape: &FlatShape, text: String| {
|
||||||
output.push((get_shape_color(shape.to_string(), &self.config), text));
|
output.push((get_shape_color(shape.to_string(), &self.config), text));
|
||||||
};
|
};
|
||||||
@ -128,23 +108,32 @@ impl Highlighter for NuHighlighter {
|
|||||||
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
FlatShape::Operator => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
FlatShape::Signature => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::String => add_colored_token(&shape.1, next_token),
|
FlatShape::String => add_colored_token(&shape.1, next_token),
|
||||||
|
FlatShape::RawString => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
FlatShape::StringInterpolation => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
FlatShape::DateTime => add_colored_token(&shape.1, next_token),
|
||||||
FlatShape::List => {
|
FlatShape::List
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
| FlatShape::Table
|
||||||
}
|
| FlatShape::Record
|
||||||
FlatShape::Table => {
|
| FlatShape::Block
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
| FlatShape::Closure => {
|
||||||
}
|
let span = shape.0;
|
||||||
FlatShape::Record => {
|
let shape = &shape.1;
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
let spans = split_span_by_highlight_positions(
|
||||||
}
|
line,
|
||||||
|
span,
|
||||||
FlatShape::Block => {
|
&matching_brackets_pos,
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
global_span_offset,
|
||||||
}
|
);
|
||||||
FlatShape::Closure => {
|
for (part, highlight) in spans {
|
||||||
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
|
let start = part.start - span.start;
|
||||||
|
let end = part.end - span.start;
|
||||||
|
let text = next_token[start..end].to_string();
|
||||||
|
let mut style = get_shape_color(shape.to_string(), &self.config);
|
||||||
|
if highlight {
|
||||||
|
style = get_matching_brackets_style(style, &self.config);
|
||||||
|
}
|
||||||
|
output.push((style, text));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
FlatShape::Filepath => add_colored_token(&shape.1, next_token),
|
||||||
@ -310,20 +299,6 @@ fn find_matching_block_end_in_expr(
|
|||||||
global_span_offset: usize,
|
global_span_offset: usize,
|
||||||
global_cursor_offset: usize,
|
global_cursor_offset: usize,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
macro_rules! find_in_expr_or_continue {
|
|
||||||
($inner_expr:ident) => {
|
|
||||||
if let Some(pos) = find_matching_block_end_in_expr(
|
|
||||||
line,
|
|
||||||
working_set,
|
|
||||||
$inner_expr,
|
|
||||||
global_span_offset,
|
|
||||||
global_cursor_offset,
|
|
||||||
) {
|
|
||||||
return Some(pos);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if expression.span.contains(global_cursor_offset) && expression.span.start >= global_span_offset
|
if expression.span.contains(global_cursor_offset) && expression.span.start >= global_span_offset
|
||||||
{
|
{
|
||||||
let expr_first = expression.span.start;
|
let expr_first = expression.span.start;
|
||||||
@ -353,6 +328,7 @@ fn find_matching_block_end_in_expr(
|
|||||||
Expr::Directory(_, _) => None,
|
Expr::Directory(_, _) => None,
|
||||||
Expr::GlobPattern(_, _) => None,
|
Expr::GlobPattern(_, _) => None,
|
||||||
Expr::String(_) => None,
|
Expr::String(_) => None,
|
||||||
|
Expr::RawString(_) => None,
|
||||||
Expr::CellPath(_) => None,
|
Expr::CellPath(_) => None,
|
||||||
Expr::ImportPattern(_) => None,
|
Expr::ImportPattern(_) => None,
|
||||||
Expr::Overlay(_) => None,
|
Expr::Overlay(_) => None,
|
||||||
@ -370,15 +346,19 @@ fn find_matching_block_end_in_expr(
|
|||||||
Some(expr_last)
|
Some(expr_last)
|
||||||
} else {
|
} else {
|
||||||
// cursor is inside table
|
// cursor is inside table
|
||||||
for inner_expr in table.columns.as_ref() {
|
table
|
||||||
find_in_expr_or_continue!(inner_expr);
|
.columns
|
||||||
}
|
.iter()
|
||||||
for row in table.rows.as_ref() {
|
.chain(table.rows.iter().flat_map(AsRef::as_ref))
|
||||||
for inner_expr in row.as_ref() {
|
.find_map(|expr| {
|
||||||
find_in_expr_or_continue!(inner_expr);
|
find_matching_block_end_in_expr(
|
||||||
}
|
line,
|
||||||
}
|
working_set,
|
||||||
None
|
expr,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,36 +371,45 @@ fn find_matching_block_end_in_expr(
|
|||||||
Some(expr_last)
|
Some(expr_last)
|
||||||
} else {
|
} else {
|
||||||
// cursor is inside record
|
// cursor is inside record
|
||||||
for expr in exprs {
|
exprs.iter().find_map(|expr| match expr {
|
||||||
match expr {
|
RecordItem::Pair(k, v) => find_matching_block_end_in_expr(
|
||||||
RecordItem::Pair(k, v) => {
|
line,
|
||||||
find_in_expr_or_continue!(k);
|
working_set,
|
||||||
find_in_expr_or_continue!(v);
|
k,
|
||||||
}
|
global_span_offset,
|
||||||
RecordItem::Spread(_, record) => {
|
global_cursor_offset,
|
||||||
find_in_expr_or_continue!(record);
|
)
|
||||||
}
|
.or_else(|| {
|
||||||
}
|
find_matching_block_end_in_expr(
|
||||||
}
|
line,
|
||||||
None
|
working_set,
|
||||||
|
v,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
RecordItem::Spread(_, record) => find_matching_block_end_in_expr(
|
||||||
|
line,
|
||||||
|
working_set,
|
||||||
|
record,
|
||||||
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Call(call) => {
|
Expr::Call(call) => call.arguments.iter().find_map(|arg| {
|
||||||
for arg in &call.arguments {
|
arg.expr().and_then(|expr| {
|
||||||
let opt_expr = match arg {
|
find_matching_block_end_in_expr(
|
||||||
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
|
line,
|
||||||
Argument::Positional(inner_expr) => Some(inner_expr),
|
working_set,
|
||||||
Argument::Unknown(inner_expr) => Some(inner_expr),
|
expr,
|
||||||
Argument::Spread(inner_expr) => Some(inner_expr),
|
global_span_offset,
|
||||||
};
|
global_cursor_offset,
|
||||||
|
)
|
||||||
if let Some(inner_expr) = opt_expr {
|
})
|
||||||
find_in_expr_or_continue!(inner_expr);
|
}),
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
Expr::FullCellPath(b) => find_matching_block_end_in_expr(
|
Expr::FullCellPath(b) => find_matching_block_end_in_expr(
|
||||||
line,
|
line,
|
||||||
@ -430,12 +419,15 @@ fn find_matching_block_end_in_expr(
|
|||||||
global_cursor_offset,
|
global_cursor_offset,
|
||||||
),
|
),
|
||||||
|
|
||||||
Expr::BinaryOp(lhs, op, rhs) => {
|
Expr::BinaryOp(lhs, op, rhs) => [lhs, op, rhs].into_iter().find_map(|expr| {
|
||||||
find_in_expr_or_continue!(lhs);
|
find_matching_block_end_in_expr(
|
||||||
find_in_expr_or_continue!(op);
|
line,
|
||||||
find_in_expr_or_continue!(rhs);
|
working_set,
|
||||||
None
|
expr,
|
||||||
}
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
Expr::Block(block_id)
|
Expr::Block(block_id)
|
||||||
| Expr::Closure(block_id)
|
| Expr::Closure(block_id)
|
||||||
@ -460,12 +452,15 @@ fn find_matching_block_end_in_expr(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::StringInterpolation(inner_expr) => {
|
Expr::StringInterpolation(exprs) => exprs.iter().find_map(|expr| {
|
||||||
for inner_expr in inner_expr {
|
find_matching_block_end_in_expr(
|
||||||
find_in_expr_or_continue!(inner_expr);
|
line,
|
||||||
}
|
working_set,
|
||||||
None
|
expr,
|
||||||
}
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
|
||||||
Expr::List(list) => {
|
Expr::List(list) => {
|
||||||
if expr_last == global_cursor_offset {
|
if expr_last == global_cursor_offset {
|
||||||
@ -475,12 +470,15 @@ fn find_matching_block_end_in_expr(
|
|||||||
// cursor is at list start
|
// cursor is at list start
|
||||||
Some(expr_last)
|
Some(expr_last)
|
||||||
} else {
|
} else {
|
||||||
// cursor is inside list
|
list.iter().find_map(|item| {
|
||||||
for item in list {
|
find_matching_block_end_in_expr(
|
||||||
let expr = item.expr();
|
line,
|
||||||
find_in_expr_or_continue!(expr);
|
working_set,
|
||||||
}
|
item.expr(),
|
||||||
None
|
global_span_offset,
|
||||||
|
global_cursor_offset,
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -6,7 +6,7 @@ use nu_parser::parse;
|
|||||||
use nu_protocol::{debugger::WithoutDebug, engine::StateWorkingSet, PipelineData};
|
use nu_protocol::{debugger::WithoutDebug, engine::StateWorkingSet, PipelineData};
|
||||||
use reedline::{Completer, Suggestion};
|
use reedline::{Completer, Suggestion};
|
||||||
use rstest::{fixture, rstest};
|
use rstest::{fixture, rstest};
|
||||||
use std::path::PathBuf;
|
use std::path::{PathBuf, MAIN_SEPARATOR};
|
||||||
use support::{
|
use support::{
|
||||||
completions_helpers::{new_partial_engine, new_quote_engine},
|
completions_helpers::{new_partial_engine, new_quote_engine},
|
||||||
file, folder, match_suggestions, new_engine,
|
file, folder, match_suggestions, new_engine,
|
||||||
@ -220,7 +220,7 @@ fn file_completions() {
|
|||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test completions for the current folder
|
// Test completions for the current folder
|
||||||
let target_dir = format!("cp {dir_str}");
|
let target_dir = format!("cp {dir_str}{MAIN_SEPARATOR}");
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
@ -664,7 +664,7 @@ fn folder_with_directorycompletions() {
|
|||||||
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
|
||||||
|
|
||||||
// Test completions for the current folder
|
// Test completions for the current folder
|
||||||
let target_dir = format!("cd {dir_str}");
|
let target_dir = format!("cd {dir_str}{MAIN_SEPARATOR}");
|
||||||
let suggestions = completer.complete(&target_dir, target_dir.len());
|
let suggestions = completer.complete(&target_dir, target_dir.len());
|
||||||
|
|
||||||
// Create the expected values
|
// Create the expected values
|
||||||
|
@ -8,9 +8,7 @@ use nu_protocol::{
|
|||||||
};
|
};
|
||||||
use nu_test_support::fs;
|
use nu_test_support::fs;
|
||||||
use reedline::Suggestion;
|
use reedline::Suggestion;
|
||||||
use std::path::PathBuf;
|
use std::path::{PathBuf, MAIN_SEPARATOR};
|
||||||
|
|
||||||
const SEP: char = std::path::MAIN_SEPARATOR;
|
|
||||||
|
|
||||||
fn create_default_context() -> EngineState {
|
fn create_default_context() -> EngineState {
|
||||||
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
|
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
|
||||||
@ -20,12 +18,11 @@ fn create_default_context() -> EngineState {
|
|||||||
pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
// Target folder inside assets
|
||||||
let dir = fs::fixtures().join("completions");
|
let dir = fs::fixtures().join("completions");
|
||||||
let mut dir_str = dir
|
let dir_str = dir
|
||||||
.clone()
|
.clone()
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.into_string()
|
.into_string()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
dir_str.push(SEP);
|
|
||||||
|
|
||||||
// Create a new engine with default context
|
// Create a new engine with default context
|
||||||
let mut engine_state = create_default_context();
|
let mut engine_state = create_default_context();
|
||||||
@ -77,12 +74,11 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
|
|||||||
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
|
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
// Target folder inside assets
|
||||||
let dir = fs::fixtures().join("quoted_completions");
|
let dir = fs::fixtures().join("quoted_completions");
|
||||||
let mut dir_str = dir
|
let dir_str = dir
|
||||||
.clone()
|
.clone()
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.into_string()
|
.into_string()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
dir_str.push(SEP);
|
|
||||||
|
|
||||||
// Create a new engine with default context
|
// Create a new engine with default context
|
||||||
let mut engine_state = create_default_context();
|
let mut engine_state = create_default_context();
|
||||||
@ -113,12 +109,11 @@ pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
|
|||||||
pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) {
|
pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) {
|
||||||
// Target folder inside assets
|
// Target folder inside assets
|
||||||
let dir = fs::fixtures().join("partial_completions");
|
let dir = fs::fixtures().join("partial_completions");
|
||||||
let mut dir_str = dir
|
let dir_str = dir
|
||||||
.clone()
|
.clone()
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
.into_string()
|
.into_string()
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
dir_str.push(SEP);
|
|
||||||
|
|
||||||
// Create a new engine with default context
|
// Create a new engine with default context
|
||||||
let mut engine_state = create_default_context();
|
let mut engine_state = create_default_context();
|
||||||
@ -165,7 +160,7 @@ pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
|
|||||||
// append the separator to the converted path
|
// append the separator to the converted path
|
||||||
pub fn folder(path: PathBuf) -> String {
|
pub fn folder(path: PathBuf) -> String {
|
||||||
let mut converted_path = file(path);
|
let mut converted_path = file(path);
|
||||||
converted_path.push(SEP);
|
converted_path.push(MAIN_SEPARATOR);
|
||||||
|
|
||||||
converted_path
|
converted_path
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ pub fn get_init_cwd() -> PathBuf {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf {
|
||||||
|
#[allow(deprecated)]
|
||||||
nu_engine::env::current_dir(engine_state, stack).unwrap_or_else(|e| {
|
nu_engine::env::current_dir(engine_state, stack).unwrap_or_else(|e| {
|
||||||
let working_set = StateWorkingSet::new(engine_state);
|
let working_set = StateWorkingSet::new(engine_state);
|
||||||
report_error(&working_set, &e);
|
report_error(&working_set, &e);
|
||||||
|
@ -26,7 +26,6 @@ impl Command for Describe {
|
|||||||
"show detailed information about the value",
|
"show detailed information about the value",
|
||||||
Some('d'),
|
Some('d'),
|
||||||
)
|
)
|
||||||
.switch("collect-lazyrecords", "collect lazy records", Some('l'))
|
|
||||||
.category(Category::Core)
|
.category(Category::Core)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,21 +43,7 @@ impl Command for Describe {
|
|||||||
let options = Options {
|
let options = Options {
|
||||||
no_collect: call.has_flag(engine_state, stack, "no-collect")?,
|
no_collect: call.has_flag(engine_state, stack, "no-collect")?,
|
||||||
detailed: call.has_flag(engine_state, stack, "detailed")?,
|
detailed: call.has_flag(engine_state, stack, "detailed")?,
|
||||||
collect_lazyrecords: call.has_flag(engine_state, stack, "collect-lazyrecords")?,
|
|
||||||
};
|
};
|
||||||
if options.collect_lazyrecords {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "Deprecated flag".into(),
|
|
||||||
msg: "the `--collect-lazyrecords` flag is deprecated, since lazy records will be removed in 0.94.0"
|
|
||||||
.into(),
|
|
||||||
span: Some(call.head),
|
|
||||||
help: None,
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
run(Some(engine_state), call, input, options)
|
run(Some(engine_state), call, input, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +56,6 @@ impl Command for Describe {
|
|||||||
let options = Options {
|
let options = Options {
|
||||||
no_collect: call.has_flag_const(working_set, "no-collect")?,
|
no_collect: call.has_flag_const(working_set, "no-collect")?,
|
||||||
detailed: call.has_flag_const(working_set, "detailed")?,
|
detailed: call.has_flag_const(working_set, "detailed")?,
|
||||||
collect_lazyrecords: call.has_flag_const(working_set, "collect-lazyrecords")?,
|
|
||||||
};
|
};
|
||||||
run(None, call, input, options)
|
run(None, call, input, options)
|
||||||
}
|
}
|
||||||
@ -89,13 +73,11 @@ impl Command for Describe {
|
|||||||
"{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| print $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
|
"{shell:'true', uwu:true, features: {bugs:false, multiplatform:true, speed: 10}, fib: [1 1 2 3 5 8], on_save: {|x| print $'Saving ($x)'}, first_commit: 2019-05-10, my_duration: (4min + 20sec)} | describe -d",
|
||||||
result: Some(Value::test_record(record!(
|
result: Some(Value::test_record(record!(
|
||||||
"type" => Value::test_string("record"),
|
"type" => Value::test_string("record"),
|
||||||
"lazy" => Value::test_bool(false),
|
|
||||||
"columns" => Value::test_record(record!(
|
"columns" => Value::test_record(record!(
|
||||||
"shell" => Value::test_string("string"),
|
"shell" => Value::test_string("string"),
|
||||||
"uwu" => Value::test_string("bool"),
|
"uwu" => Value::test_string("bool"),
|
||||||
"features" => Value::test_record(record!(
|
"features" => Value::test_record(record!(
|
||||||
"type" => Value::test_string("record"),
|
"type" => Value::test_string("record"),
|
||||||
"lazy" => Value::test_bool(false),
|
|
||||||
"columns" => Value::test_record(record!(
|
"columns" => Value::test_record(record!(
|
||||||
"bugs" => Value::test_string("bool"),
|
"bugs" => Value::test_string("bool"),
|
||||||
"multiplatform" => Value::test_string("bool"),
|
"multiplatform" => Value::test_string("bool"),
|
||||||
@ -168,7 +150,6 @@ impl Command for Describe {
|
|||||||
struct Options {
|
struct Options {
|
||||||
no_collect: bool,
|
no_collect: bool,
|
||||||
detailed: bool,
|
detailed: bool,
|
||||||
collect_lazyrecords: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
@ -243,7 +224,7 @@ fn run(
|
|||||||
if options.no_collect {
|
if options.no_collect {
|
||||||
Value::string("any", head)
|
Value::string("any", head)
|
||||||
} else {
|
} else {
|
||||||
describe_value(input.into_value(head), head, engine_state, options)?
|
describe_value(input.into_value(head), head, engine_state, )
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"metadata" => metadata_to_value(metadata, head),
|
"metadata" => metadata_to_value(metadata, head),
|
||||||
@ -264,7 +245,7 @@ fn run(
|
|||||||
if !options.detailed {
|
if !options.detailed {
|
||||||
Value::string(value.get_type().to_string(), head)
|
Value::string(value.get_type().to_string(), head)
|
||||||
} else {
|
} else {
|
||||||
describe_value(value, head, engine_state, options)?
|
describe_value(value, head, engine_state)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -288,9 +269,8 @@ fn describe_value(
|
|||||||
value: Value,
|
value: Value,
|
||||||
head: nu_protocol::Span,
|
head: nu_protocol::Span,
|
||||||
engine_state: Option<&EngineState>,
|
engine_state: Option<&EngineState>,
|
||||||
options: Options,
|
) -> Value {
|
||||||
) -> Result<Value, ShellError> {
|
match value {
|
||||||
Ok(match value {
|
|
||||||
Value::Custom { val, .. } => Value::record(
|
Value::Custom { val, .. } => Value::record(
|
||||||
record!(
|
record!(
|
||||||
"type" => Value::string("custom", head),
|
"type" => Value::string("custom", head),
|
||||||
@ -319,14 +299,12 @@ fn describe_value(
|
|||||||
std::mem::take(v),
|
std::mem::take(v),
|
||||||
head,
|
head,
|
||||||
engine_state,
|
engine_state,
|
||||||
options,
|
));
|
||||||
)?);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::record(
|
Value::record(
|
||||||
record!(
|
record!(
|
||||||
"type" => Value::string("record", head),
|
"type" => Value::string("record", head),
|
||||||
"lazy" => Value::bool(false, head),
|
|
||||||
"columns" => Value::record(val, head),
|
"columns" => Value::record(val, head),
|
||||||
),
|
),
|
||||||
head,
|
head,
|
||||||
@ -337,11 +315,9 @@ fn describe_value(
|
|||||||
"type" => Value::string("list", head),
|
"type" => Value::string("list", head),
|
||||||
"length" => Value::int(vals.len() as i64, head),
|
"length" => Value::int(vals.len() as i64, head),
|
||||||
"values" => Value::list(vals.into_iter().map(|v|
|
"values" => Value::list(vals.into_iter().map(|v|
|
||||||
Ok(compact_primitive_description(
|
compact_primitive_description(describe_value(v, head, engine_state))
|
||||||
describe_value(v, head, engine_state, options)?
|
|
||||||
))
|
|
||||||
)
|
)
|
||||||
.collect::<Result<Vec<Value>, ShellError>>()?, head),
|
.collect(), head),
|
||||||
),
|
),
|
||||||
head,
|
head,
|
||||||
),
|
),
|
||||||
@ -393,42 +369,7 @@ fn describe_value(
|
|||||||
),
|
),
|
||||||
head,
|
head,
|
||||||
),
|
),
|
||||||
Value::LazyRecord { val, .. } => {
|
}
|
||||||
let mut record = Record::new();
|
|
||||||
|
|
||||||
record.push("type", Value::string("record", head));
|
|
||||||
record.push("lazy", Value::bool(true, head));
|
|
||||||
|
|
||||||
if options.collect_lazyrecords {
|
|
||||||
let collected = val.collect()?;
|
|
||||||
if let Value::Record { val, .. } =
|
|
||||||
describe_value(collected, head, engine_state, options)?
|
|
||||||
{
|
|
||||||
let mut val = Record::clone(&val);
|
|
||||||
|
|
||||||
for (_k, v) in val.iter_mut() {
|
|
||||||
*v = compact_primitive_description(describe_value(
|
|
||||||
std::mem::take(v),
|
|
||||||
head,
|
|
||||||
engine_state,
|
|
||||||
options,
|
|
||||||
)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
record.push("length", Value::int(val.len() as i64, head));
|
|
||||||
record.push("columns", Value::record(val, head));
|
|
||||||
} else {
|
|
||||||
let cols = val.column_names();
|
|
||||||
record.push("length", Value::int(cols.len() as i64, head));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let cols = val.column_names();
|
|
||||||
record.push("length", Value::int(cols.len() as i64, head));
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::record(record, head)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metadata_to_value(metadata: Option<Box<PipelineMetadata>>, head: nu_protocol::Span) -> Value {
|
fn metadata_to_value(metadata: Option<Box<PipelineMetadata>>, head: nu_protocol::Span) -> Value {
|
||||||
|
@ -127,7 +127,7 @@ impl Command for Do {
|
|||||||
let stderr_msg = match stderr {
|
let stderr_msg = match stderr {
|
||||||
None => "".to_string(),
|
None => "".to_string(),
|
||||||
Some(stderr_stream) => {
|
Some(stderr_stream) => {
|
||||||
stderr_ctrlc = stderr_stream.ctrlc.clone();
|
stderr_ctrlc.clone_from(&stderr_stream.ctrlc);
|
||||||
stderr_stream.into_string().map(|s| s.item)?
|
stderr_stream.into_string().map(|s| s.item)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -152,7 +152,7 @@ impl Command for Do {
|
|||||||
let exit_code: Vec<Value> = match exit_code {
|
let exit_code: Vec<Value> = match exit_code {
|
||||||
None => vec![],
|
None => vec![],
|
||||||
Some(exit_code_stream) => {
|
Some(exit_code_stream) => {
|
||||||
exit_code_ctrlc = exit_code_stream.ctrlc.clone();
|
exit_code_ctrlc.clone_from(&exit_code_stream.ctrlc);
|
||||||
exit_code_stream.into_iter().collect()
|
exit_code_stream.into_iter().collect()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,179 +0,0 @@
|
|||||||
use nu_engine::{command_prelude::*, eval_block};
|
|
||||||
use nu_protocol::{debugger::WithoutDebug, engine::Closure, LazyRecord};
|
|
||||||
use std::{
|
|
||||||
collections::{hash_map::Entry, HashMap},
|
|
||||||
sync::{Arc, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct LazyMake;
|
|
||||||
|
|
||||||
impl Command for LazyMake {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
"lazy make"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn signature(&self) -> Signature {
|
|
||||||
Signature::build("lazy make")
|
|
||||||
.input_output_types(vec![(Type::Nothing, Type::record())])
|
|
||||||
.required_named(
|
|
||||||
"columns",
|
|
||||||
SyntaxShape::List(Box::new(SyntaxShape::String)),
|
|
||||||
"Closure that gets called when the LazyRecord needs to list the available column names",
|
|
||||||
Some('c')
|
|
||||||
)
|
|
||||||
.required_named(
|
|
||||||
"get-value",
|
|
||||||
SyntaxShape::Closure(Some(vec![SyntaxShape::String])),
|
|
||||||
"Closure to call when a value needs to be produced on demand",
|
|
||||||
Some('g')
|
|
||||||
)
|
|
||||||
.category(Category::Core)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usage(&self) -> &str {
|
|
||||||
"Create a lazy record."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn extra_usage(&self) -> &str {
|
|
||||||
"Lazy records are special records that only evaluate their values once the property is requested.
|
|
||||||
For example, when printing a lazy record, all of its fields will be collected. But when accessing
|
|
||||||
a specific property, only it will be evaluated.
|
|
||||||
|
|
||||||
Note that this is unrelated to the lazyframes feature bundled with dataframes."
|
|
||||||
}
|
|
||||||
|
|
||||||
fn search_terms(&self) -> Vec<&str> {
|
|
||||||
vec!["deferred", "record", "procedural"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run(
|
|
||||||
&self,
|
|
||||||
engine_state: &EngineState,
|
|
||||||
stack: &mut Stack,
|
|
||||||
call: &Call,
|
|
||||||
_input: PipelineData,
|
|
||||||
) -> Result<PipelineData, ShellError> {
|
|
||||||
nu_protocol::report_error_new(
|
|
||||||
engine_state,
|
|
||||||
&ShellError::GenericError {
|
|
||||||
error: "Deprecated command".into(),
|
|
||||||
msg: "warning: lazy records and the `lazy make` command will be removed in 0.94.0"
|
|
||||||
.into(),
|
|
||||||
span: Some(call.head),
|
|
||||||
help: None,
|
|
||||||
inner: vec![],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let span = call.head;
|
|
||||||
let columns: Vec<Spanned<String>> = call
|
|
||||||
.get_flag(engine_state, stack, "columns")?
|
|
||||||
.expect("required flag");
|
|
||||||
|
|
||||||
let get_value: Closure = call
|
|
||||||
.get_flag(engine_state, stack, "get-value")?
|
|
||||||
.expect("required flag");
|
|
||||||
|
|
||||||
let mut unique = HashMap::with_capacity(columns.len());
|
|
||||||
|
|
||||||
for col in &columns {
|
|
||||||
match unique.entry(&col.item) {
|
|
||||||
Entry::Occupied(entry) => {
|
|
||||||
return Err(ShellError::ColumnDefinedTwice {
|
|
||||||
col_name: col.item.clone(),
|
|
||||||
second_use: col.span,
|
|
||||||
first_use: *entry.get(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Entry::Vacant(entry) => {
|
|
||||||
entry.insert(col.span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let stack = stack.clone().reset_out_dest().capture();
|
|
||||||
|
|
||||||
Ok(Value::lazy_record(
|
|
||||||
Box::new(NuLazyRecord {
|
|
||||||
engine_state: engine_state.clone(),
|
|
||||||
stack: Arc::new(Mutex::new(stack)),
|
|
||||||
columns: columns.into_iter().map(|s| s.item).collect(),
|
|
||||||
get_value,
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
span,
|
|
||||||
)
|
|
||||||
.into_pipeline_data())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
|
||||||
vec![
|
|
||||||
// TODO: Figure out how to "test" these examples, or leave results as None
|
|
||||||
Example {
|
|
||||||
description: "Create a lazy record",
|
|
||||||
example: r#"lazy make --columns ["haskell", "futures", "nushell"] --get-value { |lazything| $lazything + "!" }"#,
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
Example {
|
|
||||||
description: "Test the laziness of lazy records",
|
|
||||||
example: r#"lazy make --columns ["hello"] --get-value { |key| print $"getting ($key)!"; $key | str upcase }"#,
|
|
||||||
result: None,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
struct NuLazyRecord {
|
|
||||||
engine_state: EngineState,
|
|
||||||
stack: Arc<Mutex<Stack>>,
|
|
||||||
columns: Vec<String>,
|
|
||||||
get_value: Closure,
|
|
||||||
span: Span,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Debug for NuLazyRecord {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
f.debug_struct("NuLazyRecord").finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> LazyRecord<'a> for NuLazyRecord {
|
|
||||||
fn column_names(&'a self) -> Vec<&'a str> {
|
|
||||||
self.columns.iter().map(|column| column.as_str()).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
|
||||||
let block = self.engine_state.get_block(self.get_value.block_id);
|
|
||||||
let mut stack = self.stack.lock().expect("lock must not be poisoned");
|
|
||||||
let column_value = Value::string(column, self.span);
|
|
||||||
|
|
||||||
if let Some(var) = block.signature.get_positional(0) {
|
|
||||||
if let Some(var_id) = &var.var_id {
|
|
||||||
stack.add_var(*var_id, column_value.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let pipeline_result = eval_block::<WithoutDebug>(
|
|
||||||
&self.engine_state,
|
|
||||||
&mut stack,
|
|
||||||
block,
|
|
||||||
PipelineData::Value(column_value, None),
|
|
||||||
);
|
|
||||||
|
|
||||||
pipeline_result.map(|data| match data {
|
|
||||||
PipelineData::Value(value, ..) => value,
|
|
||||||
// TODO: Proper error handling.
|
|
||||||
_ => Value::nothing(self.span),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn span(&self) -> Span {
|
|
||||||
self.span
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clone_value(&self, span: Span) -> Value {
|
|
||||||
Value::lazy_record(Box::new((*self).clone()), span)
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,7 +21,6 @@ mod hide;
|
|||||||
mod hide_env;
|
mod hide_env;
|
||||||
mod if_;
|
mod if_;
|
||||||
mod ignore;
|
mod ignore;
|
||||||
mod lazy_make;
|
|
||||||
mod let_;
|
mod let_;
|
||||||
mod loop_;
|
mod loop_;
|
||||||
mod match_;
|
mod match_;
|
||||||
@ -58,7 +57,6 @@ pub use hide::Hide;
|
|||||||
pub use hide_env::HideEnv;
|
pub use hide_env::HideEnv;
|
||||||
pub use if_::If;
|
pub use if_::If;
|
||||||
pub use ignore::Ignore;
|
pub use ignore::Ignore;
|
||||||
pub use lazy_make::LazyMake;
|
|
||||||
pub use let_::Let;
|
pub use let_::Let;
|
||||||
pub use loop_::Loop;
|
pub use loop_::Loop;
|
||||||
pub use match_::Match;
|
pub use match_::Match;
|
||||||
|
@ -43,7 +43,6 @@ pub fn create_default_context() -> EngineState {
|
|||||||
OverlayList,
|
OverlayList,
|
||||||
OverlayNew,
|
OverlayNew,
|
||||||
OverlayHide,
|
OverlayHide,
|
||||||
LazyMake,
|
|
||||||
Let,
|
Let,
|
||||||
Loop,
|
Loop,
|
||||||
Match,
|
Match,
|
||||||
|
@ -306,10 +306,6 @@ impl<'a> std::fmt::Debug for DebuggableValue<'a> {
|
|||||||
Value::Custom { val, .. } => {
|
Value::Custom { val, .. } => {
|
||||||
write!(f, "CustomValue({:?})", val)
|
write!(f, "CustomValue({:?})", val)
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
let rec = val.collect().map_err(|_| std::fmt::Error)?;
|
|
||||||
write!(f, "LazyRecord({:?})", DebuggableValue(&rec))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use std::sync::Arc;
|
#[allow(deprecated)]
|
||||||
|
|
||||||
use nu_engine::{command_prelude::*, current_dir};
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
use nu_plugin_engine::{GetPlugin, PersistentPlugin};
|
use nu_plugin_engine::{GetPlugin, PersistentPlugin};
|
||||||
use nu_protocol::{PluginGcConfig, PluginIdentity, PluginRegistryItem, RegisteredPlugin};
|
use nu_protocol::{PluginGcConfig, PluginIdentity, PluginRegistryItem, RegisteredPlugin};
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::util::{get_plugin_dirs, modify_plugin_file};
|
use crate::util::{get_plugin_dirs, modify_plugin_file};
|
||||||
|
|
||||||
@ -82,6 +82,7 @@ apparent the next time `nu` is next launched with that plugin registry file.
|
|||||||
let filename: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let filename: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
let shell: Option<Spanned<String>> = call.get_flag(engine_state, stack, "shell")?;
|
let shell: Option<Spanned<String>> = call.get_flag(engine_state, stack, "shell")?;
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
// Check the current directory, or fall back to NU_PLUGIN_DIRS
|
// Check the current directory, or fall back to NU_PLUGIN_DIRS
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
|
#[allow(deprecated)]
|
||||||
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
|
use nu_protocol::{engine::StateWorkingSet, PluginRegistryFile};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
};
|
};
|
||||||
|
|
||||||
use nu_engine::{command_prelude::*, current_dir};
|
|
||||||
use nu_protocol::{engine::StateWorkingSet, PluginRegistryFile};
|
|
||||||
|
|
||||||
pub(crate) fn modify_plugin_file(
|
pub(crate) fn modify_plugin_file(
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
@ -13,6 +13,7 @@ pub(crate) fn modify_plugin_file(
|
|||||||
custom_path: Option<Spanned<String>>,
|
custom_path: Option<Spanned<String>>,
|
||||||
operate: impl FnOnce(&mut PluginRegistryFile) -> Result<(), ShellError>,
|
operate: impl FnOnce(&mut PluginRegistryFile) -> Result<(), ShellError>,
|
||||||
) -> Result<(), ShellError> {
|
) -> Result<(), ShellError> {
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
let plugin_registry_file_path = if let Some(ref custom_path) = custom_path {
|
let plugin_registry_file_path = if let Some(ref custom_path) = custom_path {
|
||||||
@ -58,6 +59,7 @@ pub(crate) fn canonicalize_possible_filename_arg(
|
|||||||
arg: &str,
|
arg: &str,
|
||||||
) -> PathBuf {
|
) -> PathBuf {
|
||||||
// This results in the best possible chance of a match with the plugin item
|
// This results in the best possible chance of a match with the plugin item
|
||||||
|
#[allow(deprecated)]
|
||||||
if let Ok(cwd) = nu_engine::current_dir(engine_state, stack) {
|
if let Ok(cwd) = nu_engine::current_dir(engine_state, stack) {
|
||||||
let path = nu_path::expand_path_with(arg, &cwd, true);
|
let path = nu_path::expand_path_with(arg, &cwd, true);
|
||||||
// Try to canonicalize
|
// Try to canonicalize
|
||||||
|
@ -32,6 +32,7 @@ pub fn default_shape_color(shape: String) -> Style {
|
|||||||
"shape_or" => Style::new().fg(Color::Purple).bold(),
|
"shape_or" => Style::new().fg(Color::Purple).bold(),
|
||||||
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
|
"shape_pipe" => Style::new().fg(Color::Purple).bold(),
|
||||||
"shape_range" => Style::new().fg(Color::Yellow).bold(),
|
"shape_range" => Style::new().fg(Color::Yellow).bold(),
|
||||||
|
"shape_raw_string" => Style::new().fg(Color::LightMagenta).bold(),
|
||||||
"shape_record" => Style::new().fg(Color::Cyan).bold(),
|
"shape_record" => Style::new().fg(Color::Cyan).bold(),
|
||||||
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
|
"shape_redirection" => Style::new().fg(Color::Purple).bold(),
|
||||||
"shape_signature" => Style::new().fg(Color::Green).bold(),
|
"shape_signature" => Style::new().fg(Color::Green).bold(),
|
||||||
|
@ -106,10 +106,9 @@ impl<'a> StyleComputer<'a> {
|
|||||||
Value::Binary { .. } => TextStyle::with_style(Left, s),
|
Value::Binary { .. } => TextStyle::with_style(Left, s),
|
||||||
Value::CellPath { .. } => TextStyle::with_style(Left, s),
|
Value::CellPath { .. } => TextStyle::with_style(Left, s),
|
||||||
Value::Record { .. } | Value::List { .. } => TextStyle::with_style(Left, s),
|
Value::Record { .. } | Value::List { .. } => TextStyle::with_style(Left, s),
|
||||||
Value::Closure { .. }
|
Value::Closure { .. } | Value::Custom { .. } | Value::Error { .. } => {
|
||||||
| Value::Custom { .. }
|
TextStyle::basic_left()
|
||||||
| Value::Error { .. }
|
}
|
||||||
| Value::LazyRecord { .. } => TextStyle::basic_left(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +150,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
|||||||
head,
|
head,
|
||||||
),
|
),
|
||||||
Ordering::Less => Value::binary(
|
Ordering::Less => Value::binary(
|
||||||
if end == isize::max_value() {
|
if end == isize::MAX {
|
||||||
val.iter()
|
val.iter()
|
||||||
.skip(start as usize)
|
.skip(start as usize)
|
||||||
.copied()
|
.copied()
|
||||||
|
@ -272,10 +272,6 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ")
|
.join(" ")
|
||||||
),
|
),
|
||||||
Value::LazyRecord { val, .. } => match val.collect() {
|
|
||||||
Ok(val) => debug_string_without_formatting(&val),
|
|
||||||
Err(error) => format!("{error:?}"),
|
|
||||||
},
|
|
||||||
//TODO: It would be good to drill deeper into closures.
|
//TODO: It would be good to drill deeper into closures.
|
||||||
Value::Closure { val, .. } => format!("<Closure {}>", val.block_id),
|
Value::Closure { val, .. } => format!("<Closure {}>", val.block_id),
|
||||||
Value::Nothing { .. } => String::new(),
|
Value::Nothing { .. } => String::new(),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::LazyRecord;
|
|
||||||
use sysinfo::{MemoryRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
|
use sysinfo::{MemoryRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
|
||||||
|
|
||||||
const ENV_PATH_SEPARATOR_CHAR: char = {
|
const ENV_PATH_SEPARATOR_CHAR: char = {
|
||||||
@ -39,14 +38,10 @@ impl Command for DebugInfo {
|
|||||||
&self,
|
&self,
|
||||||
_engine_state: &EngineState,
|
_engine_state: &EngineState,
|
||||||
_stack: &mut Stack,
|
_stack: &mut Stack,
|
||||||
_call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = Span::unknown();
|
Ok(all_columns(call.head).into_pipeline_data())
|
||||||
|
|
||||||
let record = LazySystemInfoRecord { span };
|
|
||||||
|
|
||||||
Ok(Value::lazy_record(Box::new(record), span).into_pipeline_data())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -58,207 +53,119 @@ impl Command for DebugInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
fn all_columns(span: Span) -> Value {
|
||||||
struct LazySystemInfoRecord {
|
let rk = RefreshKind::new()
|
||||||
span: Span,
|
.with_processes(ProcessRefreshKind::everything())
|
||||||
}
|
.with_memory(MemoryRefreshKind::everything());
|
||||||
|
|
||||||
impl LazySystemInfoRecord {
|
// only get information requested
|
||||||
fn get_column_value_with_system(
|
let sys = System::new_with_specifics(rk);
|
||||||
&self,
|
|
||||||
column: &str,
|
|
||||||
system_option: Option<&System>,
|
|
||||||
) -> Result<Value, ShellError> {
|
|
||||||
let pid = Pid::from(std::process::id() as usize);
|
|
||||||
match column {
|
|
||||||
"thread_id" => Ok(Value::int(get_thread_id() as i64, self.span)),
|
|
||||||
"pid" => Ok(Value::int(pid.as_u32() as i64, self.span)),
|
|
||||||
"ppid" => {
|
|
||||||
// only get information requested
|
|
||||||
let system_opt = SystemOpt::from((system_option, || {
|
|
||||||
RefreshKind::new().with_processes(ProcessRefreshKind::everything())
|
|
||||||
}));
|
|
||||||
|
|
||||||
let system = system_opt.get_system();
|
let pid = Pid::from(std::process::id() as usize);
|
||||||
// get the process information for the nushell pid
|
let ppid = {
|
||||||
let pinfo = system.process(pid);
|
sys.process(pid)
|
||||||
|
.and_then(|p| p.parent())
|
||||||
|
.map(|p| Value::int(p.as_u32().into(), span))
|
||||||
|
.unwrap_or(Value::nothing(span))
|
||||||
|
};
|
||||||
|
|
||||||
Ok(pinfo
|
let system = Value::record(
|
||||||
.and_then(|p| p.parent())
|
record! {
|
||||||
.map(|p| Value::int(p.as_u32() as i64, self.span))
|
"total_memory" => Value::filesize(sys.total_memory() as i64, span),
|
||||||
.unwrap_or(Value::nothing(self.span)))
|
"free_memory" => Value::filesize(sys.free_memory() as i64, span),
|
||||||
}
|
"used_memory" => Value::filesize(sys.used_memory() as i64, span),
|
||||||
"system" => {
|
"available_memory" => Value::filesize(sys.available_memory() as i64, span),
|
||||||
// only get information requested
|
},
|
||||||
let system_opt = SystemOpt::from((system_option, || {
|
span,
|
||||||
RefreshKind::new().with_memory(MemoryRefreshKind::everything())
|
);
|
||||||
}));
|
|
||||||
|
|
||||||
let system = system_opt.get_system();
|
let process = if let Some(p) = sys.process(pid) {
|
||||||
|
let root = if let Some(path) = p.exe().and_then(|p| p.parent()) {
|
||||||
|
Value::string(path.to_string_lossy().to_string(), span)
|
||||||
|
} else {
|
||||||
|
Value::nothing(span)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(Value::record(
|
let cwd = if let Some(path) = p.cwd() {
|
||||||
record! {
|
Value::string(path.to_string_lossy().to_string(), span)
|
||||||
"total_memory" => Value::filesize(system.total_memory() as i64, self.span),
|
} else {
|
||||||
"free_memory" => Value::filesize(system.free_memory() as i64, self.span),
|
Value::nothing(span)
|
||||||
"used_memory" => Value::filesize(system.used_memory() as i64, self.span),
|
};
|
||||||
"available_memory" => Value::filesize(system.available_memory() as i64, self.span),
|
|
||||||
},
|
|
||||||
self.span,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
"process" => {
|
|
||||||
// only get information requested
|
|
||||||
let system_opt = SystemOpt::from((system_option, || {
|
|
||||||
RefreshKind::new().with_processes(ProcessRefreshKind::everything())
|
|
||||||
}));
|
|
||||||
|
|
||||||
let system = system_opt.get_system();
|
let exe_path = if let Some(path) = p.exe() {
|
||||||
let pinfo = system.process(pid);
|
Value::string(path.to_string_lossy().to_string(), span)
|
||||||
|
} else {
|
||||||
|
Value::nothing(span)
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(p) = pinfo {
|
let environment = {
|
||||||
Ok(Value::record(
|
let mut env_rec = Record::new();
|
||||||
record! {
|
for val in p.environ() {
|
||||||
"memory" => Value::filesize(p.memory() as i64, self.span),
|
if let Some((key, value)) = val.split_once('=') {
|
||||||
"virtual_memory" => Value::filesize(p.virtual_memory() as i64, self.span),
|
let is_env_var_a_list = {
|
||||||
"status" => Value::string(p.status().to_string(), self.span),
|
{
|
||||||
"root" => {
|
#[cfg(target_family = "windows")]
|
||||||
if let Some(path) = p.exe().and_then(|p| p.parent()) {
|
{
|
||||||
Value::string(path.to_string_lossy().to_string(), self.span)
|
key == "Path"
|
||||||
} else {
|
|| key == "PATHEXT"
|
||||||
Value::nothing(self.span)
|
|| key == "PSMODULEPATH"
|
||||||
}
|
|| key == "PSModulePath"
|
||||||
},
|
}
|
||||||
"cwd" => {
|
#[cfg(not(target_family = "windows"))]
|
||||||
if let Some(path) = p.cwd() {
|
{
|
||||||
Value::string(path.to_string_lossy().to_string(), self.span)
|
key == "PATH" || key == "DYLD_FALLBACK_LIBRARY_PATH"
|
||||||
}else{
|
}
|
||||||
Value::nothing(self.span)
|
}
|
||||||
}
|
};
|
||||||
},
|
if is_env_var_a_list {
|
||||||
"exe_path" => {
|
let items = value
|
||||||
if let Some(path)= p.exe() {
|
.split(ENV_PATH_SEPARATOR_CHAR)
|
||||||
Value::string(path.to_string_lossy().to_string(), self.span)
|
.map(|r| Value::string(r.to_string(), span))
|
||||||
}else{
|
.collect::<Vec<_>>();
|
||||||
Value::nothing(self.span)
|
env_rec.push(key.to_string(), Value::list(items, span));
|
||||||
}
|
} else if key == "LS_COLORS" {
|
||||||
},
|
// LS_COLORS is a special case, it's a colon separated list of key=value pairs
|
||||||
"command" => Value::string(p.cmd().join(" "), self.span),
|
let items = value
|
||||||
"name" => Value::string(p.name().to_string(), self.span),
|
.split(':')
|
||||||
"environment" => {
|
.map(|r| Value::string(r.to_string(), span))
|
||||||
let mut env_rec = Record::new();
|
.collect::<Vec<_>>();
|
||||||
for val in p.environ() {
|
env_rec.push(key.to_string(), Value::list(items, span));
|
||||||
if let Some((key, value)) = val.split_once('=') {
|
} else {
|
||||||
let is_env_var_a_list = {
|
env_rec.push(key.to_string(), Value::string(value.to_string(), span));
|
||||||
{
|
}
|
||||||
#[cfg(target_family = "windows")]
|
|
||||||
{
|
|
||||||
key == "Path" || key == "PATHEXT" || key == "PSMODULEPATH" || key == "PSModulePath"
|
|
||||||
}
|
|
||||||
#[cfg(not(target_family = "windows"))]
|
|
||||||
{
|
|
||||||
key == "PATH" || key == "DYLD_FALLBACK_LIBRARY_PATH"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if is_env_var_a_list {
|
|
||||||
let items = value.split(ENV_PATH_SEPARATOR_CHAR).map(|r| Value::string(r.to_string(), self.span)).collect::<Vec<_>>();
|
|
||||||
env_rec.push(key.to_string(), Value::list(items, self.span));
|
|
||||||
} else if key == "LS_COLORS" { // LS_COLORS is a special case, it's a colon separated list of key=value pairs
|
|
||||||
let items = value.split(':').map(|r| Value::string(r.to_string(), self.span)).collect::<Vec<_>>();
|
|
||||||
env_rec.push(key.to_string(), Value::list(items, self.span));
|
|
||||||
} else {
|
|
||||||
env_rec.push(key.to_string(), Value::string(value.to_string(), self.span));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Value::record(env_rec, self.span)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
self.span,
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
// If we can't get the process information, just return the system information
|
|
||||||
// only get information requested
|
|
||||||
let system_opt = SystemOpt::from((system_option, || {
|
|
||||||
RefreshKind::new().with_memory(MemoryRefreshKind::everything())
|
|
||||||
}));
|
|
||||||
let system = system_opt.get_system();
|
|
||||||
|
|
||||||
Ok(Value::record(
|
|
||||||
record! {
|
|
||||||
"total_memory" => Value::filesize(system.total_memory() as i64, self.span),
|
|
||||||
"free_memory" => Value::filesize(system.free_memory() as i64, self.span),
|
|
||||||
"used_memory" => Value::filesize(system.used_memory() as i64, self.span),
|
|
||||||
"available_memory" => Value::filesize(system.available_memory() as i64, self.span),
|
|
||||||
},
|
|
||||||
self.span,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Err(ShellError::IncompatibleParametersSingle {
|
Value::record(env_rec, span)
|
||||||
msg: format!("Unknown column: {}", column),
|
};
|
||||||
span: self.span,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> LazyRecord<'a> for LazySystemInfoRecord {
|
Value::record(
|
||||||
fn column_names(&'a self) -> Vec<&'a str> {
|
record! {
|
||||||
vec!["thread_id", "pid", "ppid", "process", "system"]
|
"memory" => Value::filesize(p.memory() as i64, span),
|
||||||
}
|
"virtual_memory" => Value::filesize(p.virtual_memory() as i64, span),
|
||||||
|
"status" => Value::string(p.status().to_string(), span),
|
||||||
|
"root" => root,
|
||||||
|
"cwd" => cwd,
|
||||||
|
"exe_path" => exe_path,
|
||||||
|
"command" => Value::string(p.cmd().join(" "), span),
|
||||||
|
"name" => Value::string(p.name(), span),
|
||||||
|
"environment" => environment,
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Value::nothing(span)
|
||||||
|
};
|
||||||
|
|
||||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
Value::record(
|
||||||
self.get_column_value_with_system(column, None)
|
record! {
|
||||||
}
|
"thread_id" => Value::int(get_thread_id() as i64, span),
|
||||||
|
"pid" => Value::int(pid.as_u32().into(), span),
|
||||||
fn span(&self) -> Span {
|
"ppid" => ppid,
|
||||||
self.span
|
"system" => system,
|
||||||
}
|
"process" => process,
|
||||||
|
},
|
||||||
fn clone_value(&self, span: Span) -> Value {
|
span,
|
||||||
Value::lazy_record(Box::new(LazySystemInfoRecord { span }), span)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
fn collect(&'a self) -> Result<Value, ShellError> {
|
|
||||||
let rk = RefreshKind::new()
|
|
||||||
.with_processes(ProcessRefreshKind::everything())
|
|
||||||
.with_memory(MemoryRefreshKind::everything());
|
|
||||||
// only get information requested
|
|
||||||
let system = System::new_with_specifics(rk);
|
|
||||||
|
|
||||||
self.column_names()
|
|
||||||
.into_iter()
|
|
||||||
.map(|col| {
|
|
||||||
let val = self.get_column_value_with_system(col, Some(&system))?;
|
|
||||||
Ok((col.to_owned(), val))
|
|
||||||
})
|
|
||||||
.collect::<Result<Record, _>>()
|
|
||||||
.map(|record| Value::record(record, self.span()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum SystemOpt<'a> {
|
|
||||||
Ptr(&'a System),
|
|
||||||
Owned(Box<System>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> SystemOpt<'a> {
|
|
||||||
fn get_system(&'a self) -> &'a System {
|
|
||||||
match self {
|
|
||||||
SystemOpt::Ptr(system) => system,
|
|
||||||
SystemOpt::Owned(system) => system,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, F: Fn() -> RefreshKind> From<(Option<&'a System>, F)> for SystemOpt<'a> {
|
|
||||||
fn from((system_opt, refresh_kind_create): (Option<&'a System>, F)) -> Self {
|
|
||||||
match system_opt {
|
|
||||||
Some(system) => SystemOpt::<'a>::Ptr(system),
|
|
||||||
None => SystemOpt::Owned(Box::new(System::new_with_specifics(refresh_kind_create()))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_thread_id() -> u64 {
|
fn get_thread_id() -> u64 {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use nu_engine::{command_prelude::*, current_dir};
|
use nu_engine::command_prelude::*;
|
||||||
use nu_utils::filesystem::{have_permission, PermissionResult};
|
use nu_utils::filesystem::{have_permission, PermissionResult};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@ -20,6 +20,7 @@ impl Command for Cd {
|
|||||||
fn signature(&self) -> nu_protocol::Signature {
|
fn signature(&self) -> nu_protocol::Signature {
|
||||||
Signature::build("cd")
|
Signature::build("cd")
|
||||||
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
.input_output_types(vec![(Type::Nothing, Type::Nothing)])
|
||||||
|
.switch("physical", "use the physical directory structure; resolve symbolic links before processing instances of ..", Some('P'))
|
||||||
.optional("path", SyntaxShape::Directory, "The path to change to.")
|
.optional("path", SyntaxShape::Directory, "The path to change to.")
|
||||||
.input_output_types(vec![
|
.input_output_types(vec![
|
||||||
(Type::Nothing, Type::Nothing),
|
(Type::Nothing, Type::Nothing),
|
||||||
@ -36,8 +37,9 @@ impl Command for Cd {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
let physical = call.has_flag(engine_state, stack, "physical")?;
|
||||||
let path_val: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
|
let path_val: Option<Spanned<String>> = call.opt(engine_state, stack, 0)?;
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = engine_state.cwd(Some(stack))?;
|
||||||
|
|
||||||
let path_val = {
|
let path_val = {
|
||||||
if let Some(path) = path_val {
|
if let Some(path) = path_val {
|
||||||
@ -53,54 +55,53 @@ impl Command for Cd {
|
|||||||
let (path, span) = match path_val {
|
let (path, span) = match path_val {
|
||||||
Some(v) => {
|
Some(v) => {
|
||||||
if v.item == "-" {
|
if v.item == "-" {
|
||||||
let oldpwd = stack.get_env_var(engine_state, "OLDPWD");
|
if let Some(oldpwd) = stack.get_env_var(engine_state, "OLDPWD") {
|
||||||
|
(oldpwd.to_path()?, v.span)
|
||||||
if let Some(oldpwd) = oldpwd {
|
|
||||||
let path = oldpwd.to_path()?;
|
|
||||||
let path = match nu_path::canonicalize_with(path.clone(), &cwd) {
|
|
||||||
Ok(p) => p,
|
|
||||||
Err(_) => {
|
|
||||||
return Err(ShellError::DirectoryNotFound {
|
|
||||||
dir: path.to_string_lossy().to_string(),
|
|
||||||
span: v.span,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
(path.to_string_lossy().to_string(), v.span)
|
|
||||||
} else {
|
} else {
|
||||||
(cwd.to_string_lossy().to_string(), v.span)
|
(cwd, v.span)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Trim whitespace from the end of path.
|
||||||
let path_no_whitespace =
|
let path_no_whitespace =
|
||||||
&v.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
|
&v.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
|
||||||
|
|
||||||
let path = match nu_path::canonicalize_with(path_no_whitespace, &cwd) {
|
// If `--physical` is specified, canonicalize the path; otherwise expand the path.
|
||||||
Ok(p) => {
|
let path = if physical {
|
||||||
if !p.is_dir() {
|
if let Ok(path) = nu_path::canonicalize_with(path_no_whitespace, &cwd) {
|
||||||
|
if !path.is_dir() {
|
||||||
return Err(ShellError::NotADirectory { span: v.span });
|
return Err(ShellError::NotADirectory { span: v.span });
|
||||||
};
|
};
|
||||||
p
|
path
|
||||||
}
|
} else {
|
||||||
|
|
||||||
// if canonicalize failed, let's check to see if it's abbreviated
|
|
||||||
Err(_) => {
|
|
||||||
return Err(ShellError::DirectoryNotFound {
|
return Err(ShellError::DirectoryNotFound {
|
||||||
dir: path_no_whitespace.to_string(),
|
dir: path_no_whitespace.to_string(),
|
||||||
span: v.span,
|
span: v.span,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let path = nu_path::expand_path_with(path_no_whitespace, &cwd, true);
|
||||||
|
if !path.exists() {
|
||||||
|
return Err(ShellError::DirectoryNotFound {
|
||||||
|
dir: path_no_whitespace.to_string(),
|
||||||
|
span: v.span,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
if !path.is_dir() {
|
||||||
|
return Err(ShellError::NotADirectory { span: v.span });
|
||||||
|
};
|
||||||
|
path
|
||||||
};
|
};
|
||||||
(path.to_string_lossy().to_string(), v.span)
|
(path, v.span)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let path = nu_path::expand_tilde("~");
|
let path = nu_path::expand_tilde("~");
|
||||||
(path.to_string_lossy().to_string(), call.head)
|
(path, call.head)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let path_value = Value::string(path.clone(), span);
|
// Set OLDPWD.
|
||||||
|
// We're using `Stack::get_env_var()` instead of `EngineState::cwd()` to avoid a conversion roundtrip.
|
||||||
if let Some(oldpwd) = stack.get_env_var(engine_state, "PWD") {
|
if let Some(oldpwd) = stack.get_env_var(engine_state, "PWD") {
|
||||||
stack.add_env_var("OLDPWD".into(), oldpwd)
|
stack.add_env_var("OLDPWD".into(), oldpwd)
|
||||||
}
|
}
|
||||||
@ -109,11 +110,15 @@ impl Command for Cd {
|
|||||||
//FIXME: this only changes the current scope, but instead this environment variable
|
//FIXME: this only changes the current scope, but instead this environment variable
|
||||||
//should probably be a block that loads the information from the state in the overlay
|
//should probably be a block that loads the information from the state in the overlay
|
||||||
PermissionResult::PermissionOk => {
|
PermissionResult::PermissionOk => {
|
||||||
stack.add_env_var("PWD".into(), path_value);
|
stack.add_env_var("PWD".into(), Value::string(path.to_string_lossy(), span));
|
||||||
Ok(PipelineData::empty())
|
Ok(PipelineData::empty())
|
||||||
}
|
}
|
||||||
PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError {
|
PermissionResult::PermissionDenied(reason) => Err(ShellError::IOError {
|
||||||
msg: format!("Cannot change directory to {path}: {reason}"),
|
msg: format!(
|
||||||
|
"Cannot change directory to {}: {}",
|
||||||
|
path.to_string_lossy(),
|
||||||
|
reason
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use super::util::get_rest_for_glob_pattern;
|
use super::util::get_rest_for_glob_pattern;
|
||||||
use crate::{DirBuilder, DirInfo, FileInfo};
|
use crate::{DirBuilder, DirInfo, FileInfo};
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir};
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
use nu_glob::Pattern;
|
use nu_glob::Pattern;
|
||||||
use nu_protocol::NuGlob;
|
use nu_protocol::NuGlob;
|
||||||
@ -98,6 +99,7 @@ impl Command for Du {
|
|||||||
let all = call.has_flag(engine_state, stack, "all")?;
|
let all = call.has_flag(engine_state, stack, "all")?;
|
||||||
let deref = call.has_flag(engine_state, stack, "deref")?;
|
let deref = call.has_flag(engine_state, stack, "deref")?;
|
||||||
let exclude = call.get_flag(engine_state, stack, "exclude")?;
|
let exclude = call.get_flag(engine_state, stack, "exclude")?;
|
||||||
|
#[allow(deprecated)]
|
||||||
let current_dir = current_dir(engine_state, stack)?;
|
let current_dir = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
let paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
let paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, env::current_dir};
|
use nu_engine::{command_prelude::*, env::current_dir};
|
||||||
use std::sync::{atomic::AtomicBool, Arc};
|
use std::sync::{atomic::AtomicBool, Arc};
|
||||||
use wax::{Glob as WaxGlob, WalkBehavior, WalkEntry};
|
use wax::{Glob as WaxGlob, WalkBehavior, WalkEntry};
|
||||||
@ -178,6 +179,7 @@ impl Command for Glob {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let path = current_dir(engine_state, stack)?;
|
let path = current_dir(engine_state, stack)?;
|
||||||
let path = match nu_path::canonicalize_with(prefix, path) {
|
let path = match nu_path::canonicalize_with(prefix, path) {
|
||||||
Ok(path) => path,
|
Ok(path) => path,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use super::util::get_rest_for_glob_pattern;
|
use super::util::get_rest_for_glob_pattern;
|
||||||
use crate::{DirBuilder, DirInfo};
|
use crate::{DirBuilder, DirInfo};
|
||||||
use chrono::{DateTime, Local, LocalResult, TimeZone, Utc};
|
use chrono::{DateTime, Local, LocalResult, TimeZone, Utc};
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, env::current_dir};
|
use nu_engine::{command_prelude::*, env::current_dir};
|
||||||
use nu_glob::{MatchOptions, Pattern};
|
use nu_glob::{MatchOptions, Pattern};
|
||||||
use nu_path::expand_to_real_path;
|
use nu_path::expand_to_real_path;
|
||||||
@ -91,6 +92,7 @@ impl Command for Ls {
|
|||||||
let use_mime_type = call.has_flag(engine_state, stack, "mime-type")?;
|
let use_mime_type = call.has_flag(engine_state, stack, "mime-type")?;
|
||||||
let ctrl_c = engine_state.ctrlc.clone();
|
let ctrl_c = engine_state.ctrlc.clone();
|
||||||
let call_span = call.head;
|
let call_span = call.head;
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
let args = Args {
|
let args = Args {
|
||||||
@ -429,7 +431,7 @@ fn ls_for_one_pattern(
|
|||||||
Err(err) => Some(Value::error(err, call_span)),
|
Err(err) => Some(Value::error(err, call_span)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Some(Value::nothing(call_span)),
|
Err(err) => Some(Value::error(err, call_span)),
|
||||||
})))
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, env::current_dir};
|
use nu_engine::{command_prelude::*, env::current_dir};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@ -90,6 +91,7 @@ impl Command for Mktemp {
|
|||||||
} else if directory || tmpdir {
|
} else if directory || tmpdir {
|
||||||
Some(std::env::temp_dir())
|
Some(std::env::temp_dir())
|
||||||
} else {
|
} else {
|
||||||
|
#[allow(deprecated)]
|
||||||
Some(current_dir(engine_state, stack)?)
|
Some(current_dir(engine_state, stack)?)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::util::get_rest_for_glob_pattern;
|
use super::util::get_rest_for_glob_pattern;
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir, get_eval_block};
|
use nu_engine::{command_prelude::*, current_dir, get_eval_block};
|
||||||
use nu_protocol::{BufferedReader, DataSource, NuGlob, PipelineMetadata, RawStream};
|
use nu_protocol::{BufferedReader, DataSource, NuGlob, PipelineMetadata, RawStream};
|
||||||
use std::{io::BufReader, path::Path};
|
use std::{io::BufReader, path::Path};
|
||||||
@ -51,6 +52,7 @@ impl Command for Open {
|
|||||||
let raw = call.has_flag(engine_state, stack, "raw")?;
|
let raw = call.has_flag(engine_state, stack, "raw")?;
|
||||||
let call_span = call.head;
|
let call_span = call.head;
|
||||||
let ctrlc = engine_state.ctrlc.clone();
|
let ctrlc = engine_state.ctrlc.clone();
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||||
let eval_block = get_eval_block(engine_state);
|
let eval_block = get_eval_block(engine_state);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::util::{get_rest_for_glob_pattern, try_interaction};
|
use super::util::{get_rest_for_glob_pattern, try_interaction};
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, env::current_dir};
|
use nu_engine::{command_prelude::*, env::current_dir};
|
||||||
use nu_glob::MatchOptions;
|
use nu_glob::MatchOptions;
|
||||||
use nu_path::expand_path_with;
|
use nu_path::expand_path_with;
|
||||||
@ -130,6 +131,7 @@ fn rm(
|
|||||||
|
|
||||||
let mut unique_argument_check = None;
|
let mut unique_argument_check = None;
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let currentdir_path = current_dir(engine_state, stack)?;
|
let currentdir_path = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
let home: Option<String> = nu_path::home_dir().map(|path| {
|
let home: Option<String> = nu_path::home_dir().map(|path| {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::progress_bar;
|
use crate::progress_bar;
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir};
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
use nu_path::expand_path_with;
|
use nu_path::expand_path_with;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
@ -85,6 +86,7 @@ impl Command for Save {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let span = call.head;
|
let span = call.head;
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
let path_arg = call.req::<Spanned<PathBuf>>(engine_state, stack, 0)?;
|
let path_arg = call.req::<Spanned<PathBuf>>(engine_state, stack, 0)?;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use filetime::FileTime;
|
use filetime::FileTime;
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir};
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
use nu_path::expand_path_with;
|
use nu_path::expand_path_with;
|
||||||
use nu_protocol::NuGlob;
|
use nu_protocol::NuGlob;
|
||||||
@ -113,6 +114,7 @@ impl Command for Touch {
|
|||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
|
|
||||||
for (index, glob) in files.into_iter().enumerate() {
|
for (index, glob) in files.into_iter().enumerate() {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::util::get_rest_for_glob_pattern;
|
use super::util::get_rest_for_glob_pattern;
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir};
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use uu_cp::{BackupMode, CopyMode, UpdateMode};
|
use uu_cp::{BackupMode, CopyMode, UpdateMode};
|
||||||
@ -177,6 +178,7 @@ impl Command for UCp {
|
|||||||
let target_path = PathBuf::from(&nu_utils::strip_ansi_string_unlikely(
|
let target_path = PathBuf::from(&nu_utils::strip_ansi_string_unlikely(
|
||||||
target.item.to_string(),
|
target.item.to_string(),
|
||||||
));
|
));
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
let target_path = nu_path::expand_path_with(target_path, &cwd, target.item.is_expand());
|
let target_path = nu_path::expand_path_with(target_path, &cwd, target.item.is_expand());
|
||||||
if target.item.as_ref().ends_with(PATH_SEPARATOR) && !target_path.is_dir() {
|
if target.item.as_ref().ends_with(PATH_SEPARATOR) && !target_path.is_dir() {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir};
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
|
|
||||||
use uu_mkdir::mkdir;
|
use uu_mkdir::mkdir;
|
||||||
@ -58,6 +59,7 @@ impl Command for UMkdir {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
let mut directories = get_rest_for_glob_pattern(engine_state, stack, call, 0)?
|
let mut directories = get_rest_for_glob_pattern(engine_state, stack, call, 0)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::util::get_rest_for_glob_pattern;
|
use super::util::get_rest_for_glob_pattern;
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir};
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
use nu_path::expand_path_with;
|
use nu_path::expand_path_with;
|
||||||
use nu_protocol::NuGlob;
|
use nu_protocol::NuGlob;
|
||||||
@ -77,6 +78,7 @@ impl Command for UMv {
|
|||||||
uu_mv::OverwriteMode::Force
|
uu_mv::OverwriteMode::Force
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
|
||||||
if paths.is_empty() {
|
if paths.is_empty() {
|
||||||
|
@ -5,6 +5,7 @@ use notify_debouncer_full::{
|
|||||||
EventKind, RecursiveMode, Watcher,
|
EventKind, RecursiveMode, Watcher,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir, ClosureEval};
|
use nu_engine::{command_prelude::*, current_dir, ClosureEval};
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
engine::{Closure, StateWorkingSet},
|
engine::{Closure, StateWorkingSet},
|
||||||
@ -73,6 +74,7 @@ impl Command for Watch {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
let path_arg: Spanned<String> = call.req(engine_state, stack, 0)?;
|
let path_arg: Spanned<String> = call.req(engine_state, stack, 0)?;
|
||||||
|
|
||||||
|
@ -105,18 +105,6 @@ fn getcol(
|
|||||||
.into_pipeline_data(ctrlc)
|
.into_pipeline_data(ctrlc)
|
||||||
.set_metadata(metadata))
|
.set_metadata(metadata))
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
Ok({
|
|
||||||
// Unfortunate casualty to LazyRecord's column_names not generating 'static strs
|
|
||||||
let cols: Vec<_> =
|
|
||||||
val.column_names().iter().map(|s| s.to_string()).collect();
|
|
||||||
|
|
||||||
cols.into_iter()
|
|
||||||
.map(move |x| Value::string(x, head))
|
|
||||||
.into_pipeline_data(ctrlc)
|
|
||||||
.set_metadata(metadata)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
Value::Record { val, .. } => Ok(val
|
Value::Record { val, .. } => Ok(val
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(move |(x, _)| Value::string(x, head))
|
.map(move |(x, _)| Value::string(x, head))
|
||||||
|
@ -533,15 +533,6 @@ fn value_should_be_printed(
|
|||||||
Value::Record { val, .. } => {
|
Value::Record { val, .. } => {
|
||||||
record_matches_term(val, columns_to_search, filter_config, term, span)
|
record_matches_term(val, columns_to_search, filter_config, term, span)
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => match val.collect() {
|
|
||||||
Ok(val) => match val {
|
|
||||||
Value::Record { val, .. } => {
|
|
||||||
record_matches_term(&val, columns_to_search, filter_config, term, span)
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
},
|
|
||||||
Err(_) => false,
|
|
||||||
},
|
|
||||||
Value::Binary { .. } => false,
|
Value::Binary { .. } => false,
|
||||||
});
|
});
|
||||||
if invert {
|
if invert {
|
||||||
|
@ -44,12 +44,6 @@ impl Command for Items {
|
|||||||
match input {
|
match input {
|
||||||
PipelineData::Empty => Ok(PipelineData::Empty),
|
PipelineData::Empty => Ok(PipelineData::Empty),
|
||||||
PipelineData::Value(value, ..) => {
|
PipelineData::Value(value, ..) => {
|
||||||
let value = if let Value::LazyRecord { val, .. } = value {
|
|
||||||
val.collect()?
|
|
||||||
} else {
|
|
||||||
value
|
|
||||||
};
|
|
||||||
|
|
||||||
let span = value.span();
|
let span = value.span();
|
||||||
match value {
|
match value {
|
||||||
Value::Record { val, .. } => {
|
Value::Record { val, .. } => {
|
||||||
|
@ -161,19 +161,6 @@ fn values(
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.into_pipeline_data_with_metadata(metadata, ctrlc)),
|
.into_pipeline_data_with_metadata(metadata, ctrlc)),
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
let record = match val.collect()? {
|
|
||||||
Value::Record { val, .. } => val,
|
|
||||||
_ => Err(ShellError::NushellFailedSpanned {
|
|
||||||
msg: "`LazyRecord::collect()` promises `Value::Record`".into(),
|
|
||||||
label: "Violating lazy record found here".into(),
|
|
||||||
span,
|
|
||||||
})?,
|
|
||||||
};
|
|
||||||
Ok(record
|
|
||||||
.into_values()
|
|
||||||
.into_pipeline_data_with_metadata(metadata, ctrlc))
|
|
||||||
}
|
|
||||||
// Propagate errors
|
// Propagate errors
|
||||||
Value::Error { error, .. } => Err(*error),
|
Value::Error { error, .. } => Err(*error),
|
||||||
other => Err(ShellError::OnlySupportsThisInputType {
|
other => Err(ShellError::OnlySupportsThisInputType {
|
||||||
|
@ -135,10 +135,6 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
|
|||||||
}
|
}
|
||||||
nu_json::Value::Object(m)
|
nu_json::Value::Object(m)
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
let collected = val.collect()?;
|
|
||||||
value_to_json_value(&collected)?
|
|
||||||
}
|
|
||||||
Value::Custom { val, .. } => {
|
Value::Custom { val, .. } => {
|
||||||
let collected = val.to_base_value(span)?;
|
let collected = val.to_base_value(span)?;
|
||||||
value_to_json_value(&collected)?
|
value_to_json_value(&collected)?
|
||||||
|
@ -246,9 +246,6 @@ pub(crate) fn write_value(
|
|||||||
Value::Custom { val, .. } => {
|
Value::Custom { val, .. } => {
|
||||||
write_value(out, &val.to_base_value(span)?, depth)?;
|
write_value(out, &val.to_base_value(span)?, depth)?;
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
write_value(out, &val.collect()?, depth)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -129,10 +129,6 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
|||||||
.map(|(x, y)| format!("{}: {}", x, local_into_string(y, ", ", config)))
|
.map(|(x, y)| format!("{}: {}", x, local_into_string(y, ", ", config)))
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(separator),
|
.join(separator),
|
||||||
Value::LazyRecord { val, .. } => match val.collect() {
|
|
||||||
Ok(val) => local_into_string(val, separator, config),
|
|
||||||
Err(error) => format!("{error:?}"),
|
|
||||||
},
|
|
||||||
Value::Closure { val, .. } => format!("<Closure {}>", val.block_id),
|
Value::Closure { val, .. } => format!("<Closure {}>", val.block_id),
|
||||||
Value::Nothing { .. } => String::new(),
|
Value::Nothing { .. } => String::new(),
|
||||||
Value::Error { error, .. } => format!("{error:?}"),
|
Value::Error { error, .. } => format!("{error:?}"),
|
||||||
|
@ -62,10 +62,6 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result<toml::Value, ShellErr
|
|||||||
}
|
}
|
||||||
toml::Value::Table(m)
|
toml::Value::Table(m)
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
let collected = val.collect()?;
|
|
||||||
helper(engine_state, &collected)?
|
|
||||||
}
|
|
||||||
Value::List { vals, .. } => toml::Value::Array(toml_list(engine_state, vals)?),
|
Value::List { vals, .. } => toml::Value::Array(toml_list(engine_state, vals)?),
|
||||||
Value::Closure { .. } => {
|
Value::Closure { .. } => {
|
||||||
let code = engine_state.get_span_contents(span);
|
let code = engine_state.get_span_contents(span);
|
||||||
|
@ -62,10 +62,6 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
|
|||||||
}
|
}
|
||||||
serde_yaml::Value::Mapping(m)
|
serde_yaml::Value::Mapping(m)
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
let collected = val.collect()?;
|
|
||||||
value_to_yaml_value(&collected)?
|
|
||||||
}
|
|
||||||
Value::List { vals, .. } => {
|
Value::List { vals, .. } => {
|
||||||
let mut out = vec![];
|
let mut out = vec![];
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::PathSubcommandArguments;
|
use super::PathSubcommandArguments;
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir, current_dir_const};
|
use nu_engine::{command_prelude::*, current_dir, current_dir_const};
|
||||||
use nu_path::expand_path_with;
|
use nu_path::expand_path_with;
|
||||||
use nu_protocol::engine::StateWorkingSet;
|
use nu_protocol::engine::StateWorkingSet;
|
||||||
@ -53,6 +54,7 @@ If you need to distinguish dirs and files, please use `path type`."#
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
#[allow(deprecated)]
|
||||||
let args = Arguments {
|
let args = Arguments {
|
||||||
pwd: current_dir(engine_state, stack)?,
|
pwd: current_dir(engine_state, stack)?,
|
||||||
not_follow_symlink: call.has_flag(engine_state, stack, "no-symlink")?,
|
not_follow_symlink: call.has_flag(engine_state, stack, "no-symlink")?,
|
||||||
@ -74,6 +76,7 @@ If you need to distinguish dirs and files, please use `path type`."#
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
#[allow(deprecated)]
|
||||||
let args = Arguments {
|
let args = Arguments {
|
||||||
pwd: current_dir_const(working_set)?,
|
pwd: current_dir_const(working_set)?,
|
||||||
not_follow_symlink: call.has_flag_const(working_set, "no-symlink")?,
|
not_follow_symlink: call.has_flag_const(working_set, "no-symlink")?,
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::PathSubcommandArguments;
|
use super::PathSubcommandArguments;
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{
|
use nu_engine::{
|
||||||
command_prelude::*,
|
command_prelude::*,
|
||||||
env::{current_dir_str, current_dir_str_const},
|
env::{current_dir_str, current_dir_str_const},
|
||||||
@ -57,6 +58,7 @@ impl Command for SubCommand {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
#[allow(deprecated)]
|
||||||
let args = Arguments {
|
let args = Arguments {
|
||||||
strict: call.has_flag(engine_state, stack, "strict")?,
|
strict: call.has_flag(engine_state, stack, "strict")?,
|
||||||
cwd: current_dir_str(engine_state, stack)?,
|
cwd: current_dir_str(engine_state, stack)?,
|
||||||
@ -79,6 +81,7 @@ impl Command for SubCommand {
|
|||||||
input: PipelineData,
|
input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
#[allow(deprecated)]
|
||||||
let args = Arguments {
|
let args = Arguments {
|
||||||
strict: call.has_flag_const(working_set, "strict")?,
|
strict: call.has_flag_const(working_set, "strict")?,
|
||||||
cwd: current_dir_str_const(working_set)?,
|
cwd: current_dir_str_const(working_set)?,
|
||||||
|
@ -149,7 +149,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
|
|||||||
),
|
),
|
||||||
Ordering::Less => Value::string(
|
Ordering::Less => Value::string(
|
||||||
{
|
{
|
||||||
if end == isize::max_value() {
|
if end == isize::MAX {
|
||||||
if args.graphemes {
|
if args.graphemes {
|
||||||
s.graphemes(true)
|
s.graphemes(true)
|
||||||
.skip(start as usize)
|
.skip(start as usize)
|
||||||
@ -245,7 +245,7 @@ mod tests {
|
|||||||
expectation("andre", (0, -1)),
|
expectation("andre", (0, -1)),
|
||||||
// str substring [ -4 , _ ]
|
// str substring [ -4 , _ ]
|
||||||
// str substring -4 ,
|
// str substring -4 ,
|
||||||
expectation("dres", (-4, isize::max_value())),
|
expectation("dres", (-4, isize::MAX)),
|
||||||
expectation("", (0, -110)),
|
expectation("", (0, -110)),
|
||||||
expectation("", (6, 0)),
|
expectation("", (6, 0)),
|
||||||
expectation("", (6, -1)),
|
expectation("", (6, -1)),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::run_external::create_external_command;
|
use super::run_external::create_external_command;
|
||||||
|
#[allow(deprecated)]
|
||||||
use nu_engine::{command_prelude::*, current_dir};
|
use nu_engine::{command_prelude::*, current_dir};
|
||||||
use nu_protocol::OutDest;
|
use nu_protocol::OutDest;
|
||||||
|
|
||||||
@ -62,6 +63,7 @@ fn exec(
|
|||||||
external_command.out = OutDest::Inherit;
|
external_command.out = OutDest::Inherit;
|
||||||
external_command.err = OutDest::Inherit;
|
external_command.err = OutDest::Inherit;
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
let mut command = external_command.spawn_simple_command(&cwd.to_string_lossy())?;
|
let mut command = external_command.spawn_simple_command(&cwd.to_string_lossy())?;
|
||||||
command.current_dir(cwd);
|
command.current_dir(cwd);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
use chrono::{DateTime, Local};
|
use chrono::{DateTime, Local};
|
||||||
use nu_engine::command_prelude::*;
|
use nu_engine::command_prelude::*;
|
||||||
use nu_protocol::LazyRecord;
|
|
||||||
use std::time::{Duration, UNIX_EPOCH};
|
use std::time::{Duration, UNIX_EPOCH};
|
||||||
use sysinfo::{
|
use sysinfo::{
|
||||||
Components, CpuRefreshKind, Disks, Networks, System, Users, MINIMUM_CPU_UPDATE_INTERVAL,
|
Components, CpuRefreshKind, Disks, Networks, System, Users, MINIMUM_CPU_UPDATE_INTERVAL,
|
||||||
@ -32,10 +31,7 @@ impl Command for Sys {
|
|||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
let span = call.span();
|
Ok(all_columns(call.head).into_pipeline_data())
|
||||||
let ret = Value::lazy_record(Box::new(SysResult { span }), span);
|
|
||||||
|
|
||||||
Ok(ret.into_pipeline_data())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn examples(&self) -> Vec<Example> {
|
fn examples(&self) -> Vec<Example> {
|
||||||
@ -64,36 +60,18 @@ pub struct SysResult {
|
|||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LazyRecord<'_> for SysResult {
|
fn all_columns(span: Span) -> Value {
|
||||||
fn column_names(&self) -> Vec<&'static str> {
|
Value::record(
|
||||||
vec!["host", "cpu", "disks", "mem", "temp", "net"]
|
record! {
|
||||||
}
|
"host" => host(span),
|
||||||
|
"cpu" => cpu(span),
|
||||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
"disks" => disks(span),
|
||||||
let span = self.span;
|
"mem" => mem(span),
|
||||||
|
"temp" => temp(span),
|
||||||
match column {
|
"net" => net(span),
|
||||||
"host" => Ok(host(span)),
|
},
|
||||||
"cpu" => Ok(cpu(span)),
|
span,
|
||||||
"disks" => Ok(disks(span)),
|
)
|
||||||
"mem" => Ok(mem(span)),
|
|
||||||
"temp" => Ok(temp(span)),
|
|
||||||
"net" => Ok(net(span)),
|
|
||||||
_ => Err(ShellError::LazyRecordAccessFailed {
|
|
||||||
message: format!("Could not find column '{column}'"),
|
|
||||||
column_name: column.to_string(),
|
|
||||||
span,
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn span(&self) -> Span {
|
|
||||||
self.span
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clone_value(&self, span: Span) -> Value {
|
|
||||||
Value::lazy_record(Box::new((*self).clone()), span)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn trim_cstyle_null(s: String) -> String {
|
pub fn trim_cstyle_null(s: String) -> String {
|
||||||
|
@ -229,6 +229,7 @@ fn which(
|
|||||||
|
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = env::current_dir_str(engine_state, stack)?;
|
let cwd = env::current_dir_str(engine_state, stack)?;
|
||||||
let paths = env::path_str(engine_state, stack, call.head)?;
|
let paths = env::path_str(engine_state, stack, call.head)?;
|
||||||
|
|
||||||
|
@ -394,10 +394,6 @@ fn handle_table_command(
|
|||||||
input.data = PipelineData::Empty;
|
input.data = PipelineData::Empty;
|
||||||
handle_record(input, cfg, val)
|
handle_record(input, cfg, val)
|
||||||
}
|
}
|
||||||
PipelineData::Value(Value::LazyRecord { val, .. }, ..) => {
|
|
||||||
input.data = val.collect()?.into_pipeline_data();
|
|
||||||
handle_table_command(input, cfg)
|
|
||||||
}
|
|
||||||
PipelineData::Value(Value::Error { error, .. }, ..) => {
|
PipelineData::Value(Value::Error { error, .. }, ..) => {
|
||||||
// Propagate this error outward, so that it goes to stderr
|
// Propagate this error outward, so that it goes to stderr
|
||||||
// instead of stdout.
|
// instead of stdout.
|
||||||
@ -942,7 +938,11 @@ fn render_path_name(
|
|||||||
|
|
||||||
// clickable links don't work in remote SSH sessions
|
// clickable links don't work in remote SSH sessions
|
||||||
let in_ssh_session = std::env::var("SSH_CLIENT").is_ok();
|
let in_ssh_session = std::env::var("SSH_CLIENT").is_ok();
|
||||||
let show_clickable_links = config.show_clickable_links_in_ls && !in_ssh_session && has_metadata;
|
//TODO: Deprecated show_clickable_links_in_ls in favor of shell_integration_osc8
|
||||||
|
let show_clickable_links = config.show_clickable_links_in_ls
|
||||||
|
&& !in_ssh_session
|
||||||
|
&& has_metadata
|
||||||
|
&& config.shell_integration_osc8;
|
||||||
|
|
||||||
let ansi_style = style.map(Style::to_nu_ansi_term_style).unwrap_or_default();
|
let ansi_style = style.map(Style::to_nu_ansi_term_style).unwrap_or_default();
|
||||||
|
|
||||||
|
@ -207,7 +207,15 @@ fn filesystem_change_directory_to_symlink_relative() {
|
|||||||
$env.PWD
|
$env.PWD
|
||||||
"
|
"
|
||||||
);
|
);
|
||||||
|
assert_eq!(PathBuf::from(actual.out), dirs.test().join("foo_link"));
|
||||||
|
|
||||||
|
let actual = nu!(
|
||||||
|
cwd: dirs.test().join("boo"),
|
||||||
|
"
|
||||||
|
cd -P ../foo_link
|
||||||
|
$env.PWD
|
||||||
|
"
|
||||||
|
);
|
||||||
assert_eq!(PathBuf::from(actual.out), dirs.test().join("foo"));
|
assert_eq!(PathBuf::from(actual.out), dirs.test().join("foo"));
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -98,21 +98,6 @@ fn insert_uses_enumerate_index() {
|
|||||||
assert_eq!(actual.out, "[[index, a, b]; [0, 7, 8], [1, 6, 8]]");
|
assert_eq!(actual.out, "[[index, a, b]; [0, 7, 8], [1, 6, 8]]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn insert_support_lazy_record() {
|
|
||||||
let actual =
|
|
||||||
nu!(r#"let x = (lazy make -c ["h"] -g {|a| $a | str upcase}); $x | insert a 10 | get a"#);
|
|
||||||
assert_eq!(actual.out, "10");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn lazy_record_test_values() {
|
|
||||||
let actual = nu!(
|
|
||||||
r#"lazy make --columns ["haskell", "futures", "nushell"] --get-value { |lazything| $lazything + "!" } | values | length"#
|
|
||||||
);
|
|
||||||
assert_eq!(actual.out, "3");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deep_cell_path_creates_all_nested_records() {
|
fn deep_cell_path_creates_all_nested_records() {
|
||||||
let actual = nu!("{a: {}} | insert a.b.c 0 | get a.b.c");
|
let actual = nu!("{a: {}} | insert a.b.c 0 | get a.b.c");
|
||||||
|
@ -91,3 +91,18 @@ fn let_glob_type() {
|
|||||||
let actual = nu!("let x: glob = 'aa'; $x | describe");
|
let actual = nu!("let x: glob = 'aa'; $x | describe");
|
||||||
assert_eq!(actual.out, "glob");
|
assert_eq!(actual.out, "glob");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn let_raw_string() {
|
||||||
|
let actual = nu!(r#"let x = r#'abcde""fghi"''''jkl'#; $x"#);
|
||||||
|
assert_eq!(actual.out, r#"abcde""fghi"''''jkl"#);
|
||||||
|
|
||||||
|
let actual = nu!(r#"let x = r##'abcde""fghi"''''#jkl'##; $x"#);
|
||||||
|
assert_eq!(actual.out, r#"abcde""fghi"''''#jkl"#);
|
||||||
|
|
||||||
|
let actual = nu!(r#"let x = r###'abcde""fghi"'''##'#jkl'###; $x"#);
|
||||||
|
assert_eq!(actual.out, r#"abcde""fghi"'''##'#jkl"#);
|
||||||
|
|
||||||
|
let actual = nu!(r#"let x = r#'abc'#; $x"#);
|
||||||
|
assert_eq!(actual.out, "abc");
|
||||||
|
}
|
||||||
|
@ -125,3 +125,18 @@ fn mut_glob_type() {
|
|||||||
let actual = nu!("mut x: glob = 'aa'; $x | describe");
|
let actual = nu!("mut x: glob = 'aa'; $x | describe");
|
||||||
assert_eq!(actual.out, "glob");
|
assert_eq!(actual.out, "glob");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn mut_raw_string() {
|
||||||
|
let actual = nu!(r#"mut x = r#'abcde""fghi"''''jkl'#; $x"#);
|
||||||
|
assert_eq!(actual.out, r#"abcde""fghi"''''jkl"#);
|
||||||
|
|
||||||
|
let actual = nu!(r#"mut x = r##'abcde""fghi"''''#jkl'##; $x"#);
|
||||||
|
assert_eq!(actual.out, r#"abcde""fghi"''''#jkl"#);
|
||||||
|
|
||||||
|
let actual = nu!(r#"mut x = r###'abcde""fghi"'''##'#jkl'###; $x"#);
|
||||||
|
assert_eq!(actual.out, r#"abcde""fghi"'''##'#jkl"#);
|
||||||
|
|
||||||
|
let actual = nu!(r#"mut x = r#'abc'#; $x"#);
|
||||||
|
assert_eq!(actual.out, "abc");
|
||||||
|
}
|
||||||
|
@ -103,13 +103,6 @@ fn update_uses_enumerate_index() {
|
|||||||
assert_eq!(actual.out, "[[index, a]; [0, 8], [1, 8]]");
|
assert_eq!(actual.out, "[[index, a]; [0, 8], [1, 8]]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn update_support_lazy_record() {
|
|
||||||
let actual =
|
|
||||||
nu!(r#"let x = (lazy make -c ["h"] -g {|a| $a | str upcase}); $x | update h 10 | get h"#);
|
|
||||||
assert_eq!(actual.out, "10");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn list_replacement_closure() {
|
fn list_replacement_closure() {
|
||||||
let actual = nu!("[1, 2] | update 1 {|i| $i + 1 } | to nuon");
|
let actual = nu!("[1, 2] | update 1 {|i| $i + 1 } | to nuon");
|
||||||
|
@ -112,17 +112,6 @@ fn upsert_past_end_of_list_stream() {
|
|||||||
.contains("can't insert at index (the next available index is 3)"));
|
.contains("can't insert at index (the next available index is 3)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn upsert_support_lazy_record() {
|
|
||||||
let actual =
|
|
||||||
nu!(r#"let x = (lazy make -c ["h"] -g {|a| $a | str upcase}); $x | upsert h 10 | get h"#);
|
|
||||||
assert_eq!(actual.out, "10");
|
|
||||||
|
|
||||||
let actual =
|
|
||||||
nu!(r#"let x = (lazy make -c ["h"] -g {|a| $a | str upcase}); $x | upsert aa 10 | get aa"#);
|
|
||||||
assert_eq!(actual.out, "10");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn deep_cell_path_creates_all_nested_records() {
|
fn deep_cell_path_creates_all_nested_records() {
|
||||||
let actual = nu!("{a: {}} | upsert a.b.c 0 | get a.b.c");
|
let actual = nu!("{a: {}} | upsert a.b.c 0 | get a.b.c");
|
||||||
|
@ -2,7 +2,7 @@ use crate::ClosureEvalOnce;
|
|||||||
use nu_path::canonicalize_with;
|
use nu_path::canonicalize_with;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{Call, Expr},
|
ast::{Call, Expr},
|
||||||
engine::{EngineState, Stack, StateWorkingSet, PWD_ENV},
|
engine::{EngineState, Stack, StateWorkingSet},
|
||||||
Config, ShellError, Span, Value, VarId,
|
Config, ShellError, Span, Value, VarId,
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
@ -156,85 +156,56 @@ pub fn env_to_strings(
|
|||||||
Ok(env_vars_str)
|
Ok(env_vars_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Shorthand for env_to_string() for PWD with custom error
|
/// Returns the current working directory as a String, which is guaranteed to be canonicalized.
|
||||||
|
/// Unlike `current_dir_str_const()`, this also considers modifications to the current working directory made on the stack.
|
||||||
|
///
|
||||||
|
/// Returns an error if $env.PWD doesn't exist, is not a String, or is not an absolute path.
|
||||||
|
#[deprecated(since = "0.92.3", note = "please use `EngineState::cwd()` instead")]
|
||||||
pub fn current_dir_str(engine_state: &EngineState, stack: &Stack) -> Result<String, ShellError> {
|
pub fn current_dir_str(engine_state: &EngineState, stack: &Stack) -> Result<String, ShellError> {
|
||||||
if let Some(pwd) = stack.get_env_var(engine_state, PWD_ENV) {
|
#[allow(deprecated)]
|
||||||
// TODO: PWD should be string by default, we don't need to run ENV_CONVERSIONS on it
|
current_dir(engine_state, stack).map(|path| path.to_string_lossy().to_string())
|
||||||
match env_to_string(PWD_ENV, &pwd, engine_state, stack) {
|
|
||||||
Ok(cwd) => {
|
|
||||||
if Path::new(&cwd).is_absolute() {
|
|
||||||
Ok(cwd)
|
|
||||||
} else {
|
|
||||||
Err(ShellError::GenericError {
|
|
||||||
error: "Invalid current directory".into(),
|
|
||||||
msg: format!("The 'PWD' environment variable must be set to an absolute path. Found: '{cwd}'"),
|
|
||||||
span: Some(pwd.span()),
|
|
||||||
help: None,
|
|
||||||
inner: vec![]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => Err(e),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(ShellError::GenericError {
|
|
||||||
error: "Current directory not found".into(),
|
|
||||||
msg: "".into(),
|
|
||||||
span: None,
|
|
||||||
help: Some("The environment variable 'PWD' was not found. It is required to define the current directory.".into()),
|
|
||||||
inner: vec![],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Simplified version of current_dir_str() for constant evaluation
|
/// Returns the current working directory as a String, which is guaranteed to be canonicalized.
|
||||||
|
///
|
||||||
|
/// Returns an error if $env.PWD doesn't exist, is not a String, or is not an absolute path.
|
||||||
|
#[deprecated(since = "0.92.3", note = "please use `EngineState::cwd()` instead")]
|
||||||
pub fn current_dir_str_const(working_set: &StateWorkingSet) -> Result<String, ShellError> {
|
pub fn current_dir_str_const(working_set: &StateWorkingSet) -> Result<String, ShellError> {
|
||||||
if let Some(pwd) = working_set.get_env_var(PWD_ENV) {
|
#[allow(deprecated)]
|
||||||
let span = pwd.span();
|
current_dir_const(working_set).map(|path| path.to_string_lossy().to_string())
|
||||||
match pwd {
|
|
||||||
Value::String { val, .. } => {
|
|
||||||
if Path::new(val).is_absolute() {
|
|
||||||
Ok(val.clone())
|
|
||||||
} else {
|
|
||||||
Err(ShellError::GenericError {
|
|
||||||
error: "Invalid current directory".into(),
|
|
||||||
msg: format!("The 'PWD' environment variable must be set to an absolute path. Found: '{val}'"),
|
|
||||||
span: Some(span),
|
|
||||||
help: None,
|
|
||||||
inner: vec![]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => Err(ShellError::GenericError {
|
|
||||||
error: "PWD is not a string".into(),
|
|
||||||
msg: "".into(),
|
|
||||||
span: None,
|
|
||||||
help: Some(
|
|
||||||
"Cusrrent working directory environment variable 'PWD' must be a string."
|
|
||||||
.into(),
|
|
||||||
),
|
|
||||||
inner: vec![],
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Err(ShellError::GenericError{
|
|
||||||
error: "Current directory not found".into(),
|
|
||||||
msg: "".into(),
|
|
||||||
span: None,
|
|
||||||
help: Some("The environment variable 'PWD' was not found. It is required to define the current directory.".into()),
|
|
||||||
inner: vec![],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calls current_dir_str() and returns the current directory as a PathBuf
|
/// Returns the current working directory, which is guaranteed to be canonicalized.
|
||||||
|
/// Unlike `current_dir_const()`, this also considers modifications to the current working directory made on the stack.
|
||||||
|
///
|
||||||
|
/// Returns an error if $env.PWD doesn't exist, is not a String, or is not an absolute path.
|
||||||
|
#[deprecated(since = "0.92.3", note = "please use `EngineState::cwd()` instead")]
|
||||||
pub fn current_dir(engine_state: &EngineState, stack: &Stack) -> Result<PathBuf, ShellError> {
|
pub fn current_dir(engine_state: &EngineState, stack: &Stack) -> Result<PathBuf, ShellError> {
|
||||||
current_dir_str(engine_state, stack).map(PathBuf::from)
|
let cwd = engine_state.cwd(Some(stack))?;
|
||||||
|
// `EngineState::cwd()` always returns absolute path.
|
||||||
|
// We're using `canonicalize_with` instead of `fs::canonicalize()` because
|
||||||
|
// we still need to simplify Windows paths. "." is safe because `cwd` should
|
||||||
|
// be an absolute path already.
|
||||||
|
canonicalize_with(&cwd, ".").map_err(|_| ShellError::DirectoryNotFound {
|
||||||
|
dir: cwd.to_string_lossy().to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of current_dir() for constant evaluation
|
/// Returns the current working directory, which is guaranteed to be canonicalized.
|
||||||
|
///
|
||||||
|
/// Returns an error if $env.PWD doesn't exist, is not a String, or is not an absolute path.
|
||||||
|
#[deprecated(since = "0.92.3", note = "please use `EngineState::cwd()` instead")]
|
||||||
pub fn current_dir_const(working_set: &StateWorkingSet) -> Result<PathBuf, ShellError> {
|
pub fn current_dir_const(working_set: &StateWorkingSet) -> Result<PathBuf, ShellError> {
|
||||||
current_dir_str_const(working_set).map(PathBuf::from)
|
let cwd = working_set.permanent_state.cwd(None)?;
|
||||||
|
// `EngineState::cwd()` always returns absolute path.
|
||||||
|
// We're using `canonicalize_with` instead of `fs::canonicalize()` because
|
||||||
|
// we still need to simplify Windows paths. "." is safe because `cwd` should
|
||||||
|
// be an absolute path already.
|
||||||
|
canonicalize_with(&cwd, ".").map_err(|_| ShellError::DirectoryNotFound {
|
||||||
|
dir: cwd.to_string_lossy().to_string(),
|
||||||
|
span: Span::unknown(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the contents of path environment variable as a list of strings
|
/// Get the contents of path environment variable as a list of strings
|
||||||
@ -315,6 +286,7 @@ pub fn find_in_dirs_env(
|
|||||||
Err(e) => return Err(e),
|
Err(e) => return Err(e),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
#[allow(deprecated)]
|
||||||
current_dir_str(engine_state, stack)?
|
current_dir_str(engine_state, stack)?
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use crate::{current_dir, current_dir_str, get_config, get_full_help};
|
#[allow(deprecated)]
|
||||||
|
use crate::{current_dir, get_config, get_full_help};
|
||||||
use nu_path::expand_path_with;
|
use nu_path::expand_path_with;
|
||||||
use nu_protocol::{
|
use nu_protocol::{
|
||||||
ast::{
|
ast::{
|
||||||
@ -325,6 +326,7 @@ fn eval_redirection<D: DebugContext>(
|
|||||||
) -> Result<Redirection, ShellError> {
|
) -> Result<Redirection, ShellError> {
|
||||||
match target {
|
match target {
|
||||||
RedirectionTarget::File { expr, append, .. } => {
|
RedirectionTarget::File { expr, append, .. } => {
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = current_dir(engine_state, stack)?;
|
let cwd = current_dir(engine_state, stack)?;
|
||||||
let value = eval_expression::<D>(engine_state, stack, expr)?;
|
let value = eval_expression::<D>(engine_state, stack, expr)?;
|
||||||
let path = Spanned::<PathBuf>::from_value(value)?.item;
|
let path = Spanned::<PathBuf>::from_value(value)?.item;
|
||||||
@ -633,7 +635,7 @@ impl Eval for EvalRuntime {
|
|||||||
if quoted {
|
if quoted {
|
||||||
Ok(Value::string(path, span))
|
Ok(Value::string(path, span))
|
||||||
} else {
|
} else {
|
||||||
let cwd = current_dir_str(engine_state, stack)?;
|
let cwd = engine_state.cwd(Some(stack))?;
|
||||||
let path = expand_path_with(path, cwd, true);
|
let path = expand_path_with(path, cwd, true);
|
||||||
|
|
||||||
Ok(Value::string(path.to_string_lossy(), span))
|
Ok(Value::string(path.to_string_lossy(), span))
|
||||||
@ -652,7 +654,7 @@ impl Eval for EvalRuntime {
|
|||||||
} else if quoted {
|
} else if quoted {
|
||||||
Ok(Value::string(path, span))
|
Ok(Value::string(path, span))
|
||||||
} else {
|
} else {
|
||||||
let cwd = current_dir_str(engine_state, stack)?;
|
let cwd = engine_state.cwd(Some(stack))?;
|
||||||
let path = expand_path_with(path, cwd, true);
|
let path = expand_path_with(path, cwd, true);
|
||||||
|
|
||||||
Ok(Value::string(path.to_string_lossy(), span))
|
Ok(Value::string(path.to_string_lossy(), span))
|
||||||
|
@ -51,10 +51,3 @@ pub trait ViewCommand {
|
|||||||
value: Option<Value>,
|
value: Option<Value>,
|
||||||
) -> Result<Self::View>;
|
) -> Result<Self::View>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub struct Shortcode {
|
|
||||||
pub code: &'static str,
|
|
||||||
pub context: &'static str,
|
|
||||||
pub description: &'static str,
|
|
||||||
}
|
|
||||||
|
@ -112,10 +112,6 @@ pub fn collect_input(value: Value) -> Result<(Vec<String>, Vec<Vec<Value>>)> {
|
|||||||
|
|
||||||
Ok((vec![String::from("")], lines))
|
Ok((vec![String::from("")], lines))
|
||||||
}
|
}
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
let materialized = val.collect()?;
|
|
||||||
collect_input(materialized)
|
|
||||||
}
|
|
||||||
Value::Nothing { .. } => Ok((vec![], vec![])),
|
Value::Nothing { .. } => Ok((vec![], vec![])),
|
||||||
Value::Custom { val, .. } => {
|
Value::Custom { val, .. } => {
|
||||||
let materialized = val.to_base_value(span)?;
|
let materialized = val.to_base_value(span)?;
|
||||||
|
@ -4,7 +4,7 @@ use ratatui::{
|
|||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
text::Span,
|
text::Span,
|
||||||
widgets::{Paragraph, StatefulWidget, Widget},
|
widgets::{Paragraph, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -12,10 +12,6 @@ use crate::{
|
|||||||
views::util::{nu_style_to_tui, text_style_to_tui_style},
|
views::util::{nu_style_to_tui, text_style_to_tui_style},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::Layout;
|
|
||||||
|
|
||||||
type OptStyle = Option<NuStyle>;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BinaryWidget<'a> {
|
pub struct BinaryWidget<'a> {
|
||||||
data: &'a [u8],
|
data: &'a [u8],
|
||||||
@ -73,7 +69,7 @@ impl BinarySettings {
|
|||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
pub struct BinaryStyle {
|
pub struct BinaryStyle {
|
||||||
colors: BinaryStyleColors,
|
color_index: Option<NuStyle>,
|
||||||
indent_index: Indent,
|
indent_index: Indent,
|
||||||
indent_data: Indent,
|
indent_data: Indent,
|
||||||
indent_ascii: Indent,
|
indent_ascii: Indent,
|
||||||
@ -83,7 +79,7 @@ pub struct BinaryStyle {
|
|||||||
|
|
||||||
impl BinaryStyle {
|
impl BinaryStyle {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
colors: BinaryStyleColors,
|
color_index: Option<NuStyle>,
|
||||||
indent_index: Indent,
|
indent_index: Indent,
|
||||||
indent_data: Indent,
|
indent_data: Indent,
|
||||||
indent_ascii: Indent,
|
indent_ascii: Indent,
|
||||||
@ -91,7 +87,7 @@ impl BinaryStyle {
|
|||||||
show_split: bool,
|
show_split: bool,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
colors,
|
color_index,
|
||||||
indent_index,
|
indent_index,
|
||||||
indent_data,
|
indent_data,
|
||||||
indent_ascii,
|
indent_ascii,
|
||||||
@ -113,61 +109,8 @@ impl Indent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
impl Widget for BinaryWidget<'_> {
|
||||||
pub struct BinaryStyleColors {
|
fn render(self, area: Rect, buf: &mut Buffer) {
|
||||||
pub split_left: OptStyle,
|
|
||||||
pub split_right: OptStyle,
|
|
||||||
pub index: OptStyle,
|
|
||||||
pub data: SymbolColor,
|
|
||||||
pub ascii: SymbolColor,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub struct SymbolColor {
|
|
||||||
pub default: OptStyle,
|
|
||||||
pub zero: OptStyle,
|
|
||||||
pub unknown: OptStyle,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SymbolColor {
|
|
||||||
pub fn new(default: OptStyle, zero: OptStyle, unknown: OptStyle) -> Self {
|
|
||||||
Self {
|
|
||||||
default,
|
|
||||||
zero,
|
|
||||||
unknown,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BinaryStyleColors {
|
|
||||||
pub fn new(
|
|
||||||
index: OptStyle,
|
|
||||||
data: SymbolColor,
|
|
||||||
ascii: SymbolColor,
|
|
||||||
split_left: OptStyle,
|
|
||||||
split_right: OptStyle,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
split_left,
|
|
||||||
split_right,
|
|
||||||
index,
|
|
||||||
data,
|
|
||||||
ascii,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
|
||||||
pub struct BinaryWidgetState {
|
|
||||||
pub layout_index: Layout,
|
|
||||||
pub layout_data: Layout,
|
|
||||||
pub layout_ascii: Layout,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StatefulWidget for BinaryWidget<'_> {
|
|
||||||
type State = BinaryWidgetState;
|
|
||||||
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
|
||||||
let min_width = get_widget_width(&self);
|
let min_width = get_widget_width(&self);
|
||||||
|
|
||||||
if (area.width as usize) < min_width {
|
if (area.width as usize) < min_width {
|
||||||
@ -178,12 +121,12 @@ impl StatefulWidget for BinaryWidget<'_> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
render_hexdump(area, buf, state, self);
|
render_hexdump(area, buf, self);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: indent color
|
// todo: indent color
|
||||||
fn render_hexdump(area: Rect, buf: &mut Buffer, _state: &mut BinaryWidgetState, w: BinaryWidget) {
|
fn render_hexdump(area: Rect, buf: &mut Buffer, w: BinaryWidget) {
|
||||||
const MIN_INDEX_SIZE: usize = 8;
|
const MIN_INDEX_SIZE: usize = 8;
|
||||||
|
|
||||||
let show_index = !w.opts.disable_index;
|
let show_index = !w.opts.disable_index;
|
||||||
@ -211,7 +154,7 @@ fn render_hexdump(area: Rect, buf: &mut Buffer, _state: &mut BinaryWidgetState,
|
|||||||
|
|
||||||
if show_index {
|
if show_index {
|
||||||
x += render_space(buf, x, y, 1, w.style.indent_index.left);
|
x += render_space(buf, x, y, 1, w.style.indent_index.left);
|
||||||
x += render_hex_usize(buf, x, y, address, index_width, false, get_index_style(&w));
|
x += render_hex_usize(buf, x, y, address, index_width, get_index_style(&w));
|
||||||
x += render_space(buf, x, y, 1, w.style.indent_index.right);
|
x += render_space(buf, x, y, 1, w.style.indent_index.right);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +194,7 @@ fn render_hexdump(area: Rect, buf: &mut Buffer, _state: &mut BinaryWidgetState,
|
|||||||
|
|
||||||
if show_index {
|
if show_index {
|
||||||
x += render_space(buf, x, y, 1, w.style.indent_index.left);
|
x += render_space(buf, x, y, 1, w.style.indent_index.left);
|
||||||
x += render_hex_usize(buf, x, y, address, index_width, false, get_index_style(&w));
|
x += render_hex_usize(buf, x, y, address, index_width, get_index_style(&w));
|
||||||
x += render_space(buf, x, y, 1, w.style.indent_index.right);
|
x += render_space(buf, x, y, 1, w.style.indent_index.right);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,7 +256,7 @@ fn render_segment(buf: &mut Buffer, x: u16, y: u16, line: &[u8], w: &BinaryWidge
|
|||||||
}
|
}
|
||||||
|
|
||||||
let (_, style) = get_segment_char(w, n);
|
let (_, style) = get_segment_char(w, n);
|
||||||
size += render_hex_u8(buf, x + size, y, n, false, style);
|
size += render_hex_u8(buf, x + size, y, n, style);
|
||||||
count -= 1;
|
count -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,7 +289,7 @@ fn render_ascii_line(buf: &mut Buffer, x: u16, y: u16, line: &[u8], w: &BinaryWi
|
|||||||
size
|
size
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_ascii_char(buf: &mut Buffer, x: u16, y: u16, n: char, style: OptStyle) -> u16 {
|
fn render_ascii_char(buf: &mut Buffer, x: u16, y: u16, n: char, style: Option<NuStyle>) -> u16 {
|
||||||
let text = n.to_string();
|
let text = n.to_string();
|
||||||
|
|
||||||
let mut p = Paragraph::new(text);
|
let mut p = Paragraph::new(text);
|
||||||
@ -362,8 +305,8 @@ fn render_ascii_char(buf: &mut Buffer, x: u16, y: u16, n: char, style: OptStyle)
|
|||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_hex_u8(buf: &mut Buffer, x: u16, y: u16, n: u8, big: bool, style: OptStyle) -> u16 {
|
fn render_hex_u8(buf: &mut Buffer, x: u16, y: u16, n: u8, style: Option<NuStyle>) -> u16 {
|
||||||
render_hex_usize(buf, x, y, n as usize, 2, big, style)
|
render_hex_usize(buf, x, y, n as usize, 2, style)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_hex_usize(
|
fn render_hex_usize(
|
||||||
@ -372,10 +315,9 @@ fn render_hex_usize(
|
|||||||
y: u16,
|
y: u16,
|
||||||
n: usize,
|
n: usize,
|
||||||
width: u16,
|
width: u16,
|
||||||
big: bool,
|
style: Option<NuStyle>,
|
||||||
style: OptStyle,
|
|
||||||
) -> u16 {
|
) -> u16 {
|
||||||
let text = usize_to_hex(n, width as usize, big);
|
let text = usize_to_hex(n, width as usize);
|
||||||
let mut p = Paragraph::new(text);
|
let mut p = Paragraph::new(text);
|
||||||
if let Some(style) = style {
|
if let Some(style) = style {
|
||||||
let style = nu_style_to_tui(style);
|
let style = nu_style_to_tui(style);
|
||||||
@ -389,7 +331,7 @@ fn render_hex_usize(
|
|||||||
width
|
width
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ascii_char(_w: &BinaryWidget, n: u8) -> (char, OptStyle) {
|
fn get_ascii_char(_w: &BinaryWidget, n: u8) -> (char, Option<NuStyle>) {
|
||||||
let (style, c) = categorize_byte(&n);
|
let (style, c) = categorize_byte(&n);
|
||||||
let c = c.unwrap_or(n as char);
|
let c = c.unwrap_or(n as char);
|
||||||
let style = if style.is_plain() { None } else { Some(style) };
|
let style = if style.is_plain() { None } else { Some(style) };
|
||||||
@ -397,7 +339,7 @@ fn get_ascii_char(_w: &BinaryWidget, n: u8) -> (char, OptStyle) {
|
|||||||
(c, style)
|
(c, style)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_segment_char(_w: &BinaryWidget, n: u8) -> (char, OptStyle) {
|
fn get_segment_char(_w: &BinaryWidget, n: u8) -> (char, Option<NuStyle>) {
|
||||||
let (style, c) = categorize_byte(&n);
|
let (style, c) = categorize_byte(&n);
|
||||||
let c = c.unwrap_or(n as char);
|
let c = c.unwrap_or(n as char);
|
||||||
let style = if style.is_plain() { None } else { Some(style) };
|
let style = if style.is_plain() { None } else { Some(style) };
|
||||||
@ -405,8 +347,8 @@ fn get_segment_char(_w: &BinaryWidget, n: u8) -> (char, OptStyle) {
|
|||||||
(c, style)
|
(c, style)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_index_style(w: &BinaryWidget) -> OptStyle {
|
fn get_index_style(w: &BinaryWidget) -> Option<NuStyle> {
|
||||||
w.style.colors.index
|
w.style.color_index
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_space(buf: &mut Buffer, x: u16, y: u16, height: u16, padding: u16) -> u16 {
|
fn render_space(buf: &mut Buffer, x: u16, y: u16, height: u16, padding: u16) -> u16 {
|
||||||
@ -443,7 +385,7 @@ fn get_max_index_size(w: &BinaryWidget) -> usize {
|
|||||||
let line_size = w.opts.count_segments * (w.opts.segment_size * 2);
|
let line_size = w.opts.count_segments * (w.opts.segment_size * 2);
|
||||||
let count_lines = w.data.len() / line_size;
|
let count_lines = w.data.len() / line_size;
|
||||||
let max_index = w.opts.index_offset + count_lines * line_size;
|
let max_index = w.opts.index_offset + count_lines * line_size;
|
||||||
usize_to_hex(max_index, 0, false).len()
|
usize_to_hex(max_index, 0).len()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_widget_width(w: &BinaryWidget) -> usize {
|
fn get_widget_width(w: &BinaryWidget) -> usize {
|
||||||
@ -453,7 +395,7 @@ fn get_widget_width(w: &BinaryWidget) -> usize {
|
|||||||
let count_lines = w.data.len() / line_size;
|
let count_lines = w.data.len() / line_size;
|
||||||
|
|
||||||
let max_index = w.opts.index_offset + count_lines * line_size;
|
let max_index = w.opts.index_offset + count_lines * line_size;
|
||||||
let index_size = usize_to_hex(max_index, 0, false).len();
|
let index_size = usize_to_hex(max_index, 0).len();
|
||||||
let index_size = index_size.max(MIN_INDEX_SIZE);
|
let index_size = index_size.max(MIN_INDEX_SIZE);
|
||||||
|
|
||||||
let data_split_size = w.opts.count_segments.saturating_sub(1) * w.style.indent_segment;
|
let data_split_size = w.opts.count_segments.saturating_sub(1) * w.style.indent_segment;
|
||||||
@ -479,17 +421,11 @@ fn get_widget_width(w: &BinaryWidget) -> usize {
|
|||||||
min_width
|
min_width
|
||||||
}
|
}
|
||||||
|
|
||||||
fn usize_to_hex(n: usize, width: usize, big: bool) -> String {
|
fn usize_to_hex(n: usize, width: usize) -> String {
|
||||||
if width == 0 {
|
if width == 0 {
|
||||||
match big {
|
format!("{:x}", n)
|
||||||
true => format!("{:X}", n),
|
|
||||||
false => format!("{:x}", n),
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
match big {
|
format!("{:0>width$x}", n, width = width)
|
||||||
true => format!("{:0>width$X}", n, width = width),
|
|
||||||
false => format!("{:0>width$x}", n, width = width),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -499,9 +435,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_to_hex() {
|
fn test_to_hex() {
|
||||||
assert_eq!(usize_to_hex(1, 2, false), "01");
|
assert_eq!(usize_to_hex(1, 2), "01");
|
||||||
assert_eq!(usize_to_hex(16, 2, false), "10");
|
assert_eq!(usize_to_hex(16, 2), "10");
|
||||||
assert_eq!(usize_to_hex(29, 2, false), "1d");
|
assert_eq!(usize_to_hex(29, 2), "1d");
|
||||||
assert_eq!(usize_to_hex(29, 2, true), "1D");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,29 +19,17 @@ use crate::{
|
|||||||
util::create_map,
|
util::create_map,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::binary_widget::{
|
use self::binary_widget::{BinarySettings, BinaryStyle, BinaryWidget, Indent};
|
||||||
BinarySettings, BinaryStyle, BinaryStyleColors, BinaryWidget, BinaryWidgetState, Indent,
|
|
||||||
SymbolColor,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::{cursor::XYCursor, Layout, View, ViewConfig};
|
use super::{cursor::XYCursor, Layout, View, ViewConfig};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct BinaryView {
|
pub struct BinaryView {
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
mode: Option<CursorMode>,
|
|
||||||
cursor: XYCursor,
|
cursor: XYCursor,
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)] // todo:
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
enum CursorMode {
|
|
||||||
Index,
|
|
||||||
Data,
|
|
||||||
Ascii,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone)]
|
#[derive(Debug, Default, Clone)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
opts: BinarySettings,
|
opts: BinarySettings,
|
||||||
@ -52,7 +40,6 @@ impl BinaryView {
|
|||||||
pub fn new(data: Vec<u8>) -> Self {
|
pub fn new(data: Vec<u8>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
data,
|
data,
|
||||||
mode: None,
|
|
||||||
cursor: XYCursor::default(),
|
cursor: XYCursor::default(),
|
||||||
settings: Settings::default(),
|
settings: Settings::default(),
|
||||||
}
|
}
|
||||||
@ -61,9 +48,8 @@ impl BinaryView {
|
|||||||
|
|
||||||
impl View for BinaryView {
|
impl View for BinaryView {
|
||||||
fn draw(&mut self, f: &mut Frame, area: Rect, _cfg: ViewConfig<'_>, _layout: &mut Layout) {
|
fn draw(&mut self, f: &mut Frame, area: Rect, _cfg: ViewConfig<'_>, _layout: &mut Layout) {
|
||||||
let mut state = BinaryWidgetState::default();
|
|
||||||
let widget = create_binary_widget(self);
|
let widget = create_binary_widget(self);
|
||||||
f.render_stateful_widget(widget, area, &mut state);
|
f.render_widget(widget, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_input(
|
fn handle_input(
|
||||||
@ -77,7 +63,7 @@ impl View for BinaryView {
|
|||||||
let result = handle_event_view_mode(self, &key);
|
let result = handle_event_view_mode(self, &key);
|
||||||
|
|
||||||
if matches!(&result, Some(Transition::Ok)) {
|
if matches!(&result, Some(Transition::Ok)) {
|
||||||
let report = create_report(self.mode, self.cursor);
|
let report = create_report(self.cursor);
|
||||||
info.status = Some(report);
|
info.status = Some(report);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,21 +192,7 @@ fn settings_from_config(config: &ConfigMap) -> Settings {
|
|||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
style: BinaryStyle::new(
|
style: BinaryStyle::new(
|
||||||
BinaryStyleColors::new(
|
colors.get("color_index").cloned(),
|
||||||
colors.get("color_index").cloned(),
|
|
||||||
SymbolColor::new(
|
|
||||||
colors.get("color_segment").cloned(),
|
|
||||||
colors.get("color_segment_zero").cloned(),
|
|
||||||
colors.get("color_segment_unknown").cloned(),
|
|
||||||
),
|
|
||||||
SymbolColor::new(
|
|
||||||
colors.get("color_ascii").cloned(),
|
|
||||||
colors.get("color_ascii_zero").cloned(),
|
|
||||||
colors.get("color_ascii_unknown").cloned(),
|
|
||||||
),
|
|
||||||
colors.get("color_split_left").cloned(),
|
|
||||||
colors.get("color_split_right").cloned(),
|
|
||||||
),
|
|
||||||
Indent::new(
|
Indent::new(
|
||||||
config_get_usize(config, "padding_index_left", 2) as u16,
|
config_get_usize(config, "padding_index_left", 2) as u16,
|
||||||
config_get_usize(config, "padding_index_right", 2) as u16,
|
config_get_usize(config, "padding_index_right", 2) as u16,
|
||||||
@ -254,22 +226,17 @@ fn config_get_usize(config: &ConfigMap, key: &str, default: usize) -> usize {
|
|||||||
.unwrap_or(default)
|
.unwrap_or(default)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_report(mode: Option<CursorMode>, cursor: XYCursor) -> Report {
|
fn create_report(cursor: XYCursor) -> Report {
|
||||||
let covered_percent = report_row_position(cursor);
|
let covered_percent = report_row_position(cursor);
|
||||||
let cursor = report_cursor_position(cursor);
|
let cursor = report_cursor_position(cursor);
|
||||||
let mode = report_mode_name(mode);
|
let mode = report_mode_name();
|
||||||
let msg = String::new();
|
let msg = String::new();
|
||||||
|
|
||||||
Report::new(msg, Severity::Info, mode, cursor, covered_percent)
|
Report::new(msg, Severity::Info, mode, cursor, covered_percent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_mode_name(cursor: Option<CursorMode>) -> String {
|
fn report_mode_name() -> String {
|
||||||
match cursor {
|
String::from("VIEW")
|
||||||
Some(CursorMode::Index) => String::from("ADDR"),
|
|
||||||
Some(CursorMode::Data) => String::from("DUMP"),
|
|
||||||
Some(CursorMode::Ascii) => String::from("TEXT"),
|
|
||||||
None => String::from("VIEW"),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn report_row_position(cursor: XYCursor) -> String {
|
fn report_row_position(cursor: XYCursor) -> String {
|
||||||
|
@ -32,25 +32,10 @@ impl XYCursor {
|
|||||||
self.x.index()
|
self.x.index()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn row_offset(&self) -> usize {
|
|
||||||
self.y.offset()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn column_limit(&self) -> usize {
|
|
||||||
self.x.end()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn row_limit(&self) -> usize {
|
pub fn row_limit(&self) -> usize {
|
||||||
self.y.end()
|
self.y.end()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn column_offset(&self) -> usize {
|
|
||||||
self.x.offset()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn row_starts_at(&self) -> usize {
|
pub fn row_starts_at(&self) -> usize {
|
||||||
self.y.starts_at()
|
self.y.starts_at()
|
||||||
}
|
}
|
||||||
@ -67,11 +52,6 @@ impl XYCursor {
|
|||||||
self.x.offset()
|
self.x.offset()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn row_window_size(&self) -> usize {
|
|
||||||
self.y.window()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn column_window_size(&self) -> usize {
|
pub fn column_window_size(&self) -> usize {
|
||||||
self.x.window()
|
self.x.window()
|
||||||
}
|
}
|
||||||
@ -80,11 +60,6 @@ impl XYCursor {
|
|||||||
self.y.next(1)
|
self.y.next(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn next_row_by(&mut self, i: usize) -> bool {
|
|
||||||
self.y.next(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_row_page(&mut self) -> bool {
|
pub fn next_row_page(&mut self) -> bool {
|
||||||
self.y.next_window()
|
self.y.next_window()
|
||||||
}
|
}
|
||||||
@ -101,11 +76,6 @@ impl XYCursor {
|
|||||||
self.y.prev(1)
|
self.y.prev(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn prev_row_by(&mut self, i: usize) -> bool {
|
|
||||||
self.y.prev(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn prev_row_page(&mut self) -> bool {
|
pub fn prev_row_page(&mut self) -> bool {
|
||||||
self.y.prev_window()
|
self.y.prev_window()
|
||||||
}
|
}
|
||||||
|
@ -1141,18 +1141,18 @@ mod test {
|
|||||||
|
|
||||||
let v: Value = from_str("{\"a\":1.1}").unwrap();
|
let v: Value = from_str("{\"a\":1.1}").unwrap();
|
||||||
let vo = v.as_object().unwrap();
|
let vo = v.as_object().unwrap();
|
||||||
assert!(vo["a"].as_f64().unwrap() - 1.1 < std::f64::EPSILON);
|
assert!((vo["a"].as_f64().unwrap() - 1.1).abs() < f64::EPSILON);
|
||||||
|
|
||||||
let v: Value = from_str("{\"a\":-1.1}").unwrap();
|
let v: Value = from_str("{\"a\":-1.1}").unwrap();
|
||||||
let vo = v.as_object().unwrap();
|
let vo = v.as_object().unwrap();
|
||||||
assert!(vo["a"].as_f64().unwrap() + 1.1 > -(std::f64::EPSILON));
|
assert!((vo["a"].as_f64().unwrap() + 1.1).abs() < f64::EPSILON);
|
||||||
|
|
||||||
let v: Value = from_str("{\"a\":1e6}").unwrap();
|
let v: Value = from_str("{\"a\":1e6}").unwrap();
|
||||||
let vo = v.as_object().unwrap();
|
let vo = v.as_object().unwrap();
|
||||||
assert!(vo["a"].as_f64().unwrap() - 1e6 < std::f64::EPSILON);
|
assert!((vo["a"].as_f64().unwrap() - 1e6).abs() < f64::EPSILON);
|
||||||
|
|
||||||
let v: Value = from_str("{\"a\":-1e6}").unwrap();
|
let v: Value = from_str("{\"a\":-1e6}").unwrap();
|
||||||
let vo = v.as_object().unwrap();
|
let vo = v.as_object().unwrap();
|
||||||
assert!(vo["a"].as_f64().unwrap() + 1e6 > -(std::f64::EPSILON));
|
assert!((vo["a"].as_f64().unwrap() + 1e6).abs() < f64::EPSILON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ pub enum FlatShape {
|
|||||||
Or,
|
Or,
|
||||||
Pipe,
|
Pipe,
|
||||||
Range,
|
Range,
|
||||||
|
RawString,
|
||||||
Record,
|
Record,
|
||||||
Redirection,
|
Redirection,
|
||||||
Signature,
|
Signature,
|
||||||
@ -78,6 +79,7 @@ impl Display for FlatShape {
|
|||||||
FlatShape::Or => write!(f, "shape_or"),
|
FlatShape::Or => write!(f, "shape_or"),
|
||||||
FlatShape::Pipe => write!(f, "shape_pipe"),
|
FlatShape::Pipe => write!(f, "shape_pipe"),
|
||||||
FlatShape::Range => write!(f, "shape_range"),
|
FlatShape::Range => write!(f, "shape_range"),
|
||||||
|
FlatShape::RawString => write!(f, "shape_raw_string"),
|
||||||
FlatShape::Record => write!(f, "shape_record"),
|
FlatShape::Record => write!(f, "shape_record"),
|
||||||
FlatShape::Redirection => write!(f, "shape_redirection"),
|
FlatShape::Redirection => write!(f, "shape_redirection"),
|
||||||
FlatShape::Signature => write!(f, "shape_signature"),
|
FlatShape::Signature => write!(f, "shape_signature"),
|
||||||
@ -509,6 +511,9 @@ pub fn flatten_expression(
|
|||||||
Expr::String(_) => {
|
Expr::String(_) => {
|
||||||
vec![(expr.span, FlatShape::String)]
|
vec![(expr.span, FlatShape::String)]
|
||||||
}
|
}
|
||||||
|
Expr::RawString(_) => {
|
||||||
|
vec![(expr.span, FlatShape::RawString)]
|
||||||
|
}
|
||||||
Expr::Table(table) => {
|
Expr::Table(table) => {
|
||||||
let outer_span = expr.span;
|
let outer_span = expr.span;
|
||||||
let mut last_end = outer_span.start;
|
let mut last_end = outer_span.start;
|
||||||
|
@ -503,6 +503,79 @@ fn lex_internal(
|
|||||||
} else if c == b' ' || c == b'\t' || additional_whitespace.contains(&c) {
|
} else if c == b' ' || c == b'\t' || additional_whitespace.contains(&c) {
|
||||||
// If the next character is non-newline whitespace, skip it.
|
// If the next character is non-newline whitespace, skip it.
|
||||||
curr_offset += 1;
|
curr_offset += 1;
|
||||||
|
} else if c == b'r' {
|
||||||
|
// A raw string literal looks like `echo r#'Look, I can use 'single quotes'!'#`
|
||||||
|
// If the next character is `#` we're probably looking at a raw string literal
|
||||||
|
// so we need to read all the text until we find a closing `#`. This raw string
|
||||||
|
// can contain any character, including newlines and double quotes without needing
|
||||||
|
// to escape them.
|
||||||
|
//
|
||||||
|
// A raw string can contain many `#` as prefix,
|
||||||
|
// incase if there is a `'#` or `#'` in the string itself.
|
||||||
|
// E.g: r##'I can use '#' in a raw string'##
|
||||||
|
let mut prefix_sharp_cnt = 0;
|
||||||
|
let start = curr_offset;
|
||||||
|
while let Some(b'#') = input.get(start + prefix_sharp_cnt + 1) {
|
||||||
|
prefix_sharp_cnt += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if prefix_sharp_cnt != 0 {
|
||||||
|
// curr_offset is the character `r`, we need to move forward and skip all `#`
|
||||||
|
// characters.
|
||||||
|
//
|
||||||
|
// e.g: r###'<body>
|
||||||
|
// ^
|
||||||
|
// ^
|
||||||
|
// curr_offset
|
||||||
|
curr_offset += prefix_sharp_cnt + 1;
|
||||||
|
// the next one should be a single quote.
|
||||||
|
if input.get(curr_offset) != Some(&b'\'') {
|
||||||
|
error = Some(ParseError::Expected(
|
||||||
|
"'",
|
||||||
|
Span::new(span_offset + curr_offset, span_offset + curr_offset + 1),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
curr_offset += 1;
|
||||||
|
let mut matches = false;
|
||||||
|
while let Some(ch) = input.get(curr_offset) {
|
||||||
|
// check for postfix '###
|
||||||
|
if *ch == b'#' {
|
||||||
|
let start_ch = input[curr_offset - prefix_sharp_cnt];
|
||||||
|
let postfix = &input[curr_offset - prefix_sharp_cnt + 1..=curr_offset];
|
||||||
|
if start_ch == b'\'' && postfix.iter().all(|x| *x == b'#') {
|
||||||
|
matches = true;
|
||||||
|
curr_offset += 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
curr_offset += 1
|
||||||
|
}
|
||||||
|
if matches {
|
||||||
|
output.push(Token::new(
|
||||||
|
TokenContents::Item,
|
||||||
|
Span::new(span_offset + start, span_offset + curr_offset),
|
||||||
|
));
|
||||||
|
} else if error.is_none() {
|
||||||
|
error = Some(ParseError::UnexpectedEof(
|
||||||
|
"#".to_string(),
|
||||||
|
Span::new(span_offset + curr_offset, span_offset + curr_offset),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let (token, err) = lex_item(
|
||||||
|
input,
|
||||||
|
&mut curr_offset,
|
||||||
|
span_offset,
|
||||||
|
additional_whitespace,
|
||||||
|
special_tokens,
|
||||||
|
in_signature,
|
||||||
|
);
|
||||||
|
if error.is_none() {
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
output.push(token);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
let token = try_lex_special_piped_item(input, &mut curr_offset, span_offset);
|
let token = try_lex_special_piped_item(input, &mut curr_offset, span_offset);
|
||||||
if let Some(token) = token {
|
if let Some(token) = token {
|
||||||
|
@ -1949,6 +1949,7 @@ pub fn parse_module_file_or_dir(
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
let module_path =
|
let module_path =
|
||||||
@ -3341,6 +3342,7 @@ pub fn parse_mut(working_set: &mut StateWorkingSet, spans: &[Span]) -> Pipeline
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_source(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
|
pub fn parse_source(working_set: &mut StateWorkingSet, lite_command: &LiteCommand) -> Pipeline {
|
||||||
|
trace!("parsing source");
|
||||||
let spans = &lite_command.parts;
|
let spans = &lite_command.parts;
|
||||||
let name = working_set.get_span_contents(spans[0]);
|
let name = working_set.get_span_contents(spans[0]);
|
||||||
|
|
||||||
@ -3358,6 +3360,7 @@ pub fn parse_source(working_set: &mut StateWorkingSet, lite_command: &LiteComman
|
|||||||
let scoped = name == b"source-env";
|
let scoped = name == b"source-env";
|
||||||
|
|
||||||
if let Some(decl_id) = working_set.find_decl(name) {
|
if let Some(decl_id) = working_set.find_decl(name) {
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
// Is this the right call to be using here?
|
// Is this the right call to be using here?
|
||||||
@ -3563,6 +3566,7 @@ pub fn parse_register(working_set: &mut StateWorkingSet, lite_command: &LiteComm
|
|||||||
|
|
||||||
let spans = &lite_command.parts;
|
let spans = &lite_command.parts;
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
// Checking that the function is used with the correct name
|
// Checking that the function is used with the correct name
|
||||||
@ -3784,6 +3788,7 @@ pub fn parse_register(working_set: &mut StateWorkingSet, lite_command: &LiteComm
|
|||||||
pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
|
pub fn parse_plugin_use(working_set: &mut StateWorkingSet, call: Box<Call>) -> Pipeline {
|
||||||
use nu_protocol::{FromValue, PluginRegistryFile};
|
use nu_protocol::{FromValue, PluginRegistryFile};
|
||||||
|
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = working_set.get_cwd();
|
let cwd = working_set.get_cwd();
|
||||||
|
|
||||||
if let Err(err) = (|| {
|
if let Err(err) = (|| {
|
||||||
|
@ -66,6 +66,11 @@ pub fn is_math_expression_like(working_set: &mut StateWorkingSet, span: Span) ->
|
|||||||
|
|
||||||
let b = bytes[0];
|
let b = bytes[0];
|
||||||
|
|
||||||
|
// check for raw string
|
||||||
|
if bytes.starts_with(b"r#") {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if b == b'(' || b == b'{' || b == b'[' || b == b'$' || b == b'"' || b == b'\'' || b == b'-' {
|
if b == b'(' || b == b'{' || b == b'[' || b == b'$' || b == b'"' || b == b'\'' || b == b'-' {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -578,6 +583,7 @@ pub fn parse_multispan_value(
|
|||||||
spans_idx: &mut usize,
|
spans_idx: &mut usize,
|
||||||
shape: &SyntaxShape,
|
shape: &SyntaxShape,
|
||||||
) -> Expression {
|
) -> Expression {
|
||||||
|
trace!("parse multispan value");
|
||||||
match shape {
|
match shape {
|
||||||
SyntaxShape::VarWithOptType => {
|
SyntaxShape::VarWithOptType => {
|
||||||
trace!("parsing: var with opt type");
|
trace!("parsing: var with opt type");
|
||||||
@ -1565,6 +1571,66 @@ pub(crate) fn parse_dollar_expr(working_set: &mut StateWorkingSet, span: Span) -
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_raw_string(working_set: &mut StateWorkingSet, span: Span) -> Expression {
|
||||||
|
trace!("parsing: raw-string, with required delimiters");
|
||||||
|
|
||||||
|
let bytes = working_set.get_span_contents(span);
|
||||||
|
|
||||||
|
let prefix_sharp_cnt = if bytes.starts_with(b"r#") {
|
||||||
|
// actually `sharp_cnt` is always `index - 1`
|
||||||
|
// but create a variable here to make it clearer.
|
||||||
|
let mut sharp_cnt = 1;
|
||||||
|
let mut index = 2;
|
||||||
|
while index < bytes.len() && bytes[index] == b'#' {
|
||||||
|
index += 1;
|
||||||
|
sharp_cnt += 1;
|
||||||
|
}
|
||||||
|
sharp_cnt
|
||||||
|
} else {
|
||||||
|
working_set.error(ParseError::Expected("r#", span));
|
||||||
|
return garbage(span);
|
||||||
|
};
|
||||||
|
let expect_postfix_sharp_cnt = prefix_sharp_cnt;
|
||||||
|
// check the length of whole raw string.
|
||||||
|
// the whole raw string should contains at least
|
||||||
|
// 1(r) + prefix_sharp_cnt + 1(') + 1(') + postfix_sharp characters
|
||||||
|
if bytes.len() < prefix_sharp_cnt + expect_postfix_sharp_cnt + 3 {
|
||||||
|
working_set.error(ParseError::Unclosed('\''.into(), span));
|
||||||
|
return garbage(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for unbalanced # and single quotes.
|
||||||
|
let postfix_bytes = &bytes[bytes.len() - expect_postfix_sharp_cnt..bytes.len()];
|
||||||
|
if postfix_bytes.iter().any(|b| *b != b'#') {
|
||||||
|
working_set.error(ParseError::Unbalanced(
|
||||||
|
"prefix #".to_string(),
|
||||||
|
"postfix #".to_string(),
|
||||||
|
span,
|
||||||
|
));
|
||||||
|
return garbage(span);
|
||||||
|
}
|
||||||
|
// check for unblanaced single quotes.
|
||||||
|
if bytes[1 + prefix_sharp_cnt] != b'\''
|
||||||
|
|| bytes[bytes.len() - expect_postfix_sharp_cnt - 1] != b'\''
|
||||||
|
{
|
||||||
|
working_set.error(ParseError::Unclosed('\''.into(), span));
|
||||||
|
return garbage(span);
|
||||||
|
}
|
||||||
|
|
||||||
|
let bytes = &bytes[prefix_sharp_cnt + 1 + 1..bytes.len() - 1 - prefix_sharp_cnt];
|
||||||
|
if let Ok(token) = String::from_utf8(bytes.into()) {
|
||||||
|
Expression {
|
||||||
|
expr: Expr::RawString(token),
|
||||||
|
span,
|
||||||
|
ty: Type::String,
|
||||||
|
custom_completion: None,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
working_set.error(ParseError::Expected("utf8 raw-string", span));
|
||||||
|
garbage(span)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_paren_expr(
|
pub fn parse_paren_expr(
|
||||||
working_set: &mut StateWorkingSet,
|
working_set: &mut StateWorkingSet,
|
||||||
span: Span,
|
span: Span,
|
||||||
@ -4553,6 +4619,9 @@ pub fn parse_value(
|
|||||||
return Expression::garbage(span);
|
return Expression::garbage(span);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
b'r' if bytes.len() > 1 && bytes[1] == b'#' => {
|
||||||
|
return parse_raw_string(working_set, span);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -6075,6 +6144,7 @@ pub fn discover_captures_in_expr(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::String(_) => {}
|
Expr::String(_) => {}
|
||||||
|
Expr::RawString(_) => {}
|
||||||
Expr::StringInterpolation(exprs) => {
|
Expr::StringInterpolation(exprs) => {
|
||||||
for expr in exprs {
|
for expr in exprs {
|
||||||
discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
|
discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
|
||||||
@ -6236,6 +6306,7 @@ pub fn parse(
|
|||||||
contents: &[u8],
|
contents: &[u8],
|
||||||
scoped: bool,
|
scoped: bool,
|
||||||
) -> Arc<Block> {
|
) -> Arc<Block> {
|
||||||
|
trace!("parse");
|
||||||
let name = match fname {
|
let name = match fname {
|
||||||
Some(fname) => {
|
Some(fname) => {
|
||||||
// use the canonical name for this filename
|
// use the canonical name for this filename
|
||||||
@ -6253,9 +6324,13 @@ pub fn parse(
|
|||||||
|
|
||||||
let mut output = {
|
let mut output = {
|
||||||
if let Some(block) = previously_parsed_block {
|
if let Some(block) = previously_parsed_block {
|
||||||
|
// dbg!("previous block");
|
||||||
return block;
|
return block;
|
||||||
} else {
|
} else {
|
||||||
|
// dbg!("starting lex");
|
||||||
let (output, err) = lex(contents, new_span.start, &[], &[], false);
|
let (output, err) = lex(contents, new_span.start, &[], &[], false);
|
||||||
|
// dbg!("finished lex");
|
||||||
|
// dbg!(&output);
|
||||||
if let Some(err) = err {
|
if let Some(err) = err {
|
||||||
working_set.error(err)
|
working_set.error(err)
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::ffi::OsString;
|
use std::ffi::{OsStr, OsString};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) mod tests;
|
pub(crate) mod tests;
|
||||||
@ -23,6 +23,16 @@ pub fn make_local_socket_name(unique_id: &str) -> OsString {
|
|||||||
base.into()
|
base.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interpret a local socket name for use with `interprocess`.
|
||||||
|
#[cfg(unix)]
|
||||||
|
pub fn interpret_local_socket_name(
|
||||||
|
name: &OsStr,
|
||||||
|
) -> Result<interprocess::local_socket::Name, std::io::Error> {
|
||||||
|
use interprocess::local_socket::{GenericFilePath, ToFsName};
|
||||||
|
|
||||||
|
name.to_fs_name::<GenericFilePath>()
|
||||||
|
}
|
||||||
|
|
||||||
/// Generate a name to be used for a local socket specific to this `nu` process, described by the
|
/// Generate a name to be used for a local socket specific to this `nu` process, described by the
|
||||||
/// given `unique_id`, which should be unique to the purpose of the socket.
|
/// given `unique_id`, which should be unique to the purpose of the socket.
|
||||||
///
|
///
|
||||||
@ -33,6 +43,16 @@ pub fn make_local_socket_name(unique_id: &str) -> OsString {
|
|||||||
format!("nu.{}.{}", std::process::id(), unique_id).into()
|
format!("nu.{}.{}", std::process::id(), unique_id).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Interpret a local socket name for use with `interprocess`.
|
||||||
|
#[cfg(windows)]
|
||||||
|
pub fn interpret_local_socket_name(
|
||||||
|
name: &OsStr,
|
||||||
|
) -> Result<interprocess::local_socket::Name, std::io::Error> {
|
||||||
|
use interprocess::local_socket::{GenericNamespaced, ToNsName};
|
||||||
|
|
||||||
|
name.to_ns_name::<GenericNamespaced>()
|
||||||
|
}
|
||||||
|
|
||||||
/// Determine if the error is just due to the listener not being ready yet in asynchronous mode
|
/// Determine if the error is just due to the listener not being ready yet in asynchronous mode
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
pub fn is_would_block_err(err: &std::io::Error) -> bool {
|
pub fn is_would_block_err(err: &std::io::Error) -> bool {
|
||||||
@ -48,37 +68,3 @@ pub fn is_would_block_err(err: &std::io::Error) -> bool {
|
|||||||
e as i64 == windows::Win32::Foundation::ERROR_PIPE_LISTENING.0 as i64
|
e as i64 == windows::Win32::Foundation::ERROR_PIPE_LISTENING.0 as i64
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wraps the `interprocess` local socket stream for greater compatibility
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct LocalSocketStream(pub interprocess::local_socket::LocalSocketStream);
|
|
||||||
|
|
||||||
impl From<interprocess::local_socket::LocalSocketStream> for LocalSocketStream {
|
|
||||||
fn from(value: interprocess::local_socket::LocalSocketStream) -> Self {
|
|
||||||
LocalSocketStream(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::io::Read for LocalSocketStream {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
|
|
||||||
self.0.read(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::io::Write for LocalSocketStream {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
|
|
||||||
self.0.write(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush(&mut self) -> std::io::Result<()> {
|
|
||||||
// We don't actually flush the underlying socket on Windows. The flush operation on a
|
|
||||||
// Windows named pipe actually synchronizes with read on the other side, and won't finish
|
|
||||||
// until the other side is empty. This isn't how most of our other I/O methods work, so we
|
|
||||||
// just won't do it. The BufWriter above this will have still made a write call with the
|
|
||||||
// contents of the buffer, which should be good enough.
|
|
||||||
if cfg!(not(windows)) {
|
|
||||||
self.0.flush()?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,9 +4,6 @@ use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio};
|
|||||||
|
|
||||||
use nu_protocol::ShellError;
|
use nu_protocol::ShellError;
|
||||||
|
|
||||||
#[cfg(feature = "local-socket")]
|
|
||||||
use interprocess::local_socket::LocalSocketListener;
|
|
||||||
|
|
||||||
#[cfg(feature = "local-socket")]
|
#[cfg(feature = "local-socket")]
|
||||||
mod local_socket;
|
mod local_socket;
|
||||||
|
|
||||||
@ -83,15 +80,14 @@ impl CommunicationMode {
|
|||||||
// For sockets: we need to create the server so that the child won't fail to connect.
|
// For sockets: we need to create the server so that the child won't fail to connect.
|
||||||
#[cfg(feature = "local-socket")]
|
#[cfg(feature = "local-socket")]
|
||||||
CommunicationMode::LocalSocket(name) => {
|
CommunicationMode::LocalSocket(name) => {
|
||||||
let listener = LocalSocketListener::bind(name.as_os_str()).map_err(|err| {
|
use interprocess::local_socket::ListenerOptions;
|
||||||
ShellError::IOError {
|
|
||||||
|
let listener = interpret_local_socket_name(name)
|
||||||
|
.and_then(|name| ListenerOptions::new().name(name).create_sync())
|
||||||
|
.map_err(|err| ShellError::IOError {
|
||||||
msg: format!("failed to open socket for plugin: {err}"),
|
msg: format!("failed to open socket for plugin: {err}"),
|
||||||
}
|
})?;
|
||||||
})?;
|
Ok(PreparedServerCommunication::LocalSocket { listener })
|
||||||
Ok(PreparedServerCommunication::LocalSocket {
|
|
||||||
name: name.clone(),
|
|
||||||
listener,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,11 +103,13 @@ impl CommunicationMode {
|
|||||||
// Connect to the specified socket.
|
// Connect to the specified socket.
|
||||||
let get_socket = || {
|
let get_socket = || {
|
||||||
use interprocess::local_socket as ls;
|
use interprocess::local_socket as ls;
|
||||||
ls::LocalSocketStream::connect(name.as_os_str())
|
use ls::traits::Stream;
|
||||||
|
|
||||||
|
interpret_local_socket_name(name)
|
||||||
|
.and_then(|name| ls::Stream::connect(name))
|
||||||
.map_err(|err| ShellError::IOError {
|
.map_err(|err| ShellError::IOError {
|
||||||
msg: format!("failed to connect to socket: {err}"),
|
msg: format!("failed to connect to socket: {err}"),
|
||||||
})
|
})
|
||||||
.map(LocalSocketStream::from)
|
|
||||||
};
|
};
|
||||||
// Reverse order from the server: read in, write out
|
// Reverse order from the server: read in, write out
|
||||||
let read_in = get_socket()?;
|
let read_in = get_socket()?;
|
||||||
@ -133,9 +131,7 @@ pub enum PreparedServerCommunication {
|
|||||||
/// Contains the listener to accept connections on. On Unix, the socket is unlinked on `Drop`.
|
/// Contains the listener to accept connections on. On Unix, the socket is unlinked on `Drop`.
|
||||||
#[cfg(feature = "local-socket")]
|
#[cfg(feature = "local-socket")]
|
||||||
LocalSocket {
|
LocalSocket {
|
||||||
#[cfg_attr(windows, allow(dead_code))] // not used on Windows
|
listener: interprocess::local_socket::Listener,
|
||||||
name: std::ffi::OsString,
|
|
||||||
listener: LocalSocketListener,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,6 +157,9 @@ impl PreparedServerCommunication {
|
|||||||
}
|
}
|
||||||
#[cfg(feature = "local-socket")]
|
#[cfg(feature = "local-socket")]
|
||||||
PreparedServerCommunication::LocalSocket { listener, .. } => {
|
PreparedServerCommunication::LocalSocket { listener, .. } => {
|
||||||
|
use interprocess::local_socket::traits::{
|
||||||
|
Listener, ListenerNonblockingMode, Stream,
|
||||||
|
};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
const RETRY_PERIOD: Duration = Duration::from_millis(1);
|
const RETRY_PERIOD: Duration = Duration::from_millis(1);
|
||||||
@ -170,13 +169,16 @@ impl PreparedServerCommunication {
|
|||||||
|
|
||||||
// Use a loop to try to get two clients from the listener: one for read (the plugin
|
// Use a loop to try to get two clients from the listener: one for read (the plugin
|
||||||
// output) and one for write (the plugin input)
|
// output) and one for write (the plugin input)
|
||||||
listener.set_nonblocking(true)?;
|
//
|
||||||
|
// Be non-blocking on Accept only, so we can timeout.
|
||||||
|
listener.set_nonblocking(ListenerNonblockingMode::Accept)?;
|
||||||
let mut get_socket = || {
|
let mut get_socket = || {
|
||||||
let mut result = None;
|
let mut result = None;
|
||||||
while let Ok(None) = child.try_wait() {
|
while let Ok(None) = child.try_wait() {
|
||||||
match listener.accept() {
|
match listener.accept() {
|
||||||
Ok(stream) => {
|
Ok(stream) => {
|
||||||
// Success! But make sure the stream is in blocking mode.
|
// Success! Ensure the stream is in nonblocking mode though, for
|
||||||
|
// good measure. Had an issue without this on macOS.
|
||||||
stream.set_nonblocking(false)?;
|
stream.set_nonblocking(false)?;
|
||||||
result = Some(stream);
|
result = Some(stream);
|
||||||
break;
|
break;
|
||||||
@ -198,7 +200,7 @@ impl PreparedServerCommunication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(stream) = result {
|
if let Some(stream) = result {
|
||||||
Ok(LocalSocketStream(stream))
|
Ok(stream)
|
||||||
} else {
|
} else {
|
||||||
// The process may have exited
|
// The process may have exited
|
||||||
Err(ShellError::PluginFailedToLoad {
|
Err(ShellError::PluginFailedToLoad {
|
||||||
@ -215,26 +217,13 @@ impl PreparedServerCommunication {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for PreparedServerCommunication {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
match self {
|
|
||||||
#[cfg(all(unix, feature = "local-socket"))]
|
|
||||||
PreparedServerCommunication::LocalSocket { name: path, .. } => {
|
|
||||||
// Just try to remove the socket file, it's ok if this fails
|
|
||||||
let _ = std::fs::remove_file(path);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The required streams for communication from the engine side, i.e. the server in socket terms.
|
/// The required streams for communication from the engine side, i.e. the server in socket terms.
|
||||||
pub enum ServerCommunicationIo {
|
pub enum ServerCommunicationIo {
|
||||||
Stdio(ChildStdin, ChildStdout),
|
Stdio(ChildStdin, ChildStdout),
|
||||||
#[cfg(feature = "local-socket")]
|
#[cfg(feature = "local-socket")]
|
||||||
LocalSocket {
|
LocalSocket {
|
||||||
read_out: LocalSocketStream,
|
read_out: interprocess::local_socket::Stream,
|
||||||
write_in: LocalSocketStream,
|
write_in: interprocess::local_socket::Stream,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +232,7 @@ pub enum ClientCommunicationIo {
|
|||||||
Stdio(Stdin, Stdout),
|
Stdio(Stdin, Stdout),
|
||||||
#[cfg(feature = "local-socket")]
|
#[cfg(feature = "local-socket")]
|
||||||
LocalSocket {
|
LocalSocket {
|
||||||
read_in: LocalSocketStream,
|
read_in: interprocess::local_socket::Stream,
|
||||||
write_out: LocalSocketStream,
|
write_out: interprocess::local_socket::Stream,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -202,10 +202,13 @@ where
|
|||||||
if !self.ended {
|
if !self.ended {
|
||||||
self.writer
|
self.writer
|
||||||
.write_stream_message(StreamMessage::Data(self.id, data.into()))?;
|
.write_stream_message(StreamMessage::Data(self.id, data.into()))?;
|
||||||
|
// Flush after each data message to ensure they do predictably appear on the other side
|
||||||
|
// when they're generated
|
||||||
|
//
|
||||||
|
// TODO: make the buffering configurable, as this is a factor for performance
|
||||||
|
self.writer.flush()?;
|
||||||
// This implements flow control, so we don't write too many messages:
|
// This implements flow control, so we don't write too many messages:
|
||||||
if !self.signal.notify_sent()? {
|
if !self.signal.notify_sent()? {
|
||||||
// Flush the output, and then wait for acknowledgements
|
|
||||||
self.writer.flush()?;
|
|
||||||
self.signal.wait_for_drain()
|
self.signal.wait_for_drain()
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -2,8 +2,6 @@ use nu_protocol::{CustomValue, IntoSpanned, ShellError, Spanned, Value};
|
|||||||
|
|
||||||
/// Do something with all [`CustomValue`]s recursively within a `Value`. This is not limited to
|
/// Do something with all [`CustomValue`]s recursively within a `Value`. This is not limited to
|
||||||
/// plugin custom values.
|
/// plugin custom values.
|
||||||
///
|
|
||||||
/// `LazyRecord`s will be collected to plain values for completeness.
|
|
||||||
pub fn with_custom_values_in<E>(
|
pub fn with_custom_values_in<E>(
|
||||||
value: &mut Value,
|
value: &mut Value,
|
||||||
mut f: impl FnMut(Spanned<&mut Box<dyn CustomValue>>) -> Result<(), E>,
|
mut f: impl FnMut(Spanned<&mut Box<dyn CustomValue>>) -> Result<(), E>,
|
||||||
@ -18,13 +16,6 @@ where
|
|||||||
// Operate on a CustomValue.
|
// Operate on a CustomValue.
|
||||||
f(val.into_spanned(span))
|
f(val.into_spanned(span))
|
||||||
}
|
}
|
||||||
// LazyRecord would be a problem for us, since it could return something else the
|
|
||||||
// next time, and we have to collect it anyway to serialize it. Collect it in place,
|
|
||||||
// and then use the result
|
|
||||||
Value::LazyRecord { val, .. } => {
|
|
||||||
*value = val.collect()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -33,31 +24,7 @@ where
|
|||||||
#[test]
|
#[test]
|
||||||
fn find_custom_values() {
|
fn find_custom_values() {
|
||||||
use nu_plugin_protocol::test_util::test_plugin_custom_value;
|
use nu_plugin_protocol::test_util::test_plugin_custom_value;
|
||||||
use nu_protocol::{engine::Closure, record, LazyRecord, Span};
|
use nu_protocol::{engine::Closure, record};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
struct Lazy;
|
|
||||||
impl<'a> LazyRecord<'a> for Lazy {
|
|
||||||
fn column_names(&'a self) -> Vec<&'a str> {
|
|
||||||
vec!["custom", "plain"]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
|
|
||||||
Ok(match column {
|
|
||||||
"custom" => Value::test_custom_value(Box::new(test_plugin_custom_value())),
|
|
||||||
"plain" => Value::test_int(42),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn span(&self) -> Span {
|
|
||||||
Span::test_data()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clone_value(&self, span: Span) -> Value {
|
|
||||||
Value::lazy_record(Box::new(self.clone()), span)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut cv = Value::test_custom_value(Box::new(test_plugin_custom_value()));
|
let mut cv = Value::test_custom_value(Box::new(test_plugin_custom_value()));
|
||||||
|
|
||||||
@ -73,7 +40,6 @@ fn find_custom_values() {
|
|||||||
captures: vec![(0, cv.clone()), (1, Value::test_string("foo"))]
|
captures: vec![(0, cv.clone()), (1, Value::test_string("foo"))]
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
"lazy" => Value::test_lazy_record(Box::new(Lazy)),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Do with_custom_values_in, and count the number of custom values found
|
// Do with_custom_values_in, and count the number of custom values found
|
||||||
@ -83,7 +49,7 @@ fn find_custom_values() {
|
|||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.expect("error");
|
.expect("error");
|
||||||
assert_eq!(4, found, "found in value");
|
assert_eq!(3, found, "found in value");
|
||||||
|
|
||||||
// Try it on bare custom value too
|
// Try it on bare custom value too
|
||||||
found = 0;
|
found = 0;
|
||||||
|
@ -125,6 +125,7 @@ impl<'a> PluginExecutionContext for PluginExecutionCommandContext<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_current_dir(&self) -> Result<Spanned<String>, ShellError> {
|
fn get_current_dir(&self) -> Result<Spanned<String>, ShellError> {
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = nu_engine::env::current_dir_str(&self.engine_state, &self.stack)?;
|
let cwd = nu_engine::env::current_dir_str(&self.engine_state, &self.stack)?;
|
||||||
// The span is not really used, so just give it call.head
|
// The span is not really used, so just give it call.head
|
||||||
Ok(cwd.into_spanned(self.call.head))
|
Ok(cwd.into_spanned(self.call.head))
|
||||||
|
@ -24,7 +24,10 @@ use crate::{
|
|||||||
PluginSource,
|
PluginSource,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) const OUTPUT_BUFFER_SIZE: usize = 8192;
|
/// This should be larger than the largest commonly sent message to avoid excessive fragmentation.
|
||||||
|
///
|
||||||
|
/// The buffers coming from external streams are typically each 8192 bytes, so double that.
|
||||||
|
pub(crate) const OUTPUT_BUFFER_SIZE: usize = 16384;
|
||||||
|
|
||||||
/// Spawn the command for a plugin, in the given `mode`. After spawning, it can be passed to
|
/// Spawn the command for a plugin, in the given `mode`. After spawning, it can be passed to
|
||||||
/// [`make_plugin_interface()`] to get a [`PluginInterface`].
|
/// [`make_plugin_interface()`] to get a [`PluginInterface`].
|
||||||
|
@ -179,11 +179,6 @@ impl PluginCustomValue {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Collect LazyRecord before proceeding
|
|
||||||
Value::LazyRecord { ref val, .. } => {
|
|
||||||
*value = val.collect()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -205,11 +200,6 @@ impl PluginCustomValue {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Collect LazyRecord before proceeding
|
|
||||||
Value::LazyRecord { ref val, .. } => {
|
|
||||||
*value = val.collect()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -224,11 +214,6 @@ impl PluginCustomValue {
|
|||||||
*value = val.to_base_value(span)?;
|
*value = val.to_base_value(span)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// Collect LazyRecord before proceeding
|
|
||||||
Value::LazyRecord { ref val, .. } => {
|
|
||||||
*value = val.collect()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -344,9 +344,6 @@ impl PluginTest {
|
|||||||
// All equal, and same length
|
// All equal, and same length
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
// Must collect lazy records to compare.
|
|
||||||
(Value::LazyRecord { val: a_val, .. }, _) => self.value_eq(&a_val.collect()?, b),
|
|
||||||
(_, Value::LazyRecord { val: b_val, .. }) => self.value_eq(a, &b_val.collect()?),
|
|
||||||
// Fall back to regular eq.
|
// Fall back to regular eq.
|
||||||
_ => Ok(a == b),
|
_ => Ok(a == b),
|
||||||
}
|
}
|
||||||
|
@ -381,11 +381,6 @@ pub(crate) fn render_examples(
|
|||||||
plugin.custom_value_to_base_value(engine, val.into_spanned(span))?;
|
plugin.custom_value_to_base_value(engine, val.into_spanned(span))?;
|
||||||
Ok::<_, ShellError>(())
|
Ok::<_, ShellError>(())
|
||||||
}
|
}
|
||||||
// Collect LazyRecord before proceeding
|
|
||||||
Value::LazyRecord { ref val, .. } => {
|
|
||||||
*value = val.collect()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
|
@ -28,8 +28,11 @@ mod interface;
|
|||||||
pub use command::{create_plugin_signature, PluginCommand, SimplePluginCommand};
|
pub use command::{create_plugin_signature, PluginCommand, SimplePluginCommand};
|
||||||
pub use interface::{EngineInterface, EngineInterfaceManager};
|
pub use interface::{EngineInterface, EngineInterfaceManager};
|
||||||
|
|
||||||
|
/// This should be larger than the largest commonly sent message to avoid excessive fragmentation.
|
||||||
|
///
|
||||||
|
/// The buffers coming from external streams are typically each 8192 bytes, so double that.
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) const OUTPUT_BUFFER_SIZE: usize = 8192;
|
pub(crate) const OUTPUT_BUFFER_SIZE: usize = 16384;
|
||||||
|
|
||||||
/// The API for a Nushell plugin
|
/// The API for a Nushell plugin
|
||||||
///
|
///
|
||||||
|
@ -47,6 +47,7 @@ strum_macros = "0.26"
|
|||||||
nu-test-support = { path = "../nu-test-support", version = "0.93.1" }
|
nu-test-support = { path = "../nu-test-support", version = "0.93.1" }
|
||||||
pretty_assertions = { workspace = true }
|
pretty_assertions = { workspace = true }
|
||||||
rstest = { workspace = true }
|
rstest = { workspace = true }
|
||||||
|
tempfile = { workspace = true }
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
@ -36,6 +36,15 @@ impl Argument {
|
|||||||
Argument::Spread(e) => e.span,
|
Argument::Spread(e) => e.span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expr(&self) -> Option<&Expression> {
|
||||||
|
match self {
|
||||||
|
Argument::Named((_, _, expr)) => expr.as_ref(),
|
||||||
|
Argument::Positional(expr) | Argument::Unknown(expr) | Argument::Spread(expr) => {
|
||||||
|
Some(expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
|
@ -36,6 +36,7 @@ pub enum Expr {
|
|||||||
Directory(String, bool),
|
Directory(String, bool),
|
||||||
GlobPattern(String, bool),
|
GlobPattern(String, bool),
|
||||||
String(String),
|
String(String),
|
||||||
|
RawString(String),
|
||||||
CellPath(CellPath),
|
CellPath(CellPath),
|
||||||
FullCellPath(Box<FullCellPath>),
|
FullCellPath(Box<FullCellPath>),
|
||||||
ImportPattern(Box<ImportPattern>),
|
ImportPattern(Box<ImportPattern>),
|
||||||
@ -80,6 +81,7 @@ impl Expr {
|
|||||||
| Expr::ValueWithUnit(_)
|
| Expr::ValueWithUnit(_)
|
||||||
| Expr::DateTime(_)
|
| Expr::DateTime(_)
|
||||||
| Expr::String(_)
|
| Expr::String(_)
|
||||||
|
| Expr::RawString(_)
|
||||||
| Expr::CellPath(_)
|
| Expr::CellPath(_)
|
||||||
| Expr::StringInterpolation(_)
|
| Expr::StringInterpolation(_)
|
||||||
| Expr::Nothing => {
|
| Expr::Nothing => {
|
||||||
|
@ -279,6 +279,7 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
Expr::Signature(_) => false,
|
Expr::Signature(_) => false,
|
||||||
Expr::String(_) => false,
|
Expr::String(_) => false,
|
||||||
|
Expr::RawString(_) => false,
|
||||||
Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
|
Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
|
||||||
let block = working_set.get_block(*block_id);
|
let block = working_set.get_block(*block_id);
|
||||||
|
|
||||||
@ -436,6 +437,7 @@ impl Expression {
|
|||||||
}
|
}
|
||||||
Expr::Signature(_) => {}
|
Expr::Signature(_) => {}
|
||||||
Expr::String(_) => {}
|
Expr::String(_) => {}
|
||||||
|
Expr::RawString(_) => {}
|
||||||
Expr::StringInterpolation(items) => {
|
Expr::StringInterpolation(items) => {
|
||||||
for i in items {
|
for i in items {
|
||||||
i.replace_span(working_set, replaced, new_span)
|
i.replace_span(working_set, replaced, new_span)
|
||||||
|
@ -74,7 +74,14 @@ pub struct Config {
|
|||||||
pub menus: Vec<ParsedMenu>,
|
pub menus: Vec<ParsedMenu>,
|
||||||
pub hooks: Hooks,
|
pub hooks: Hooks,
|
||||||
pub rm_always_trash: bool,
|
pub rm_always_trash: bool,
|
||||||
pub shell_integration: bool,
|
// Shell integration OSC meaning is described in the default_config.nu
|
||||||
|
pub shell_integration_osc2: bool,
|
||||||
|
pub shell_integration_osc7: bool,
|
||||||
|
pub shell_integration_osc8: bool,
|
||||||
|
pub shell_integration_osc9_9: bool,
|
||||||
|
pub shell_integration_osc133: bool,
|
||||||
|
pub shell_integration_osc633: bool,
|
||||||
|
pub shell_integration_reset_application_mode: bool,
|
||||||
pub buffer_editor: Value,
|
pub buffer_editor: Value,
|
||||||
pub table_index_mode: TableIndexMode,
|
pub table_index_mode: TableIndexMode,
|
||||||
pub case_sensitive_completions: bool,
|
pub case_sensitive_completions: bool,
|
||||||
@ -154,7 +161,15 @@ impl Default for Config {
|
|||||||
use_ansi_coloring: true,
|
use_ansi_coloring: true,
|
||||||
bracketed_paste: true,
|
bracketed_paste: true,
|
||||||
edit_mode: EditBindings::default(),
|
edit_mode: EditBindings::default(),
|
||||||
shell_integration: false,
|
// shell_integration: false,
|
||||||
|
shell_integration_osc2: false,
|
||||||
|
shell_integration_osc7: false,
|
||||||
|
shell_integration_osc8: false,
|
||||||
|
shell_integration_osc9_9: false,
|
||||||
|
shell_integration_osc133: false,
|
||||||
|
shell_integration_osc633: false,
|
||||||
|
shell_integration_reset_application_mode: false,
|
||||||
|
|
||||||
render_right_prompt_on_last_line: false,
|
render_right_prompt_on_last_line: false,
|
||||||
|
|
||||||
hooks: Hooks::new(),
|
hooks: Hooks::new(),
|
||||||
@ -639,7 +654,54 @@ impl Value {
|
|||||||
&mut errors);
|
&mut errors);
|
||||||
}
|
}
|
||||||
"shell_integration" => {
|
"shell_integration" => {
|
||||||
process_bool_config(value, &mut errors, &mut config.shell_integration);
|
if let Value::Record { val, .. } = value {
|
||||||
|
val.to_mut().retain_mut(|key2, value| {
|
||||||
|
let span = value.span();
|
||||||
|
match key2 {
|
||||||
|
"osc2" => {
|
||||||
|
process_bool_config(value, &mut errors, &mut config.shell_integration_osc2);
|
||||||
|
}
|
||||||
|
"osc7" => {
|
||||||
|
process_bool_config(value, &mut errors, &mut config.shell_integration_osc7);
|
||||||
|
}
|
||||||
|
"osc8" => {
|
||||||
|
process_bool_config(value, &mut errors, &mut config.shell_integration_osc8);
|
||||||
|
}
|
||||||
|
"osc9_9" => {
|
||||||
|
process_bool_config(value, &mut errors, &mut config.shell_integration_osc9_9);
|
||||||
|
}
|
||||||
|
"osc133" => {
|
||||||
|
process_bool_config(value, &mut errors, &mut config.shell_integration_osc133);
|
||||||
|
}
|
||||||
|
"osc633" => {
|
||||||
|
process_bool_config(value, &mut errors, &mut config.shell_integration_osc633);
|
||||||
|
}
|
||||||
|
"reset_application_mode" => {
|
||||||
|
process_bool_config(value, &mut errors, &mut config.shell_integration_reset_application_mode);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
report_invalid_key(&[key, key2], span, &mut errors);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
true
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
report_invalid_value("boolean value is deprecated, should be a record. see `config nu --default`.", span, &mut errors);
|
||||||
|
// Reconstruct
|
||||||
|
*value = Value::record(
|
||||||
|
record! {
|
||||||
|
"osc2" => Value::bool(config.shell_integration_osc2, span),
|
||||||
|
"ocs7" => Value::bool(config.shell_integration_osc7, span),
|
||||||
|
"osc8" => Value::bool(config.shell_integration_osc8, span),
|
||||||
|
"osc9_9" => Value::bool(config.shell_integration_osc9_9, span),
|
||||||
|
"osc133" => Value::bool(config.shell_integration_osc133, span),
|
||||||
|
"osc633" => Value::bool(config.shell_integration_osc633, span),
|
||||||
|
"reset_application_mode" => Value::bool(config.shell_integration_reset_application_mode, span),
|
||||||
|
},
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"buffer_editor" => match value {
|
"buffer_editor" => match value {
|
||||||
Value::Nothing { .. } | Value::String { .. } => {
|
Value::Nothing { .. } | Value::String { .. } => {
|
||||||
|
@ -253,7 +253,7 @@ fn expr_to_string(engine_state: &EngineState, expr: &Expr) -> String {
|
|||||||
Expr::Record(_) => "record".to_string(),
|
Expr::Record(_) => "record".to_string(),
|
||||||
Expr::RowCondition(_) => "row condition".to_string(),
|
Expr::RowCondition(_) => "row condition".to_string(),
|
||||||
Expr::Signature(_) => "signature".to_string(),
|
Expr::Signature(_) => "signature".to_string(),
|
||||||
Expr::String(_) => "string".to_string(),
|
Expr::String(_) | Expr::RawString(_) => "string".to_string(),
|
||||||
Expr::StringInterpolation(_) => "string interpolation".to_string(),
|
Expr::StringInterpolation(_) => "string interpolation".to_string(),
|
||||||
Expr::Subexpression(_) => "subexpression".to_string(),
|
Expr::Subexpression(_) => "subexpression".to_string(),
|
||||||
Expr::Table(_) => "table".to_string(),
|
Expr::Table(_) => "table".to_string(),
|
||||||
|
@ -26,8 +26,6 @@ type PoisonDebuggerError<'a> = PoisonError<MutexGuard<'a, Box<dyn Debugger>>>;
|
|||||||
#[cfg(feature = "plugin")]
|
#[cfg(feature = "plugin")]
|
||||||
use crate::{PluginRegistryFile, PluginRegistryItem, RegisteredPlugin};
|
use crate::{PluginRegistryFile, PluginRegistryItem, RegisteredPlugin};
|
||||||
|
|
||||||
pub static PWD_ENV: &str = "PWD";
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum VirtualPath {
|
pub enum VirtualPath {
|
||||||
File(FileId),
|
File(FileId),
|
||||||
@ -893,14 +891,6 @@ impl EngineState {
|
|||||||
self.num_files() - 1
|
self.num_files() - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_cwd(&self) -> Option<String> {
|
|
||||||
if let Some(pwd_value) = self.get_env_var(PWD_ENV) {
|
|
||||||
pwd_value.coerce_string().ok()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_config_path(&mut self, key: &str, val: PathBuf) {
|
pub fn set_config_path(&mut self, key: &str, val: PathBuf) {
|
||||||
self.config_path.insert(key.to_string(), val);
|
self.config_path.insert(key.to_string(), val);
|
||||||
}
|
}
|
||||||
@ -922,12 +912,71 @@ impl EngineState {
|
|||||||
.map(|comment_spans| self.build_usage(comment_spans))
|
.map(|comment_spans| self.build_usage(comment_spans))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current working directory, which is guaranteed to be canonicalized.
|
||||||
|
///
|
||||||
|
/// Returns an empty String if $env.PWD doesn't exist.
|
||||||
|
#[deprecated(since = "0.92.3", note = "please use `EngineState::cwd()` instead")]
|
||||||
pub fn current_work_dir(&self) -> String {
|
pub fn current_work_dir(&self) -> String {
|
||||||
self.get_env_var("PWD")
|
self.cwd(None)
|
||||||
.map(|d| d.coerce_string().unwrap_or_default())
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current working directory, which is guaranteed to be an
|
||||||
|
/// absolute path without trailing slashes, but might contain symlink
|
||||||
|
/// components.
|
||||||
|
///
|
||||||
|
/// If `stack` is supplied, also considers modifications to the working
|
||||||
|
/// directory on the stack that have yet to be merged into the engine state.
|
||||||
|
pub fn cwd(&self, stack: Option<&Stack>) -> Result<PathBuf, ShellError> {
|
||||||
|
// Helper function to create a simple generic error.
|
||||||
|
// Its messages are not especially helpful, but these errors don't occur often, so it's probably fine.
|
||||||
|
fn error(msg: &str) -> Result<PathBuf, ShellError> {
|
||||||
|
Err(ShellError::GenericError {
|
||||||
|
error: msg.into(),
|
||||||
|
msg: "".into(),
|
||||||
|
span: None,
|
||||||
|
help: None,
|
||||||
|
inner: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to check if a path has trailing slashes.
|
||||||
|
fn has_trailing_slash(path: &Path) -> bool {
|
||||||
|
nu_path::components(path).last()
|
||||||
|
== Some(std::path::Component::Normal(std::ffi::OsStr::new("")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve $env.PWD from the stack or the engine state.
|
||||||
|
let pwd = if let Some(stack) = stack {
|
||||||
|
stack.get_env_var(self, "PWD")
|
||||||
|
} else {
|
||||||
|
self.get_env_var("PWD").map(ToOwned::to_owned)
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(pwd) = pwd {
|
||||||
|
if let Value::String { val, .. } = pwd {
|
||||||
|
let path = PathBuf::from(val);
|
||||||
|
|
||||||
|
if has_trailing_slash(&path) {
|
||||||
|
error("$env.PWD contains trailing slashes")
|
||||||
|
} else if !path.is_absolute() {
|
||||||
|
error("$env.PWD is not an absolute path")
|
||||||
|
} else if !path.exists() {
|
||||||
|
error("$env.PWD points to a non-existent directory")
|
||||||
|
} else if !path.is_dir() {
|
||||||
|
error("$env.PWD points to a non-directory")
|
||||||
|
} else {
|
||||||
|
Ok(path)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error("$env.PWD is not a string")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
error("$env.PWD not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: see if we can completely get rid of this
|
// TODO: see if we can completely get rid of this
|
||||||
pub fn get_file_contents(&self) -> &[CachedFile] {
|
pub fn get_file_contents(&self) -> &[CachedFile] {
|
||||||
&self.files
|
&self.files
|
||||||
@ -1077,3 +1126,213 @@ mod engine_state_tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_cwd {
|
||||||
|
//! Here're the test cases we need to cover:
|
||||||
|
//!
|
||||||
|
//! `EngineState::cwd()` computes the result from `self.env_vars["PWD"]` and
|
||||||
|
//! optionally `stack.env_vars["PWD"]`.
|
||||||
|
//!
|
||||||
|
//! PWD may be unset in either `env_vars`.
|
||||||
|
//! PWD should NOT be an empty string.
|
||||||
|
//! PWD should NOT be a non-string value.
|
||||||
|
//! PWD should NOT be a relative path.
|
||||||
|
//! PWD should NOT contain trailing slashes.
|
||||||
|
//! PWD may point to a directory or a symlink to directory.
|
||||||
|
//! PWD should NOT point to a file or a symlink to file.
|
||||||
|
//! PWD should NOT point to non-existent entities in the filesystem.
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
engine::{EngineState, Stack},
|
||||||
|
Span, Value,
|
||||||
|
};
|
||||||
|
use nu_path::assert_path_eq;
|
||||||
|
use std::path::Path;
|
||||||
|
use tempfile::{NamedTempFile, TempDir};
|
||||||
|
|
||||||
|
/// Creates a symlink. Works on both Unix and Windows.
|
||||||
|
#[cfg(any(unix, windows))]
|
||||||
|
fn symlink(original: impl AsRef<Path>, link: impl AsRef<Path>) -> std::io::Result<()> {
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
std::os::unix::fs::symlink(original, link)
|
||||||
|
}
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
if original.as_ref().is_dir() {
|
||||||
|
std::os::windows::fs::symlink_dir(original, link)
|
||||||
|
} else {
|
||||||
|
std::os::windows::fs::symlink_file(original, link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create an engine state initialized with the given PWD.
|
||||||
|
fn engine_state_with_pwd(path: impl AsRef<Path>) -> EngineState {
|
||||||
|
let mut engine_state = EngineState::new();
|
||||||
|
engine_state.add_env_var(
|
||||||
|
"PWD".into(),
|
||||||
|
Value::String {
|
||||||
|
val: path.as_ref().to_string_lossy().to_string(),
|
||||||
|
internal_span: Span::unknown(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
engine_state
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a stack initialized with the given PWD.
|
||||||
|
fn stack_with_pwd(path: impl AsRef<Path>) -> Stack {
|
||||||
|
let mut stack = Stack::new();
|
||||||
|
stack.add_env_var(
|
||||||
|
"PWD".into(),
|
||||||
|
Value::String {
|
||||||
|
val: path.as_ref().to_string_lossy().to_string(),
|
||||||
|
internal_span: Span::unknown(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
stack
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_not_set() {
|
||||||
|
let engine_state = EngineState::new();
|
||||||
|
engine_state.cwd(None).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_is_empty_string() {
|
||||||
|
let engine_state = engine_state_with_pwd("");
|
||||||
|
engine_state.cwd(None).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_is_non_string_value() {
|
||||||
|
let mut engine_state = EngineState::new();
|
||||||
|
engine_state.add_env_var(
|
||||||
|
"PWD".into(),
|
||||||
|
Value::Glob {
|
||||||
|
val: "*".into(),
|
||||||
|
no_expand: false,
|
||||||
|
internal_span: Span::unknown(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
engine_state.cwd(None).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_is_relative_path() {
|
||||||
|
let engine_state = engine_state_with_pwd("./foo");
|
||||||
|
|
||||||
|
engine_state.cwd(None).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_has_trailing_slash() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let engine_state = engine_state_with_pwd(dir.path().join(""));
|
||||||
|
|
||||||
|
engine_state.cwd(None).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_points_to_normal_file() {
|
||||||
|
let file = NamedTempFile::new().unwrap();
|
||||||
|
let engine_state = engine_state_with_pwd(file.path());
|
||||||
|
|
||||||
|
engine_state.cwd(None).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_points_to_normal_directory() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let engine_state = engine_state_with_pwd(dir.path());
|
||||||
|
|
||||||
|
let cwd = engine_state.cwd(None).unwrap();
|
||||||
|
assert_path_eq!(cwd, dir.path());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_points_to_symlink_to_file() {
|
||||||
|
let file = NamedTempFile::new().unwrap();
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let link = dir.path().join("link");
|
||||||
|
symlink(file.path(), &link).unwrap();
|
||||||
|
let engine_state = engine_state_with_pwd(&link);
|
||||||
|
|
||||||
|
engine_state.cwd(None).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_points_to_symlink_to_directory() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let link = dir.path().join("link");
|
||||||
|
symlink(dir.path(), &link).unwrap();
|
||||||
|
let engine_state = engine_state_with_pwd(&link);
|
||||||
|
|
||||||
|
let cwd = engine_state.cwd(None).unwrap();
|
||||||
|
assert_path_eq!(cwd, link);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_points_to_broken_symlink() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let link = dir.path().join("link");
|
||||||
|
symlink(TempDir::new().unwrap().path(), &link).unwrap();
|
||||||
|
let engine_state = engine_state_with_pwd(&link);
|
||||||
|
|
||||||
|
engine_state.cwd(None).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pwd_points_to_nonexistent_entity() {
|
||||||
|
let engine_state = engine_state_with_pwd(TempDir::new().unwrap().path());
|
||||||
|
|
||||||
|
engine_state.cwd(None).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_pwd_not_set() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let engine_state = engine_state_with_pwd(dir.path());
|
||||||
|
let stack = Stack::new();
|
||||||
|
|
||||||
|
let cwd = engine_state.cwd(Some(&stack)).unwrap();
|
||||||
|
assert_eq!(cwd, dir.path());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_pwd_is_empty_string() {
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let engine_state = engine_state_with_pwd(dir.path());
|
||||||
|
let stack = stack_with_pwd("");
|
||||||
|
|
||||||
|
engine_state.cwd(Some(&stack)).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_pwd_points_to_normal_directory() {
|
||||||
|
let dir1 = TempDir::new().unwrap();
|
||||||
|
let dir2 = TempDir::new().unwrap();
|
||||||
|
let engine_state = engine_state_with_pwd(dir1.path());
|
||||||
|
let stack = stack_with_pwd(dir2.path());
|
||||||
|
|
||||||
|
let cwd = engine_state.cwd(Some(&stack)).unwrap();
|
||||||
|
assert_path_eq!(cwd, dir2.path());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn stack_pwd_points_to_normal_directory_with_symlink_components() {
|
||||||
|
// `/tmp/dir/link` points to `/tmp/dir`, then we set PWD to `/tmp/dir/link/foo`
|
||||||
|
let dir = TempDir::new().unwrap();
|
||||||
|
let link = dir.path().join("link");
|
||||||
|
symlink(dir.path(), &link).unwrap();
|
||||||
|
let foo = link.join("foo");
|
||||||
|
std::fs::create_dir(dir.path().join("foo")).unwrap();
|
||||||
|
let engine_state = EngineState::new();
|
||||||
|
let stack = stack_with_pwd(&foo);
|
||||||
|
|
||||||
|
let cwd = engine_state.cwd(Some(&stack)).unwrap();
|
||||||
|
assert_path_eq!(cwd, foo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,7 +2,7 @@ use crate::{
|
|||||||
ast::Block,
|
ast::Block,
|
||||||
engine::{
|
engine::{
|
||||||
usage::build_usage, CachedFile, Command, CommandType, EngineState, OverlayFrame,
|
usage::build_usage, CachedFile, Command, CommandType, EngineState, OverlayFrame,
|
||||||
StateDelta, Variable, VirtualPath, Visibility, PWD_ENV,
|
StateDelta, Variable, VirtualPath, Visibility,
|
||||||
},
|
},
|
||||||
BlockId, Category, Config, DeclId, FileId, Module, ModuleId, ParseError, ParseWarning, Span,
|
BlockId, Category, Config, DeclId, FileId, Module, ModuleId, ParseError, ParseWarning, Span,
|
||||||
Type, Value, VarId, VirtualPathId,
|
Type, Value, VarId, VirtualPathId,
|
||||||
@ -601,13 +601,16 @@ impl<'a> StateWorkingSet<'a> {
|
|||||||
next_id
|
next_id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current working directory as a String, which is guaranteed to be canonicalized.
|
||||||
|
/// Returns an empty string if $env.PWD doesn't exist, is not a String, or is not an absolute path.
|
||||||
|
///
|
||||||
|
/// It does NOT consider modifications to the working directory made on a stack.
|
||||||
|
#[deprecated(since = "0.92.3", note = "please use `EngineState::cwd()` instead")]
|
||||||
pub fn get_cwd(&self) -> String {
|
pub fn get_cwd(&self) -> String {
|
||||||
let pwd = self
|
self.permanent_state
|
||||||
.permanent_state
|
.cwd(None)
|
||||||
.get_env_var(PWD_ENV)
|
.map(|path| path.to_string_lossy().to_string())
|
||||||
.expect("internal error: can't find PWD");
|
.unwrap_or_default()
|
||||||
pwd.coerce_string()
|
|
||||||
.expect("internal error: PWD not a string")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_env_var(&self, name: &str) -> Option<&Value> {
|
pub fn get_env_var(&self, name: &str) -> Option<&Value> {
|
||||||
@ -622,16 +625,6 @@ impl<'a> StateWorkingSet<'a> {
|
|||||||
&self.permanent_state.config
|
&self.permanent_state.config
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_env(&self) -> Vec<String> {
|
|
||||||
let mut env_vars = vec![];
|
|
||||||
|
|
||||||
for env_var in self.permanent_state.env_vars.iter() {
|
|
||||||
env_vars.push(env_var.0.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
env_vars
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_variable_type(&mut self, var_id: VarId, ty: Type) {
|
pub fn set_variable_type(&mut self, var_id: VarId, ty: Type) {
|
||||||
let num_permanent_vars = self.permanent_state.num_vars();
|
let num_permanent_vars = self.permanent_state.num_vars();
|
||||||
if var_id < num_permanent_vars {
|
if var_id < num_permanent_vars {
|
||||||
|
@ -1178,16 +1178,6 @@ pub enum ShellError {
|
|||||||
span: Option<Span>,
|
span: Option<Span>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// An attempt to access a record column failed.
|
|
||||||
#[error("Access failure: {message}")]
|
|
||||||
#[diagnostic(code(nu::shell::lazy_record_access_failed))]
|
|
||||||
LazyRecordAccessFailed {
|
|
||||||
message: String,
|
|
||||||
column_name: String,
|
|
||||||
#[label("Could not access '{column_name}' on this record")]
|
|
||||||
span: Span,
|
|
||||||
},
|
|
||||||
|
|
||||||
/// Operation interrupted by user
|
/// Operation interrupted by user
|
||||||
#[error("Operation interrupted by user")]
|
#[error("Operation interrupted by user")]
|
||||||
InterruptedByUser {
|
InterruptedByUser {
|
||||||
|
@ -139,7 +139,7 @@ pub trait Eval {
|
|||||||
Ok(Value::list(output_rows, expr.span))
|
Ok(Value::list(output_rows, expr.span))
|
||||||
}
|
}
|
||||||
Expr::Keyword(kw) => Self::eval::<D>(state, mut_state, &kw.expr),
|
Expr::Keyword(kw) => Self::eval::<D>(state, mut_state, &kw.expr),
|
||||||
Expr::String(s) => Ok(Value::string(s.clone(), expr.span)),
|
Expr::String(s) | Expr::RawString(s) => Ok(Value::string(s.clone(), expr.span)),
|
||||||
Expr::Nothing => Ok(Value::nothing(expr.span)),
|
Expr::Nothing => Ok(Value::nothing(expr.span)),
|
||||||
Expr::ValueWithUnit(value) => match Self::eval::<D>(state, mut_state, &value.expr)? {
|
Expr::ValueWithUnit(value) => match Self::eval::<D>(state, mut_state, &value.expr)? {
|
||||||
Value::Int { val, .. } => value.unit.item.build_value(val, value.unit.span),
|
Value::Int { val, .. } => value.unit.item.build_value(val, value.unit.span),
|
||||||
|
@ -14,6 +14,7 @@ use std::{
|
|||||||
/// Create a Value for `$nu`.
|
/// Create a Value for `$nu`.
|
||||||
pub fn create_nu_constant(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
|
pub fn create_nu_constant(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
|
||||||
fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf {
|
fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf {
|
||||||
|
#[allow(deprecated)]
|
||||||
let cwd = engine_state.current_work_dir();
|
let cwd = engine_state.current_work_dir();
|
||||||
|
|
||||||
if path.exists() {
|
if path.exists() {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user