forked from extern/nushell
Compare commits
46 Commits
revert-113
...
dependabot
Author | SHA1 | Date | |
---|---|---|---|
4dbe3ecfe2 | |||
7ad0e5541e | |||
c1cc1c82cc | |||
b0b4c3dffd | |||
f3de373c59 | |||
df1fecd2cb | |||
9620e27e4f | |||
072ecec8ca | |||
748d82cec1 | |||
5e5d1ea81b | |||
bb570e42be | |||
3a050864df | |||
8cfa96b4c0 | |||
6f384da57e | |||
f8e63328d8 | |||
5d98a727ca | |||
109f629cb6 | |||
ff6a67d293 | |||
03ae01f11e | |||
697f3c03f1 | |||
1cecb37628 | |||
89436e978b | |||
cd0a52cf00 | |||
c6043eb500 | |||
729373aba0 | |||
f59a6990dc | |||
0f9094ead2 | |||
70f7db14d4 | |||
0cba269d80 | |||
ec2593efb8 | |||
c2283596ac | |||
c9399f5142 | |||
c9c93f5b4d | |||
2264682443 | |||
247c33b6d6 | |||
a6da8ce769 | |||
020e121391 | |||
7d5bd0d6be | |||
87717b9ddd | |||
3c6fac059e | |||
9092fc1b12 | |||
5b557a888e | |||
398b756aee | |||
533c1a89af | |||
84742275a1 | |||
92d968b8c8 |
33
.github/workflows/ci.yml
vendored
33
.github/workflows/ci.yml
vendored
@ -92,6 +92,17 @@ jobs:
|
||||
- name: Tests
|
||||
run: cargo test --workspace --profile ci --exclude nu_plugin_* ${{ matrix.flags }}
|
||||
|
||||
- name: Check for clean repo
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "there are changes";
|
||||
git status --porcelain
|
||||
exit 1
|
||||
else
|
||||
echo "no changes in working directory";
|
||||
fi
|
||||
|
||||
std-lib-and-python-virtualenv:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
@ -129,6 +140,17 @@ jobs:
|
||||
run: nu scripts/test_virtualenv.nu
|
||||
shell: bash
|
||||
|
||||
- name: Check for clean repo
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "there are changes";
|
||||
git status --porcelain
|
||||
exit 1
|
||||
else
|
||||
echo "no changes in working directory";
|
||||
fi
|
||||
|
||||
plugins:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
@ -150,3 +172,14 @@ jobs:
|
||||
|
||||
- name: Tests
|
||||
run: cargo test --profile ci --package nu_plugin_*
|
||||
|
||||
- name: Check for clean repo
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -n "$(git status --porcelain)" ]; then
|
||||
echo "there are changes";
|
||||
git status --porcelain
|
||||
exit 1
|
||||
else
|
||||
echo "no changes in working directory";
|
||||
fi
|
||||
|
2
.github/workflows/typos.yml
vendored
2
.github/workflows/typos.yml
vendored
@ -10,6 +10,6 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@v1.16.24
|
||||
uses: crate-ci/typos@v1.16.25
|
||||
with:
|
||||
config: ./.github/.typos.toml
|
||||
|
93
Cargo.lock
generated
93
Cargo.lock
generated
@ -1252,16 +1252,6 @@ version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b95f7c0680e4142284cf8b22c14a476e87d61b004a3a0861872b32ef7ead40a2"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fancy-regex"
|
||||
version = "0.12.0"
|
||||
@ -1798,7 +1788,7 @@ dependencies = [
|
||||
"iana-time-zone-haiku",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"windows-core",
|
||||
"windows-core 0.51.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1974,6 +1964,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
@ -2273,9 +2272,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lscolors"
|
||||
version = "0.15.0"
|
||||
version = "0.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf7015a04103ad78abb77e4b79ed151e767922d1cfde5f62640471c629a2320d"
|
||||
checksum = "ab0b209ec3976527806024406fe765474b9a1750a0ed4b8f0372364741f50e7b"
|
||||
dependencies = [
|
||||
"nu-ansi-term",
|
||||
]
|
||||
@ -2677,7 +2676,7 @@ version = "0.88.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossterm",
|
||||
"fancy-regex 0.11.0",
|
||||
"fancy-regex",
|
||||
"fuzzy-matcher",
|
||||
"is_executable",
|
||||
"log",
|
||||
@ -2726,7 +2725,7 @@ version = "0.88.2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"chrono-tz",
|
||||
"fancy-regex 0.12.0",
|
||||
"fancy-regex",
|
||||
"indexmap",
|
||||
"nu-cmd-lang",
|
||||
"nu-engine",
|
||||
@ -2740,7 +2739,7 @@ dependencies = [
|
||||
"polars-ops",
|
||||
"polars-plan",
|
||||
"serde",
|
||||
"sqlparser",
|
||||
"sqlparser 0.41.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2748,7 +2747,7 @@ name = "nu-cmd-extra"
|
||||
version = "0.88.2"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"fancy-regex 0.11.0",
|
||||
"fancy-regex",
|
||||
"heck",
|
||||
"htmlescape",
|
||||
"nu-ansi-term",
|
||||
@ -2772,8 +2771,8 @@ dependencies = [
|
||||
name = "nu-cmd-lang"
|
||||
version = "0.88.2"
|
||||
dependencies = [
|
||||
"fancy-regex 0.11.0",
|
||||
"itertools 0.11.0",
|
||||
"fancy-regex",
|
||||
"itertools 0.12.0",
|
||||
"nu-ansi-term",
|
||||
"nu-engine",
|
||||
"nu-parser",
|
||||
@ -2816,7 +2815,7 @@ dependencies = [
|
||||
"dirs-next",
|
||||
"dtparse",
|
||||
"encoding_rs",
|
||||
"fancy-regex 0.11.0",
|
||||
"fancy-regex",
|
||||
"filesize",
|
||||
"filetime",
|
||||
"fs_extra",
|
||||
@ -2824,7 +2823,7 @@ dependencies = [
|
||||
"human-date-parser",
|
||||
"indexmap",
|
||||
"indicatif",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.0",
|
||||
"libc",
|
||||
"log",
|
||||
"lscolors",
|
||||
@ -2894,7 +2893,7 @@ dependencies = [
|
||||
"uuid",
|
||||
"wax",
|
||||
"which 5.0.0",
|
||||
"windows 0.48.0",
|
||||
"windows 0.52.0",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
@ -2973,7 +2972,7 @@ version = "0.88.2"
|
||||
dependencies = [
|
||||
"bytesize",
|
||||
"chrono",
|
||||
"itertools 0.11.0",
|
||||
"itertools 0.12.0",
|
||||
"log",
|
||||
"nu-engine",
|
||||
"nu-path",
|
||||
@ -3020,7 +3019,7 @@ dependencies = [
|
||||
"byte-unit",
|
||||
"chrono",
|
||||
"chrono-humanize",
|
||||
"fancy-regex 0.11.0",
|
||||
"fancy-regex",
|
||||
"indexmap",
|
||||
"lru",
|
||||
"miette",
|
||||
@ -3069,7 +3068,7 @@ dependencies = [
|
||||
name = "nu-table"
|
||||
version = "0.88.2"
|
||||
dependencies = [
|
||||
"fancy-regex 0.11.0",
|
||||
"fancy-regex",
|
||||
"nu-ansi-term",
|
||||
"nu-color-config",
|
||||
"nu-engine",
|
||||
@ -3414,9 +3413,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "300.2.1+3.2.0"
|
||||
version = "300.1.6+3.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fe476c29791a5ca0d1273c697e96085bbabbbea2ef7afd5617e78a4b40332d3"
|
||||
checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
@ -4052,7 +4051,7 @@ dependencies = [
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sqlparser",
|
||||
"sqlparser 0.39.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4392,9 +4391,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "reedline"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147452ce32c2cba4900b410f1d18b9ca29b67301a8eed76676e42e720d71cc39"
|
||||
version = "0.27.1"
|
||||
source = "git+https://github.com/nushell/reedline.git?branch=main#e097b88dab538705c7b165cf3a1f5cf3a74a23bb"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"crossterm",
|
||||
@ -4901,9 +4899,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "shadow-rs"
|
||||
version = "0.24.1"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9198caff1c94f1a5df6664bddbc379896b51b98a55b0b3fedcb23078fe00c77"
|
||||
checksum = "615d846f7174a0850dca101bca72f6913e3376a64c5fda2b965d7fc3d1ff60cb"
|
||||
dependencies = [
|
||||
"const_format",
|
||||
"is_debug",
|
||||
@ -5076,6 +5074,15 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sqlparser"
|
||||
version = "0.41.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5cc2c25a6c66789625ef164b4c7d2e548d627902280c13710d33da8222169964"
|
||||
dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.0"
|
||||
@ -5747,9 +5754,9 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
|
||||
|
||||
[[package]]
|
||||
name = "unsafe-libyaml"
|
||||
version = "0.2.9"
|
||||
version = "0.2.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
|
||||
checksum = "ab4c90930b95a82d00dc9e9ac071b4991924390d46cbd0dfe566148667605e4b"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
@ -6138,11 +6145,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "windows"
|
||||
version = "0.48.0"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
|
||||
checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
"windows-core 0.52.0",
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -6154,6 +6162,15 @@ dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
|
@ -33,7 +33,11 @@ members = [
|
||||
"crates/nu-cmd-lang",
|
||||
"crates/nu-cmd-dataframe",
|
||||
"crates/nu-command",
|
||||
"crates/nu-color-config",
|
||||
"crates/nu-explore",
|
||||
"crates/nu-json",
|
||||
"crates/nu-lsp",
|
||||
"crates/nu-pretty-hex",
|
||||
"crates/nu-protocol",
|
||||
"crates/nu-plugin",
|
||||
"crates/nu_plugin_inc",
|
||||
@ -43,6 +47,8 @@ members = [
|
||||
"crates/nu_plugin_custom_values",
|
||||
"crates/nu_plugin_formats",
|
||||
"crates/nu-std",
|
||||
"crates/nu-table",
|
||||
"crates/nu-term-grid",
|
||||
"crates/nu-utils",
|
||||
]
|
||||
|
||||
@ -68,6 +74,7 @@ nu-table = { path = "./crates/nu-table", version = "0.88.2" }
|
||||
nu-term-grid = { path = "./crates/nu-term-grid", version = "0.88.2" }
|
||||
nu-std = { path = "./crates/nu-std", version = "0.88.2" }
|
||||
nu-utils = { path = "./crates/nu-utils", version = "0.88.2" }
|
||||
|
||||
nu-ansi-term = "0.49.0"
|
||||
reedline = { version = "0.27.0", features = ["bashisms", "sqlite"] }
|
||||
|
||||
@ -166,7 +173,7 @@ bench = false
|
||||
# To use a development version of a dependency please use a global override here
|
||||
# changing versions in each sub-crate of the workspace is tedious
|
||||
[patch.crates-io]
|
||||
# reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
|
||||
reedline = { git = "https://github.com/nushell/reedline.git", branch = "main" }
|
||||
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
|
||||
# uu_cp = { git = "https://github.com/uutils/coreutils.git", branch = "main" }
|
||||
|
||||
|
@ -24,13 +24,9 @@ fn canonicalize_path(engine_state: &EngineState, path: &Path) -> PathBuf {
|
||||
}
|
||||
|
||||
fn get_home_path(engine_state: &EngineState) -> PathBuf {
|
||||
let home_path = if let Some(path) = nu_path::home_dir() {
|
||||
let canon_home_path = canonicalize_path(engine_state, &path);
|
||||
canon_home_path
|
||||
} else {
|
||||
std::path::PathBuf::new()
|
||||
};
|
||||
home_path
|
||||
nu_path::home_dir()
|
||||
.map(|path| canonicalize_path(engine_state, &path))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
// FIXME: All benchmarks live in this 1 file to speed up build times when benchmarking.
|
||||
|
@ -29,7 +29,7 @@ reedline = { version = "0.27.0", features = ["bashisms", "sqlite"] }
|
||||
|
||||
chrono = { default-features = false, features = ["std"], version = "0.4" }
|
||||
crossterm = "0.27"
|
||||
fancy-regex = "0.11"
|
||||
fancy-regex = "0.12"
|
||||
fuzzy-matcher = "0.3"
|
||||
is_executable = "1.0"
|
||||
log = "0.4"
|
||||
|
@ -250,7 +250,9 @@ impl NuCompleter {
|
||||
working_set.get_span_contents(previous_expr.0).to_vec();
|
||||
|
||||
// Completion for .nu files
|
||||
if prev_expr_str == b"use" || prev_expr_str == b"source-env"
|
||||
if prev_expr_str == b"use"
|
||||
|| prev_expr_str == b"overlay use"
|
||||
|| prev_expr_str == b"source-env"
|
||||
{
|
||||
let mut completer =
|
||||
DotNuCompletion::new(self.engine_state.clone());
|
||||
|
@ -22,7 +22,10 @@ fn complete_rec(
|
||||
Some(base) if matches(base, &entry_name, options) => {
|
||||
let partial = &partial[1..];
|
||||
if !partial.is_empty() || isdir {
|
||||
completions.extend(complete_rec(partial, &path, options, dir, isdir))
|
||||
completions.extend(complete_rec(partial, &path, options, dir, isdir));
|
||||
if entry_name.eq(base) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
completions.push(path)
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use nu_protocol::{
|
||||
};
|
||||
use reedline::Suggestion;
|
||||
use std::{
|
||||
path::{is_separator, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
|
||||
path::{is_separator, Path, MAIN_SEPARATOR as SEP, MAIN_SEPARATOR_STR},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
@ -91,16 +91,21 @@ impl Completer for DotNuCompletion {
|
||||
// and transform them into suggestions
|
||||
let output: Vec<Suggestion> = search_dirs
|
||||
.into_iter()
|
||||
.flat_map(|it| {
|
||||
file_path_completion(span, &partial, &it, options)
|
||||
.flat_map(|search_dir| {
|
||||
let completions = file_path_completion(span, &partial, &search_dir, options);
|
||||
completions
|
||||
.into_iter()
|
||||
.filter(|it| {
|
||||
.filter(move |it| {
|
||||
// Different base dir, so we list the .nu files or folders
|
||||
if !is_current_folder {
|
||||
it.1.ends_with(".nu") || it.1.ends_with(SEP)
|
||||
} else {
|
||||
// Lib dirs, so we filter only the .nu files
|
||||
it.1.ends_with(".nu")
|
||||
// Lib dirs, so we filter only the .nu files or directory modules
|
||||
if it.1.ends_with(SEP) {
|
||||
Path::new(&search_dir).join(&it.1).join("mod.nu").exists()
|
||||
} else {
|
||||
it.1.ends_with(".nu")
|
||||
}
|
||||
}
|
||||
})
|
||||
.map(move |x| Suggestion {
|
||||
|
@ -35,6 +35,10 @@ pub fn evaluate_commands(
|
||||
let mut working_set = StateWorkingSet::new(engine_state);
|
||||
|
||||
let output = parse(&mut working_set, None, commands.item.as_bytes(), false);
|
||||
if let Some(warning) = working_set.parse_warnings.first() {
|
||||
report_error(&working_set, warning);
|
||||
}
|
||||
|
||||
if let Some(err) = working_set.parse_errors.first() {
|
||||
report_error(&working_set, err);
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::prompt_update::{POST_PROMPT_MARKER, PRE_PROMPT_MARKER};
|
||||
#[cfg(windows)]
|
||||
use nu_utils::enable_vt_processing;
|
||||
use reedline::DefaultPrompt;
|
||||
@ -11,6 +12,7 @@ use {
|
||||
/// Nushell prompt definition
|
||||
#[derive(Clone)]
|
||||
pub struct NushellPrompt {
|
||||
shell_integration: bool,
|
||||
left_prompt_string: Option<String>,
|
||||
right_prompt_string: Option<String>,
|
||||
default_prompt_indicator: Option<String>,
|
||||
@ -20,15 +22,10 @@ pub struct NushellPrompt {
|
||||
render_right_prompt_on_last_line: bool,
|
||||
}
|
||||
|
||||
impl Default for NushellPrompt {
|
||||
fn default() -> Self {
|
||||
NushellPrompt::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl NushellPrompt {
|
||||
pub fn new() -> NushellPrompt {
|
||||
pub fn new(shell_integration: bool) -> NushellPrompt {
|
||||
NushellPrompt {
|
||||
shell_integration,
|
||||
left_prompt_string: None,
|
||||
right_prompt_string: None,
|
||||
default_prompt_indicator: None,
|
||||
@ -111,7 +108,11 @@ impl Prompt for NushellPrompt {
|
||||
.to_string()
|
||||
.replace('\n', "\r\n");
|
||||
|
||||
prompt.into()
|
||||
if self.shell_integration {
|
||||
format!("{PRE_PROMPT_MARKER}{prompt}{POST_PROMPT_MARKER}").into()
|
||||
} else {
|
||||
prompt.into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,6 @@ use nu_protocol::{
|
||||
Config, PipelineData, Value,
|
||||
};
|
||||
use reedline::Prompt;
|
||||
use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
|
||||
// Name of environment variable where the prompt could be stored
|
||||
pub(crate) const PROMPT_COMMAND: &str = "PROMPT_COMMAND";
|
||||
@ -28,8 +26,8 @@ pub(crate) const TRANSIENT_PROMPT_MULTILINE_INDICATOR: &str =
|
||||
"TRANSIENT_PROMPT_MULTILINE_INDICATOR";
|
||||
// According to Daniel Imms @Tyriar, we need to do these this way:
|
||||
// <133 A><prompt><133 B><command><133 C><command output>
|
||||
const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
||||
const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
|
||||
pub(crate) const PRE_PROMPT_MARKER: &str = "\x1b]133;A\x1b\\";
|
||||
pub(crate) const POST_PROMPT_MARKER: &str = "\x1b]133;B\x1b\\";
|
||||
|
||||
fn get_prompt_string(
|
||||
prompt: &str,
|
||||
@ -98,12 +96,12 @@ fn get_prompt_string(
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn update_prompt<'prompt>(
|
||||
pub(crate) fn update_prompt(
|
||||
config: &Config,
|
||||
engine_state: &EngineState,
|
||||
stack: &Stack,
|
||||
nu_prompt: &'prompt mut NushellPrompt,
|
||||
) -> &'prompt dyn Prompt {
|
||||
nu_prompt: &mut NushellPrompt,
|
||||
) {
|
||||
let mut stack = stack.clone();
|
||||
|
||||
let left_prompt_string = get_prompt_string(PROMPT_COMMAND, config, engine_state, &mut stack);
|
||||
@ -146,125 +144,55 @@ pub(crate) fn update_prompt<'prompt>(
|
||||
(prompt_vi_insert_string, prompt_vi_normal_string),
|
||||
config.render_right_prompt_on_last_line,
|
||||
);
|
||||
|
||||
let ret_val = nu_prompt as &dyn Prompt;
|
||||
trace!("update_prompt {}:{}:{}", file!(), line!(), column!());
|
||||
|
||||
ret_val
|
||||
}
|
||||
|
||||
struct TransientPrompt {
|
||||
engine_state: Arc<EngineState>,
|
||||
stack: Stack,
|
||||
}
|
||||
|
||||
/// Try getting `$env.TRANSIENT_PROMPT_<X>`, and get `$env.PROMPT_<X>` if that fails
|
||||
fn get_transient_prompt_string(
|
||||
transient_prompt: &str,
|
||||
prompt: &str,
|
||||
/// Construct the transient prompt based on the normal nu_prompt
|
||||
pub(crate) fn make_transient_prompt(
|
||||
config: &Config,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
) -> Option<String> {
|
||||
get_prompt_string(transient_prompt, config, engine_state, stack)
|
||||
.or_else(|| get_prompt_string(prompt, config, engine_state, stack))
|
||||
}
|
||||
nu_prompt: &NushellPrompt,
|
||||
) -> Box<dyn Prompt> {
|
||||
let mut nu_prompt = nu_prompt.clone();
|
||||
|
||||
impl Prompt for TransientPrompt {
|
||||
fn render_prompt_left(&self) -> Cow<str> {
|
||||
let mut nu_prompt = NushellPrompt::new();
|
||||
let config = &self.engine_state.get_config().clone();
|
||||
let mut stack = self.stack.clone();
|
||||
nu_prompt.update_prompt_left(get_transient_prompt_string(
|
||||
TRANSIENT_PROMPT_COMMAND,
|
||||
PROMPT_COMMAND,
|
||||
config,
|
||||
&self.engine_state,
|
||||
&mut stack,
|
||||
));
|
||||
nu_prompt.render_prompt_left().to_string().into()
|
||||
if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND, config, engine_state, stack) {
|
||||
nu_prompt.update_prompt_left(Some(s))
|
||||
}
|
||||
|
||||
fn render_prompt_right(&self) -> Cow<str> {
|
||||
let mut nu_prompt = NushellPrompt::new();
|
||||
let config = &self.engine_state.get_config().clone();
|
||||
let mut stack = self.stack.clone();
|
||||
nu_prompt.update_prompt_right(
|
||||
get_transient_prompt_string(
|
||||
TRANSIENT_PROMPT_COMMAND_RIGHT,
|
||||
PROMPT_COMMAND_RIGHT,
|
||||
config,
|
||||
&self.engine_state,
|
||||
&mut stack,
|
||||
),
|
||||
config.render_right_prompt_on_last_line,
|
||||
);
|
||||
nu_prompt.render_prompt_right().to_string().into()
|
||||
if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_COMMAND_RIGHT, config, engine_state, stack)
|
||||
{
|
||||
nu_prompt.update_prompt_right(Some(s), config.render_right_prompt_on_last_line)
|
||||
}
|
||||
|
||||
fn render_prompt_indicator(&self, prompt_mode: reedline::PromptEditMode) -> Cow<str> {
|
||||
let mut nu_prompt = NushellPrompt::new();
|
||||
let config = &self.engine_state.get_config().clone();
|
||||
let mut stack = self.stack.clone();
|
||||
nu_prompt.update_prompt_indicator(get_transient_prompt_string(
|
||||
TRANSIENT_PROMPT_INDICATOR,
|
||||
PROMPT_INDICATOR,
|
||||
config,
|
||||
&self.engine_state,
|
||||
&mut stack,
|
||||
));
|
||||
nu_prompt.update_prompt_vi_insert(get_transient_prompt_string(
|
||||
TRANSIENT_PROMPT_INDICATOR_VI_INSERT,
|
||||
PROMPT_INDICATOR_VI_INSERT,
|
||||
config,
|
||||
&self.engine_state,
|
||||
&mut stack,
|
||||
));
|
||||
nu_prompt.update_prompt_vi_normal(get_transient_prompt_string(
|
||||
TRANSIENT_PROMPT_INDICATOR_VI_NORMAL,
|
||||
PROMPT_INDICATOR_VI_NORMAL,
|
||||
config,
|
||||
&self.engine_state,
|
||||
&mut stack,
|
||||
));
|
||||
nu_prompt
|
||||
.render_prompt_indicator(prompt_mode)
|
||||
.to_string()
|
||||
.into()
|
||||
if let Some(s) = get_prompt_string(TRANSIENT_PROMPT_INDICATOR, config, engine_state, stack) {
|
||||
nu_prompt.update_prompt_indicator(Some(s))
|
||||
}
|
||||
|
||||
fn render_prompt_multiline_indicator(&self) -> Cow<str> {
|
||||
let mut nu_prompt = NushellPrompt::new();
|
||||
let config = &self.engine_state.get_config().clone();
|
||||
let mut stack = self.stack.clone();
|
||||
nu_prompt.update_prompt_multiline(get_transient_prompt_string(
|
||||
TRANSIENT_PROMPT_MULTILINE_INDICATOR,
|
||||
PROMPT_MULTILINE_INDICATOR,
|
||||
config,
|
||||
&self.engine_state,
|
||||
&mut stack,
|
||||
));
|
||||
nu_prompt
|
||||
.render_prompt_multiline_indicator()
|
||||
.to_string()
|
||||
.into()
|
||||
}
|
||||
|
||||
fn render_prompt_history_search_indicator(
|
||||
&self,
|
||||
history_search: reedline::PromptHistorySearch,
|
||||
) -> Cow<str> {
|
||||
NushellPrompt::new()
|
||||
.render_prompt_history_search_indicator(history_search)
|
||||
.to_string()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct the transient prompt
|
||||
pub(crate) fn transient_prompt(engine_state: Arc<EngineState>, stack: &Stack) -> Box<dyn Prompt> {
|
||||
Box::new(TransientPrompt {
|
||||
if let Some(s) = get_prompt_string(
|
||||
TRANSIENT_PROMPT_INDICATOR_VI_INSERT,
|
||||
config,
|
||||
engine_state,
|
||||
stack: stack.clone(),
|
||||
})
|
||||
stack,
|
||||
) {
|
||||
nu_prompt.update_prompt_vi_insert(Some(s))
|
||||
}
|
||||
if let Some(s) = get_prompt_string(
|
||||
TRANSIENT_PROMPT_INDICATOR_VI_NORMAL,
|
||||
config,
|
||||
engine_state,
|
||||
stack,
|
||||
) {
|
||||
nu_prompt.update_prompt_vi_normal(Some(s))
|
||||
}
|
||||
|
||||
if let Some(s) = get_prompt_string(
|
||||
TRANSIENT_PROMPT_MULTILINE_INDICATOR,
|
||||
config,
|
||||
engine_state,
|
||||
stack,
|
||||
) {
|
||||
nu_prompt.update_prompt_multiline(Some(s))
|
||||
}
|
||||
|
||||
Box::new(nu_prompt)
|
||||
}
|
||||
|
@ -54,7 +54,8 @@ pub fn evaluate_repl(
|
||||
) -> Result<()> {
|
||||
use nu_cmd_base::hook;
|
||||
use reedline::Signal;
|
||||
let use_color = engine_state.get_config().use_ansi_coloring;
|
||||
let config = engine_state.get_config();
|
||||
let use_color = config.use_ansi_coloring;
|
||||
|
||||
// Guard against invocation without a connected terminal.
|
||||
// reedline / crossterm event polling will fail without a connected tty
|
||||
@ -68,7 +69,7 @@ pub fn evaluate_repl(
|
||||
|
||||
let mut entry_num = 0;
|
||||
|
||||
let mut nu_prompt = NushellPrompt::new();
|
||||
let mut nu_prompt = NushellPrompt::new(config.shell_integration);
|
||||
|
||||
let start_time = std::time::Instant::now();
|
||||
// Translate environment variables from Strings to Values
|
||||
@ -212,20 +213,6 @@ pub fn evaluate_repl(
|
||||
use_color,
|
||||
);
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
// Reset the SIGQUIT handler
|
||||
if let Some(sig_quit) = engine_state.get_sig_quit() {
|
||||
sig_quit.store(false, Ordering::SeqCst);
|
||||
}
|
||||
perf(
|
||||
"reset sig_quit",
|
||||
start_time,
|
||||
file!(),
|
||||
line!(),
|
||||
column!(),
|
||||
use_color,
|
||||
);
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
let config = engine_state.get_config();
|
||||
|
||||
@ -267,11 +254,7 @@ pub fn evaluate_repl(
|
||||
.with_quick_completions(config.quick_completions)
|
||||
.with_partial_completions(config.partial_completions)
|
||||
.with_ansi_colors(config.use_ansi_coloring)
|
||||
.with_cursor_config(cursor_config)
|
||||
.with_transient_prompt(prompt_update::transient_prompt(
|
||||
engine_reference.clone(),
|
||||
stack,
|
||||
));
|
||||
.with_cursor_config(cursor_config);
|
||||
perf(
|
||||
"reedline builder",
|
||||
start_time,
|
||||
@ -424,7 +407,9 @@ pub fn evaluate_repl(
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
let config = &engine_state.get_config().clone();
|
||||
let prompt = prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
|
||||
prompt_update::update_prompt(config, engine_state, stack, &mut nu_prompt);
|
||||
let transient_prompt =
|
||||
prompt_update::make_transient_prompt(config, engine_state, stack, &nu_prompt);
|
||||
perf(
|
||||
"update_prompt",
|
||||
start_time,
|
||||
@ -437,7 +422,8 @@ pub fn evaluate_repl(
|
||||
entry_num += 1;
|
||||
|
||||
start_time = std::time::Instant::now();
|
||||
let input = line_editor.read_line(prompt);
|
||||
line_editor = line_editor.with_transient_prompt(transient_prompt);
|
||||
let input = line_editor.read_line(&nu_prompt);
|
||||
let shell_integration = config.shell_integration;
|
||||
|
||||
match input {
|
||||
@ -764,7 +750,7 @@ fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> Option<SetCursorSty
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
|
||||
fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String {
|
||||
let exit_code = stack
|
||||
.get_env_var(engine_state, "LAST_EXIT_CODE")
|
||||
.and_then(|e| e.as_i64().ok());
|
||||
|
@ -329,7 +329,6 @@ fn find_matching_block_end_in_expr(
|
||||
Expr::ImportPattern(_) => None,
|
||||
Expr::Overlay(_) => None,
|
||||
Expr::Signature(_) => None,
|
||||
Expr::MatchPattern(_) => None,
|
||||
Expr::MatchBlock(_) => None,
|
||||
Expr::Nothing => None,
|
||||
Expr::Garbage => None,
|
||||
|
@ -220,6 +220,10 @@ pub fn eval_source(
|
||||
source,
|
||||
false,
|
||||
);
|
||||
if let Some(warning) = working_set.parse_warnings.first() {
|
||||
report_error(&working_set, warning);
|
||||
}
|
||||
|
||||
if let Some(err) = working_set.parse_errors.first() {
|
||||
set_last_exit_code(stack, 1);
|
||||
report_error(&working_set, err);
|
||||
|
@ -91,7 +91,7 @@ fn variables_dollar_sign_with_varialblecompletion() {
|
||||
let target_dir = "$ ";
|
||||
let suggestions = completer.complete(target_dir, target_dir.len());
|
||||
|
||||
assert_eq!(7, suggestions.len());
|
||||
assert_eq!(8, suggestions.len());
|
||||
}
|
||||
|
||||
#[rstest]
|
||||
@ -144,15 +144,34 @@ fn dotnu_completions() {
|
||||
let completion_str = "source-env ".to_string();
|
||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||
|
||||
assert_eq!(1, suggestions.len());
|
||||
assert_eq!(2, suggestions.len());
|
||||
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
|
||||
#[cfg(windows)]
|
||||
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
|
||||
#[cfg(not(windows))]
|
||||
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
|
||||
|
||||
// Test use completion
|
||||
let completion_str = "use ".to_string();
|
||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||
|
||||
assert_eq!(1, suggestions.len());
|
||||
assert_eq!(2, suggestions.len());
|
||||
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
|
||||
#[cfg(windows)]
|
||||
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
|
||||
#[cfg(not(windows))]
|
||||
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
|
||||
|
||||
// Test overlay use completion
|
||||
let completion_str = "overlay use ".to_string();
|
||||
let suggestions = completer.complete(&completion_str, completion_str.len());
|
||||
|
||||
assert_eq!(2, suggestions.len());
|
||||
assert_eq!("custom_completion.nu", suggestions.first().unwrap().value);
|
||||
#[cfg(windows)]
|
||||
assert_eq!("directory_completion\\", suggestions.get(1).unwrap().value);
|
||||
#[cfg(not(windows))]
|
||||
assert_eq!("directory_completion/", suggestions.get(1).unwrap().value);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -208,6 +227,7 @@ fn file_completions() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
folder(dir.join("another")),
|
||||
file(dir.join("custom_completion.nu")),
|
||||
folder(dir.join("directory_completion")),
|
||||
file(dir.join("nushell")),
|
||||
folder(dir.join("test_a")),
|
||||
folder(dir.join("test_b")),
|
||||
@ -323,6 +343,7 @@ fn command_ls_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another\\".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion\\".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a\\".to_string(),
|
||||
"test_b\\".to_string(),
|
||||
@ -333,6 +354,7 @@ fn command_ls_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another/".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion/".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a/".to_string(),
|
||||
"test_b/".to_string(),
|
||||
@ -355,6 +377,7 @@ fn command_open_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another\\".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion\\".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a\\".to_string(),
|
||||
"test_b\\".to_string(),
|
||||
@ -365,6 +388,7 @@ fn command_open_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another/".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion/".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a/".to_string(),
|
||||
"test_b/".to_string(),
|
||||
@ -388,6 +412,7 @@ fn command_rm_with_globcompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another\\".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion\\".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a\\".to_string(),
|
||||
"test_b\\".to_string(),
|
||||
@ -398,6 +423,7 @@ fn command_rm_with_globcompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another/".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion/".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a/".to_string(),
|
||||
"test_b/".to_string(),
|
||||
@ -421,6 +447,7 @@ fn command_cp_with_globcompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another\\".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion\\".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a\\".to_string(),
|
||||
"test_b\\".to_string(),
|
||||
@ -431,6 +458,7 @@ fn command_cp_with_globcompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another/".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion/".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a/".to_string(),
|
||||
"test_b/".to_string(),
|
||||
@ -454,6 +482,7 @@ fn command_save_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another\\".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion\\".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a\\".to_string(),
|
||||
"test_b\\".to_string(),
|
||||
@ -464,6 +493,7 @@ fn command_save_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another/".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion/".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a/".to_string(),
|
||||
"test_b/".to_string(),
|
||||
@ -487,6 +517,7 @@ fn command_touch_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another\\".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion\\".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a\\".to_string(),
|
||||
"test_b\\".to_string(),
|
||||
@ -497,6 +528,7 @@ fn command_touch_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another/".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion/".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a/".to_string(),
|
||||
"test_b/".to_string(),
|
||||
@ -520,6 +552,7 @@ fn command_watch_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another\\".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion\\".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a\\".to_string(),
|
||||
"test_b\\".to_string(),
|
||||
@ -530,6 +563,7 @@ fn command_watch_with_filecompletion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another/".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion/".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a/".to_string(),
|
||||
"test_b/".to_string(),
|
||||
@ -625,6 +659,7 @@ fn folder_with_directorycompletions() {
|
||||
// Create the expected values
|
||||
let expected_paths: Vec<String> = vec![
|
||||
folder(dir.join("another")),
|
||||
folder(dir.join("directory_completion")),
|
||||
folder(dir.join("test_a")),
|
||||
folder(dir.join("test_b")),
|
||||
folder(dir.join(".hidden_folder")),
|
||||
@ -839,6 +874,7 @@ fn unknown_command_completion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another\\".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion\\".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a\\".to_string(),
|
||||
"test_b\\".to_string(),
|
||||
@ -849,6 +885,7 @@ fn unknown_command_completion() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another/".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion/".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a/".to_string(),
|
||||
"test_b/".to_string(),
|
||||
@ -899,6 +936,7 @@ fn filecompletions_triggers_after_cursor() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another\\".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion\\".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a\\".to_string(),
|
||||
"test_b\\".to_string(),
|
||||
@ -909,6 +947,7 @@ fn filecompletions_triggers_after_cursor() {
|
||||
let expected_paths: Vec<String> = vec![
|
||||
"another/".to_string(),
|
||||
"custom_completion.nu".to_string(),
|
||||
"directory_completion/".to_string(),
|
||||
"nushell".to_string(),
|
||||
"test_a/".to_string(),
|
||||
"test_b/".to_string(),
|
||||
|
@ -24,11 +24,11 @@ fancy-regex = "0.12"
|
||||
indexmap = { version = "2.1" }
|
||||
num = { version = "0.4", optional = true }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
sqlparser = { version = "0.39", optional = true }
|
||||
sqlparser = { version = "0.41", optional = true }
|
||||
polars-io = { version = "0.35", features = ["avro"], optional = true }
|
||||
polars-arrow = "0.35"
|
||||
polars-ops = "0.35"
|
||||
polars-plan = "0.35"
|
||||
polars-arrow = { version = "0.35", optional = true }
|
||||
polars-ops = { version = "0.35", optional = true }
|
||||
polars-plan = { version = "0.35", optional = true }
|
||||
|
||||
[dependencies.polars]
|
||||
features = [
|
||||
@ -65,7 +65,7 @@ optional = true
|
||||
version = "0.35"
|
||||
|
||||
[features]
|
||||
dataframe = ["num", "polars", "polars-io", "sqlparser"]
|
||||
dataframe = ["num", "polars", "polars-io", "polars-arrow", "polars-ops", "polars-plan", "sqlparser"]
|
||||
default = []
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -874,7 +874,7 @@ fn series_to_values(
|
||||
.iter()
|
||||
.map(|field| field.name.to_string())
|
||||
.collect();
|
||||
let record = Record { cols, vals: vals? };
|
||||
let record = Record::from_raw_cols_vals(cols, vals?);
|
||||
Ok(Value::record(record, span))
|
||||
})
|
||||
.collect();
|
||||
@ -982,10 +982,7 @@ fn any_value_to_value(any_value: &AnyValue, span: Span) -> Result<Value, ShellEr
|
||||
.map(|f| f.name().to_string())
|
||||
.collect();
|
||||
Ok(Value::Record {
|
||||
val: Record {
|
||||
cols: fields,
|
||||
vals: values?,
|
||||
},
|
||||
val: Record::from_raw_cols_vals(fields, values?),
|
||||
internal_span: span,
|
||||
})
|
||||
}
|
||||
@ -1059,6 +1056,7 @@ fn time_from_midnight(nanos: i64, span: Span) -> Result<Value, ShellError> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use indexmap::indexmap;
|
||||
use nu_protocol::record;
|
||||
use polars::export::arrow::array::{BooleanArray, PrimitiveArray};
|
||||
use polars::prelude::Field;
|
||||
use polars_io::prelude::StructArray;
|
||||
@ -1249,13 +1247,10 @@ mod tests {
|
||||
Field::new(field_name_1, DataType::Boolean),
|
||||
];
|
||||
let test_owned_struct = AnyValue::StructOwned(Box::new((values, fields.clone())));
|
||||
let comparison_owned_record = Value::record(
|
||||
Record {
|
||||
cols: vec![field_name_0.to_owned(), field_name_1.to_owned()],
|
||||
vals: vec![Value::int(1, span), Value::bool(true, span)],
|
||||
},
|
||||
span,
|
||||
);
|
||||
let comparison_owned_record = Value::test_record(record!(
|
||||
field_name_0 => Value::int(1, span),
|
||||
field_name_1 => Value::bool(true, span),
|
||||
));
|
||||
assert_eq!(
|
||||
any_value_to_value(&test_owned_struct, span)?,
|
||||
comparison_owned_record.clone()
|
||||
|
@ -158,7 +158,10 @@ impl NuDataFrame {
|
||||
.map(|i| format!("{i}"))
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
conversion::insert_record(&mut column_values, Record { cols, vals })?
|
||||
conversion::insert_record(
|
||||
&mut column_values,
|
||||
Record::from_raw_cols_vals(cols, vals),
|
||||
)?
|
||||
}
|
||||
Value::Record { val: record, .. } => {
|
||||
conversion::insert_record(&mut column_values, record)?
|
||||
|
@ -24,7 +24,7 @@ heck = "0.4.1"
|
||||
num-traits = "0.2"
|
||||
ahash = "0.8.3"
|
||||
nu-ansi-term = "0.49.0"
|
||||
fancy-regex = "0.11.0"
|
||||
fancy-regex = "0.12.0"
|
||||
rust-embed = "8.0.0"
|
||||
serde = "1.0.164"
|
||||
nu-pretty-hex = { version = "0.88.2", path = "../nu-pretty-hex" }
|
||||
|
@ -65,7 +65,7 @@ pub fn add_extra_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
bind_command!(platform::ansi::Gradient);
|
||||
|
||||
bind_command!(
|
||||
strings::format::Format,
|
||||
strings::format::FormatPattern,
|
||||
strings::encode_decode::EncodeHex,
|
||||
strings::encode_decode::DecodeHex,
|
||||
strings::str_::case::Str,
|
||||
|
@ -10,15 +10,15 @@ use nu_protocol::{
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Format;
|
||||
pub struct FormatPattern;
|
||||
|
||||
impl Command for Format {
|
||||
impl Command for FormatPattern {
|
||||
fn name(&self) -> &str {
|
||||
"format"
|
||||
"format pattern"
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("format")
|
||||
Signature::build("format pattern")
|
||||
.input_output_types(vec![
|
||||
(Type::Table(vec![]), Type::List(Box::new(Type::String))),
|
||||
(Type::Record(vec![]), Type::Any),
|
||||
@ -80,12 +80,12 @@ impl Command for Format {
|
||||
vec![
|
||||
Example {
|
||||
description: "Print filenames with their sizes",
|
||||
example: "ls | format '{name}: {size}'",
|
||||
example: "ls | format pattern '{name}: {size}'",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Print elements from some columns of a table",
|
||||
example: "[[col1, col2]; [v1, v2] [v3, v4]] | format '{col2}'",
|
||||
example: "[[col1, col2]; [v1, v2] [v3, v4]] | format pattern '{col2}'",
|
||||
result: Some(Value::list(
|
||||
vec![Value::test_string("v2"), Value::test_string("v4")],
|
||||
Span::test_data(),
|
||||
@ -318,8 +318,8 @@ fn format_record(
|
||||
mod test {
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use super::Format;
|
||||
use super::FormatPattern;
|
||||
use crate::test_examples;
|
||||
test_examples(Format {})
|
||||
test_examples(FormatPattern {})
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,3 @@
|
||||
mod command;
|
||||
|
||||
pub(crate) use command::Format;
|
||||
pub(crate) use command::FormatPattern;
|
||||
|
@ -18,12 +18,12 @@ nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
|
||||
nu-utils = { path = "../nu-utils", version = "0.88.2" }
|
||||
nu-ansi-term = "0.49.0"
|
||||
|
||||
fancy-regex = "0.11"
|
||||
itertools = "0.11"
|
||||
shadow-rs = { version = "0.24", default-features = false }
|
||||
fancy-regex = "0.12"
|
||||
itertools = "0.12"
|
||||
shadow-rs = { version = "0.25", default-features = false }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = { version = "0.24", default-features = false }
|
||||
shadow-rs = { version = "0.25", default-features = false }
|
||||
|
||||
[features]
|
||||
mimalloc = []
|
||||
|
@ -290,7 +290,6 @@ fn describe_value(
|
||||
| Value::Date { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::String { .. }
|
||||
| Value::MatchPattern { .. }
|
||||
| Value::Nothing { .. } => Value::record(
|
||||
record!(
|
||||
"type" => Value::string(value.get_type().to_string(), head),
|
||||
|
@ -278,9 +278,6 @@ impl<'a> std::fmt::Debug for DebuggableValue<'a> {
|
||||
let rec = val.collect().map_err(|_| std::fmt::Error)?;
|
||||
write!(f, "LazyRecord({:?})", DebuggableValue(&rec))
|
||||
}
|
||||
Value::MatchPattern { val, .. } => {
|
||||
write!(f, "MatchPattern({:?})", val)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ fn color_string_to_nustyle(color_string: String) -> Style {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use nu_ansi_term::{Color, Style};
|
||||
use nu_protocol::{Span, Value};
|
||||
use nu_protocol::{record, Span, Value};
|
||||
|
||||
#[test]
|
||||
fn test_color_string_to_nustyle_empty_string() {
|
||||
@ -120,13 +120,10 @@ mod tests {
|
||||
#[test]
|
||||
fn test_get_style_from_value() {
|
||||
// Test case 1: all values are valid
|
||||
let record = Record {
|
||||
cols: vec!["bg".to_string(), "fg".to_string(), "attr".to_string()],
|
||||
vals: vec![
|
||||
Value::string("red", Span::unknown()),
|
||||
Value::string("blue", Span::unknown()),
|
||||
Value::string("bold", Span::unknown()),
|
||||
],
|
||||
let record = record! {
|
||||
"bg" => Value::test_string("red"),
|
||||
"fg" => Value::test_string("blue"),
|
||||
"attr" => Value::test_string("bold"),
|
||||
};
|
||||
let expected_style = NuStyle {
|
||||
bg: Some("red".to_string()),
|
||||
@ -136,19 +133,15 @@ mod tests {
|
||||
assert_eq!(get_style_from_value(&record), Some(expected_style));
|
||||
|
||||
// Test case 2: no values are valid
|
||||
let record = Record {
|
||||
cols: vec!["invalid".to_string()],
|
||||
vals: vec![Value::nothing(Span::unknown())],
|
||||
let record = record! {
|
||||
"invalid" => Value::nothing(Span::test_data()),
|
||||
};
|
||||
assert_eq!(get_style_from_value(&record), None);
|
||||
|
||||
// Test case 3: some values are valid
|
||||
let record = Record {
|
||||
cols: vec!["bg".to_string(), "invalid".to_string()],
|
||||
vals: vec![
|
||||
Value::string("green", Span::unknown()),
|
||||
Value::nothing(Span::unknown()),
|
||||
],
|
||||
let record = record! {
|
||||
"bg" => Value::test_string("green"),
|
||||
"invalid" => Value::nothing(Span::unknown()),
|
||||
};
|
||||
let expected_style = NuStyle {
|
||||
bg: Some("green".to_string()),
|
||||
|
@ -131,8 +131,7 @@ impl<'a> StyleComputer<'a> {
|
||||
Value::Closure { .. }
|
||||
| Value::CustomValue { .. }
|
||||
| Value::Error { .. }
|
||||
| Value::LazyRecord { .. }
|
||||
| Value::MatchPattern { .. } => TextStyle::basic_left(),
|
||||
| Value::LazyRecord { .. } => TextStyle::basic_left(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ dialoguer = { default-features = false, features = ["fuzzy-select"], version = "
|
||||
digest = { default-features = false, version = "0.10" }
|
||||
dtparse = "2.0"
|
||||
encoding_rs = "0.8"
|
||||
fancy-regex = "0.11"
|
||||
fancy-regex = "0.12"
|
||||
filesize = "0.2"
|
||||
filetime = "0.2"
|
||||
fs_extra = "1.3"
|
||||
@ -50,9 +50,9 @@ htmlescape = "0.3"
|
||||
human-date-parser = "0.1.1"
|
||||
indexmap = "2.1"
|
||||
indicatif = "0.17"
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
log = "0.4"
|
||||
lscolors = { version = "0.15", default-features = false, features = ["nu-ansi-term"] }
|
||||
lscolors = { version = "0.16", default-features = false, features = ["nu-ansi-term"] }
|
||||
md5 = { package = "md-5", version = "0.10" }
|
||||
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
|
||||
mime = "0.3"
|
||||
@ -104,9 +104,9 @@ winreg = "0.52"
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2"
|
||||
umask = "2.1"
|
||||
nix = { version = "0.27", default-features = false, features = ["user"] }
|
||||
nix = { version = "0.27", default-features = false, features = ["user", "resource"] }
|
||||
|
||||
[target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "android"), not(target_os = "ios")))'.dependencies]
|
||||
[target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies]
|
||||
procfs = "0.16.0"
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash]
|
||||
@ -122,7 +122,7 @@ features = [
|
||||
"Win32_Security",
|
||||
"Win32_System_Threading",
|
||||
]
|
||||
version = "0.48"
|
||||
version = "0.52"
|
||||
|
||||
[features]
|
||||
plugin = ["nu-parser/plugin"]
|
||||
|
@ -258,16 +258,16 @@ fn histogram_impl(
|
||||
result.push((
|
||||
count, // attach count first for easily sorting.
|
||||
Value::record(
|
||||
Record {
|
||||
cols: result_cols.clone(),
|
||||
vals: vec![
|
||||
Record::from_raw_cols_vals(
|
||||
result_cols.clone(),
|
||||
vec![
|
||||
val.into_value(),
|
||||
Value::int(count, span),
|
||||
Value::float(quantile, span),
|
||||
Value::string(percentage, span),
|
||||
Value::string(freq, span),
|
||||
],
|
||||
},
|
||||
),
|
||||
span,
|
||||
),
|
||||
));
|
||||
|
@ -260,7 +260,6 @@ fn nu_value_to_string(value: Value, separator: &str) -> String {
|
||||
Value::Binary { val, .. } => format!("{val:?}"),
|
||||
Value::CellPath { val, .. } => val.to_string(),
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
Value::MatchPattern { val, .. } => format!("{:?}", val),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -499,13 +499,7 @@ pub fn convert_sqlite_row_to_nu_value(row: &Row, span: Span, column_names: Vec<S
|
||||
vals.push(val);
|
||||
}
|
||||
|
||||
Value::record(
|
||||
Record {
|
||||
cols: column_names,
|
||||
vals,
|
||||
},
|
||||
span,
|
||||
)
|
||||
Value::record(Record::from_raw_cols_vals(column_names, vals), span)
|
||||
}
|
||||
|
||||
pub fn convert_sqlite_value_to_nu_value(value: ValueRef, span: Span) -> Value {
|
||||
|
@ -259,6 +259,5 @@ pub fn debug_string_without_formatting(value: &Value) -> String {
|
||||
Value::Binary { val, .. } => format!("{val:?}"),
|
||||
Value::CellPath { val, .. } => val.to_string(),
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
Value::MatchPattern { val, .. } => format!("{:?}", val),
|
||||
}
|
||||
}
|
||||
|
@ -233,6 +233,9 @@ pub fn add_shell_command_context(mut engine_state: EngineState) -> EngineState {
|
||||
Whoami,
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
bind_command! { ULimit };
|
||||
|
||||
// Date
|
||||
bind_command! {
|
||||
Date,
|
||||
|
@ -66,16 +66,17 @@ fn is_root_impl() -> bool {
|
||||
System::Threading::{GetCurrentProcess, OpenProcessToken},
|
||||
};
|
||||
|
||||
let mut handle = HANDLE::default();
|
||||
let mut elevated = false;
|
||||
|
||||
// Checks whether the access token associated with the current process has elevated privileges.
|
||||
// SAFETY: `elevated` only touched by safe code.
|
||||
// `handle` lives long enough, initialized, mutated as out param, used, closed with validity check.
|
||||
// `handle` lives long enough, initialized, mutated, used and closed with validity check.
|
||||
// `elevation` only read on success and passed with correct `size`.
|
||||
unsafe {
|
||||
let mut handle = HANDLE::default();
|
||||
|
||||
// Opens the access token associated with the current process.
|
||||
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle).as_bool() {
|
||||
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut handle).is_ok() {
|
||||
let mut elevation = TOKEN_ELEVATION::default();
|
||||
let mut size = std::mem::size_of::<TOKEN_ELEVATION>() as u32;
|
||||
|
||||
@ -89,7 +90,7 @@ fn is_root_impl() -> bool {
|
||||
size,
|
||||
&mut size,
|
||||
)
|
||||
.as_bool()
|
||||
.is_ok()
|
||||
{
|
||||
// Whether the token has elevated privileges.
|
||||
// Safe to read as `GetTokenInformation` will not write outside `elevation` and it succeeded
|
||||
@ -100,7 +101,7 @@ fn is_root_impl() -> bool {
|
||||
|
||||
if !handle.is_invalid() {
|
||||
// Closes the object handle.
|
||||
CloseHandle(handle);
|
||||
let _ = CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,7 +229,16 @@ fn have_permission(dir: impl AsRef<Path>) -> PermissionResult<'static> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn any_group(_current_user_gid: gid_t, owner_group: u32) -> bool {
|
||||
use crate::filesystem::util::users;
|
||||
let Some(user_groups) = users::current_user_groups() else {
|
||||
return false;
|
||||
};
|
||||
user_groups.iter().any(|gid| gid.as_raw() == owner_group)
|
||||
}
|
||||
|
||||
#[cfg(all(unix, not(any(target_os = "linux", target_os = "android"))))]
|
||||
fn any_group(current_user_gid: gid_t, owner_group: u32) -> bool {
|
||||
use crate::filesystem::util::users;
|
||||
|
||||
|
@ -165,9 +165,8 @@ pub fn is_older(src: &Path, dst: &Path) -> Option<bool> {
|
||||
|
||||
#[cfg(unix)]
|
||||
pub mod users {
|
||||
use libc::{c_int, gid_t, uid_t};
|
||||
use libc::{gid_t, uid_t};
|
||||
use nix::unistd::{Gid, Group, Uid, User};
|
||||
use std::ffi::CString;
|
||||
|
||||
pub fn get_user_by_uid(uid: uid_t) -> Option<User> {
|
||||
User::from_uid(Uid::from_raw(uid)).ok().flatten()
|
||||
@ -185,6 +184,7 @@ pub mod users {
|
||||
Gid::current().as_raw()
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn get_current_username() -> Option<String> {
|
||||
User::from_uid(Uid::current())
|
||||
.ok()
|
||||
@ -192,6 +192,30 @@ pub mod users {
|
||||
.map(|user| user.name)
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn current_user_groups() -> Option<Vec<Gid>> {
|
||||
// SAFETY:
|
||||
// if first arg is 0 then it ignores second argument and returns number of groups present for given user.
|
||||
let ngroups = unsafe { libc::getgroups(0, core::ptr::null::<gid_t> as *mut _) };
|
||||
let mut buff: Vec<gid_t> = vec![0; ngroups as usize];
|
||||
|
||||
// SAFETY:
|
||||
// buff is the size of ngroups and getgroups reads max ngroups elements into buff
|
||||
let found = unsafe { libc::getgroups(ngroups, buff.as_mut_ptr()) };
|
||||
|
||||
if found < 0 {
|
||||
None
|
||||
} else {
|
||||
buff.truncate(found as usize);
|
||||
buff.sort_unstable();
|
||||
buff.dedup();
|
||||
buff.into_iter()
|
||||
.filter_map(|i| get_group_by_gid(i as gid_t))
|
||||
.map(|group| group.gid)
|
||||
.collect::<Vec<_>>()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
/// Returns groups for a provided user name and primary group id.
|
||||
///
|
||||
/// # libc functions used
|
||||
@ -207,7 +231,9 @@ pub mod users {
|
||||
/// println!("User is a member of group #{group}");
|
||||
/// }
|
||||
/// ```
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn get_user_groups(username: &str, gid: gid_t) -> Option<Vec<Gid>> {
|
||||
use std::ffi::CString;
|
||||
// MacOS uses i32 instead of gid_t in getgrouplist for unknown reasons
|
||||
#[cfg(target_os = "macos")]
|
||||
let mut buff: Vec<i32> = vec![0; 1024];
|
||||
@ -218,7 +244,7 @@ pub mod users {
|
||||
return None;
|
||||
};
|
||||
|
||||
let mut count = buff.len() as c_int;
|
||||
let mut count = buff.len() as libc::c_int;
|
||||
|
||||
// MacOS uses i32 instead of gid_t in getgrouplist for unknown reasons
|
||||
// SAFETY:
|
||||
|
@ -516,7 +516,6 @@ fn value_should_be_printed(
|
||||
Err(_) => false,
|
||||
},
|
||||
Value::Binary { .. } => false,
|
||||
Value::MatchPattern { .. } => false,
|
||||
});
|
||||
if invert {
|
||||
match_found = !match_found;
|
||||
|
@ -174,7 +174,7 @@ pub fn group_by(
|
||||
Value::CellPath { val, .. } => group_cell_path(val, values)?,
|
||||
Value::Block { .. } | Value::Closure { .. } => {
|
||||
let block: Option<Closure> = call.opt(engine_state, stack, 0)?;
|
||||
group_closure(&values, span, block, stack, engine_state, call)?
|
||||
group_closure(values, span, block, stack, engine_state, call)?
|
||||
}
|
||||
|
||||
_ => {
|
||||
@ -231,9 +231,8 @@ pub fn group_no_grouper(values: Vec<Value>) -> Result<IndexMap<String, Vec<Value
|
||||
Ok(groups)
|
||||
}
|
||||
|
||||
// TODO: refactor this, it's a bit of a mess
|
||||
fn group_closure(
|
||||
values: &[Value],
|
||||
values: Vec<Value>,
|
||||
span: Span,
|
||||
block: Option<Closure>,
|
||||
stack: &mut Stack,
|
||||
@ -241,13 +240,13 @@ fn group_closure(
|
||||
call: &Call,
|
||||
) -> Result<IndexMap<String, Vec<Value>>, ShellError> {
|
||||
let error_key = "error";
|
||||
let mut keys: Vec<Result<String, ShellError>> = vec![];
|
||||
let value_list = Value::list(values.to_vec(), span);
|
||||
let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
|
||||
|
||||
for value in values {
|
||||
if let Some(capture_block) = &block {
|
||||
if let Some(capture_block) = &block {
|
||||
let block = engine_state.get_block(capture_block.block_id);
|
||||
|
||||
for value in values {
|
||||
let mut stack = stack.captures_to_stack(capture_block.captures.clone());
|
||||
let block = engine_state.get_block(capture_block.block_id);
|
||||
let pipeline = eval_block(
|
||||
engine_state,
|
||||
&mut stack,
|
||||
@ -257,11 +256,16 @@ fn group_closure(
|
||||
call.redirect_stderr,
|
||||
);
|
||||
|
||||
match pipeline {
|
||||
let group_key = match pipeline {
|
||||
Ok(s) => {
|
||||
let collection: Vec<Value> = s.into_iter().collect();
|
||||
let mut s = s.into_iter();
|
||||
|
||||
if collection.len() > 1 {
|
||||
let key = match s.next() {
|
||||
Some(Value::Error { .. }) | None => error_key.into(),
|
||||
Some(return_value) => return_value.as_string()?,
|
||||
};
|
||||
|
||||
if s.next().is_some() {
|
||||
return Err(ShellError::GenericError {
|
||||
error: "expected one value from the block".into(),
|
||||
msg: "requires a table with one value for grouping".into(),
|
||||
@ -271,39 +275,14 @@ fn group_closure(
|
||||
});
|
||||
}
|
||||
|
||||
let value = match collection.first() {
|
||||
Some(Value::Error { .. }) | None => Value::string(error_key, span),
|
||||
Some(return_value) => return_value.clone(),
|
||||
};
|
||||
key
|
||||
}
|
||||
Err(_) => error_key.into(),
|
||||
};
|
||||
|
||||
keys.push(value.as_string());
|
||||
}
|
||||
Err(_) => {
|
||||
keys.push(Ok(error_key.into()));
|
||||
}
|
||||
}
|
||||
groups.entry(group_key).or_default().push(value);
|
||||
}
|
||||
}
|
||||
let map = keys;
|
||||
let block = Box::new(move |idx: usize, row: &Value| match map.get(idx) {
|
||||
Some(Ok(key)) => Ok(key.clone()),
|
||||
Some(Err(reason)) => Err(reason.clone()),
|
||||
None => row.as_string(),
|
||||
});
|
||||
|
||||
let grouper = &Some(block);
|
||||
let mut groups: IndexMap<String, Vec<Value>> = IndexMap::new();
|
||||
|
||||
for (idx, value) in value_list.into_pipeline_data().into_iter().enumerate() {
|
||||
let group_key = if let Some(ref grouper) = grouper {
|
||||
grouper(idx, &value)
|
||||
} else {
|
||||
value.as_string()
|
||||
};
|
||||
|
||||
let group = groups.entry(group_key?).or_default();
|
||||
group.push(value);
|
||||
}
|
||||
|
||||
Ok(groups)
|
||||
}
|
||||
|
@ -55,10 +55,7 @@ fn from_delimited_string_to_value(
|
||||
.collect::<Vec<Value>>();
|
||||
|
||||
rows.push(Value::record(
|
||||
Record {
|
||||
cols: headers.clone(),
|
||||
vals: output_row,
|
||||
},
|
||||
Record::from_raw_cols_vals(headers.clone(), output_row),
|
||||
span,
|
||||
));
|
||||
}
|
||||
|
@ -242,12 +242,6 @@ fn convert_to_value(
|
||||
msg: "extra tokens in input file".into(),
|
||||
span: expr.span,
|
||||
}),
|
||||
Expr::MatchPattern(..) => Err(ShellError::OutsideSpannedLabeledError {
|
||||
src: original_text.to_string(),
|
||||
error: "Error when loading".into(),
|
||||
msg: "extra tokens in input file".into(),
|
||||
span: expr.span,
|
||||
}),
|
||||
Expr::GlobPattern(val) => Ok(Value::string(val, span)),
|
||||
Expr::ImportPattern(..) => Err(ShellError::OutsideSpannedLabeledError {
|
||||
src: original_text.to_string(),
|
||||
@ -409,13 +403,7 @@ fn convert_to_value(
|
||||
}
|
||||
|
||||
for row in cells {
|
||||
let mut vals = vec![];
|
||||
|
||||
for cell in row {
|
||||
vals.push(convert_to_value(cell, span, original_text)?);
|
||||
}
|
||||
|
||||
if cols.len() != vals.len() {
|
||||
if cols.len() != row.len() {
|
||||
return Err(ShellError::OutsideSpannedLabeledError {
|
||||
src: original_text.to_string(),
|
||||
error: "Error when loading".into(),
|
||||
@ -423,12 +411,13 @@ fn convert_to_value(
|
||||
span: expr.span,
|
||||
});
|
||||
}
|
||||
let vals: Vec<Value> = row
|
||||
.into_iter()
|
||||
.map(|cell| convert_to_value(cell, span, original_text))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
output.push(Value::record(
|
||||
Record {
|
||||
cols: cols.clone(),
|
||||
vals,
|
||||
},
|
||||
Record::from_raw_cols_vals(cols.clone(), vals),
|
||||
span,
|
||||
));
|
||||
}
|
||||
|
@ -127,10 +127,7 @@ pub fn value_to_json_value(v: &Value) -> Result<nu_json::Value, ShellError> {
|
||||
|
||||
Value::List { vals, .. } => nu_json::Value::Array(json_list(vals)?),
|
||||
Value::Error { error, .. } => return Err(*error.clone()),
|
||||
Value::Closure { .. }
|
||||
| Value::Block { .. }
|
||||
| Value::Range { .. }
|
||||
| Value::MatchPattern { .. } => nu_json::Value::Null,
|
||||
Value::Closure { .. } | Value::Block { .. } | Value::Range { .. } => nu_json::Value::Null,
|
||||
Value::Binary { val, .. } => {
|
||||
nu_json::Value::Array(val.iter().map(|x| nu_json::Value::U64(*x as u64)).collect())
|
||||
}
|
||||
|
@ -241,12 +241,6 @@ pub fn value_to_string(
|
||||
))
|
||||
}
|
||||
}
|
||||
Value::MatchPattern { .. } => Err(ShellError::UnsupportedInput {
|
||||
msg: "match patterns are currently not nuon-compatible".to_string(),
|
||||
input: "value originates from here".into(),
|
||||
msg_span: span,
|
||||
input_span: v.span(),
|
||||
}),
|
||||
Value::Nothing { .. } => Ok("null".to_string()),
|
||||
Value::Range { val, .. } => Ok(format!(
|
||||
"{}..{}{}",
|
||||
|
@ -148,7 +148,6 @@ fn local_into_string(value: Value, separator: &str, config: &Config) -> String {
|
||||
Value::Binary { val, .. } => format!("{val:?}"),
|
||||
Value::CellPath { val, .. } => val.to_string(),
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
Value::MatchPattern { val, .. } => format!("{:?}", val),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,6 @@ fn helper(engine_state: &EngineState, v: &Value) -> Result<toml::Value, ShellErr
|
||||
.collect::<Result<Vec<toml::Value>, ShellError>>()?,
|
||||
),
|
||||
Value::CustomValue { .. } => toml::Value::String("<Custom Value>".to_string()),
|
||||
Value::MatchPattern { .. } => toml::Value::String("<Match Pattern>".to_string()),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,6 @@ pub fn value_to_yaml_value(v: &Value) -> Result<serde_yaml::Value, ShellError> {
|
||||
.collect::<Result<Vec<serde_yaml::Value>, ShellError>>()?,
|
||||
),
|
||||
Value::CustomValue { .. } => serde_yaml::Value::Null,
|
||||
Value::MatchPattern { .. } => serde_yaml::Value::Null,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,8 @@ mod is_terminal;
|
||||
mod kill;
|
||||
mod sleep;
|
||||
mod term_size;
|
||||
#[cfg(unix)]
|
||||
mod ulimit;
|
||||
mod whoami;
|
||||
|
||||
pub use ansi::{Ansi, AnsiLink, AnsiStrip};
|
||||
@ -20,4 +22,6 @@ pub use is_terminal::IsTerminal;
|
||||
pub use kill::Kill;
|
||||
pub use sleep::Sleep;
|
||||
pub use term_size::TermSize;
|
||||
#[cfg(unix)]
|
||||
pub use ulimit::ULimit;
|
||||
pub use whoami::Whoami;
|
||||
|
603
crates/nu-command/src/platform/ulimit.rs
Normal file
603
crates/nu-command/src/platform/ulimit.rs
Normal file
@ -0,0 +1,603 @@
|
||||
use nix::sys::resource::{rlim_t, Resource, RLIM_INFINITY};
|
||||
use nu_engine::CallExt;
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
Category, Example, IntoPipelineData, PipelineData, Record, ShellError, Signature, Span,
|
||||
SyntaxShape, Type, Value,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
/// An object contains resource related parameters
|
||||
struct ResourceInfo<'a> {
|
||||
name: &'a str,
|
||||
desc: &'a str,
|
||||
flag: char,
|
||||
multiplier: rlim_t,
|
||||
resource: Resource,
|
||||
}
|
||||
|
||||
impl<'a> ResourceInfo<'a> {
|
||||
/// Create a `ResourceInfo` object
|
||||
fn new(
|
||||
name: &'a str,
|
||||
desc: &'a str,
|
||||
flag: char,
|
||||
multiplier: rlim_t,
|
||||
resource: Resource,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
desc,
|
||||
flag,
|
||||
multiplier,
|
||||
resource,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get unit
|
||||
fn get_unit(&self) -> &str {
|
||||
if self.resource == Resource::RLIMIT_CPU {
|
||||
"(seconds, "
|
||||
} else if self.multiplier == 1 {
|
||||
"("
|
||||
} else {
|
||||
"(kB, "
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Default for ResourceInfo<'a> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: "file-size",
|
||||
desc: "Maximum size of files created by the shell",
|
||||
flag: 'f',
|
||||
multiplier: 1024,
|
||||
resource: Resource::RLIMIT_FSIZE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static RESOURCE_ARRAY: Lazy<Vec<ResourceInfo>> = Lazy::new(|| {
|
||||
let resources = [
|
||||
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
|
||||
(
|
||||
"socket-buffers",
|
||||
"Maximum size of socket buffers",
|
||||
'b',
|
||||
1024,
|
||||
Resource::RLIMIT_SBSIZE,
|
||||
),
|
||||
(
|
||||
"core-size",
|
||||
"Maximum size of core files created",
|
||||
'c',
|
||||
1024,
|
||||
Resource::RLIMIT_CORE,
|
||||
),
|
||||
(
|
||||
"data-size",
|
||||
"Maximum size of a process's data segment",
|
||||
'd',
|
||||
1024,
|
||||
Resource::RLIMIT_DATA,
|
||||
),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
(
|
||||
"nice",
|
||||
"Controls of maximum nice priority",
|
||||
'e',
|
||||
1,
|
||||
Resource::RLIMIT_NICE,
|
||||
),
|
||||
(
|
||||
"file-size",
|
||||
"Maximum size of files created by the shell",
|
||||
'f',
|
||||
1024,
|
||||
Resource::RLIMIT_FSIZE,
|
||||
),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
(
|
||||
"pending-signals",
|
||||
"Maximum number of pending signals",
|
||||
'i',
|
||||
1,
|
||||
Resource::RLIMIT_SIGPENDING,
|
||||
),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "linux",
|
||||
target_os = "netbsd"
|
||||
))]
|
||||
(
|
||||
"lock-size",
|
||||
"Maximum size that may be locked into memory",
|
||||
'l',
|
||||
1024,
|
||||
Resource::RLIMIT_MEMLOCK,
|
||||
),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "linux",
|
||||
target_os = "aix",
|
||||
))]
|
||||
(
|
||||
"resident-set-size",
|
||||
"Maximum resident set size",
|
||||
'm',
|
||||
1024,
|
||||
Resource::RLIMIT_RSS,
|
||||
),
|
||||
(
|
||||
"file-descriptor-count",
|
||||
"Maximum number of open file descriptors",
|
||||
'n',
|
||||
1,
|
||||
Resource::RLIMIT_NOFILE,
|
||||
),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
(
|
||||
"queue-size",
|
||||
"Maximum bytes in POSIX message queues",
|
||||
'q',
|
||||
1024,
|
||||
Resource::RLIMIT_MSGQUEUE,
|
||||
),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
(
|
||||
"realtime-priority",
|
||||
"Maximum realtime scheduling priority",
|
||||
'r',
|
||||
1,
|
||||
Resource::RLIMIT_RTPRIO,
|
||||
),
|
||||
(
|
||||
"stack-size",
|
||||
"Maximum stack size",
|
||||
's',
|
||||
1024,
|
||||
Resource::RLIMIT_STACK,
|
||||
),
|
||||
(
|
||||
"cpu-time",
|
||||
"Maximum amount of CPU time in seconds",
|
||||
't',
|
||||
1,
|
||||
Resource::RLIMIT_CPU,
|
||||
),
|
||||
#[cfg(any(
|
||||
target_os = "android",
|
||||
target_os = "freebsd",
|
||||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "linux",
|
||||
target_os = "aix",
|
||||
))]
|
||||
(
|
||||
"process-count",
|
||||
"Maximum number of processes available to the current user",
|
||||
'u',
|
||||
1,
|
||||
Resource::RLIMIT_NPROC,
|
||||
),
|
||||
#[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
|
||||
(
|
||||
"virtual-memory-size",
|
||||
"Maximum amount of virtual memory available to each process",
|
||||
'v',
|
||||
1024,
|
||||
Resource::RLIMIT_AS,
|
||||
),
|
||||
#[cfg(target_os = "freebsd")]
|
||||
(
|
||||
"swap-size",
|
||||
"Maximum swap space",
|
||||
'w',
|
||||
1024,
|
||||
Resource::RLIMIT_SWAP,
|
||||
),
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
(
|
||||
"file-locks",
|
||||
"Maximum number of file locks",
|
||||
'x',
|
||||
1,
|
||||
Resource::RLIMIT_LOCKS,
|
||||
),
|
||||
#[cfg(target_os = "linux")]
|
||||
(
|
||||
"realtime-maxtime",
|
||||
"Maximum contiguous realtime CPU time",
|
||||
'y',
|
||||
1,
|
||||
Resource::RLIMIT_RTTIME,
|
||||
),
|
||||
#[cfg(target_os = "freebsd")]
|
||||
(
|
||||
"kernel-queues",
|
||||
"Maximum number of kqueues",
|
||||
'K',
|
||||
1,
|
||||
Resource::RLIMIT_KQUEUES,
|
||||
),
|
||||
#[cfg(target_os = "freebsd")]
|
||||
(
|
||||
"ptys",
|
||||
"Maximum number of pseudo-terminals",
|
||||
'P',
|
||||
1,
|
||||
Resource::RLIMIT_NPTS,
|
||||
),
|
||||
];
|
||||
|
||||
let mut resource_array = Vec::new();
|
||||
for (name, desc, flag, multiplier, res) in resources {
|
||||
resource_array.push(ResourceInfo::new(name, desc, flag, multiplier, res));
|
||||
}
|
||||
|
||||
resource_array
|
||||
});
|
||||
|
||||
/// Convert `rlim_t` to `Value` representation
|
||||
fn limit_to_value(limit: rlim_t, multiplier: rlim_t, span: Span) -> Result<Value, ShellError> {
|
||||
if limit == RLIM_INFINITY {
|
||||
return Ok(Value::string("unlimited", span));
|
||||
}
|
||||
|
||||
let val = match i64::try_from(limit / multiplier) {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
return Err(ShellError::CantConvert {
|
||||
to_type: "i64".into(),
|
||||
from_type: "rlim_t".into(),
|
||||
span,
|
||||
help: Some(e.to_string()),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Value::int(val, span))
|
||||
}
|
||||
|
||||
/// Get maximum length of all flag descriptions
|
||||
fn max_desc_len(call: &Call, print_all: bool) -> usize {
|
||||
let mut desc_len = 0;
|
||||
let mut unit_len = 0;
|
||||
|
||||
for res in RESOURCE_ARRAY.iter() {
|
||||
if !print_all && !call.has_flag(res.name) {
|
||||
continue;
|
||||
}
|
||||
|
||||
desc_len = res.desc.len().max(desc_len);
|
||||
unit_len = res.get_unit().len().max(unit_len);
|
||||
}
|
||||
|
||||
// Use `RLIMIT_FSIZE` limit if no resource flag provided.
|
||||
if desc_len == 0 {
|
||||
let res = ResourceInfo::default();
|
||||
desc_len = res.desc.len().max(desc_len);
|
||||
unit_len = res.get_unit().len().max(unit_len);
|
||||
}
|
||||
|
||||
// desc.len() + unit.len() + '-X)'.len()
|
||||
desc_len + unit_len + 3
|
||||
}
|
||||
|
||||
/// Fill `ResourceInfo` to the record entry
|
||||
fn fill_record(
|
||||
res: &ResourceInfo,
|
||||
max_len: usize,
|
||||
soft: bool,
|
||||
hard: bool,
|
||||
span: Span,
|
||||
) -> Result<Record, ShellError> {
|
||||
let mut record = Record::new();
|
||||
let mut desc = String::new();
|
||||
|
||||
desc.push_str(res.desc);
|
||||
|
||||
debug_assert!(res.desc.len() + res.get_unit().len() + 3 <= max_len);
|
||||
let width = max_len - res.desc.len() - res.get_unit().len() - 3;
|
||||
if width == 0 {
|
||||
desc.push_str(format!(" {}-{})", res.get_unit(), res.flag).as_str());
|
||||
} else {
|
||||
desc.push_str(format!("{:>width$} {}-{})", ' ', res.get_unit(), res.flag).as_str());
|
||||
}
|
||||
|
||||
record.push("description", Value::string(desc, span));
|
||||
|
||||
let (soft_limit, hard_limit) = getrlimit(res.resource)?;
|
||||
|
||||
if soft {
|
||||
let soft_limit = limit_to_value(soft_limit, res.multiplier, span)?;
|
||||
record.push("soft", soft_limit);
|
||||
}
|
||||
|
||||
if hard {
|
||||
let hard_limit = limit_to_value(hard_limit, res.multiplier, span)?;
|
||||
record.push("hard", hard_limit);
|
||||
}
|
||||
|
||||
Ok(record)
|
||||
}
|
||||
|
||||
/// Set limits
|
||||
fn set_limits(
|
||||
limit_value: &Value,
|
||||
res: &ResourceInfo,
|
||||
soft: bool,
|
||||
hard: bool,
|
||||
call_span: Span,
|
||||
) -> Result<(), ShellError> {
|
||||
let (mut soft_limit, mut hard_limit) = getrlimit(res.resource)?;
|
||||
let new_limit = parse_limit(limit_value, res, soft, soft_limit, hard_limit, call_span)?;
|
||||
|
||||
if hard {
|
||||
hard_limit = new_limit;
|
||||
}
|
||||
|
||||
if soft {
|
||||
soft_limit = new_limit;
|
||||
|
||||
// Do not attempt to set the soft limit higher than the hard limit.
|
||||
if (new_limit > hard_limit || new_limit == RLIM_INFINITY) && hard_limit != RLIM_INFINITY {
|
||||
soft_limit = hard_limit;
|
||||
}
|
||||
}
|
||||
|
||||
setrlimit(res.resource, soft_limit, hard_limit)
|
||||
}
|
||||
|
||||
/// Print limits
|
||||
fn print_limits(
|
||||
call: &Call,
|
||||
print_all: bool,
|
||||
soft: bool,
|
||||
hard: bool,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let mut output = Vec::new();
|
||||
let mut print_default_limit = true;
|
||||
let max_len = max_desc_len(call, print_all);
|
||||
|
||||
for res in RESOURCE_ARRAY.iter() {
|
||||
if !print_all {
|
||||
// Print specified limit.
|
||||
if !call.has_flag(res.name) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
let record = fill_record(res, max_len, soft, hard, call.head)?;
|
||||
output.push(Value::record(record, call.head));
|
||||
|
||||
if print_default_limit {
|
||||
print_default_limit = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Print `RLIMIT_FSIZE` limit if no resource flag provided.
|
||||
if print_default_limit {
|
||||
let res = ResourceInfo::default();
|
||||
let record = fill_record(&res, max_len, soft, hard, call.head)?;
|
||||
output.push(Value::record(record, call.head));
|
||||
}
|
||||
|
||||
Ok(Value::list(output, call.head).into_pipeline_data())
|
||||
}
|
||||
|
||||
/// Wrap `nix::sys::resource::getrlimit`
|
||||
fn setrlimit(res: Resource, soft_limit: rlim_t, hard_limit: rlim_t) -> Result<(), ShellError> {
|
||||
nix::sys::resource::setrlimit(res, soft_limit, hard_limit).map_err(|e| {
|
||||
ShellError::GenericError {
|
||||
error: e.to_string(),
|
||||
msg: String::new(),
|
||||
span: None,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Wrap `nix::sys::resource::setrlimit`
|
||||
fn getrlimit(res: Resource) -> Result<(rlim_t, rlim_t), ShellError> {
|
||||
nix::sys::resource::getrlimit(res).map_err(|e| ShellError::GenericError {
|
||||
error: e.to_string(),
|
||||
msg: String::new(),
|
||||
span: None,
|
||||
help: None,
|
||||
inner: vec![],
|
||||
})
|
||||
}
|
||||
|
||||
/// Parse user input
|
||||
fn parse_limit(
|
||||
limit_value: &Value,
|
||||
res: &ResourceInfo,
|
||||
soft: bool,
|
||||
soft_limit: rlim_t,
|
||||
hard_limit: rlim_t,
|
||||
call_span: Span,
|
||||
) -> Result<rlim_t, ShellError> {
|
||||
match limit_value {
|
||||
Value::Int { val, internal_span } => {
|
||||
let value = rlim_t::try_from(*val).map_err(|e| ShellError::CantConvert {
|
||||
to_type: "rlim_t".into(),
|
||||
from_type: "i64".into(),
|
||||
span: *internal_span,
|
||||
help: Some(e.to_string()),
|
||||
})?;
|
||||
|
||||
let (limit, overflow) = value.overflowing_mul(res.multiplier);
|
||||
if overflow {
|
||||
Ok(RLIM_INFINITY)
|
||||
} else {
|
||||
Ok(limit)
|
||||
}
|
||||
}
|
||||
Value::Filesize { val, internal_span } => {
|
||||
if res.multiplier != 1024 {
|
||||
return Err(ShellError::TypeMismatch {
|
||||
err_message: format!(
|
||||
"filesize is not compatible with resource {:?}",
|
||||
res.resource
|
||||
),
|
||||
span: *internal_span,
|
||||
});
|
||||
}
|
||||
|
||||
rlim_t::try_from(*val).map_err(|e| ShellError::CantConvert {
|
||||
to_type: "rlim_t".into(),
|
||||
from_type: "i64".into(),
|
||||
span: *internal_span,
|
||||
help: Some(e.to_string()),
|
||||
})
|
||||
}
|
||||
Value::String { val, internal_span } => {
|
||||
if val == "unlimited" {
|
||||
Ok(RLIM_INFINITY)
|
||||
} else if val == "soft" {
|
||||
if soft {
|
||||
Ok(hard_limit)
|
||||
} else {
|
||||
Ok(soft_limit)
|
||||
}
|
||||
} else if val == "hard" {
|
||||
Ok(hard_limit)
|
||||
} else {
|
||||
return Err(ShellError::IncorrectValue {
|
||||
msg: "Only unlimited, soft and hard are supported for strings".into(),
|
||||
val_span: *internal_span,
|
||||
call_span,
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => Err(ShellError::TypeMismatch {
|
||||
err_message: format!(
|
||||
"string, int or filesize required, you provide {}",
|
||||
limit_value.get_type()
|
||||
),
|
||||
span: limit_value.span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ULimit;
|
||||
|
||||
impl Command for ULimit {
|
||||
fn name(&self) -> &str {
|
||||
"ulimit"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Set or get resource usage limits."
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
let mut sig = Signature::build("ulimit")
|
||||
.input_output_types(vec![
|
||||
(Type::Nothing, Type::Table(vec![])),
|
||||
(Type::Nothing, Type::Nothing),
|
||||
])
|
||||
.switch("soft", "Sets soft resource limit", Some('S'))
|
||||
.switch("hard", "Sets hard resource limit", Some('H'))
|
||||
.switch("all", "Prints all current limits", Some('a'))
|
||||
.optional("limit", SyntaxShape::Any, "Limit value.")
|
||||
.category(Category::Platform);
|
||||
|
||||
for res in RESOURCE_ARRAY.iter() {
|
||||
sig = sig.switch(res.name, res.desc, Some(res.flag));
|
||||
}
|
||||
|
||||
sig
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let mut soft = call.has_flag("soft");
|
||||
let mut hard = call.has_flag("hard");
|
||||
let all = call.has_flag("all");
|
||||
|
||||
if !hard && !soft {
|
||||
// Set both hard and soft limits if neither was specified.
|
||||
hard = true;
|
||||
soft = true;
|
||||
}
|
||||
|
||||
if let Some(limit_value) = call.opt::<Value>(engine_state, stack, 0)? {
|
||||
let mut set_default_limit = true;
|
||||
|
||||
for res in RESOURCE_ARRAY.iter() {
|
||||
if call.has_flag(res.name) {
|
||||
set_limits(&limit_value, res, soft, hard, call.head)?;
|
||||
|
||||
if set_default_limit {
|
||||
set_default_limit = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set `RLIMIT_FSIZE` limit if no resource flag provided.
|
||||
if set_default_limit {
|
||||
let res = ResourceInfo::default();
|
||||
set_limits(&limit_value, &res, hard, soft, call.head)?;
|
||||
}
|
||||
|
||||
Ok(PipelineData::Empty)
|
||||
} else {
|
||||
print_limits(call, all, soft, hard)
|
||||
}
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "Print all current limits",
|
||||
example: "ulimit -a",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Print specified limits",
|
||||
example: "ulimit --core-size --data-size --file-size",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set limit",
|
||||
example: "ulimit --core-size 102400",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set stack size soft limit",
|
||||
example: "ulimit -s -S 10240",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set virtual memory size hard limit",
|
||||
example: "ulimit -v -H 10240",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "Set core size limit to unlimited",
|
||||
example: "ulimit -c unlimited",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn search_terms(&self) -> Vec<&str> {
|
||||
vec!["resource", "limits"]
|
||||
}
|
||||
}
|
@ -199,7 +199,10 @@ fn detect_columns(
|
||||
};
|
||||
|
||||
if !(l_idx <= r_idx && (r_idx >= 0 || l_idx < (cols.len() as isize))) {
|
||||
return Value::record(Record { cols, vals }, name_span);
|
||||
return Value::record(
|
||||
Record::from_raw_cols_vals(cols, vals),
|
||||
name_span,
|
||||
);
|
||||
}
|
||||
|
||||
(l_idx.max(0) as usize, (r_idx as usize + 1).min(cols.len()))
|
||||
@ -210,7 +213,7 @@ fn detect_columns(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Value::record(Record { cols, vals }, name_span);
|
||||
return Value::record(Record::from_raw_cols_vals(cols, vals), name_span);
|
||||
};
|
||||
|
||||
// Merge Columns
|
||||
@ -232,7 +235,7 @@ fn detect_columns(
|
||||
vals.push(binding);
|
||||
last_seg.into_iter().for_each(|v| vals.push(v));
|
||||
|
||||
Value::record(Record { cols, vals }, name_span)
|
||||
Value::record(Record::from_raw_cols_vals(cols, vals), name_span)
|
||||
})
|
||||
.into_pipeline_data(ctrlc))
|
||||
} else {
|
||||
|
@ -133,27 +133,22 @@ impl Command for SubCommand {
|
||||
result: Some(Value::test_string("Nu shell")),
|
||||
},
|
||||
Example {
|
||||
description: "Trim a specific character",
|
||||
example: "'=== Nu shell ===' | str trim --char '=' | str trim",
|
||||
result: Some(Value::test_string("Nu shell")),
|
||||
description: "Trim a specific character (not the whitespace)",
|
||||
example: "'=== Nu shell ===' | str trim --char '='",
|
||||
result: Some(Value::test_string(" Nu shell ")),
|
||||
},
|
||||
Example {
|
||||
description: "Trim whitespace from the beginning of string",
|
||||
example: "' Nu shell ' | str trim --left",
|
||||
result: Some(Value::test_string("Nu shell ")),
|
||||
},
|
||||
Example {
|
||||
description: "Trim a specific character",
|
||||
example: "'=== Nu shell ===' | str trim --char '='",
|
||||
result: Some(Value::test_string(" Nu shell ")),
|
||||
},
|
||||
Example {
|
||||
description: "Trim whitespace from the end of string",
|
||||
example: "' Nu shell ' | str trim --right",
|
||||
result: Some(Value::test_string(" Nu shell")),
|
||||
},
|
||||
Example {
|
||||
description: "Trim a specific character",
|
||||
description: "Trim a specific character only from the end of the string",
|
||||
example: "'=== Nu shell ===' | str trim --right --char '='",
|
||||
result: Some(Value::test_string("=== Nu shell ")),
|
||||
},
|
||||
|
@ -101,6 +101,8 @@ impl Command for Complete {
|
||||
|
||||
Ok(Value::record(record, call.head).into_pipeline_data())
|
||||
}
|
||||
// bubble up errors from the previous command
|
||||
PipelineData::Value(Value::Error { error, .. }, _) => Err(*error),
|
||||
_ => Err(ShellError::GenericError {
|
||||
error: "Complete only works with external streams".into(),
|
||||
msg: "complete only works on external streams".into(),
|
||||
|
@ -5,7 +5,6 @@ use itertools::Itertools;
|
||||
not(target_os = "macos"),
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "android"),
|
||||
not(target_os = "ios")
|
||||
))]
|
||||
use nu_protocol::Span;
|
||||
use nu_protocol::{
|
||||
@ -19,7 +18,6 @@ use nu_protocol::{
|
||||
not(target_os = "macos"),
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "android"),
|
||||
not(target_os = "ios")
|
||||
))]
|
||||
use procfs::WithCurrentSystemInfo;
|
||||
|
||||
@ -123,7 +121,6 @@ fn run_ps(engine_state: &EngineState, call: &Call) -> Result<PipelineData, Shell
|
||||
not(target_os = "macos"),
|
||||
not(target_os = "windows"),
|
||||
not(target_os = "android"),
|
||||
not(target_os = "ios")
|
||||
))]
|
||||
{
|
||||
let proc_stat = proc
|
||||
|
17
crates/nu-command/tests/commands/complete.rs
Normal file
17
crates/nu-command/tests/commands/complete.rs
Normal file
@ -0,0 +1,17 @@
|
||||
use nu_test_support::nu;
|
||||
|
||||
#[test]
|
||||
fn basic() {
|
||||
let actual = nu!(r#"
|
||||
(^echo a | complete) == {stdout: "a\n", exit_code: 0}
|
||||
"#);
|
||||
|
||||
assert_eq!(actual.out, "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error() {
|
||||
let actual = nu!("do { not-found } | complete");
|
||||
|
||||
assert!(actual.err.contains("executable was not found"));
|
||||
}
|
@ -7,6 +7,7 @@ mod break_;
|
||||
mod cal;
|
||||
mod cd;
|
||||
mod compact;
|
||||
mod complete;
|
||||
mod config_env_default;
|
||||
mod config_nu_default;
|
||||
mod continue_;
|
||||
@ -106,6 +107,8 @@ mod touch;
|
||||
mod transpose;
|
||||
mod try_;
|
||||
mod ucp;
|
||||
#[cfg(unix)]
|
||||
mod ulimit;
|
||||
mod umkdir;
|
||||
mod uniq;
|
||||
mod uniq_by;
|
||||
|
@ -161,9 +161,14 @@ fn same_target_redirection_with_too_much_stderr_not_hang_nushell() {
|
||||
|
||||
#[test]
|
||||
fn redirection_keep_exit_codes() {
|
||||
let out = nu!("do -i { nu --testbin fail e> a.txt } | complete | get exit_code");
|
||||
// needs to use contains "1", because it complete will output `Some(RawStream)`.
|
||||
assert!(out.out.contains('1'));
|
||||
Playground::setup("redirection preserves exit code", |dirs, _| {
|
||||
let out = nu!(
|
||||
cwd: dirs.test(),
|
||||
"do -i { nu --testbin fail e> a.txt } | complete | get exit_code"
|
||||
);
|
||||
// needs to use contains "1", because it complete will output `Some(RawStream)`.
|
||||
assert!(out.out.contains('1'));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -302,24 +307,29 @@ fn separate_redirection_support_variable() {
|
||||
|
||||
#[test]
|
||||
fn redirection_should_have_a_target() {
|
||||
let scripts = [
|
||||
"echo asdf o+e>",
|
||||
"echo asdf o>",
|
||||
"echo asdf e>",
|
||||
"echo asdf o> e>",
|
||||
"echo asdf o> tmp.txt e>",
|
||||
"echo asdf o> e> tmp.txt",
|
||||
"echo asdf o> | ignore",
|
||||
"echo asdf o>; echo asdf",
|
||||
];
|
||||
for code in scripts {
|
||||
let actual = nu!(code);
|
||||
assert!(
|
||||
actual.err.contains("expected redirection target",),
|
||||
"should be error, code: {}",
|
||||
code
|
||||
);
|
||||
}
|
||||
Playground::setup("redirection_should_have_a_target", |dirs, _| {
|
||||
let scripts = [
|
||||
"echo asdf o+e>",
|
||||
"echo asdf o>",
|
||||
"echo asdf e>",
|
||||
"echo asdf o> e>",
|
||||
"echo asdf o> tmp.txt e>",
|
||||
"echo asdf o> e> tmp.txt",
|
||||
"echo asdf o> | ignore",
|
||||
"echo asdf o>; echo asdf",
|
||||
];
|
||||
for code in scripts {
|
||||
let actual = nu!(cwd: dirs.test(), code);
|
||||
assert!(
|
||||
actual.err.contains("expected redirection target",),
|
||||
"should be error, code: {code}",
|
||||
);
|
||||
assert!(
|
||||
!dirs.test().join("tmp.txt").exists(),
|
||||
"No file should be created on error: {code}",
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -352,8 +362,24 @@ fn redirection_with_pipe() {
|
||||
|
||||
#[test]
|
||||
fn no_duplicate_redirection() {
|
||||
let actual = nu!("echo 3 o> a.txt o> a.txt");
|
||||
assert!(actual.err.contains("Redirection can be set only once"));
|
||||
let actual = nu!("echo 3 e> a.txt e> a.txt");
|
||||
assert!(actual.err.contains("Redirection can be set only once"));
|
||||
Playground::setup("redirection does not accept duplicate", |dirs, _| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"echo 3 o> a.txt o> a.txt"
|
||||
);
|
||||
assert!(actual.err.contains("Redirection can be set only once"));
|
||||
assert!(
|
||||
!dirs.test().join("a.txt").exists(),
|
||||
"No file should be created on error"
|
||||
);
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"echo 3 e> a.txt e> a.txt"
|
||||
);
|
||||
assert!(actual.err.contains("Redirection can be set only once"));
|
||||
assert!(
|
||||
!dirs.test().join("a.txt").exists(),
|
||||
"No file should be created on error"
|
||||
);
|
||||
});
|
||||
}
|
||||
|
218
crates/nu-command/tests/commands/ulimit.rs
Normal file
218
crates/nu-command/tests/commands/ulimit.rs
Normal file
@ -0,0 +1,218 @@
|
||||
use nu_test_support::playground::Playground;
|
||||
use nu_test_support::{nu, pipeline};
|
||||
|
||||
#[test]
|
||||
fn limit_set_soft1() {
|
||||
Playground::setup("limit_set_soft1", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
"
|
||||
let soft = (ulimit -s | first | get soft);
|
||||
ulimit -s -H $soft;
|
||||
let hard = (ulimit -s | first | get hard);
|
||||
$soft == $hard
|
||||
"
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("true"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_set_soft2() {
|
||||
Playground::setup("limit_set_soft2", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
"
|
||||
let soft = (ulimit -s | first | get soft);
|
||||
ulimit -s -H soft;
|
||||
let hard = (ulimit -s | first | get hard);
|
||||
$soft == $hard
|
||||
"
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("true"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_set_hard1() {
|
||||
Playground::setup("limit_set_hard1", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
"
|
||||
let hard = (ulimit -s | first | get hard);
|
||||
ulimit -s $hard;
|
||||
let soft = (ulimit -s | first | get soft);
|
||||
$soft == $hard
|
||||
"
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("true"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_set_hard2() {
|
||||
Playground::setup("limit_set_hard2", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
"
|
||||
let hard = (ulimit -s | first | get hard);
|
||||
ulimit -s hard;
|
||||
let soft = (ulimit -s | first | get soft);
|
||||
$soft == $hard
|
||||
"
|
||||
));
|
||||
|
||||
assert!(actual.out.contains("true"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_set_invalid1() {
|
||||
Playground::setup("limit_set_invalid1", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
"
|
||||
let hard = (ulimit -s | first | get hard);
|
||||
match $hard {
|
||||
\"unlimited\" => { echo \"unlimited\" },
|
||||
$x => {
|
||||
let new = $x + 1;
|
||||
ulimit -s $new
|
||||
}
|
||||
}
|
||||
"
|
||||
));
|
||||
|
||||
assert!(
|
||||
actual.out.contains("unlimited")
|
||||
|| actual.err.contains("EPERM: Operation not permitted")
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "macos"))]
|
||||
#[test]
|
||||
fn limit_set_invalid2() {
|
||||
Playground::setup("limit_set_invalid2", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"
|
||||
let val = -100;
|
||||
ulimit -c $val
|
||||
"
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("can't convert i64 to rlim_t"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_set_invalid3() {
|
||||
Playground::setup("limit_set_invalid3", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"
|
||||
ulimit -c abcd
|
||||
"
|
||||
);
|
||||
|
||||
assert!(actual
|
||||
.err
|
||||
.contains("Only unlimited, soft and hard are supported for strings"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_set_invalid4() {
|
||||
Playground::setup("limit_set_invalid4", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"
|
||||
ulimit -c 100.0
|
||||
"
|
||||
);
|
||||
|
||||
assert!(actual.err.contains("string, int or filesize required"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_set_invalid5() {
|
||||
use nix::sys::resource::rlim_t;
|
||||
|
||||
let max = (rlim_t::MAX / 1024) + 1;
|
||||
|
||||
Playground::setup("limit_set_invalid5", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
format!(
|
||||
"
|
||||
let hard = (ulimit -c | first | get hard);
|
||||
match $hard {{
|
||||
\"unlimited\" => {{
|
||||
ulimit -c -S 0;
|
||||
ulimit -c {max};
|
||||
ulimit -c
|
||||
| first
|
||||
| get soft
|
||||
}},
|
||||
_ => {{
|
||||
echo \"unlimited\"
|
||||
}}
|
||||
}}
|
||||
").as_str()
|
||||
));
|
||||
|
||||
assert!(actual.out.eq("unlimited"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_set_filesize1() {
|
||||
Playground::setup("limit_set_filesize1", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(), pipeline(
|
||||
"
|
||||
let hard = (ulimit -c | first | get hard);
|
||||
match $hard {
|
||||
\"unlimited\" => {
|
||||
ulimit -c 1Mib;
|
||||
ulimit -c
|
||||
| first
|
||||
| get soft
|
||||
},
|
||||
$x if $x >= 1024 * 1024 => {
|
||||
ulimit -c 1Mib;
|
||||
ulimit -c
|
||||
| first
|
||||
| get soft
|
||||
}
|
||||
_ => {
|
||||
echo \"hard limit too small\"
|
||||
}
|
||||
}
|
||||
"
|
||||
));
|
||||
|
||||
assert!(actual.out.eq("1024") || actual.out.eq("hard limit too small"));
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn limit_set_filesize2() {
|
||||
Playground::setup("limit_set_filesize2", |dirs, _sandbox| {
|
||||
let actual = nu!(
|
||||
cwd: dirs.test(),
|
||||
"
|
||||
ulimit -n 10Kib
|
||||
"
|
||||
);
|
||||
|
||||
assert!(actual
|
||||
.err
|
||||
.contains("filesize is not compatible with resource RLIMIT_NOFILE"));
|
||||
});
|
||||
}
|
@ -70,7 +70,7 @@ impl CallExt for Call {
|
||||
name: &str,
|
||||
) -> Result<Option<T>, ShellError> {
|
||||
if let Some(expr) = self.get_flag_expr(name) {
|
||||
let result = eval_expression(engine_state, stack, &expr)?;
|
||||
let result = eval_expression(engine_state, stack, expr)?;
|
||||
FromValue::from_value(result).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
@ -83,7 +83,7 @@ impl CallExt for Call {
|
||||
name: &str,
|
||||
) -> Result<Option<T>, ShellError> {
|
||||
if let Some(expr) = self.get_flag_expr(name) {
|
||||
let result = eval_constant(working_set, &expr)?;
|
||||
let result = eval_constant(working_set, expr)?;
|
||||
FromValue::from_value(result).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
|
@ -26,4 +26,4 @@ crossterm = "0.27"
|
||||
ratatui = "0.23"
|
||||
ansi-str = "0.8"
|
||||
unicode-width = "0.1"
|
||||
lscolors = { version = "0.15", default-features = false, features = ["nu-ansi-term"] }
|
||||
lscolors = { version = "0.16", default-features = false, features = ["nu-ansi-term"] }
|
||||
|
@ -312,7 +312,7 @@ impl LanguageServer {
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
Id::Value(_) => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
@ -347,11 +347,22 @@ impl LanguageServer {
|
||||
Id::Declaration(decl_id) => {
|
||||
let decl = working_set.get_decl(decl_id);
|
||||
|
||||
let mut description = "\n### Signature\n```\n".to_string();
|
||||
let mut description = String::new();
|
||||
|
||||
// First description
|
||||
description.push_str(&format!("{}\n", decl.usage().replace('\r', "")));
|
||||
|
||||
// Additional description
|
||||
if !decl.extra_usage().is_empty() {
|
||||
description.push_str(&format!("\n{}\n", decl.extra_usage()));
|
||||
}
|
||||
|
||||
// Usage
|
||||
description.push_str("### Usage \n```\n");
|
||||
let signature = decl.signature();
|
||||
description.push_str(&format!(" {}", signature.name));
|
||||
if !signature.named.is_empty() {
|
||||
description.push_str(" {flags}")
|
||||
description.push_str(" {flags}");
|
||||
}
|
||||
for required_arg in &signature.required_positional {
|
||||
description.push_str(&format!(" <{}>", required_arg.name));
|
||||
@ -363,6 +374,39 @@ impl LanguageServer {
|
||||
description.push_str(&format!(" <...{}>", arg.name));
|
||||
}
|
||||
description.push_str("\n```\n");
|
||||
|
||||
// Flags
|
||||
if !signature.named.is_empty() {
|
||||
description.push_str("\n### Flags\n\n");
|
||||
let mut first = true;
|
||||
for named in &signature.named {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
description.push('\n');
|
||||
}
|
||||
description.push_str(" ");
|
||||
if let Some(short_flag) = &named.short {
|
||||
description.push_str(&format!("`-{short_flag}`"));
|
||||
}
|
||||
if !named.long.is_empty() {
|
||||
if named.short.is_some() {
|
||||
description.push_str(", ");
|
||||
}
|
||||
description.push_str(&format!("`--{}`", named.long));
|
||||
}
|
||||
if let Some(arg) = &named.arg {
|
||||
description.push_str(&format!(" `<{}>`", arg.to_type()));
|
||||
}
|
||||
if !named.desc.is_empty() {
|
||||
description.push_str(&format!(" - {}", named.desc));
|
||||
}
|
||||
description.push('\n');
|
||||
}
|
||||
description.push('\n');
|
||||
}
|
||||
|
||||
// Parameters
|
||||
if !signature.required_positional.is_empty()
|
||||
|| !signature.optional_positional.is_empty()
|
||||
|| signature.rest_positional.is_some()
|
||||
@ -370,10 +414,10 @@ impl LanguageServer {
|
||||
description.push_str("\n### Parameters\n\n");
|
||||
let mut first = true;
|
||||
for required_arg in &signature.required_positional {
|
||||
if !first {
|
||||
description.push('\n');
|
||||
} else {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
description.push('\n');
|
||||
}
|
||||
description.push_str(&format!(
|
||||
" `{}: {}`",
|
||||
@ -386,10 +430,10 @@ impl LanguageServer {
|
||||
description.push('\n');
|
||||
}
|
||||
for optional_arg in &signature.optional_positional {
|
||||
if !first {
|
||||
description.push('\n');
|
||||
} else {
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
description.push('\n');
|
||||
}
|
||||
description.push_str(&format!(
|
||||
" `{}: {}`",
|
||||
@ -417,36 +461,10 @@ impl LanguageServer {
|
||||
}
|
||||
description.push('\n');
|
||||
}
|
||||
if !signature.named.is_empty() {
|
||||
description.push_str("\n### Flags\n\n");
|
||||
let mut first = true;
|
||||
for named in &signature.named {
|
||||
if !first {
|
||||
description.push('\n');
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
description.push_str(" ");
|
||||
if let Some(short_flag) = &named.short {
|
||||
description.push_str(&format!("`-{}`", short_flag));
|
||||
}
|
||||
if !named.long.is_empty() {
|
||||
if named.short.is_some() {
|
||||
description.push_str(", ")
|
||||
}
|
||||
description.push_str(&format!("`--{}`", named.long));
|
||||
}
|
||||
if let Some(arg) = &named.arg {
|
||||
description.push_str(&format!(" `<{}>`", arg.to_type()))
|
||||
}
|
||||
if !named.desc.is_empty() {
|
||||
description.push_str(&format!(" - {}", named.desc));
|
||||
}
|
||||
}
|
||||
description.push('\n');
|
||||
}
|
||||
|
||||
// Input/output types
|
||||
if !signature.input_output_types.is_empty() {
|
||||
description.push_str("\n### Input/output\n");
|
||||
description.push_str("\n### Input/output types\n");
|
||||
description.push_str("\n```\n");
|
||||
for input_output in &signature.input_output_types {
|
||||
description
|
||||
@ -454,14 +472,8 @@ impl LanguageServer {
|
||||
}
|
||||
description.push_str("\n```\n");
|
||||
}
|
||||
description.push_str(&format!(
|
||||
"### Usage\n {}\n",
|
||||
decl.usage().replace('\r', "")
|
||||
));
|
||||
if !decl.extra_usage().is_empty() {
|
||||
description
|
||||
.push_str(&format!("\n### Extra usage:\n {}\n", decl.extra_usage()));
|
||||
}
|
||||
|
||||
// Examples
|
||||
if !decl.examples().is_empty() {
|
||||
description.push_str("### Example(s)\n");
|
||||
for example in decl.examples() {
|
||||
@ -578,8 +590,9 @@ mod tests {
|
||||
},
|
||||
request::{Completion, GotoDefinition, HoverRequest, Initialize, Request, Shutdown},
|
||||
CompletionParams, DidChangeTextDocumentParams, DidOpenTextDocumentParams,
|
||||
GotoDefinitionParams, InitializeParams, InitializedParams, TextDocumentContentChangeEvent,
|
||||
TextDocumentIdentifier, TextDocumentItem, TextDocumentPositionParams, Url,
|
||||
GotoDefinitionParams, InitializeParams, InitializedParams, PartialResultParams,
|
||||
TextDocumentContentChangeEvent, TextDocumentIdentifier, TextDocumentItem,
|
||||
TextDocumentPositionParams, Url, WorkDoneProgressParams,
|
||||
};
|
||||
use nu_test_support::fs::{fixtures, root};
|
||||
use std::sync::mpsc::Receiver;
|
||||
@ -671,8 +684,8 @@ mod tests {
|
||||
character: 0,
|
||||
},
|
||||
},
|
||||
work_done_progress_params: Default::default(),
|
||||
partial_result_params: Default::default(),
|
||||
work_done_progress_params: WorkDoneProgressParams::default(),
|
||||
partial_result_params: PartialResultParams::default(),
|
||||
})
|
||||
.unwrap(),
|
||||
}))
|
||||
@ -777,8 +790,8 @@ mod tests {
|
||||
text_document: TextDocumentIdentifier { uri },
|
||||
position: lsp_types::Position { line, character },
|
||||
},
|
||||
work_done_progress_params: Default::default(),
|
||||
partial_result_params: Default::default(),
|
||||
work_done_progress_params: WorkDoneProgressParams::default(),
|
||||
partial_result_params: PartialResultParams::default(),
|
||||
})
|
||||
.unwrap(),
|
||||
}))
|
||||
@ -894,7 +907,7 @@ mod tests {
|
||||
text_document: TextDocumentIdentifier { uri },
|
||||
position: lsp_types::Position { line, character },
|
||||
},
|
||||
work_done_progress_params: Default::default(),
|
||||
work_done_progress_params: WorkDoneProgressParams::default(),
|
||||
})
|
||||
.unwrap(),
|
||||
}))
|
||||
@ -957,7 +970,7 @@ mod tests {
|
||||
serde_json::json!({
|
||||
"contents": {
|
||||
"kind": "markdown",
|
||||
"value": "\n### Signature\n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n### Usage\n Renders some greeting message\n"
|
||||
"value": "Renders some greeting message\n### Usage \n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
|
||||
}
|
||||
})
|
||||
);
|
||||
@ -974,8 +987,8 @@ mod tests {
|
||||
text_document: TextDocumentIdentifier { uri },
|
||||
position: lsp_types::Position { line, character },
|
||||
},
|
||||
work_done_progress_params: Default::default(),
|
||||
partial_result_params: Default::default(),
|
||||
work_done_progress_params: WorkDoneProgressParams::default(),
|
||||
partial_result_params: PartialResultParams::default(),
|
||||
context: None,
|
||||
})
|
||||
.unwrap(),
|
||||
|
@ -121,7 +121,7 @@ mod tests {
|
||||
serde_json::json!({
|
||||
"contents": {
|
||||
"kind": "markdown",
|
||||
"value": "\n### Signature\n```\n let {flags} <var_name> <initial_value>\n```\n\n### Parameters\n\n `var_name: any` - Variable name.\n\n `initial_value: any` - Equals sign followed by value.\n\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n### Input/output\n\n```\n any | nothing\n\n```\n### Usage\n Create a variable and give it a value.\n\n### Extra usage:\n This command is a parser keyword. For details, check:\n https://www.nushell.sh/book/thinking_in_nu.html\n### Example(s)\n Set a variable to a value\n```\n let x = 10\n```\n Set a variable to the result of an expression\n```\n let x = 10 + 100\n```\n Set a variable based on the condition\n```\n let x = if false { -1 } else { 1 }\n```\n"
|
||||
"value": "Create a variable and give it a value.\n\nThis command is a parser keyword. For details, check:\n https://www.nushell.sh/book/thinking_in_nu.html\n### Usage \n```\n let {flags} <var_name> <initial_value>\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n\n### Parameters\n\n `var_name: any` - Variable name.\n\n `initial_value: any` - Equals sign followed by value.\n\n\n### Input/output types\n\n```\n any | nothing\n\n```\n### Example(s)\n Set a variable to a value\n```\n let x = 10\n```\n Set a variable to the result of an expression\n```\n let x = 10 + 100\n```\n Set a variable based on the condition\n```\n let x = if false { -1 } else { 1 }\n```\n"
|
||||
}
|
||||
})
|
||||
);
|
||||
@ -162,7 +162,7 @@ hello"#,
|
||||
serde_json::json!({
|
||||
"contents": {
|
||||
"kind": "markdown",
|
||||
"value": "\n### Signature\n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n### Usage\n Renders some updated greeting message\n"
|
||||
"value": "Renders some updated greeting message\n### Usage \n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
|
||||
}
|
||||
})
|
||||
);
|
||||
@ -207,7 +207,7 @@ hello"#,
|
||||
serde_json::json!({
|
||||
"contents": {
|
||||
"kind": "markdown",
|
||||
"value": "\n### Signature\n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n### Usage\n Renders some updated greeting message\n"
|
||||
"value": "Renders some updated greeting message\n### Usage \n```\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
|
||||
}
|
||||
})
|
||||
);
|
||||
|
@ -19,7 +19,7 @@ nu-protocol = { path = "../nu-protocol", version = "0.88.2" }
|
||||
|
||||
bytesize = "1.3"
|
||||
chrono = { default-features = false, features = ['std'], version = "0.4" }
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
log = "0.4"
|
||||
serde_json = "1.0"
|
||||
|
||||
|
@ -264,10 +264,6 @@ pub fn flatten_expression(
|
||||
Expr::Float(_) => {
|
||||
vec![(expr.span, FlatShape::Float)]
|
||||
}
|
||||
Expr::MatchPattern(pattern) => {
|
||||
// FIXME: do nicer flattening later
|
||||
flatten_pattern(pattern)
|
||||
}
|
||||
Expr::MatchBlock(matches) => {
|
||||
let mut output = vec![];
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use nu_protocol::{
|
||||
ast::{Expr, Expression, MatchPattern, Pattern},
|
||||
ast::{MatchPattern, Pattern},
|
||||
engine::StateWorkingSet,
|
||||
ParseError, Span, SyntaxShape, Type, VarId,
|
||||
};
|
||||
@ -18,19 +18,6 @@ pub fn garbage(span: Span) -> MatchPattern {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_match_pattern(working_set: &mut StateWorkingSet, span: Span) -> Expression {
|
||||
working_set.enter_scope();
|
||||
let output = parse_pattern(working_set, span);
|
||||
working_set.exit_scope();
|
||||
|
||||
Expression {
|
||||
expr: Expr::MatchPattern(Box::new(output)),
|
||||
span,
|
||||
ty: Type::Any,
|
||||
custom_completion: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_pattern(working_set: &mut StateWorkingSet, span: Span) -> MatchPattern {
|
||||
let bytes = working_set.get_span_contents(span);
|
||||
|
||||
|
@ -2,7 +2,7 @@ use crate::{
|
||||
lex::{lex, lex_signature},
|
||||
lite_parser::{lite_parse, LiteCommand, LiteElement, LitePipeline},
|
||||
parse_mut,
|
||||
parse_patterns::{parse_match_pattern, parse_pattern},
|
||||
parse_patterns::parse_pattern,
|
||||
parse_shape_specs::{parse_shape_name, parse_type, ShapeDescriptorUse},
|
||||
type_check::{self, math_result_type, type_compatible},
|
||||
Token, TokenContents,
|
||||
@ -18,8 +18,8 @@ use nu_protocol::{
|
||||
},
|
||||
engine::StateWorkingSet,
|
||||
eval_const::eval_constant,
|
||||
span, BlockId, DidYouMean, Flag, ParseError, PositionalArg, Signature, Span, Spanned,
|
||||
SyntaxShape, Type, Unit, VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID,
|
||||
span, BlockId, DidYouMean, Flag, ParseError, ParseWarning, PositionalArg, Signature, Span,
|
||||
Spanned, SyntaxShape, Type, Unit, VarId, ENV_VARIABLE_ID, IN_VARIABLE_ID,
|
||||
};
|
||||
|
||||
use crate::parse_keywords::{
|
||||
@ -344,6 +344,37 @@ pub fn parse_external_call(
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_flag_arg_type(
|
||||
working_set: &mut StateWorkingSet,
|
||||
arg_name: String,
|
||||
arg: Expression,
|
||||
arg_shape: &SyntaxShape,
|
||||
long_name_span: Span,
|
||||
) -> (Spanned<String>, Expression) {
|
||||
if !type_compatible(&arg.ty, &arg_shape.to_type()) {
|
||||
working_set.error(ParseError::TypeMismatch(
|
||||
arg_shape.to_type(),
|
||||
arg.ty,
|
||||
arg.span,
|
||||
));
|
||||
(
|
||||
Spanned {
|
||||
item: arg_name,
|
||||
span: long_name_span,
|
||||
},
|
||||
Expression::garbage(arg.span),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
Spanned {
|
||||
item: arg_name,
|
||||
span: long_name_span,
|
||||
},
|
||||
arg,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_long_flag(
|
||||
working_set: &mut StateWorkingSet,
|
||||
spans: &[Span],
|
||||
@ -368,25 +399,21 @@ fn parse_long_flag(
|
||||
span.start += long_name_len + 3; //offset by long flag and '='
|
||||
|
||||
let arg = parse_value(working_set, span, arg_shape);
|
||||
|
||||
(
|
||||
Some(Spanned {
|
||||
item: long_name,
|
||||
span: Span::new(arg_span.start, arg_span.start + long_name_len + 2),
|
||||
}),
|
||||
Some(arg),
|
||||
)
|
||||
let (arg_name, val_expression) = ensure_flag_arg_type(
|
||||
working_set,
|
||||
long_name,
|
||||
arg,
|
||||
arg_shape,
|
||||
Span::new(arg_span.start, arg_span.start + long_name_len + 2),
|
||||
);
|
||||
(Some(arg_name), Some(val_expression))
|
||||
} else if let Some(arg) = spans.get(*spans_idx + 1) {
|
||||
let arg = parse_value(working_set, *arg, arg_shape);
|
||||
|
||||
*spans_idx += 1;
|
||||
(
|
||||
Some(Spanned {
|
||||
item: long_name,
|
||||
span: arg_span,
|
||||
}),
|
||||
Some(arg),
|
||||
)
|
||||
let (arg_name, val_expression) =
|
||||
ensure_flag_arg_type(working_set, long_name, arg, arg_shape, arg_span);
|
||||
(Some(arg_name), Some(val_expression))
|
||||
} else {
|
||||
working_set.error(ParseError::MissingFlagParam(
|
||||
arg_shape.to_string(),
|
||||
@ -411,13 +438,14 @@ fn parse_long_flag(
|
||||
|
||||
let arg = parse_value(working_set, span, &SyntaxShape::Boolean);
|
||||
|
||||
(
|
||||
Some(Spanned {
|
||||
item: long_name,
|
||||
span: Span::new(arg_span.start, arg_span.start + long_name_len + 2),
|
||||
}),
|
||||
Some(arg),
|
||||
)
|
||||
let (arg_name, val_expression) = ensure_flag_arg_type(
|
||||
working_set,
|
||||
long_name,
|
||||
arg,
|
||||
&SyntaxShape::Boolean,
|
||||
Span::new(arg_span.start, arg_span.start + long_name_len + 2),
|
||||
);
|
||||
(Some(arg_name), Some(val_expression))
|
||||
} else {
|
||||
(
|
||||
Some(Spanned {
|
||||
@ -3492,6 +3520,13 @@ pub fn parse_signature_helper(working_set: &mut StateWorkingSet, span: Span) ->
|
||||
type_annotated,
|
||||
} => {
|
||||
working_set.set_variable_type(var_id.expect("internal error: all custom parameters must have var_ids"), syntax_shape.to_type());
|
||||
if syntax_shape == SyntaxShape::Boolean {
|
||||
working_set.warning(ParseWarning::DeprecatedWarning(
|
||||
"--flag: bool".to_string(),
|
||||
"--flag".to_string(),
|
||||
span,
|
||||
));
|
||||
}
|
||||
*arg = Some(syntax_shape);
|
||||
*type_annotated = true;
|
||||
}
|
||||
@ -4447,10 +4482,6 @@ pub fn parse_value(
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if matches!(shape, SyntaxShape::MatchPattern) {
|
||||
return parse_match_pattern(working_set, span);
|
||||
}
|
||||
|
||||
match bytes[0] {
|
||||
b'$' => return parse_dollar_expr(working_set, span),
|
||||
b'(' => return parse_paren_expr(working_set, span, shape),
|
||||
@ -4488,7 +4519,6 @@ pub fn parse_value(
|
||||
SyntaxShape::GlobPattern => parse_glob_pattern(working_set, span),
|
||||
SyntaxShape::String => parse_string(working_set, span),
|
||||
SyntaxShape::Binary => parse_binary(working_set, span),
|
||||
SyntaxShape::MatchPattern => parse_match_pattern(working_set, span),
|
||||
SyntaxShape::Signature => {
|
||||
if bytes.starts_with(b"[") {
|
||||
parse_signature(working_set, span)
|
||||
@ -5932,7 +5962,6 @@ pub fn discover_captures_in_expr(
|
||||
discover_captures_in_expr(working_set, expr, seen, seen_blocks, output)?;
|
||||
}
|
||||
}
|
||||
Expr::MatchPattern(_) => {}
|
||||
Expr::MatchBlock(match_block) => {
|
||||
for match_ in match_block {
|
||||
discover_captures_in_pattern(&match_.0, seen);
|
||||
|
@ -20,7 +20,7 @@ nu-system = { path = "../nu-system", version = "0.88.2" }
|
||||
byte-unit = "4.0"
|
||||
chrono = { version = "0.4", features = [ "serde", "std", "unstable-locales" ], default-features = false }
|
||||
chrono-humanize = "0.2"
|
||||
fancy-regex = "0.11"
|
||||
fancy-regex = "0.12"
|
||||
indexmap = "2.1"
|
||||
lru = "0.12"
|
||||
miette = { version = "5.10", features = ["fancy-no-backtrace"] }
|
||||
|
@ -164,10 +164,10 @@ impl Call {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn get_flag_expr(&self, flag_name: &str) -> Option<Expression> {
|
||||
pub fn get_flag_expr(&self, flag_name: &str) -> Option<&Expression> {
|
||||
for name in self.named_iter() {
|
||||
if flag_name == name.0.item {
|
||||
return name.2.clone();
|
||||
return name.2.as_ref();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,6 @@ pub enum Expr {
|
||||
Overlay(Option<BlockId>), // block ID of the overlay's origin module
|
||||
Signature(Box<Signature>),
|
||||
StringInterpolation(Vec<Expression>),
|
||||
MatchPattern(Box<MatchPattern>),
|
||||
Spread(Box<Expression>),
|
||||
Nothing,
|
||||
Garbage,
|
||||
|
@ -221,7 +221,6 @@ impl Expression {
|
||||
}
|
||||
false
|
||||
}
|
||||
Expr::MatchPattern(_) => false,
|
||||
Expr::Operator(_) => false,
|
||||
Expr::MatchBlock(_) => false,
|
||||
Expr::Range(left, middle, right, ..) => {
|
||||
@ -407,7 +406,6 @@ impl Expression {
|
||||
Expr::Nothing => {}
|
||||
Expr::GlobPattern(_) => {}
|
||||
Expr::Int(_) => {}
|
||||
Expr::MatchPattern(_) => {}
|
||||
Expr::MatchBlock(_) => {}
|
||||
Expr::Keyword(_, _, expr) => expr.replace_in_variable(working_set, new_var_id),
|
||||
Expr::List(list) => {
|
||||
@ -576,7 +574,6 @@ impl Expression {
|
||||
Expr::Garbage => {}
|
||||
Expr::Nothing => {}
|
||||
Expr::GlobPattern(_) => {}
|
||||
Expr::MatchPattern(_) => {}
|
||||
Expr::MatchBlock(_) => {}
|
||||
Expr::Int(_) => {}
|
||||
Expr::Keyword(_, _, expr) => expr.replace_span(working_set, replaced, new_span),
|
||||
|
@ -95,8 +95,6 @@ pub struct EngineState {
|
||||
pub table_decl_id: Option<usize>,
|
||||
#[cfg(feature = "plugin")]
|
||||
pub plugin_signatures: Option<PathBuf>,
|
||||
#[cfg(not(windows))]
|
||||
sig_quit: Option<Arc<AtomicBool>>,
|
||||
config_path: HashMap<String, PathBuf>,
|
||||
pub history_session_id: i64,
|
||||
// If Nushell was started, e.g., with `nu spam.nu`, the file's parent is stored here
|
||||
@ -152,8 +150,6 @@ impl EngineState {
|
||||
table_decl_id: None,
|
||||
#[cfg(feature = "plugin")]
|
||||
plugin_signatures: None,
|
||||
#[cfg(not(windows))]
|
||||
sig_quit: None,
|
||||
config_path: HashMap::new(),
|
||||
history_session_id: 0,
|
||||
currently_parsed_cwd: None,
|
||||
@ -862,21 +858,6 @@ impl EngineState {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn get_sig_quit(&self) -> &Option<Arc<AtomicBool>> {
|
||||
&self.sig_quit
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub fn get_sig_quit(&self) -> &Option<Arc<AtomicBool>> {
|
||||
&None
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub fn set_sig_quit(&mut self, sig_quit: Arc<AtomicBool>) {
|
||||
self.sig_quit = Some(sig_quit)
|
||||
}
|
||||
|
||||
pub fn set_config_path(&mut self, key: &str, val: PathBuf) {
|
||||
self.config_path.insert(key.to_string(), val);
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use crate::ast::Block;
|
||||
use crate::{
|
||||
BlockId, Config, DeclId, FileId, Module, ModuleId, Span, Type, VarId, Variable, VirtualPathId,
|
||||
};
|
||||
use crate::{Category, ParseError, Value};
|
||||
use crate::{Category, ParseError, ParseWarning, Value};
|
||||
use core::panic;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::path::PathBuf;
|
||||
@ -27,6 +27,7 @@ pub struct StateWorkingSet<'a> {
|
||||
/// Whether or not predeclarations are searched when looking up a command (used with aliases)
|
||||
pub search_predecls: bool,
|
||||
pub parse_errors: Vec<ParseError>,
|
||||
pub parse_warnings: Vec<ParseWarning>,
|
||||
}
|
||||
|
||||
impl<'a> StateWorkingSet<'a> {
|
||||
@ -39,6 +40,7 @@ impl<'a> StateWorkingSet<'a> {
|
||||
parsed_module_files: vec![],
|
||||
search_predecls: true,
|
||||
parse_errors: vec![],
|
||||
parse_warnings: vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -50,6 +52,10 @@ impl<'a> StateWorkingSet<'a> {
|
||||
self.parse_errors.push(parse_error)
|
||||
}
|
||||
|
||||
pub fn warning(&mut self, parse_warning: ParseWarning) {
|
||||
self.parse_warnings.push(parse_warning)
|
||||
}
|
||||
|
||||
pub fn num_files(&self) -> usize {
|
||||
self.delta.num_files() + self.permanent_state.num_files()
|
||||
}
|
||||
|
@ -277,7 +277,6 @@ pub trait Eval {
|
||||
Expr::GlobPattern(pattern) => {
|
||||
Self::eval_glob_pattern(state, mut_state, pattern.clone(), expr.span)
|
||||
}
|
||||
Expr::MatchPattern(pattern) => Ok(Value::match_pattern(*pattern.clone(), expr.span)),
|
||||
Expr::MatchBlock(_) // match blocks are handled by `match`
|
||||
| Expr::VarDecl(_)
|
||||
| Expr::ImportPattern(_)
|
||||
|
@ -12,6 +12,7 @@ mod id;
|
||||
mod lev_distance;
|
||||
mod module;
|
||||
mod parse_error;
|
||||
mod parse_warning;
|
||||
mod pipeline_data;
|
||||
#[cfg(feature = "plugin")]
|
||||
mod plugin_signature;
|
||||
@ -35,6 +36,7 @@ pub use id::*;
|
||||
pub use lev_distance::levenshtein_distance;
|
||||
pub use module::*;
|
||||
pub use parse_error::{DidYouMean, ParseError};
|
||||
pub use parse_warning::ParseWarning;
|
||||
pub use pipeline_data::*;
|
||||
#[cfg(feature = "plugin")]
|
||||
pub use plugin_signature::*;
|
||||
|
23
crates/nu-protocol/src/parse_warning.rs
Normal file
23
crates/nu-protocol/src/parse_warning.rs
Normal file
@ -0,0 +1,23 @@
|
||||
use crate::Span;
|
||||
use miette::Diagnostic;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Clone, Debug, Error, Diagnostic, Serialize, Deserialize)]
|
||||
pub enum ParseWarning {
|
||||
#[error("Deprecated: {0}")]
|
||||
DeprecatedWarning(
|
||||
String,
|
||||
String,
|
||||
#[label = "`{0}` is deprecated and will be removed in 0.90. Please use `{1}` instead, more info: https://www.nushell.sh/book/custom_commands.html"]
|
||||
Span,
|
||||
),
|
||||
}
|
||||
|
||||
impl ParseWarning {
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
ParseWarning::DeprecatedWarning(_, _, s) => *s,
|
||||
}
|
||||
}
|
||||
}
|
@ -82,9 +82,6 @@ pub enum SyntaxShape {
|
||||
/// A block of matches, used by `match`
|
||||
MatchBlock,
|
||||
|
||||
/// A match pattern, eg `{a: $foo}`
|
||||
MatchPattern,
|
||||
|
||||
/// Nothing
|
||||
Nothing,
|
||||
|
||||
@ -163,7 +160,6 @@ impl SyntaxShape {
|
||||
}
|
||||
SyntaxShape::Keyword(_, expr) => expr.to_type(),
|
||||
SyntaxShape::MatchBlock => Type::Any,
|
||||
SyntaxShape::MatchPattern => Type::Any,
|
||||
SyntaxShape::MathExpression => Type::Any,
|
||||
SyntaxShape::Nothing => Type::Nothing,
|
||||
SyntaxShape::Number => Type::Number,
|
||||
@ -240,7 +236,6 @@ impl Display for SyntaxShape {
|
||||
SyntaxShape::MathExpression => write!(f, "variable"),
|
||||
SyntaxShape::VarWithOptType => write!(f, "vardecl"),
|
||||
SyntaxShape::Signature => write!(f, "signature"),
|
||||
SyntaxShape::MatchPattern => write!(f, "match-pattern"),
|
||||
SyntaxShape::MatchBlock => write!(f, "match-block"),
|
||||
SyntaxShape::Expression => write!(f, "expression"),
|
||||
SyntaxShape::Boolean => write!(f, "bool"),
|
||||
|
@ -24,7 +24,6 @@ pub enum Type {
|
||||
Int,
|
||||
List(Box<Type>),
|
||||
ListStream,
|
||||
MatchPattern,
|
||||
#[default]
|
||||
Nothing,
|
||||
Number,
|
||||
@ -110,7 +109,6 @@ impl Type {
|
||||
Type::Binary => SyntaxShape::Binary,
|
||||
Type::Custom(_) => SyntaxShape::Any,
|
||||
Type::Signature => SyntaxShape::Signature,
|
||||
Type::MatchPattern => SyntaxShape::MatchPattern,
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,7 +129,6 @@ impl Type {
|
||||
Type::Record(_) => String::from("record"),
|
||||
Type::Table(_) => String::from("table"),
|
||||
Type::List(_) => String::from("list"),
|
||||
Type::MatchPattern => String::from("match-pattern"),
|
||||
Type::Nothing => String::from("nothing"),
|
||||
Type::Number => String::from("number"),
|
||||
Type::String => String::from("string"),
|
||||
@ -198,7 +195,6 @@ impl Display for Type {
|
||||
Type::Binary => write!(f, "binary"),
|
||||
Type::Custom(custom) => write!(f, "{custom}"),
|
||||
Type::Signature => write!(f, "signature"),
|
||||
Type::MatchPattern => write!(f, "match-pattern"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::ast::{CellPath, MatchPattern, PathMember};
|
||||
use crate::ast::{CellPath, PathMember};
|
||||
use crate::engine::{Block, Closure};
|
||||
use crate::{Range, Record, ShellError, Spanned, Value};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
@ -535,32 +535,3 @@ impl FromValue for Spanned<Closure> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for Spanned<MatchPattern> {
|
||||
fn from_value(v: Value) -> Result<Self, ShellError> {
|
||||
let span = v.span();
|
||||
match v {
|
||||
Value::MatchPattern { val, .. } => Ok(Spanned { item: *val, span }),
|
||||
v => Err(ShellError::CantConvert {
|
||||
to_type: "Match pattern".into(),
|
||||
from_type: v.get_type().to_string(),
|
||||
span: v.span(),
|
||||
help: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromValue for MatchPattern {
|
||||
fn from_value(v: Value) -> Result<Self, ShellError> {
|
||||
match v {
|
||||
Value::MatchPattern { val, .. } => Ok(*val),
|
||||
v => Err(ShellError::CantConvert {
|
||||
to_type: "Match pattern".into(),
|
||||
from_type: v.get_type().to_string(),
|
||||
span: v.span(),
|
||||
help: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ mod record;
|
||||
mod stream;
|
||||
mod unit;
|
||||
|
||||
use crate::ast::{Bits, Boolean, CellPath, Comparison, MatchPattern, PathMember};
|
||||
use crate::ast::{Bits, Boolean, CellPath, Comparison, PathMember};
|
||||
use crate::ast::{Math, Operator};
|
||||
use crate::engine::{Closure, EngineState};
|
||||
use crate::ShellError;
|
||||
@ -20,8 +20,9 @@ pub use custom_value::CustomValue;
|
||||
use fancy_regex::Regex;
|
||||
pub use from_value::FromValue;
|
||||
pub use lazy_record::LazyRecord;
|
||||
use nu_utils::locale::get_system_locale_string;
|
||||
use nu_utils::{get_system_locale, IgnoreCaseExt};
|
||||
use nu_utils::{
|
||||
contains_emoji, get_system_locale, locale::get_system_locale_string, IgnoreCaseExt,
|
||||
};
|
||||
use num_format::ToFormattedString;
|
||||
pub use range::*;
|
||||
pub use record::Record;
|
||||
@ -150,12 +151,6 @@ pub enum Value {
|
||||
// please use .span() instead of matching this span value
|
||||
internal_span: Span,
|
||||
},
|
||||
MatchPattern {
|
||||
val: Box<MatchPattern>,
|
||||
// note: spans are being refactored out of Value
|
||||
// please use .span() instead of matching this span value
|
||||
internal_span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
impl Clone for Value {
|
||||
@ -223,10 +218,6 @@ impl Clone for Value {
|
||||
internal_span: *internal_span,
|
||||
},
|
||||
Value::CustomValue { val, internal_span } => val.clone_value(*internal_span),
|
||||
Value::MatchPattern { val, internal_span } => Value::MatchPattern {
|
||||
val: val.clone(),
|
||||
internal_span: *internal_span,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -507,18 +498,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_match_pattern(&self) -> Result<&MatchPattern, ShellError> {
|
||||
match self {
|
||||
Value::MatchPattern { val, .. } => Ok(val.as_ref()),
|
||||
x => Err(ShellError::CantConvert {
|
||||
to_type: "match-pattern".into(),
|
||||
from_type: x.get_type().to_string(),
|
||||
span: self.span(),
|
||||
help: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the span for the current value
|
||||
pub fn span(&self) -> Span {
|
||||
match self {
|
||||
@ -539,7 +518,6 @@ impl Value {
|
||||
| Value::CellPath { internal_span, .. }
|
||||
| Value::CustomValue { internal_span, .. }
|
||||
| Value::LazyRecord { internal_span, .. }
|
||||
| Value::MatchPattern { internal_span, .. }
|
||||
| Value::Error { internal_span, .. } => *internal_span,
|
||||
}
|
||||
}
|
||||
@ -563,8 +541,7 @@ impl Value {
|
||||
| Value::Nothing { internal_span, .. }
|
||||
| Value::Binary { internal_span, .. }
|
||||
| Value::CellPath { internal_span, .. }
|
||||
| Value::CustomValue { internal_span, .. }
|
||||
| Value::MatchPattern { internal_span, .. } => *internal_span = new_span,
|
||||
| Value::CustomValue { internal_span, .. } => *internal_span = new_span,
|
||||
Value::Error { .. } => (),
|
||||
}
|
||||
|
||||
@ -621,7 +598,6 @@ impl Value {
|
||||
Value::Binary { .. } => Type::Binary,
|
||||
Value::CellPath { .. } => Type::CellPath,
|
||||
Value::CustomValue { val, .. } => Type::Custom(val.typetag_name().into()),
|
||||
Value::MatchPattern { .. } => Type::MatchPattern,
|
||||
}
|
||||
}
|
||||
|
||||
@ -727,7 +703,6 @@ impl Value {
|
||||
Value::Binary { val, .. } => format!("{val:?}"),
|
||||
Value::CellPath { val, .. } => val.to_string(),
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
Value::MatchPattern { val, .. } => format!("<Pattern: {:?}>", val),
|
||||
}
|
||||
}
|
||||
|
||||
@ -782,7 +757,6 @@ impl Value {
|
||||
Value::Binary { val, .. } => format!("{val:?}"),
|
||||
Value::CellPath { val, .. } => val.to_string(),
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
Value::MatchPattern { .. } => "<Pattern>".into(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -816,7 +790,20 @@ impl Value {
|
||||
|
||||
/// Convert Value into a debug string
|
||||
pub fn debug_value(&self) -> String {
|
||||
format!("{self:#?}")
|
||||
match self {
|
||||
Value::String { val, .. } => {
|
||||
if contains_emoji(val) {
|
||||
// This has to be an emoji, so let's display the code points that make it up.
|
||||
format!(
|
||||
"{:#?}",
|
||||
Value::string(val.escape_unicode().to_string(), self.span())
|
||||
)
|
||||
} else {
|
||||
format!("{self:#?}")
|
||||
}
|
||||
}
|
||||
_ => format!("{self:#?}"),
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert Value into a parsable string (quote strings)
|
||||
@ -890,7 +877,6 @@ impl Value {
|
||||
Value::Binary { val, .. } => format!("{val:?}"),
|
||||
Value::CellPath { val, .. } => val.to_string(),
|
||||
Value::CustomValue { val, .. } => val.value_string(),
|
||||
Value::MatchPattern { val, .. } => format!("<Pattern {:?}>", val),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1834,13 +1820,6 @@ impl Value {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn match_pattern(val: MatchPattern, span: Span) -> Value {
|
||||
Value::MatchPattern {
|
||||
val: Box::new(val),
|
||||
internal_span: span,
|
||||
}
|
||||
}
|
||||
|
||||
/// Note: Only use this for test data, *not* live data, as it will point into unknown source
|
||||
/// when used in errors.
|
||||
pub fn test_bool(val: bool) -> Value {
|
||||
@ -1942,12 +1921,6 @@ impl Value {
|
||||
pub fn test_lazy_record(val: Box<dyn for<'a> LazyRecord<'a>>) -> Value {
|
||||
Value::lazy_record(val, Span::test_data())
|
||||
}
|
||||
|
||||
/// Note: Only use this for test data, *not* live data, as it will point into unknown source
|
||||
/// when used in errors.
|
||||
pub fn test_match_pattern(val: MatchPattern) -> Value {
|
||||
Value::match_pattern(val, Span::test_data())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Value {
|
||||
@ -1992,7 +1965,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Int { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2013,7 +1985,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Float { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2034,7 +2005,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Filesize { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2055,7 +2025,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Duration { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2076,7 +2045,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Date { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2097,7 +2065,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Range { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2118,7 +2085,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::String { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2139,7 +2105,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Record { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2179,7 +2144,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::List { vals: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2200,7 +2164,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Block { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2221,7 +2184,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Closure { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2242,7 +2204,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Nothing { .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2263,7 +2224,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Error { .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2284,7 +2244,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Less),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::Binary { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2305,7 +2264,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { val: rhs, .. } => lhs.partial_cmp(rhs),
|
||||
Value::CellPath { .. } => Some(Ordering::Less),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::CellPath { val: lhs, .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
@ -2326,7 +2284,6 @@ impl PartialOrd for Value {
|
||||
Value::Binary { .. } => Some(Ordering::Greater),
|
||||
Value::CellPath { val: rhs, .. } => lhs.partial_cmp(rhs),
|
||||
Value::CustomValue { .. } => Some(Ordering::Less),
|
||||
Value::MatchPattern { .. } => Some(Ordering::Less),
|
||||
},
|
||||
(Value::CustomValue { val: lhs, .. }, rhs) => lhs.partial_cmp(rhs),
|
||||
(Value::LazyRecord { val, .. }, rhs) => {
|
||||
@ -2336,27 +2293,6 @@ impl PartialOrd for Value {
|
||||
None
|
||||
}
|
||||
}
|
||||
(Value::MatchPattern { .. }, rhs) => match rhs {
|
||||
Value::Bool { .. } => Some(Ordering::Greater),
|
||||
Value::Int { .. } => Some(Ordering::Greater),
|
||||
Value::Float { .. } => Some(Ordering::Greater),
|
||||
Value::Filesize { .. } => Some(Ordering::Greater),
|
||||
Value::Duration { .. } => Some(Ordering::Greater),
|
||||
Value::Date { .. } => Some(Ordering::Greater),
|
||||
Value::Range { .. } => Some(Ordering::Greater),
|
||||
Value::String { .. } => Some(Ordering::Greater),
|
||||
Value::Record { .. } => Some(Ordering::Greater),
|
||||
Value::LazyRecord { .. } => Some(Ordering::Greater),
|
||||
Value::List { .. } => Some(Ordering::Greater),
|
||||
Value::Block { .. } => Some(Ordering::Greater),
|
||||
Value::Closure { .. } => Some(Ordering::Greater),
|
||||
Value::Nothing { .. } => Some(Ordering::Greater),
|
||||
Value::Error { .. } => Some(Ordering::Greater),
|
||||
Value::Binary { .. } => Some(Ordering::Greater),
|
||||
Value::CellPath { .. } => Some(Ordering::Greater),
|
||||
Value::CustomValue { .. } => Some(Ordering::Greater),
|
||||
Value::MatchPattern { .. } => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,12 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct Record {
|
||||
/// Don't use this field publicly!
|
||||
///
|
||||
/// Only public as command `rename` is not reimplemented in a sane way yet
|
||||
/// Using it or making `vals` public will draw shaming by @sholderbach
|
||||
pub cols: Vec<String>,
|
||||
pub vals: Vec<Value>,
|
||||
vals: Vec<Value>,
|
||||
}
|
||||
|
||||
impl Record {
|
||||
|
@ -17,7 +17,7 @@ nu-engine = { path = "../nu-engine", version = "0.88.2" }
|
||||
nu-color-config = { path = "../nu-color-config", version = "0.88.2" }
|
||||
nu-ansi-term = "0.49.0"
|
||||
once_cell = "1.18"
|
||||
fancy-regex = "0.11"
|
||||
fancy-regex = "0.12"
|
||||
tabled = { version = "0.14.0", features = ["color"], default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -18,7 +18,7 @@ bench = false
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
lscolors = { version = "0.15", default-features = false, features = ["nu-ansi-term"] }
|
||||
lscolors = { version = "0.16", default-features = false, features = ["nu-ansi-term"] }
|
||||
num-format = { version = "0.4" }
|
||||
strip-ansi-escapes = "0.2.0"
|
||||
sys-locale = "0.3"
|
||||
|
16
crates/nu-utils/src/emoji.rs
Normal file
16
crates/nu-utils/src/emoji.rs
Normal file
@ -0,0 +1,16 @@
|
||||
pub fn contains_emoji(val: &str) -> bool {
|
||||
// Let's do some special handling for emojis
|
||||
const ZERO_WIDTH_JOINER: &str = "\u{200d}";
|
||||
const VARIATION_SELECTOR_16: &str = "\u{fe0f}";
|
||||
const SKIN_TONES: [&str; 5] = [
|
||||
"\u{1f3fb}", // Light Skin Tone
|
||||
"\u{1f3fc}", // Medium-Light Skin Tone
|
||||
"\u{1f3fd}", // Medium Skin Tone
|
||||
"\u{1f3fe}", // Medium-Dark Skin Tone
|
||||
"\u{1f3ff}", // Dark Skin Tone
|
||||
];
|
||||
|
||||
val.contains(ZERO_WIDTH_JOINER)
|
||||
|| val.contains(VARIATION_SELECTOR_16)
|
||||
|| SKIN_TONES.iter().any(|skin_tone| val.contains(skin_tone))
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
mod casing;
|
||||
pub mod ctrl_c;
|
||||
mod deansi;
|
||||
pub mod emoji;
|
||||
pub mod locale;
|
||||
pub mod utils;
|
||||
|
||||
@ -14,3 +15,4 @@ pub use casing::IgnoreCaseExt;
|
||||
pub use deansi::{
|
||||
strip_ansi_likely, strip_ansi_string_likely, strip_ansi_string_unlikely, strip_ansi_unlikely,
|
||||
};
|
||||
pub use emoji::contains_emoji;
|
||||
|
@ -53,4 +53,5 @@ def main [] {
|
||||
let msg = $"OUTPUT:\n($o)\n\nEXPECTED:\n($e)"
|
||||
error make {msg: $"Output does not match the expected value:\n($msg)"}
|
||||
}
|
||||
rm script.nu
|
||||
}
|
||||
|
@ -93,23 +93,23 @@ pub(crate) fn parse_commandline_args(
|
||||
let redirect_stdin = call.get_named_arg("stdin");
|
||||
let login_shell = call.get_named_arg("login");
|
||||
let interactive_shell = call.get_named_arg("interactive");
|
||||
let commands: Option<Expression> = call.get_flag_expr("commands");
|
||||
let testbin: Option<Expression> = call.get_flag_expr("testbin");
|
||||
let commands = call.get_flag_expr("commands");
|
||||
let testbin = call.get_flag_expr("testbin");
|
||||
#[cfg(feature = "plugin")]
|
||||
let plugin_file: Option<Expression> = call.get_flag_expr("plugin-config");
|
||||
let plugin_file = call.get_flag_expr("plugin-config");
|
||||
let no_config_file = call.get_named_arg("no-config-file");
|
||||
let no_std_lib = call.get_named_arg("no-std-lib");
|
||||
let config_file: Option<Expression> = call.get_flag_expr("config");
|
||||
let env_file: Option<Expression> = call.get_flag_expr("env-config");
|
||||
let log_level: Option<Expression> = call.get_flag_expr("log-level");
|
||||
let log_target: Option<Expression> = call.get_flag_expr("log-target");
|
||||
let execute: Option<Expression> = call.get_flag_expr("execute");
|
||||
let config_file = call.get_flag_expr("config");
|
||||
let env_file = call.get_flag_expr("env-config");
|
||||
let log_level = call.get_flag_expr("log-level");
|
||||
let log_target = call.get_flag_expr("log-target");
|
||||
let execute = call.get_flag_expr("execute");
|
||||
let table_mode: Option<Value> =
|
||||
call.get_flag(engine_state, &mut stack, "table-mode")?;
|
||||
|
||||
// ide flags
|
||||
let lsp = call.has_flag("lsp");
|
||||
let include_path: Option<Expression> = call.get_flag_expr("include-path");
|
||||
let include_path = call.get_flag_expr("include-path");
|
||||
let ide_goto_def: Option<Value> =
|
||||
call.get_flag(engine_state, &mut stack, "ide-goto-def")?;
|
||||
let ide_hover: Option<Value> = call.get_flag(engine_state, &mut stack, "ide-hover")?;
|
||||
@ -119,7 +119,7 @@ pub(crate) fn parse_commandline_args(
|
||||
let ide_ast: Option<Spanned<String>> = call.get_named_arg("ide-ast");
|
||||
|
||||
fn extract_contents(
|
||||
expression: Option<Expression>,
|
||||
expression: Option<&Expression>,
|
||||
) -> Result<Option<Spanned<String>>, ShellError> {
|
||||
if let Some(expr) = expression {
|
||||
let str = expr.as_string();
|
||||
|
91
src/ide.rs
91
src/ide.rs
@ -211,8 +211,19 @@ pub fn hover(engine_state: &mut EngineState, file_path: &str, location: &Value)
|
||||
Some((Id::Declaration(decl_id), offset, span)) => {
|
||||
let decl = working_set.get_decl(decl_id);
|
||||
|
||||
let mut description = "```\n### Signature\n```\n".to_string();
|
||||
//let mut description = "```\n### Signature\n```\n".to_string();
|
||||
let mut description = "```\n".to_string();
|
||||
|
||||
// first description
|
||||
description.push_str(&format!("{}\n", decl.usage()));
|
||||
|
||||
// additional description
|
||||
if !decl.extra_usage().is_empty() {
|
||||
description.push_str(&format!("\n{}\n", decl.extra_usage()));
|
||||
}
|
||||
|
||||
// Usage
|
||||
description.push_str("### Usage\n```\n");
|
||||
let signature = decl.signature();
|
||||
description.push_str(&format!(" {}", signature.name));
|
||||
if !signature.named.is_empty() {
|
||||
@ -230,6 +241,41 @@ pub fn hover(engine_state: &mut EngineState, file_path: &str, location: &Value)
|
||||
|
||||
description.push_str("\n```\n");
|
||||
|
||||
// Flags
|
||||
if !signature.named.is_empty() {
|
||||
description.push_str("\n### Flags\n\n");
|
||||
|
||||
let mut first = true;
|
||||
for named in &signature.named {
|
||||
if !first {
|
||||
description.push_str("\\\n");
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
description.push_str(" ");
|
||||
if let Some(short_flag) = &named.short {
|
||||
description.push_str(&format!("`-{}`", short_flag));
|
||||
}
|
||||
|
||||
if !named.long.is_empty() {
|
||||
if named.short.is_some() {
|
||||
description.push_str(", ")
|
||||
}
|
||||
description.push_str(&format!("`--{}`", named.long));
|
||||
}
|
||||
|
||||
if let Some(arg) = &named.arg {
|
||||
description.push_str(&format!(" `<{}>`", arg.to_type()))
|
||||
}
|
||||
|
||||
if !named.desc.is_empty() {
|
||||
description.push_str(&format!(" - {}", named.desc));
|
||||
}
|
||||
}
|
||||
description.push('\n');
|
||||
}
|
||||
|
||||
// Parameters
|
||||
if !signature.required_positional.is_empty()
|
||||
|| !signature.optional_positional.is_empty()
|
||||
|| signature.rest_positional.is_some()
|
||||
@ -285,41 +331,9 @@ pub fn hover(engine_state: &mut EngineState, file_path: &str, location: &Value)
|
||||
description.push('\n');
|
||||
}
|
||||
|
||||
if !signature.named.is_empty() {
|
||||
description.push_str("\n### Flags\n\n");
|
||||
|
||||
let mut first = true;
|
||||
for named in &signature.named {
|
||||
if !first {
|
||||
description.push_str("\\\n");
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
description.push_str(" ");
|
||||
if let Some(short_flag) = &named.short {
|
||||
description.push_str(&format!("`-{}`", short_flag));
|
||||
}
|
||||
|
||||
if !named.long.is_empty() {
|
||||
if named.short.is_some() {
|
||||
description.push_str(", ")
|
||||
}
|
||||
description.push_str(&format!("`--{}`", named.long));
|
||||
}
|
||||
|
||||
if let Some(arg) = &named.arg {
|
||||
description.push_str(&format!(" `<{}>`", arg.to_type()))
|
||||
}
|
||||
|
||||
if !named.desc.is_empty() {
|
||||
description.push_str(&format!(" - {}", named.desc));
|
||||
}
|
||||
}
|
||||
description.push('\n');
|
||||
}
|
||||
|
||||
// Input/output types
|
||||
if !signature.input_output_types.is_empty() {
|
||||
description.push_str("\n### Input/output\n");
|
||||
description.push_str("\n### Input/output types\n");
|
||||
|
||||
description.push_str("\n```\n");
|
||||
for input_output in &signature.input_output_types {
|
||||
@ -328,12 +342,7 @@ pub fn hover(engine_state: &mut EngineState, file_path: &str, location: &Value)
|
||||
description.push_str("\n```\n");
|
||||
}
|
||||
|
||||
description.push_str(&format!("### Usage\n {}\n", decl.usage()));
|
||||
|
||||
if !decl.extra_usage().is_empty() {
|
||||
description.push_str(&format!("\n### Extra usage:\n {}\n", decl.extra_usage()));
|
||||
}
|
||||
|
||||
// Examples
|
||||
if !decl.examples().is_empty() {
|
||||
description.push_str("### Example(s)\n```\n");
|
||||
|
||||
|
@ -32,7 +32,7 @@ use nu_protocol::{
|
||||
use nu_std::load_standard_library;
|
||||
use nu_utils::utils::perf;
|
||||
use run::{run_commands, run_file, run_repl};
|
||||
use signals::{ctrlc_protection, sigquit_protection};
|
||||
use signals::ctrlc_protection;
|
||||
use std::{
|
||||
io::BufReader,
|
||||
str::FromStr,
|
||||
@ -78,7 +78,6 @@ fn main() -> Result<()> {
|
||||
let ctrlc = Arc::new(AtomicBool::new(false));
|
||||
// TODO: make this conditional in the future
|
||||
ctrlc_protection(&mut engine_state, &ctrlc);
|
||||
sigquit_protection(&mut engine_state);
|
||||
|
||||
// Begin: Default NU_LIB_DIRS, NU_PLUGIN_DIRS
|
||||
// Set default NU_LIB_DIRS and NU_PLUGIN_DIRS here before the env.nu is processed. If
|
||||
|
@ -16,14 +16,3 @@ pub(crate) fn ctrlc_protection(engine_state: &mut EngineState, ctrlc: &Arc<Atomi
|
||||
|
||||
engine_state.ctrlc = Some(engine_state_ctrlc);
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
pub(crate) fn sigquit_protection(engine_state: &mut EngineState) {
|
||||
use signal_hook::consts::SIGQUIT;
|
||||
let sig_quit = Arc::new(AtomicBool::new(false));
|
||||
signal_hook::flag::register(SIGQUIT, sig_quit.clone()).expect("Error setting SIGQUIT flag");
|
||||
engine_state.set_sig_quit(sig_quit);
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub(crate) fn sigquit_protection(_engine_state: &mut EngineState) {}
|
||||
|
@ -30,7 +30,8 @@ pub(crate) fn acquire_terminal(interactive: bool) {
|
||||
std::process::exit(1);
|
||||
};
|
||||
|
||||
// SIGINT and SIGQUIT have special handling
|
||||
// SIGINT has special handling
|
||||
signal(Signal::SIGQUIT, SigHandler::SigIgn).expect("signal ignore");
|
||||
signal(Signal::SIGTSTP, SigHandler::SigIgn).expect("signal ignore");
|
||||
signal(Signal::SIGTTIN, SigHandler::SigIgn).expect("signal ignore");
|
||||
signal(Signal::SIGTTOU, SigHandler::SigIgn).expect("signal ignore");
|
||||
|
@ -73,6 +73,14 @@ fn custom_switch1() -> TestResult {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_flag_with_type_checking() -> TestResult {
|
||||
fail_test(
|
||||
r#"def florb [--dry-run: int] { $dry_run }; let y = "3"; florb --dry-run=$y"#,
|
||||
"type_mismatch",
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn custom_switch2() -> TestResult {
|
||||
run_test(
|
||||
@ -116,7 +124,7 @@ fn custom_flag1() -> TestResult {
|
||||
r#"def florb [
|
||||
--age: int = 0
|
||||
--name = "foobar"
|
||||
] {
|
||||
] {
|
||||
($age | into string) + $name
|
||||
}
|
||||
florb"#,
|
||||
@ -138,6 +146,13 @@ fn custom_flag2() -> TestResult {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deprecated_boolean_flag() {
|
||||
let actual = nu!(r#"def florb [--dry-run: bool, --another-flag] { "aaa" }; florb"#);
|
||||
assert!(actual.err.contains("Deprecated"));
|
||||
assert_eq!(actual.out, "aaa");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple_var_closing() -> TestResult {
|
||||
run_test("let $x = 10; def foo [] { $x }; foo", "10")
|
||||
|
0
tests/fixtures/completions/directory_completion/mod.nu
vendored
Normal file
0
tests/fixtures/completions/directory_completion/mod.nu
vendored
Normal file
Reference in New Issue
Block a user