Merge branch 'main' into ecow-record

This commit is contained in:
Ian Manske 2024-05-03 18:49:34 -04:00
commit e67221edec
143 changed files with 1787 additions and 2065 deletions

View File

@ -39,7 +39,7 @@ jobs:
uses: hustcer/setup-nu@v3.10
if: github.repository == 'nushell/nightly'
with:
version: 0.91.0
version: 0.93.0
# Synchronize the main branch of nightly repo with the main branch of Nushell official repo
- name: Prepare for Nightly Release
@ -141,7 +141,7 @@ jobs:
- name: Setup Nushell
uses: hustcer/setup-nu@v3.10
with:
version: 0.91.0
version: 0.93.0
- name: Release Nu Binary
id: nu
@ -253,7 +253,7 @@ jobs:
- name: Setup Nushell
uses: hustcer/setup-nu@v3.10
with:
version: 0.91.0
version: 0.93.0
- name: Release Nu Binary
id: nu
@ -317,7 +317,7 @@ jobs:
- name: Setup Nushell
uses: hustcer/setup-nu@v3.10
with:
version: 0.91.0
version: 0.93.0
# Keep the last a few releases
- name: Delete Older Releases

View File

@ -89,7 +89,7 @@ jobs:
- name: Setup Nushell
uses: hustcer/setup-nu@v3.10
with:
version: 0.91.0
version: 0.93.0
- name: Release Nu Binary
id: nu
@ -179,7 +179,7 @@ jobs:
- name: Setup Nushell
uses: hustcer/setup-nu@v3.10
with:
version: 0.91.0
version: 0.93.0
- name: Release Nu Binary
id: nu

182
Cargo.lock generated
View File

@ -240,30 +240,6 @@ dependencies = [
"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]]
name = "async-stream"
version = "0.3.5"
@ -286,12 +262,6 @@ dependencies = [
"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]]
name = "async-trait"
version = "0.1.79"
@ -318,12 +288,6 @@ version = "0.15.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ae037714f313c1353189ead58ef9eec30a8e8dc101b2622d461418fd59e28a9"
[[package]]
name = "atomic-waker"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
[[package]]
name = "autocfg"
version = "1.2.0"
@ -472,22 +436,6 @@ dependencies = [
"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]]
name = "borsh"
version = "1.4.0"
@ -905,15 +853,6 @@ dependencies = [
"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]]
name = "condtype"
version = "1.3.0"
@ -1446,48 +1385,6 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "fallible-iterator"
version = "0.3.0"
@ -1704,16 +1601,6 @@ version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "futures-macro"
version = "0.3.30"
@ -2135,30 +2022,16 @@ dependencies = [
[[package]]
name = "interprocess"
version = "1.2.1"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81f2533f3be42fffe3b5e63b71aeca416c1c3bc33e4e27be018521e76b1f38fb"
checksum = "6d5f0e3c218e7a86a6712fd3adc84672304f9e839402b866685b9117a077c37f"
dependencies = [
"blocking",
"cfg-if",
"futures-core",
"futures-io",
"intmap",
"libc",
"once_cell",
"rustc_version",
"spinning",
"thiserror",
"to_method",
"winapi",
"recvmsg",
"widestring",
"windows-sys 0.52.0",
]
[[package]]
name = "intmap"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9"
[[package]]
name = "inventory"
version = "0.3.15"
@ -3364,6 +3237,7 @@ dependencies = [
"serde_json",
"strum",
"strum_macros 0.26.2",
"tempfile",
"thiserror",
"typetag",
]
@ -3840,12 +3714,6 @@ dependencies = [
"unicode-width",
]
[[package]]
name = "parking"
version = "2.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
[[package]]
name = "parking_lot"
version = "0.12.1"
@ -4069,17 +3937,6 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "pkg-config"
version = "0.3.30"
@ -4875,6 +4732,12 @@ dependencies = [
"syn 2.0.58",
]
[[package]]
name = "recvmsg"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175"
[[package]]
name = "redox_syscall"
version = "0.4.1"
@ -5615,15 +5478,6 @@ dependencies = [
"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]]
name = "sqlparser"
version = "0.39.0"
@ -6061,12 +5915,6 @@ dependencies = [
"regex",
]
[[package]]
name = "to_method"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7c4ceeeca15c8384bbc3e011dbd8fccb7f068a440b752b7d9b32ceb0ca0e2e8"
[[package]]
name = "tokio"
version = "1.37.0"
@ -6772,6 +6620,12 @@ dependencies = [
"winsafe",
]
[[package]]
name = "widestring"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
[[package]]
name = "wild"
version = "2.2.1"

View File

@ -94,7 +94,7 @@ heck = "0.5.0"
human-date-parser = "0.1.1"
indexmap = "2.2"
indicatif = "0.17"
interprocess = "1.2.1"
interprocess = "2.0.0"
is_executable = "1.0"
itertools = "0.12"
libc = "0.2"

View File

@ -21,6 +21,7 @@ fn load_bench_commands() -> EngineState {
}
fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf {
#[allow(deprecated)]
let cwd = engine_state.current_work_dir();
if path.exists() {

View File

@ -43,6 +43,7 @@ impl Completer for DirectoryCompletion {
let AdjustView { prefix, span, .. } = adjust_if_intermediate(&prefix, working_set, span);
// Filter only the folders
#[allow(deprecated)]
let output: Vec<_> = directory_completion(
span,
&prefix,

View File

@ -84,6 +84,7 @@ impl Completer for DotNuCompletion {
partial = base_dir_partial;
} else {
// Fetch the current folder
#[allow(deprecated)]
let current_folder = self.engine_state.current_work_dir();
is_current_folder = true;

View File

@ -47,6 +47,7 @@ impl Completer for FileCompletion {
readjusted,
} = adjust_if_intermediate(&prefix, working_set, span);
#[allow(deprecated)]
let output: Vec<_> = complete_item(
readjusted,
span,

View File

@ -267,24 +267,6 @@ fn nested_suggestions(
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, .. } => {
for column_name in get_columns(vals.as_slice()) {
output.push(SemanticSuggestion {
@ -321,17 +303,6 @@ fn recursive_value(val: &Value, sublevels: &[Vec<u8>]) -> Result<Value, 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, .. } => {
for col in get_columns(vals.as_slice()) {
if col.as_bytes() == *sublevel {

View File

@ -177,6 +177,7 @@ pub fn add_plugin_file(
use std::path::Path;
let working_set = StateWorkingSet::new(engine_state);
#[allow(deprecated)]
let cwd = working_set.get_cwd();
if let Some(plugin_file) = plugin_file {
@ -235,6 +236,7 @@ pub fn eval_config_contents(
engine_state.file = prev_file;
// Merge the environment in case env vars changed in the config
#[allow(deprecated)]
match nu_engine::env::current_dir(engine_state, stack) {
Ok(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();
#[allow(deprecated)]
let cwd = engine_state.current_work_dir();
let Some(config_dir) = nu_path::config_dir().and_then(|mut dir| {

View File

@ -1,6 +1,7 @@
use crate::util::eval_source;
use log::{info, trace};
use miette::{IntoDiagnostic, Result};
#[allow(deprecated)]
use nu_engine::{convert_env_values, current_dir, eval_block};
use nu_parser::parse;
use nu_path::canonicalize_with;
@ -29,6 +30,7 @@ pub fn evaluate_file(
std::process::exit(1);
}
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let file_path = canonicalize_with(&path, cwd).unwrap_or_else(|e| {

View File

@ -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)]
use nu_utils::enable_vt_processing;
use reedline::{
@ -10,7 +16,8 @@ use std::borrow::Cow;
/// Nushell prompt definition
#[derive(Clone)]
pub struct NushellPrompt {
shell_integration: bool,
shell_integration_osc133: bool,
shell_integration_osc633: bool,
left_prompt_string: Option<String>,
right_prompt_string: Option<String>,
default_prompt_indicator: Option<String>,
@ -18,12 +25,20 @@ pub struct NushellPrompt {
default_vi_normal_prompt_indicator: Option<String>,
default_multiline_indicator: Option<String>,
render_right_prompt_on_last_line: bool,
engine_state: EngineState,
stack: Stack,
}
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 {
shell_integration,
shell_integration_osc133,
shell_integration_osc633,
left_prompt_string: None,
right_prompt_string: None,
default_prompt_indicator: None,
@ -31,6 +46,8 @@ impl NushellPrompt {
default_vi_normal_prompt_indicator: None,
default_multiline_indicator: None,
render_right_prompt_on_last_line: false,
engine_state,
stack,
}
}
@ -106,7 +123,17 @@ impl Prompt for NushellPrompt {
.to_string()
.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()
} else {
prompt.into()

View File

@ -23,10 +23,37 @@ pub(crate) const TRANSIENT_PROMPT_INDICATOR_VI_NORMAL: &str =
"TRANSIENT_PROMPT_INDICATOR_VI_NORMAL";
pub(crate) const TRANSIENT_PROMPT_MULTILINE_INDICATOR: &str =
"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:
// <133 A><prompt><133 B><command><133 C><command output>
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 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(
prompt: &str,
@ -85,16 +112,46 @@ pub(crate) fn update_prompt(
// Now that we have the prompt string lets ansify it.
// <133 A><prompt><133 B><command><133 C><command output>
let left_prompt_string = if config.shell_integration {
if let Some(prompt_string) = left_prompt_string {
let left_prompt_string_133 = if config.shell_integration_osc133 {
if let Some(prompt_string) = left_prompt_string.clone() {
Some(format!(
"{PRE_PROMPT_MARKER}{prompt_string}{POST_PROMPT_MARKER}"
))
} else {
left_prompt_string
left_prompt_string.clone()
}
} 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);

View File

@ -1,6 +1,7 @@
use crate::{menus::NuMenuCompleter, NuHelpCompleter};
use crossterm::event::{KeyCode, KeyModifiers};
use log::trace;
use nu_ansi_term::Style;
use nu_color_config::{color_record_to_nustyle, lookup_ansi_color_style};
use nu_engine::eval_block;
use nu_parser::parse;
@ -158,21 +159,14 @@ fn add_menu(
}
}
macro_rules! add_style {
// first arm match add!(1,2), add!(2,3) etc
($name:expr, $record: expr, $span:expr, $config: expr, $menu:expr, $f:expr) => {
$menu = match extract_value($name, $record, $span) {
Ok(text) => {
let style = match text {
Value::String { val, .. } => lookup_ansi_color_style(&val),
Value::Record { .. } => color_record_to_nustyle(&text),
_ => lookup_ansi_color_style("green"),
};
$f($menu, style)
}
Err(_) => $menu,
};
};
fn get_style(record: &Record, name: &str, span: Span) -> Option<Style> {
extract_value(name, record, span)
.ok()
.map(|text| match text {
Value::String { val, .. } => lookup_ansi_color_style(val),
Value::Record { .. } => color_record_to_nustyle(text),
_ => lookup_ansi_color_style("green"),
})
}
// Adds a columnar menu to the editor engine
@ -215,46 +209,21 @@ pub(crate) fn add_columnar_menu(
let span = menu.style.span();
if let Value::Record { val, .. } = &menu.style {
add_style!(
"text",
val,
span,
config,
columnar_menu,
ColumnarMenu::with_text_style
);
add_style!(
"selected_text",
val,
span,
config,
columnar_menu,
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
);
if let Some(style) = get_style(val, "text", span) {
columnar_menu = columnar_menu.with_text_style(style);
}
if let Some(style) = get_style(val, "selected_text", span) {
columnar_menu = columnar_menu.with_selected_text_style(style);
}
if let Some(style) = get_style(val, "description_text", span) {
columnar_menu = columnar_menu.with_description_text_style(style);
}
if let Some(style) = get_style(val, "match_text", span) {
columnar_menu = columnar_menu.with_match_text_style(style);
}
if let Some(style) = get_style(val, "selected_match_text", span) {
columnar_menu = columnar_menu.with_selected_match_text_style(style);
}
}
let marker = menu.marker.to_expanded_string("", config);
@ -313,30 +282,15 @@ pub(crate) fn add_list_menu(
let span = menu.style.span();
if let Value::Record { val, .. } = &menu.style {
add_style!(
"text",
val,
span,
config,
list_menu,
ListMenu::with_text_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
);
if let Some(style) = get_style(val, "text", span) {
list_menu = list_menu.with_text_style(style);
}
if let Some(style) = get_style(val, "selected_text", span) {
list_menu = list_menu.with_selected_text_style(style);
}
if let Some(style) = get_style(val, "description_text", span) {
list_menu = list_menu.with_description_text_style(style);
}
}
let marker = menu.marker.to_expanded_string("", config);
@ -520,46 +474,21 @@ pub(crate) fn add_ide_menu(
let span = menu.style.span();
if let Value::Record { val, .. } = &menu.style {
add_style!(
"text",
val,
span,
config,
ide_menu,
IdeMenu::with_text_style
);
add_style!(
"selected_text",
val,
span,
config,
ide_menu,
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
);
if let Some(style) = get_style(val, "text", span) {
ide_menu = ide_menu.with_text_style(style);
}
if let Some(style) = get_style(val, "selected_text", span) {
ide_menu = ide_menu.with_selected_text_style(style);
}
if let Some(style) = get_style(val, "description_text", span) {
ide_menu = ide_menu.with_description_text_style(style);
}
if let Some(style) = get_style(val, "match_text", span) {
ide_menu = ide_menu.with_match_text_style(style);
}
if let Some(style) = get_style(val, "selected_match_text", span) {
ide_menu = ide_menu.with_selected_match_text_style(style);
}
}
let marker = menu.marker.to_expanded_string("", config);
@ -650,30 +579,15 @@ pub(crate) fn add_description_menu(
let span = menu.style.span();
if let Value::Record { val, .. } = &menu.style {
add_style!(
"text",
val,
span,
config,
description_menu,
DescriptionMenu::with_text_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
);
if let Some(style) = get_style(val, "text", span) {
description_menu = description_menu.with_text_style(style);
}
if let Some(style) = get_style(val, "selected_text", span) {
description_menu = description_menu.with_selected_text_style(style);
}
if let Some(style) = get_style(val, "description_text", span) {
description_menu = description_menu.with_description_text_style(style);
}
}
let marker = menu.marker.to_expanded_string("", config);

View File

@ -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::{
completions::NuCompleter,
nu_highlight::NoOpHighlighter,
@ -14,7 +20,8 @@ use nu_cmd_base::{
util::{get_editor, get_guaranteed_cwd},
};
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_protocol::{
config::NuCursorShape,
@ -42,16 +49,6 @@ use std::{
};
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.
pub fn evaluate_repl(
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
// 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
let mut unique_stack = stack;
let mut unique_stack = stack.clone();
let config = engine_state.get_config();
let use_color = config.use_ansi_coloring;
@ -74,8 +71,19 @@ pub fn evaluate_repl(
let mut entry_num = 0;
let shell_integration = config.shell_integration;
let nu_prompt = NushellPrompt::new(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 nu_prompt = NushellPrompt::new(
shell_integration_osc133,
shell_integration_osc633,
engine_state.clone(),
stack.clone(),
);
let start_time = std::time::Instant::now();
// Translate environment variables from Strings to Values
@ -116,8 +124,22 @@ pub fn evaluate_repl(
}
let hostname = System::host_name();
if shell_integration {
shell_integration_osc_7_633_2(hostname.as_deref(), engine_state, &mut unique_stack);
if shell_integration_osc2 {
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);
@ -513,7 +535,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
.with_highlighter(Box::<NoOpHighlighter>::default())
// CLEAR STACK-REFERENCE 2
.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);
@ -575,10 +604,40 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
repl.buffer = line_editor.current_buffer_contents().to_string();
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();
run_ansi_sequence(PRE_EXECUTE_MARKER);
run_ansi_sequence(PRE_EXECUTION_MARKER);
perf(
"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 } => {
do_auto_cd(target, cwd, &mut stack, engine_state, span);
if shell_integration {
start_time = Instant::now();
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
perf(
"post_execute_marker (133;D) ansi escape sequences",
start_time,
file!(),
line!(),
column!(),
use_color,
);
}
run_finaliziation_ansi_sequence(
&stack,
engine_state,
shell_integration_osc633,
shell_integration_osc133,
use_color,
);
}
ReplOperation::RunCommand(cmd) => {
line_editor = do_run_cmd(
@ -619,25 +671,18 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
&mut stack,
engine_state,
line_editor,
shell_integration,
shell_integration_osc2,
*entry_num,
use_color,
);
if shell_integration {
start_time = Instant::now();
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
perf(
"post_execute_marker (133;D) ansi escape sequences",
start_time,
file!(),
line!(),
column!(),
use_color,
);
}
run_finaliziation_ansi_sequence(
&stack,
engine_state,
shell_integration_osc633,
shell_integration_osc133,
use_color,
);
}
// as the name implies, we do nothing in this case
ReplOperation::DoNothing => {}
@ -663,56 +708,45 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
}
}
if shell_integration {
start_time = Instant::now();
shell_integration_osc_7_633_2(hostname, engine_state, &mut stack);
perf(
"shell_integration_finalize ansi escape sequences",
start_time,
file!(),
line!(),
column!(),
use_color,
);
if shell_integration_osc2 {
run_shell_integration_osc2(None, engine_state, &mut stack, use_color);
}
if shell_integration_osc7 {
run_shell_integration_osc7(hostname, engine_state, &mut stack, use_color);
}
if shell_integration_osc9_9 {
run_shell_integration_osc9_9(engine_state, &mut stack, use_color);
}
if shell_integration_osc633 {
run_shell_integration_osc633(engine_state, &mut stack, use_color);
}
if shell_integration_reset_application_mode {
run_shell_integration_reset_application_mode();
}
flush_engine_state_repl_buffer(engine_state, &mut line_editor);
}
Ok(Signal::CtrlC) => {
// `Reedline` clears the line content. New prompt is shown
if shell_integration {
start_time = Instant::now();
run_ansi_sequence(&get_command_finished_marker(&stack, engine_state));
perf(
"command_finished_marker ansi escape sequence",
start_time,
file!(),
line!(),
column!(),
use_color,
);
}
run_finaliziation_ansi_sequence(
&stack,
engine_state,
shell_integration_osc633,
shell_integration_osc133,
use_color,
);
}
Ok(Signal::CtrlD) => {
// 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!();
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
// 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));
perf(
"command_finished_marker ansi escape sequence",
start_time,
file!(),
line!(),
column!(),
use_color,
);
}
run_finaliziation_ansi_sequence(
&stack,
engine_state,
shell_integration_osc633,
shell_integration_osc133,
use_color,
);
}
}
perf(
@ -772,6 +800,7 @@ fn prepare_history_metadata(
line_editor: &mut Reedline,
) {
if !s.is_empty() && line_editor.has_last_command_context() {
#[allow(deprecated)]
let result = line_editor
.update_last_command_context(&|mut c| {
c.start_timestamp = Some(chrono::Utc::now());
@ -842,6 +871,7 @@ fn parse_operation(
) -> Result<ReplOperation, ErrReport> {
let tokens = lex(s.as_bytes(), 0, &[], &[], false);
// 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 mut orig = s.clone();
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()
};
@ -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
// (in the normal case we don't want to drop it so return it as-is otherwise)
line_editor: Reedline,
shell_integration: bool,
shell_integration_osc2: bool,
entry_num: usize,
use_color: bool,
) -> Reedline {
@ -973,39 +1001,8 @@ fn do_run_cmd(
}
}
if shell_integration {
let start_time = Instant::now();
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,
);
if shell_integration_osc2 {
run_shell_integration_osc2(Some(s), engine_state, stack, use_color);
}
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
/// 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>,
engine_state: &EngineState,
stack: &mut Stack,
use_color: bool,
) {
if let Some(cwd) = stack.get_env_var(engine_state, "PWD") {
match cwd.coerce_into_string() {
Ok(path) => {
// 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)
));
}
#[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
};
// 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)
));
// 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;{maybe_abbrev_path}\x07"));
}
Err(e) => {
warn!("Could not coerce working directory to string {e}");
}
perf(
"communicate path to terminal with osc7",
start_time,
file!(),
line!(),
column!(),
use_color,
);
}
}
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);
}
@ -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
.get_env_var(engine_state, "LAST_EXIT_CODE")
.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) {
@ -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'
#[cfg(windows)]
static DRIVE_PATH_REGEX: once_cell::sync::Lazy<fancy_regex::Regex> =

View File

@ -4,7 +4,7 @@ use nu_color_config::{get_matching_brackets_style, get_shape_color};
use nu_engine::env;
use nu_parser::{flatten_block, parse, FlatShape};
use nu_protocol::{
ast::{Argument, Block, Expr, Expression, PipelineRedirection, RecordItem},
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
engine::{EngineState, Stack, StateWorkingSet},
Config, Span,
};
@ -37,6 +37,7 @@ impl Highlighter for NuHighlighter {
let str_word = String::from_utf8_lossy(str_contents).to_string();
let paths = env::path_str(&self.engine_state, &self.stack, *span).ok();
#[allow(deprecated)]
let res = if let Ok(cwd) =
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)]
.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| {
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::Signature => 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::DateTime => add_colored_token(&shape.1, next_token),
FlatShape::List => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Table => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Record => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Block => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
}
FlatShape::Closure => {
add_colored_token_with_bracket_highlight!(shape.1, shape.0, next_token)
FlatShape::List
| FlatShape::Table
| FlatShape::Record
| FlatShape::Block
| FlatShape::Closure => {
let span = shape.0;
let shape = &shape.1;
let spans = split_span_by_highlight_positions(
line,
span,
&matching_brackets_pos,
global_span_offset,
);
for (part, highlight) in spans {
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),
@ -310,20 +299,6 @@ fn find_matching_block_end_in_expr(
global_span_offset: usize,
global_cursor_offset: usize,
) -> Option<usize> {
macro_rules! find_in_expr_or_continue {
($inner_expr:ident) => {
if let Some(pos) = find_matching_block_end_in_expr(
line,
working_set,
$inner_expr,
global_span_offset,
global_cursor_offset,
) {
return Some(pos);
}
};
}
if expression.span.contains(global_cursor_offset) && expression.span.start >= global_span_offset
{
let expr_first = expression.span.start;
@ -353,6 +328,7 @@ fn find_matching_block_end_in_expr(
Expr::Directory(_, _) => None,
Expr::GlobPattern(_, _) => None,
Expr::String(_) => None,
Expr::RawString(_) => None,
Expr::CellPath(_) => None,
Expr::ImportPattern(_) => None,
Expr::Overlay(_) => None,
@ -370,15 +346,19 @@ fn find_matching_block_end_in_expr(
Some(expr_last)
} else {
// cursor is inside table
for inner_expr in table.columns.as_ref() {
find_in_expr_or_continue!(inner_expr);
}
for row in table.rows.as_ref() {
for inner_expr in row.as_ref() {
find_in_expr_or_continue!(inner_expr);
}
}
None
table
.columns
.iter()
.chain(table.rows.iter().flat_map(AsRef::as_ref))
.find_map(|expr| {
find_matching_block_end_in_expr(
line,
working_set,
expr,
global_span_offset,
global_cursor_offset,
)
})
}
}
@ -391,36 +371,45 @@ fn find_matching_block_end_in_expr(
Some(expr_last)
} else {
// cursor is inside record
for expr in exprs {
match expr {
RecordItem::Pair(k, v) => {
find_in_expr_or_continue!(k);
find_in_expr_or_continue!(v);
}
RecordItem::Spread(_, record) => {
find_in_expr_or_continue!(record);
}
}
}
None
exprs.iter().find_map(|expr| match expr {
RecordItem::Pair(k, v) => find_matching_block_end_in_expr(
line,
working_set,
k,
global_span_offset,
global_cursor_offset,
)
.or_else(|| {
find_matching_block_end_in_expr(
line,
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) => {
for arg in &call.arguments {
let opt_expr = match arg {
Argument::Named((_, _, opt_expr)) => opt_expr.as_ref(),
Argument::Positional(inner_expr) => Some(inner_expr),
Argument::Unknown(inner_expr) => Some(inner_expr),
Argument::Spread(inner_expr) => Some(inner_expr),
};
if let Some(inner_expr) = opt_expr {
find_in_expr_or_continue!(inner_expr);
}
}
None
}
Expr::Call(call) => call.arguments.iter().find_map(|arg| {
arg.expr().and_then(|expr| {
find_matching_block_end_in_expr(
line,
working_set,
expr,
global_span_offset,
global_cursor_offset,
)
})
}),
Expr::FullCellPath(b) => find_matching_block_end_in_expr(
line,
@ -430,12 +419,15 @@ fn find_matching_block_end_in_expr(
global_cursor_offset,
),
Expr::BinaryOp(lhs, op, rhs) => {
find_in_expr_or_continue!(lhs);
find_in_expr_or_continue!(op);
find_in_expr_or_continue!(rhs);
None
}
Expr::BinaryOp(lhs, op, rhs) => [lhs, op, rhs].into_iter().find_map(|expr| {
find_matching_block_end_in_expr(
line,
working_set,
expr,
global_span_offset,
global_cursor_offset,
)
}),
Expr::Block(block_id)
| Expr::Closure(block_id)
@ -460,12 +452,15 @@ fn find_matching_block_end_in_expr(
}
}
Expr::StringInterpolation(inner_expr) => {
for inner_expr in inner_expr {
find_in_expr_or_continue!(inner_expr);
}
None
}
Expr::StringInterpolation(exprs) => exprs.iter().find_map(|expr| {
find_matching_block_end_in_expr(
line,
working_set,
expr,
global_span_offset,
global_cursor_offset,
)
}),
Expr::List(list) => {
if expr_last == global_cursor_offset {
@ -475,12 +470,15 @@ fn find_matching_block_end_in_expr(
// cursor is at list start
Some(expr_last)
} else {
// cursor is inside list
for item in list {
let expr = item.expr();
find_in_expr_or_continue!(expr);
}
None
list.iter().find_map(|item| {
find_matching_block_end_in_expr(
line,
working_set,
item.expr(),
global_span_offset,
global_cursor_offset,
)
})
}
}
};

View File

@ -6,7 +6,7 @@ use nu_parser::parse;
use nu_protocol::{debugger::WithoutDebug, engine::StateWorkingSet, PipelineData};
use reedline::{Completer, Suggestion};
use rstest::{fixture, rstest};
use std::path::PathBuf;
use std::path::{PathBuf, MAIN_SEPARATOR};
use support::{
completions_helpers::{new_partial_engine, new_quote_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);
// 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());
// Create the expected values
@ -664,7 +664,7 @@ fn folder_with_directorycompletions() {
let mut completer = NuCompleter::new(std::sync::Arc::new(engine), stack);
// 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());
// Create the expected values

View File

@ -8,9 +8,7 @@ use nu_protocol::{
};
use nu_test_support::fs;
use reedline::Suggestion;
use std::path::PathBuf;
const SEP: char = std::path::MAIN_SEPARATOR;
use std::path::{PathBuf, MAIN_SEPARATOR};
fn create_default_context() -> EngineState {
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) {
// Target folder inside assets
let dir = fs::fixtures().join("completions");
let mut dir_str = dir
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
dir_str.push(SEP);
// Create a new engine with 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) {
// Target folder inside assets
let dir = fs::fixtures().join("quoted_completions");
let mut dir_str = dir
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
dir_str.push(SEP);
// Create a new engine with 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) {
// Target folder inside assets
let dir = fs::fixtures().join("partial_completions");
let mut dir_str = dir
let dir_str = dir
.clone()
.into_os_string()
.into_string()
.unwrap_or_default();
dir_str.push(SEP);
// Create a new engine with 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
pub fn folder(path: PathBuf) -> String {
let mut converted_path = file(path);
converted_path.push(SEP);
converted_path.push(MAIN_SEPARATOR);
converted_path
}

View File

@ -13,6 +13,7 @@ pub fn get_init_cwd() -> 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| {
let working_set = StateWorkingSet::new(engine_state);
report_error(&working_set, &e);

View File

@ -26,7 +26,6 @@ impl Command for Describe {
"show detailed information about the value",
Some('d'),
)
.switch("collect-lazyrecords", "collect lazy records", Some('l'))
.category(Category::Core)
}
@ -44,21 +43,7 @@ impl Command for Describe {
let options = Options {
no_collect: call.has_flag(engine_state, stack, "no-collect")?,
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)
}
@ -71,7 +56,6 @@ impl Command for Describe {
let options = Options {
no_collect: call.has_flag_const(working_set, "no-collect")?,
detailed: call.has_flag_const(working_set, "detailed")?,
collect_lazyrecords: call.has_flag_const(working_set, "collect-lazyrecords")?,
};
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",
result: Some(Value::test_record(record!(
"type" => Value::test_string("record"),
"lazy" => Value::test_bool(false),
"columns" => Value::test_record(record!(
"shell" => Value::test_string("string"),
"uwu" => Value::test_string("bool"),
"features" => Value::test_record(record!(
"type" => Value::test_string("record"),
"lazy" => Value::test_bool(false),
"columns" => Value::test_record(record!(
"bugs" => Value::test_string("bool"),
"multiplatform" => Value::test_string("bool"),
@ -168,7 +150,6 @@ impl Command for Describe {
struct Options {
no_collect: bool,
detailed: bool,
collect_lazyrecords: bool,
}
fn run(
@ -243,7 +224,7 @@ fn run(
if options.no_collect {
Value::string("any", head)
} 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),
@ -264,7 +245,7 @@ fn run(
if !options.detailed {
Value::string(value.get_type().to_string(), head)
} else {
describe_value(value, head, engine_state, options)?
describe_value(value, head, engine_state)
}
}
};
@ -288,9 +269,8 @@ fn describe_value(
value: Value,
head: nu_protocol::Span,
engine_state: Option<&EngineState>,
options: Options,
) -> Result<Value, ShellError> {
Ok(match value {
) -> Value {
match value {
Value::Custom { val, .. } => Value::record(
record!(
"type" => Value::string("custom", head),
@ -319,14 +299,12 @@ fn describe_value(
std::mem::take(v),
head,
engine_state,
options,
)?);
));
}
Value::record(
record!(
"type" => Value::string("record", head),
"lazy" => Value::bool(false, head),
"columns" => Value::record(val, head),
),
head,
@ -337,11 +315,9 @@ fn describe_value(
"type" => Value::string("list", head),
"length" => Value::int(vals.len() as i64, head),
"values" => Value::list(vals.into_iter().map(|v|
Ok(compact_primitive_description(
describe_value(v, head, engine_state, options)?
))
compact_primitive_description(describe_value(v, head, engine_state))
)
.collect::<Result<Vec<Value>, ShellError>>()?, head),
.collect(), head),
),
head,
),
@ -393,42 +369,7 @@ fn describe_value(
),
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 {

View File

@ -127,7 +127,7 @@ impl Command for Do {
let stderr_msg = match stderr {
None => "".to_string(),
Some(stderr_stream) => {
stderr_ctrlc = stderr_stream.ctrlc.clone();
stderr_ctrlc.clone_from(&stderr_stream.ctrlc);
stderr_stream.into_string().map(|s| s.item)?
}
};
@ -152,7 +152,7 @@ impl Command for Do {
let exit_code: Vec<Value> = match exit_code {
None => vec![],
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()
}
};

View File

@ -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)
}
}

View File

@ -21,7 +21,6 @@ mod hide;
mod hide_env;
mod if_;
mod ignore;
mod lazy_make;
mod let_;
mod loop_;
mod match_;
@ -58,7 +57,6 @@ pub use hide::Hide;
pub use hide_env::HideEnv;
pub use if_::If;
pub use ignore::Ignore;
pub use lazy_make::LazyMake;
pub use let_::Let;
pub use loop_::Loop;
pub use match_::Match;

View File

@ -43,7 +43,6 @@ pub fn create_default_context() -> EngineState {
OverlayList,
OverlayNew,
OverlayHide,
LazyMake,
Let,
Loop,
Match,

View File

@ -306,10 +306,6 @@ impl<'a> std::fmt::Debug for DebuggableValue<'a> {
Value::Custom { val, .. } => {
write!(f, "CustomValue({:?})", val)
}
Value::LazyRecord { val, .. } => {
let rec = val.collect().map_err(|_| std::fmt::Error)?;
write!(f, "LazyRecord({:?})", DebuggableValue(&rec))
}
}
}
}

View File

@ -1,8 +1,8 @@
use std::sync::Arc;
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_plugin_engine::{GetPlugin, PersistentPlugin};
use nu_protocol::{PluginGcConfig, PluginIdentity, PluginRegistryItem, RegisteredPlugin};
use std::sync::Arc;
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 shell: Option<Spanned<String>> = call.get_flag(engine_state, stack, "shell")?;
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
// Check the current directory, or fall back to NU_PLUGIN_DIRS

View File

@ -1,11 +1,11 @@
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_protocol::{engine::StateWorkingSet, PluginRegistryFile};
use std::{
fs::{self, File},
path::PathBuf,
};
use nu_engine::{command_prelude::*, current_dir};
use nu_protocol::{engine::StateWorkingSet, PluginRegistryFile};
pub(crate) fn modify_plugin_file(
engine_state: &EngineState,
stack: &mut Stack,
@ -13,6 +13,7 @@ pub(crate) fn modify_plugin_file(
custom_path: Option<Spanned<String>>,
operate: impl FnOnce(&mut PluginRegistryFile) -> Result<(), ShellError>,
) -> Result<(), ShellError> {
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
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,
) -> PathBuf {
// 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) {
let path = nu_path::expand_path_with(arg, &cwd, true);
// Try to canonicalize

View File

@ -32,6 +32,7 @@ pub fn default_shape_color(shape: String) -> Style {
"shape_or" => Style::new().fg(Color::Purple).bold(),
"shape_pipe" => Style::new().fg(Color::Purple).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_redirection" => Style::new().fg(Color::Purple).bold(),
"shape_signature" => Style::new().fg(Color::Green).bold(),

View File

@ -106,10 +106,9 @@ impl<'a> StyleComputer<'a> {
Value::Binary { .. } => TextStyle::with_style(Left, s),
Value::CellPath { .. } => TextStyle::with_style(Left, s),
Value::Record { .. } | Value::List { .. } => TextStyle::with_style(Left, s),
Value::Closure { .. }
| Value::Custom { .. }
| Value::Error { .. }
| Value::LazyRecord { .. } => TextStyle::basic_left(),
Value::Closure { .. } | Value::Custom { .. } | Value::Error { .. } => {
TextStyle::basic_left()
}
}
}

View File

@ -150,7 +150,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
head,
),
Ordering::Less => Value::binary(
if end == isize::max_value() {
if end == isize::MAX {
val.iter()
.skip(start as usize)
.copied()

View File

@ -272,10 +272,6 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
.collect::<Vec<_>>()
.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.
Value::Closure { val, .. } => format!("<Closure {}>", val.block_id),
Value::Nothing { .. } => String::new(),

View File

@ -1,5 +1,4 @@
use nu_engine::command_prelude::*;
use nu_protocol::LazyRecord;
use sysinfo::{MemoryRefreshKind, Pid, ProcessRefreshKind, RefreshKind, System};
const ENV_PATH_SEPARATOR_CHAR: char = {
@ -39,14 +38,10 @@ impl Command for DebugInfo {
&self,
_engine_state: &EngineState,
_stack: &mut Stack,
_call: &Call,
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let span = Span::unknown();
let record = LazySystemInfoRecord { span };
Ok(Value::lazy_record(Box::new(record), span).into_pipeline_data())
Ok(all_columns(call.head).into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {
@ -58,207 +53,119 @@ impl Command for DebugInfo {
}
}
#[derive(Debug, Clone)]
struct LazySystemInfoRecord {
span: Span,
}
fn all_columns(span: Span) -> Value {
let rk = RefreshKind::new()
.with_processes(ProcessRefreshKind::everything())
.with_memory(MemoryRefreshKind::everything());
impl LazySystemInfoRecord {
fn get_column_value_with_system(
&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())
}));
// only get information requested
let sys = System::new_with_specifics(rk);
let system = system_opt.get_system();
// get the process information for the nushell pid
let pinfo = system.process(pid);
let pid = Pid::from(std::process::id() as usize);
let ppid = {
sys.process(pid)
.and_then(|p| p.parent())
.map(|p| Value::int(p.as_u32().into(), span))
.unwrap_or(Value::nothing(span))
};
Ok(pinfo
.and_then(|p| p.parent())
.map(|p| Value::int(p.as_u32() as i64, self.span))
.unwrap_or(Value::nothing(self.span)))
}
"system" => {
// only get information requested
let system_opt = SystemOpt::from((system_option, || {
RefreshKind::new().with_memory(MemoryRefreshKind::everything())
}));
let system = Value::record(
record! {
"total_memory" => Value::filesize(sys.total_memory() as i64, span),
"free_memory" => Value::filesize(sys.free_memory() as i64, span),
"used_memory" => Value::filesize(sys.used_memory() as i64, span),
"available_memory" => Value::filesize(sys.available_memory() as i64, span),
},
span,
);
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(
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,
))
}
"process" => {
// only get information requested
let system_opt = SystemOpt::from((system_option, || {
RefreshKind::new().with_processes(ProcessRefreshKind::everything())
}));
let cwd = if let Some(path) = p.cwd() {
Value::string(path.to_string_lossy().to_string(), span)
} else {
Value::nothing(span)
};
let system = system_opt.get_system();
let pinfo = system.process(pid);
let exe_path = if let Some(path) = p.exe() {
Value::string(path.to_string_lossy().to_string(), span)
} else {
Value::nothing(span)
};
if let Some(p) = pinfo {
Ok(Value::record(
record! {
"memory" => Value::filesize(p.memory() as i64, self.span),
"virtual_memory" => Value::filesize(p.virtual_memory() as i64, self.span),
"status" => Value::string(p.status().to_string(), self.span),
"root" => {
if let Some(path) = p.exe().and_then(|p| p.parent()) {
Value::string(path.to_string_lossy().to_string(), self.span)
} else {
Value::nothing(self.span)
}
},
"cwd" => {
if let Some(path) = p.cwd() {
Value::string(path.to_string_lossy().to_string(), self.span)
}else{
Value::nothing(self.span)
}
},
"exe_path" => {
if let Some(path)= p.exe() {
Value::string(path.to_string_lossy().to_string(), self.span)
}else{
Value::nothing(self.span)
}
},
"command" => Value::string(p.cmd().join(" "), self.span),
"name" => Value::string(p.name().to_string(), self.span),
"environment" => {
let mut env_rec = Record::new();
for val in p.environ() {
if let Some((key, value)) = val.split_once('=') {
let is_env_var_a_list = {
{
#[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,
))
let environment = {
let mut env_rec = Record::new();
for val in p.environ() {
if let Some((key, value)) = val.split_once('=') {
let is_env_var_a_list = {
{
#[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(), span))
.collect::<Vec<_>>();
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
let items = value
.split(':')
.map(|r| Value::string(r.to_string(), span))
.collect::<Vec<_>>();
env_rec.push(key.to_string(), Value::list(items, span));
} else {
env_rec.push(key.to_string(), Value::string(value.to_string(), span));
}
}
}
_ => Err(ShellError::IncompatibleParametersSingle {
msg: format!("Unknown column: {}", column),
span: self.span,
}),
}
}
}
Value::record(env_rec, span)
};
impl<'a> LazyRecord<'a> for LazySystemInfoRecord {
fn column_names(&'a self) -> Vec<&'a str> {
vec!["thread_id", "pid", "ppid", "process", "system"]
}
Value::record(
record! {
"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> {
self.get_column_value_with_system(column, None)
}
fn span(&self) -> Span {
self.span
}
fn clone_value(&self, span: Span) -> Value {
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()))),
}
}
Value::record(
record! {
"thread_id" => Value::int(get_thread_id() as i64, span),
"pid" => Value::int(pid.as_u32().into(), span),
"ppid" => ppid,
"system" => system,
"process" => process,
},
span,
)
}
fn get_thread_id() -> u64 {

View File

@ -1,4 +1,4 @@
use nu_engine::{command_prelude::*, current_dir};
use nu_engine::command_prelude::*;
use nu_utils::filesystem::{have_permission, PermissionResult};
#[derive(Clone)]
@ -20,6 +20,7 @@ impl Command for Cd {
fn signature(&self) -> nu_protocol::Signature {
Signature::build("cd")
.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.")
.input_output_types(vec![
(Type::Nothing, Type::Nothing),
@ -36,8 +37,9 @@ impl Command for Cd {
call: &Call,
_input: PipelineData,
) -> 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 cwd = current_dir(engine_state, stack)?;
let cwd = engine_state.cwd(Some(stack))?;
let path_val = {
if let Some(path) = path_val {
@ -53,54 +55,53 @@ impl Command for Cd {
let (path, span) = match path_val {
Some(v) => {
if v.item == "-" {
let oldpwd = stack.get_env_var(engine_state, "OLDPWD");
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)
if let Some(oldpwd) = stack.get_env_var(engine_state, "OLDPWD") {
(oldpwd.to_path()?, v.span)
} else {
(cwd.to_string_lossy().to_string(), v.span)
(cwd, v.span)
}
} else {
// Trim whitespace from the end of path.
let path_no_whitespace =
&v.item.trim_end_matches(|x| matches!(x, '\x09'..='\x0d'));
let path = match nu_path::canonicalize_with(path_no_whitespace, &cwd) {
Ok(p) => {
if !p.is_dir() {
// If `--physical` is specified, canonicalize the path; otherwise expand the path.
let path = if physical {
if let Ok(path) = nu_path::canonicalize_with(path_no_whitespace, &cwd) {
if !path.is_dir() {
return Err(ShellError::NotADirectory { span: v.span });
};
p
}
// if canonicalize failed, let's check to see if it's abbreviated
Err(_) => {
path
} else {
return Err(ShellError::DirectoryNotFound {
dir: path_no_whitespace.to_string(),
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 => {
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") {
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
//should probably be a block that loads the information from the state in the overlay
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())
}
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
),
}),
}
}

View File

@ -1,5 +1,6 @@
use super::util::get_rest_for_glob_pattern;
use crate::{DirBuilder, DirInfo, FileInfo};
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_glob::Pattern;
use nu_protocol::NuGlob;
@ -98,6 +99,7 @@ impl Command for Du {
let all = call.has_flag(engine_state, stack, "all")?;
let deref = call.has_flag(engine_state, stack, "deref")?;
let exclude = call.get_flag(engine_state, stack, "exclude")?;
#[allow(deprecated)]
let current_dir = current_dir(engine_state, stack)?;
let paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;

View File

@ -1,3 +1,4 @@
#[allow(deprecated)]
use nu_engine::{command_prelude::*, env::current_dir};
use std::sync::{atomic::AtomicBool, Arc};
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 = match nu_path::canonicalize_with(prefix, path) {
Ok(path) => path,

View File

@ -1,6 +1,7 @@
use super::util::get_rest_for_glob_pattern;
use crate::{DirBuilder, DirInfo};
use chrono::{DateTime, Local, LocalResult, TimeZone, Utc};
#[allow(deprecated)]
use nu_engine::{command_prelude::*, env::current_dir};
use nu_glob::{MatchOptions, Pattern};
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 ctrl_c = engine_state.ctrlc.clone();
let call_span = call.head;
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let args = Args {
@ -429,7 +431,7 @@ fn ls_for_one_pattern(
Err(err) => Some(Value::error(err, call_span)),
}
}
_ => Some(Value::nothing(call_span)),
Err(err) => Some(Value::error(err, call_span)),
})))
}

View File

@ -1,3 +1,4 @@
#[allow(deprecated)]
use nu_engine::{command_prelude::*, env::current_dir};
use std::path::PathBuf;
@ -90,6 +91,7 @@ impl Command for Mktemp {
} else if directory || tmpdir {
Some(std::env::temp_dir())
} else {
#[allow(deprecated)]
Some(current_dir(engine_state, stack)?)
};

View File

@ -1,4 +1,5 @@
use super::util::get_rest_for_glob_pattern;
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir, get_eval_block};
use nu_protocol::{BufferedReader, DataSource, NuGlob, PipelineMetadata, RawStream};
use std::{io::BufReader, path::Path};
@ -51,6 +52,7 @@ impl Command for Open {
let raw = call.has_flag(engine_state, stack, "raw")?;
let call_span = call.head;
let ctrlc = engine_state.ctrlc.clone();
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
let eval_block = get_eval_block(engine_state);

View File

@ -1,4 +1,5 @@
use super::util::{get_rest_for_glob_pattern, try_interaction};
#[allow(deprecated)]
use nu_engine::{command_prelude::*, env::current_dir};
use nu_glob::MatchOptions;
use nu_path::expand_path_with;
@ -130,6 +131,7 @@ fn rm(
let mut unique_argument_check = None;
#[allow(deprecated)]
let currentdir_path = current_dir(engine_state, stack)?;
let home: Option<String> = nu_path::home_dir().map(|path| {

View File

@ -1,4 +1,5 @@
use crate::progress_bar;
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_path::expand_path_with;
use nu_protocol::{
@ -85,6 +86,7 @@ impl Command for Save {
};
let span = call.head;
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let path_arg = call.req::<Spanned<PathBuf>>(engine_state, stack, 0)?;

View File

@ -1,4 +1,5 @@
use filetime::FileTime;
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_path::expand_path_with;
use nu_protocol::NuGlob;
@ -113,6 +114,7 @@ impl Command for Touch {
})?;
}
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
for (index, glob) in files.into_iter().enumerate() {

View File

@ -1,4 +1,5 @@
use super::util::get_rest_for_glob_pattern;
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use std::path::PathBuf;
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(
target.item.to_string(),
));
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
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() {

View File

@ -1,3 +1,4 @@
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use uu_mkdir::mkdir;
@ -58,6 +59,7 @@ impl Command for UMkdir {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let mut directories = get_rest_for_glob_pattern(engine_state, stack, call, 0)?
.into_iter()

View File

@ -1,4 +1,5 @@
use super::util::get_rest_for_glob_pattern;
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_path::expand_path_with;
use nu_protocol::NuGlob;
@ -77,6 +78,7 @@ impl Command for UMv {
uu_mv::OverwriteMode::Force
};
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let mut paths = get_rest_for_glob_pattern(engine_state, stack, call, 0)?;
if paths.is_empty() {

View File

@ -5,6 +5,7 @@ use notify_debouncer_full::{
EventKind, RecursiveMode, Watcher,
},
};
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir, ClosureEval};
use nu_protocol::{
engine::{Closure, StateWorkingSet},
@ -73,6 +74,7 @@ impl Command for Watch {
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let path_arg: Spanned<String> = call.req(engine_state, stack, 0)?;

View File

@ -105,18 +105,6 @@ fn getcol(
.into_pipeline_data(ctrlc)
.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
.into_iter()
.map(move |(x, _)| Value::string(x, head))

View File

@ -533,15 +533,6 @@ fn value_should_be_printed(
Value::Record { val, .. } => {
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,
});
if invert {

View File

@ -44,12 +44,6 @@ impl Command for Items {
match input {
PipelineData::Empty => Ok(PipelineData::Empty),
PipelineData::Value(value, ..) => {
let value = if let Value::LazyRecord { val, .. } = value {
val.collect()?
} else {
value
};
let span = value.span();
match value {
Value::Record { val, .. } => {

View File

@ -161,19 +161,6 @@ fn values(
.cloned()
.collect::<Vec<_>>()
.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
Value::Error { error, .. } => Err(*error),
other => Err(ShellError::OnlySupportsThisInputType {

View File

@ -135,10 +135,6 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
}
nu_json::Value::Object(m)
}
Value::LazyRecord { val, .. } => {
let collected = val.collect()?;
value_to_json_value(&collected)?
}
Value::Custom { val, .. } => {
let collected = val.to_base_value(span)?;
value_to_json_value(&collected)?

View File

@ -246,9 +246,6 @@ pub(crate) fn write_value(
Value::Custom { val, .. } => {
write_value(out, &val.to_base_value(span)?, depth)?;
}
Value::LazyRecord { val, .. } => {
write_value(out, &val.collect()?, depth)?;
}
}
Ok(())
}

View File

@ -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)))
.collect::<Vec<_>>()
.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::Nothing { .. } => String::new(),
Value::Error { error, .. } => format!("{error:?}"),

View File

@ -62,10 +62,6 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result<toml::Value, ShellErr
}
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::Closure { .. } => {
let code = engine_state.get_span_contents(span);

View File

@ -62,10 +62,6 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
}
serde_yaml::Value::Mapping(m)
}
Value::LazyRecord { val, .. } => {
let collected = val.collect()?;
value_to_yaml_value(&collected)?
}
Value::List { vals, .. } => {
let mut out = vec![];

View File

@ -1,4 +1,5 @@
use super::PathSubcommandArguments;
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir, current_dir_const};
use nu_path::expand_path_with;
use nu_protocol::engine::StateWorkingSet;
@ -53,6 +54,7 @@ If you need to distinguish dirs and files, please use `path type`."#
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
#[allow(deprecated)]
let args = Arguments {
pwd: current_dir(engine_state, stack)?,
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,
) -> Result<PipelineData, ShellError> {
let head = call.head;
#[allow(deprecated)]
let args = Arguments {
pwd: current_dir_const(working_set)?,
not_follow_symlink: call.has_flag_const(working_set, "no-symlink")?,

View File

@ -1,4 +1,5 @@
use super::PathSubcommandArguments;
#[allow(deprecated)]
use nu_engine::{
command_prelude::*,
env::{current_dir_str, current_dir_str_const},
@ -57,6 +58,7 @@ impl Command for SubCommand {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
#[allow(deprecated)]
let args = Arguments {
strict: call.has_flag(engine_state, stack, "strict")?,
cwd: current_dir_str(engine_state, stack)?,
@ -79,6 +81,7 @@ impl Command for SubCommand {
input: PipelineData,
) -> Result<PipelineData, ShellError> {
let head = call.head;
#[allow(deprecated)]
let args = Arguments {
strict: call.has_flag_const(working_set, "strict")?,
cwd: current_dir_str_const(working_set)?,

View File

@ -149,7 +149,7 @@ fn action(input: &Value, args: &Arguments, head: Span) -> Value {
),
Ordering::Less => Value::string(
{
if end == isize::max_value() {
if end == isize::MAX {
if args.graphemes {
s.graphemes(true)
.skip(start as usize)
@ -245,7 +245,7 @@ mod tests {
expectation("andre", (0, -1)),
// str substring [ -4 , _ ]
// str substring -4 ,
expectation("dres", (-4, isize::max_value())),
expectation("dres", (-4, isize::MAX)),
expectation("", (0, -110)),
expectation("", (6, 0)),
expectation("", (6, -1)),

View File

@ -1,4 +1,5 @@
use super::run_external::create_external_command;
#[allow(deprecated)]
use nu_engine::{command_prelude::*, current_dir};
use nu_protocol::OutDest;
@ -62,6 +63,7 @@ fn exec(
external_command.out = OutDest::Inherit;
external_command.err = OutDest::Inherit;
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let mut command = external_command.spawn_simple_command(&cwd.to_string_lossy())?;
command.current_dir(cwd);

View File

@ -1,6 +1,5 @@
use chrono::{DateTime, Local};
use nu_engine::command_prelude::*;
use nu_protocol::LazyRecord;
use std::time::{Duration, UNIX_EPOCH};
use sysinfo::{
Components, CpuRefreshKind, Disks, Networks, System, Users, MINIMUM_CPU_UPDATE_INTERVAL,
@ -32,10 +31,7 @@ impl Command for Sys {
call: &Call,
_input: PipelineData,
) -> Result<PipelineData, ShellError> {
let span = call.span();
let ret = Value::lazy_record(Box::new(SysResult { span }), span);
Ok(ret.into_pipeline_data())
Ok(all_columns(call.head).into_pipeline_data())
}
fn examples(&self) -> Vec<Example> {
@ -64,36 +60,18 @@ pub struct SysResult {
pub span: Span,
}
impl LazyRecord<'_> for SysResult {
fn column_names(&self) -> Vec<&'static str> {
vec!["host", "cpu", "disks", "mem", "temp", "net"]
}
fn get_column_value(&self, column: &str) -> Result<Value, ShellError> {
let span = self.span;
match column {
"host" => Ok(host(span)),
"cpu" => Ok(cpu(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)
}
fn all_columns(span: Span) -> Value {
Value::record(
record! {
"host" => host(span),
"cpu" => cpu(span),
"disks" => disks(span),
"mem" => mem(span),
"temp" => temp(span),
"net" => net(span),
},
span,
)
}
pub fn trim_cstyle_null(s: String) -> String {

View File

@ -229,6 +229,7 @@ fn which(
let mut output = vec![];
#[allow(deprecated)]
let cwd = env::current_dir_str(engine_state, stack)?;
let paths = env::path_str(engine_state, stack, call.head)?;

View File

@ -394,10 +394,6 @@ fn handle_table_command(
input.data = PipelineData::Empty;
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, .. }, ..) => {
// Propagate this error outward, so that it goes to stderr
// instead of stdout.
@ -942,7 +938,11 @@ fn render_path_name(
// clickable links don't work in remote SSH sessions
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();

View File

@ -207,7 +207,15 @@ fn filesystem_change_directory_to_symlink_relative() {
$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"));
})
}

View File

@ -98,21 +98,6 @@ fn insert_uses_enumerate_index() {
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]
fn deep_cell_path_creates_all_nested_records() {
let actual = nu!("{a: {}} | insert a.b.c 0 | get a.b.c");

View File

@ -91,3 +91,18 @@ fn let_glob_type() {
let actual = nu!("let x: glob = 'aa'; $x | describe");
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");
}

View File

@ -125,3 +125,18 @@ fn mut_glob_type() {
let actual = nu!("mut x: glob = 'aa'; $x | describe");
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");
}

View File

@ -103,13 +103,6 @@ fn update_uses_enumerate_index() {
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]
fn list_replacement_closure() {
let actual = nu!("[1, 2] | update 1 {|i| $i + 1 } | to nuon");

View File

@ -112,17 +112,6 @@ fn upsert_past_end_of_list_stream() {
.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]
fn deep_cell_path_creates_all_nested_records() {
let actual = nu!("{a: {}} | upsert a.b.c 0 | get a.b.c");

View File

@ -2,7 +2,7 @@ use crate::ClosureEvalOnce;
use nu_path::canonicalize_with;
use nu_protocol::{
ast::{Call, Expr},
engine::{EngineState, Stack, StateWorkingSet, PWD_ENV},
engine::{EngineState, Stack, StateWorkingSet},
Config, ShellError, Span, Value, VarId,
};
use std::{
@ -156,85 +156,56 @@ pub fn env_to_strings(
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> {
if let Some(pwd) = stack.get_env_var(engine_state, PWD_ENV) {
// TODO: PWD should be string by default, we don't need to run ENV_CONVERSIONS on it
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![],
})
}
#[allow(deprecated)]
current_dir(engine_state, stack).map(|path| path.to_string_lossy().to_string())
}
/// 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> {
if let Some(pwd) = working_set.get_env_var(PWD_ENV) {
let span = pwd.span();
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![],
})
}
#[allow(deprecated)]
current_dir_const(working_set).map(|path| path.to_string_lossy().to_string())
}
/// 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> {
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> {
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
@ -315,6 +286,7 @@ pub fn find_in_dirs_env(
Err(e) => return Err(e),
}
} else {
#[allow(deprecated)]
current_dir_str(engine_state, stack)?
};

View File

@ -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_protocol::{
ast::{
@ -325,6 +326,7 @@ fn eval_redirection<D: DebugContext>(
) -> Result<Redirection, ShellError> {
match target {
RedirectionTarget::File { expr, append, .. } => {
#[allow(deprecated)]
let cwd = current_dir(engine_state, stack)?;
let value = eval_expression::<D>(engine_state, stack, expr)?;
let path = Spanned::<PathBuf>::from_value(value)?.item;
@ -633,7 +635,7 @@ impl Eval for EvalRuntime {
if quoted {
Ok(Value::string(path, span))
} else {
let cwd = current_dir_str(engine_state, stack)?;
let cwd = engine_state.cwd(Some(stack))?;
let path = expand_path_with(path, cwd, true);
Ok(Value::string(path.to_string_lossy(), span))
@ -652,7 +654,7 @@ impl Eval for EvalRuntime {
} else if quoted {
Ok(Value::string(path, span))
} else {
let cwd = current_dir_str(engine_state, stack)?;
let cwd = engine_state.cwd(Some(stack))?;
let path = expand_path_with(path, cwd, true);
Ok(Value::string(path.to_string_lossy(), span))

View File

@ -51,10 +51,3 @@ pub trait ViewCommand {
value: Option<Value>,
) -> Result<Self::View>;
}
#[derive(Debug, Default, Clone)]
pub struct Shortcode {
pub code: &'static str,
pub context: &'static str,
pub description: &'static str,
}

View File

@ -112,10 +112,6 @@ pub fn collect_input(value: Value) -> Result<(Vec<String>, Vec<Vec<Value>>)> {
Ok((vec![String::from("")], lines))
}
Value::LazyRecord { val, .. } => {
let materialized = val.collect()?;
collect_input(materialized)
}
Value::Nothing { .. } => Ok((vec![], vec![])),
Value::Custom { val, .. } => {
let materialized = val.to_base_value(span)?;

View File

@ -4,7 +4,7 @@ use ratatui::{
buffer::Buffer,
layout::Rect,
text::Span,
widgets::{Paragraph, StatefulWidget, Widget},
widgets::{Paragraph, Widget},
};
use crate::{
@ -12,10 +12,6 @@ use crate::{
views::util::{nu_style_to_tui, text_style_to_tui_style},
};
use super::Layout;
type OptStyle = Option<NuStyle>;
#[derive(Debug, Clone)]
pub struct BinaryWidget<'a> {
data: &'a [u8],
@ -73,7 +69,7 @@ impl BinarySettings {
#[derive(Debug, Default, Clone)]
pub struct BinaryStyle {
colors: BinaryStyleColors,
color_index: Option<NuStyle>,
indent_index: Indent,
indent_data: Indent,
indent_ascii: Indent,
@ -83,7 +79,7 @@ pub struct BinaryStyle {
impl BinaryStyle {
pub fn new(
colors: BinaryStyleColors,
color_index: Option<NuStyle>,
indent_index: Indent,
indent_data: Indent,
indent_ascii: Indent,
@ -91,7 +87,7 @@ impl BinaryStyle {
show_split: bool,
) -> Self {
Self {
colors,
color_index,
indent_index,
indent_data,
indent_ascii,
@ -113,61 +109,8 @@ impl Indent {
}
}
#[derive(Debug, Default, Clone)]
pub struct BinaryStyleColors {
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) {
impl Widget for BinaryWidget<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let min_width = get_widget_width(&self);
if (area.width as usize) < min_width {
@ -178,12 +121,12 @@ impl StatefulWidget for BinaryWidget<'_> {
return;
}
render_hexdump(area, buf, state, self);
render_hexdump(area, buf, self);
}
}
// 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;
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 {
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);
}
@ -251,7 +194,7 @@ fn render_hexdump(area: Rect, buf: &mut Buffer, _state: &mut BinaryWidgetState,
if show_index {
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);
}
@ -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);
size += render_hex_u8(buf, x + size, y, n, false, style);
size += render_hex_u8(buf, x + size, y, n, style);
count -= 1;
}
@ -346,7 +289,7 @@ fn render_ascii_line(buf: &mut Buffer, x: u16, y: u16, line: &[u8], w: &BinaryWi
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 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
}
fn render_hex_u8(buf: &mut Buffer, x: u16, y: u16, n: u8, big: bool, style: OptStyle) -> u16 {
render_hex_usize(buf, x, y, n as usize, 2, big, style)
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, style)
}
fn render_hex_usize(
@ -372,10 +315,9 @@ fn render_hex_usize(
y: u16,
n: usize,
width: u16,
big: bool,
style: OptStyle,
style: Option<NuStyle>,
) -> 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);
if let Some(style) = style {
let style = nu_style_to_tui(style);
@ -389,7 +331,7 @@ fn render_hex_usize(
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 c = c.unwrap_or(n as char);
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)
}
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 c = c.unwrap_or(n as char);
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)
}
fn get_index_style(w: &BinaryWidget) -> OptStyle {
w.style.colors.index
fn get_index_style(w: &BinaryWidget) -> Option<NuStyle> {
w.style.color_index
}
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 count_lines = w.data.len() / 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 {
@ -453,7 +395,7 @@ fn get_widget_width(w: &BinaryWidget) -> usize {
let count_lines = w.data.len() / 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 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
}
fn usize_to_hex(n: usize, width: usize, big: bool) -> String {
fn usize_to_hex(n: usize, width: usize) -> String {
if width == 0 {
match big {
true => format!("{:X}", n),
false => format!("{:x}", n),
}
format!("{:x}", n)
} else {
match big {
true => format!("{:0>width$X}", n, width = width),
false => format!("{:0>width$x}", n, width = width),
}
format!("{:0>width$x}", n, width = width)
}
}
@ -499,9 +435,8 @@ mod tests {
#[test]
fn test_to_hex() {
assert_eq!(usize_to_hex(1, 2, false), "01");
assert_eq!(usize_to_hex(16, 2, false), "10");
assert_eq!(usize_to_hex(29, 2, false), "1d");
assert_eq!(usize_to_hex(29, 2, true), "1D");
assert_eq!(usize_to_hex(1, 2), "01");
assert_eq!(usize_to_hex(16, 2), "10");
assert_eq!(usize_to_hex(29, 2), "1d");
}
}

View File

@ -19,29 +19,17 @@ use crate::{
util::create_map,
};
use self::binary_widget::{
BinarySettings, BinaryStyle, BinaryStyleColors, BinaryWidget, BinaryWidgetState, Indent,
SymbolColor,
};
use self::binary_widget::{BinarySettings, BinaryStyle, BinaryWidget, Indent};
use super::{cursor::XYCursor, Layout, View, ViewConfig};
#[derive(Debug, Clone)]
pub struct BinaryView {
data: Vec<u8>,
mode: Option<CursorMode>,
cursor: XYCursor,
settings: Settings,
}
#[allow(dead_code)] // todo:
#[derive(Debug, Clone, Copy)]
enum CursorMode {
Index,
Data,
Ascii,
}
#[derive(Debug, Default, Clone)]
struct Settings {
opts: BinarySettings,
@ -52,7 +40,6 @@ impl BinaryView {
pub fn new(data: Vec<u8>) -> Self {
Self {
data,
mode: None,
cursor: XYCursor::default(),
settings: Settings::default(),
}
@ -61,9 +48,8 @@ impl BinaryView {
impl View for BinaryView {
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);
f.render_stateful_widget(widget, area, &mut state);
f.render_widget(widget, area);
}
fn handle_input(
@ -77,7 +63,7 @@ impl View for BinaryView {
let result = handle_event_view_mode(self, &key);
if matches!(&result, Some(Transition::Ok)) {
let report = create_report(self.mode, self.cursor);
let report = create_report(self.cursor);
info.status = Some(report);
}
@ -206,21 +192,7 @@ fn settings_from_config(config: &ConfigMap) -> Settings {
0,
),
style: BinaryStyle::new(
BinaryStyleColors::new(
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(),
),
colors.get("color_index").cloned(),
Indent::new(
config_get_usize(config, "padding_index_left", 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)
}
fn create_report(mode: Option<CursorMode>, cursor: XYCursor) -> Report {
fn create_report(cursor: XYCursor) -> Report {
let covered_percent = report_row_position(cursor);
let cursor = report_cursor_position(cursor);
let mode = report_mode_name(mode);
let mode = report_mode_name();
let msg = String::new();
Report::new(msg, Severity::Info, mode, cursor, covered_percent)
}
fn report_mode_name(cursor: Option<CursorMode>) -> String {
match cursor {
Some(CursorMode::Index) => String::from("ADDR"),
Some(CursorMode::Data) => String::from("DUMP"),
Some(CursorMode::Ascii) => String::from("TEXT"),
None => String::from("VIEW"),
}
fn report_mode_name() -> String {
String::from("VIEW")
}
fn report_row_position(cursor: XYCursor) -> String {

View File

@ -32,25 +32,10 @@ impl XYCursor {
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 {
self.y.end()
}
#[allow(dead_code)]
pub fn column_offset(&self) -> usize {
self.x.offset()
}
pub fn row_starts_at(&self) -> usize {
self.y.starts_at()
}
@ -67,11 +52,6 @@ impl XYCursor {
self.x.offset()
}
#[allow(dead_code)]
pub fn row_window_size(&self) -> usize {
self.y.window()
}
pub fn column_window_size(&self) -> usize {
self.x.window()
}
@ -80,11 +60,6 @@ impl XYCursor {
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 {
self.y.next_window()
}
@ -101,11 +76,6 @@ impl XYCursor {
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 {
self.y.prev_window()
}

View File

@ -1141,18 +1141,18 @@ mod test {
let v: Value = from_str("{\"a\":1.1}").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 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 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 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);
}
}

View File

@ -38,6 +38,7 @@ pub enum FlatShape {
Or,
Pipe,
Range,
RawString,
Record,
Redirection,
Signature,
@ -78,6 +79,7 @@ impl Display for FlatShape {
FlatShape::Or => write!(f, "shape_or"),
FlatShape::Pipe => write!(f, "shape_pipe"),
FlatShape::Range => write!(f, "shape_range"),
FlatShape::RawString => write!(f, "shape_raw_string"),
FlatShape::Record => write!(f, "shape_record"),
FlatShape::Redirection => write!(f, "shape_redirection"),
FlatShape::Signature => write!(f, "shape_signature"),
@ -509,6 +511,9 @@ pub fn flatten_expression(
Expr::String(_) => {
vec![(expr.span, FlatShape::String)]
}
Expr::RawString(_) => {
vec![(expr.span, FlatShape::RawString)]
}
Expr::Table(table) => {
let outer_span = expr.span;
let mut last_end = outer_span.start;

View File

@ -503,6 +503,79 @@ fn lex_internal(
} else if c == b' ' || c == b'\t' || additional_whitespace.contains(&c) {
// If the next character is non-newline whitespace, skip it.
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 {
let token = try_lex_special_piped_item(input, &mut curr_offset, span_offset);
if let Some(token) = token {

View File

@ -1949,6 +1949,7 @@ pub fn parse_module_file_or_dir(
return None;
}
#[allow(deprecated)]
let cwd = working_set.get_cwd();
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 {
trace!("parsing source");
let spans = &lite_command.parts;
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";
if let Some(decl_id) = working_set.find_decl(name) {
#[allow(deprecated)]
let cwd = working_set.get_cwd();
// 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;
#[allow(deprecated)]
let cwd = working_set.get_cwd();
// 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 {
use nu_protocol::{FromValue, PluginRegistryFile};
#[allow(deprecated)]
let cwd = working_set.get_cwd();
if let Err(err) = (|| {

View File

@ -66,6 +66,11 @@ pub fn is_math_expression_like(working_set: &mut StateWorkingSet, span: Span) ->
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'-' {
return true;
}
@ -578,6 +583,7 @@ pub fn parse_multispan_value(
spans_idx: &mut usize,
shape: &SyntaxShape,
) -> Expression {
trace!("parse multispan value");
match shape {
SyntaxShape::VarWithOptType => {
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(
working_set: &mut StateWorkingSet,
span: Span,
@ -4553,6 +4619,9 @@ pub fn parse_value(
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::RawString(_) => {}
Expr::StringInterpolation(exprs) => {
for expr in exprs {
discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
@ -6236,6 +6306,7 @@ pub fn parse(
contents: &[u8],
scoped: bool,
) -> Arc<Block> {
trace!("parse");
let name = match fname {
Some(fname) => {
// use the canonical name for this filename
@ -6253,9 +6324,13 @@ pub fn parse(
let mut output = {
if let Some(block) = previously_parsed_block {
// dbg!("previous block");
return block;
} else {
// dbg!("starting lex");
let (output, err) = lex(contents, new_span.start, &[], &[], false);
// dbg!("finished lex");
// dbg!(&output);
if let Some(err) = err {
working_set.error(err)
}

View File

@ -1,4 +1,4 @@
use std::ffi::OsString;
use std::ffi::{OsStr, OsString};
#[cfg(test)]
pub(crate) mod tests;
@ -23,6 +23,16 @@ pub fn make_local_socket_name(unique_id: &str) -> OsString {
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
/// 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()
}
/// 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
#[cfg(not(windows))]
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
})
}
/// 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(())
}
}

View File

@ -4,9 +4,6 @@ use std::process::{Child, ChildStdin, ChildStdout, Command, Stdio};
use nu_protocol::ShellError;
#[cfg(feature = "local-socket")]
use interprocess::local_socket::LocalSocketListener;
#[cfg(feature = "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.
#[cfg(feature = "local-socket")]
CommunicationMode::LocalSocket(name) => {
let listener = LocalSocketListener::bind(name.as_os_str()).map_err(|err| {
ShellError::IOError {
use interprocess::local_socket::ListenerOptions;
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}"),
}
})?;
Ok(PreparedServerCommunication::LocalSocket {
name: name.clone(),
listener,
})
})?;
Ok(PreparedServerCommunication::LocalSocket { listener })
}
}
}
@ -107,11 +103,13 @@ impl CommunicationMode {
// Connect to the specified socket.
let get_socket = || {
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 {
msg: format!("failed to connect to socket: {err}"),
})
.map(LocalSocketStream::from)
};
// Reverse order from the server: read in, write out
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`.
#[cfg(feature = "local-socket")]
LocalSocket {
#[cfg_attr(windows, allow(dead_code))] // not used on Windows
name: std::ffi::OsString,
listener: LocalSocketListener,
listener: interprocess::local_socket::Listener,
},
}
@ -161,6 +157,9 @@ impl PreparedServerCommunication {
}
#[cfg(feature = "local-socket")]
PreparedServerCommunication::LocalSocket { listener, .. } => {
use interprocess::local_socket::traits::{
Listener, ListenerNonblockingMode, Stream,
};
use std::time::{Duration, Instant};
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
// 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 result = None;
while let Ok(None) = child.try_wait() {
match listener.accept() {
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)?;
result = Some(stream);
break;
@ -198,7 +200,7 @@ impl PreparedServerCommunication {
}
}
if let Some(stream) = result {
Ok(LocalSocketStream(stream))
Ok(stream)
} else {
// The process may have exited
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.
pub enum ServerCommunicationIo {
Stdio(ChildStdin, ChildStdout),
#[cfg(feature = "local-socket")]
LocalSocket {
read_out: LocalSocketStream,
write_in: LocalSocketStream,
read_out: interprocess::local_socket::Stream,
write_in: interprocess::local_socket::Stream,
},
}
@ -243,7 +232,7 @@ pub enum ClientCommunicationIo {
Stdio(Stdin, Stdout),
#[cfg(feature = "local-socket")]
LocalSocket {
read_in: LocalSocketStream,
write_out: LocalSocketStream,
read_in: interprocess::local_socket::Stream,
write_out: interprocess::local_socket::Stream,
},
}

View File

@ -202,10 +202,13 @@ where
if !self.ended {
self.writer
.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:
if !self.signal.notify_sent()? {
// Flush the output, and then wait for acknowledgements
self.writer.flush()?;
self.signal.wait_for_drain()
} else {
Ok(())

View File

@ -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
/// plugin custom values.
///
/// `LazyRecord`s will be collected to plain values for completeness.
pub fn with_custom_values_in<E>(
value: &mut Value,
mut f: impl FnMut(Spanned<&mut Box<dyn CustomValue>>) -> Result<(), E>,
@ -18,13 +16,6 @@ where
// Operate on a CustomValue.
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(()),
}
})
@ -33,31 +24,7 @@ where
#[test]
fn find_custom_values() {
use nu_plugin_protocol::test_util::test_plugin_custom_value;
use nu_protocol::{engine::Closure, record, LazyRecord, Span};
#[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)
}
}
use nu_protocol::{engine::Closure, record};
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"))]
}
),
"lazy" => Value::test_lazy_record(Box::new(Lazy)),
});
// Do with_custom_values_in, and count the number of custom values found
@ -83,7 +49,7 @@ fn find_custom_values() {
Ok(())
})
.expect("error");
assert_eq!(4, found, "found in value");
assert_eq!(3, found, "found in value");
// Try it on bare custom value too
found = 0;

View File

@ -125,6 +125,7 @@ impl<'a> PluginExecutionContext for PluginExecutionCommandContext<'a> {
}
fn get_current_dir(&self) -> Result<Spanned<String>, ShellError> {
#[allow(deprecated)]
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
Ok(cwd.into_spanned(self.call.head))

View File

@ -24,7 +24,10 @@ use crate::{
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
/// [`make_plugin_interface()`] to get a [`PluginInterface`].

View File

@ -179,11 +179,6 @@ impl PluginCustomValue {
Ok(())
}
}
// Collect LazyRecord before proceeding
Value::LazyRecord { ref val, .. } => {
*value = val.collect()?;
Ok(())
}
_ => Ok(()),
}
})
@ -205,11 +200,6 @@ impl PluginCustomValue {
Ok(())
}
}
// Collect LazyRecord before proceeding
Value::LazyRecord { ref val, .. } => {
*value = val.collect()?;
Ok(())
}
_ => Ok(()),
}
})
@ -224,11 +214,6 @@ impl PluginCustomValue {
*value = val.to_base_value(span)?;
Ok(())
}
// Collect LazyRecord before proceeding
Value::LazyRecord { ref val, .. } => {
*value = val.collect()?;
Ok(())
}
_ => Ok(()),
}
})

View File

@ -344,9 +344,6 @@ impl PluginTest {
// All equal, and same length
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.
_ => Ok(a == b),
}

View File

@ -381,11 +381,6 @@ pub(crate) fn render_examples(
plugin.custom_value_to_base_value(engine, val.into_spanned(span))?;
Ok::<_, ShellError>(())
}
// Collect LazyRecord before proceeding
Value::LazyRecord { ref val, .. } => {
*value = val.collect()?;
Ok(())
}
_ => Ok(()),
}
})?;

View File

@ -28,8 +28,11 @@ mod interface;
pub use command::{create_plugin_signature, PluginCommand, SimplePluginCommand};
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)]
pub(crate) const OUTPUT_BUFFER_SIZE: usize = 8192;
pub(crate) const OUTPUT_BUFFER_SIZE: usize = 16384;
/// The API for a Nushell plugin
///

View File

@ -47,6 +47,7 @@ strum_macros = "0.26"
nu-test-support = { path = "../nu-test-support", version = "0.93.1" }
pretty_assertions = { workspace = true }
rstest = { workspace = true }
tempfile = { workspace = true }
[package.metadata.docs.rs]
all-features = true

View File

@ -36,6 +36,15 @@ impl Argument {
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)]

View File

@ -36,6 +36,7 @@ pub enum Expr {
Directory(String, bool),
GlobPattern(String, bool),
String(String),
RawString(String),
CellPath(CellPath),
FullCellPath(Box<FullCellPath>),
ImportPattern(Box<ImportPattern>),
@ -80,6 +81,7 @@ impl Expr {
| Expr::ValueWithUnit(_)
| Expr::DateTime(_)
| Expr::String(_)
| Expr::RawString(_)
| Expr::CellPath(_)
| Expr::StringInterpolation(_)
| Expr::Nothing => {

View File

@ -279,6 +279,7 @@ impl Expression {
}
Expr::Signature(_) => false,
Expr::String(_) => false,
Expr::RawString(_) => false,
Expr::RowCondition(block_id) | Expr::Subexpression(block_id) => {
let block = working_set.get_block(*block_id);
@ -436,6 +437,7 @@ impl Expression {
}
Expr::Signature(_) => {}
Expr::String(_) => {}
Expr::RawString(_) => {}
Expr::StringInterpolation(items) => {
for i in items {
i.replace_span(working_set, replaced, new_span)

View File

@ -74,7 +74,14 @@ pub struct Config {
pub menus: Vec<ParsedMenu>,
pub hooks: Hooks,
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 table_index_mode: TableIndexMode,
pub case_sensitive_completions: bool,
@ -154,7 +161,15 @@ impl Default for Config {
use_ansi_coloring: true,
bracketed_paste: true,
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,
hooks: Hooks::new(),
@ -639,7 +654,54 @@ impl Value {
&mut errors);
}
"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 {
Value::Nothing { .. } | Value::String { .. } => {

View File

@ -253,7 +253,7 @@ fn expr_to_string(engine_state: &EngineState, expr: &Expr) -> String {
Expr::Record(_) => "record".to_string(),
Expr::RowCondition(_) => "row condition".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::Subexpression(_) => "subexpression".to_string(),
Expr::Table(_) => "table".to_string(),

View File

@ -26,8 +26,6 @@ type PoisonDebuggerError<'a> = PoisonError<MutexGuard<'a, Box<dyn Debugger>>>;
#[cfg(feature = "plugin")]
use crate::{PluginRegistryFile, PluginRegistryItem, RegisteredPlugin};
pub static PWD_ENV: &str = "PWD";
#[derive(Clone, Debug)]
pub enum VirtualPath {
File(FileId),
@ -893,14 +891,6 @@ impl EngineState {
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) {
self.config_path.insert(key.to_string(), val);
}
@ -922,12 +912,71 @@ impl EngineState {
.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 {
self.get_env_var("PWD")
.map(|d| d.coerce_string().unwrap_or_default())
self.cwd(None)
.map(|path| path.to_string_lossy().to_string())
.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
pub fn get_file_contents(&self) -> &[CachedFile] {
&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);
}
}

View File

@ -2,7 +2,7 @@ use crate::{
ast::Block,
engine::{
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,
Type, Value, VarId, VirtualPathId,
@ -601,13 +601,16 @@ impl<'a> StateWorkingSet<'a> {
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 {
let pwd = self
.permanent_state
.get_env_var(PWD_ENV)
.expect("internal error: can't find PWD");
pwd.coerce_string()
.expect("internal error: PWD not a string")
self.permanent_state
.cwd(None)
.map(|path| path.to_string_lossy().to_string())
.unwrap_or_default()
}
pub fn get_env_var(&self, name: &str) -> Option<&Value> {
@ -622,16 +625,6 @@ impl<'a> StateWorkingSet<'a> {
&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) {
let num_permanent_vars = self.permanent_state.num_vars();
if var_id < num_permanent_vars {

View File

@ -1178,16 +1178,6 @@ pub enum ShellError {
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
#[error("Operation interrupted by user")]
InterruptedByUser {

View File

@ -139,7 +139,7 @@ pub trait Eval {
Ok(Value::list(output_rows, expr.span))
}
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::ValueWithUnit(value) => match Self::eval::<D>(state, mut_state, &value.expr)? {
Value::Int { val, .. } => value.unit.item.build_value(val, value.unit.span),

View File

@ -14,6 +14,7 @@ use std::{
/// Create a Value for `$nu`.
pub fn create_nu_constant(engine_state: &EngineState, span: Span) -> Result<Value, ShellError> {
fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf {
#[allow(deprecated)]
let cwd = engine_state.current_work_dir();
if path.exists() {

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