This commit is contained in:
Andy Gayton 2024-07-17 23:34:51 -04:00
commit 579c55708b
478 changed files with 12093 additions and 3614 deletions

View File

@ -26,6 +26,13 @@ updates:
patterns: patterns:
- "polars" - "polars"
- "polars-*" - "polars-*"
# uutils/coreutils also versions all their workspace crates the same at the moment
# Most of them have bleeding edge version requirements (some not)
# see: https://github.com/uutils/coreutils/blob/main/Cargo.toml
uutils:
patterns:
- "uucore"
- "uu_*"
- package-ecosystem: "github-actions" - package-ecosystem: "github-actions"
directory: "/" directory: "/"
schedule: schedule:

View File

@ -99,13 +99,13 @@ jobs:
extra: msi extra: msi
os: windows-latest os: windows-latest
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
os: ubuntu-20.04 os: ubuntu-22.04
- target: x86_64-unknown-linux-musl - target: x86_64-unknown-linux-musl
os: ubuntu-20.04 os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu - target: aarch64-unknown-linux-gnu
os: ubuntu-20.04 os: ubuntu-22.04
- target: armv7-unknown-linux-gnueabihf - target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04 os: ubuntu-22.04
- target: riscv64gc-unknown-linux-gnu - target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-latest

View File

@ -49,13 +49,13 @@ jobs:
extra: msi extra: msi
os: windows-latest os: windows-latest
- target: x86_64-unknown-linux-gnu - target: x86_64-unknown-linux-gnu
os: ubuntu-20.04 os: ubuntu-22.04
- target: x86_64-unknown-linux-musl - target: x86_64-unknown-linux-musl
os: ubuntu-20.04 os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu - target: aarch64-unknown-linux-gnu
os: ubuntu-20.04 os: ubuntu-22.04
- target: armv7-unknown-linux-gnueabihf - target: armv7-unknown-linux-gnueabihf
os: ubuntu-20.04 os: ubuntu-22.04
- target: riscv64gc-unknown-linux-gnu - target: riscv64gc-unknown-linux-gnu
os: ubuntu-latest os: ubuntu-latest

View File

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

65
Cargo.lock generated
View File

@ -1219,24 +1219,24 @@ dependencies = [
] ]
[[package]] [[package]]
name = "dirs-next" name = "dirs"
version = "2.0.0" version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [ dependencies = [
"cfg-if", "dirs-sys",
"dirs-sys-next",
] ]
[[package]] [[package]]
name = "dirs-sys-next" name = "dirs-sys"
version = "0.1.2" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [ dependencies = [
"libc", "libc",
"option-ext",
"redox_users", "redox_users",
"winapi", "windows-sys 0.48.0",
] ]
[[package]] [[package]]
@ -2342,9 +2342,9 @@ dependencies = [
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.154" version = "0.2.155"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
[[package]] [[package]]
name = "libflate" name = "libflate"
@ -2382,9 +2382,9 @@ dependencies = [
[[package]] [[package]]
name = "libloading" name = "libloading"
version = "0.8.3" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-targets 0.52.5", "windows-targets 0.52.5",
@ -2873,7 +2873,7 @@ dependencies = [
"assert_cmd", "assert_cmd",
"crossterm", "crossterm",
"ctrlc", "ctrlc",
"dirs-next", "dirs",
"log", "log",
"miette", "miette",
"mimalloc", "mimalloc",
@ -2900,6 +2900,7 @@ dependencies = [
"openssl", "openssl",
"pretty_assertions", "pretty_assertions",
"reedline", "reedline",
"regex",
"rstest", "rstest",
"serde_json", "serde_json",
"serial_test", "serial_test",
@ -3046,7 +3047,7 @@ dependencies = [
"deunicode", "deunicode",
"dialoguer", "dialoguer",
"digest", "digest",
"dirs-next", "dirs",
"dtparse", "dtparse",
"encoding_rs", "encoding_rs",
"fancy-regex", "fancy-regex",
@ -3151,6 +3152,7 @@ dependencies = [
name = "nu-engine" name = "nu-engine"
version = "0.95.1" version = "0.95.1"
dependencies = [ dependencies = [
"log",
"nu-glob", "nu-glob",
"nu-path", "nu-path",
"nu-protocol", "nu-protocol",
@ -3193,7 +3195,10 @@ dependencies = [
name = "nu-json" name = "nu-json"
version = "0.95.1" version = "0.95.1"
dependencies = [ dependencies = [
"fancy-regex",
"linked-hash-map", "linked-hash-map",
"nu-path",
"nu-test-support",
"num-traits", "num-traits",
"serde", "serde",
"serde_json", "serde_json",
@ -3240,7 +3245,7 @@ dependencies = [
name = "nu-path" name = "nu-path"
version = "0.95.1" version = "0.95.1"
dependencies = [ dependencies = [
"dirs-next", "dirs",
"omnipath", "omnipath",
"pwd", "pwd",
] ]
@ -3255,6 +3260,7 @@ dependencies = [
"nu-plugin-core", "nu-plugin-core",
"nu-plugin-protocol", "nu-plugin-protocol",
"nu-protocol", "nu-protocol",
"nu-utils",
"serde", "serde",
"thiserror", "thiserror",
"typetag", "typetag",
@ -3284,6 +3290,7 @@ dependencies = [
"nu-plugin-protocol", "nu-plugin-protocol",
"nu-protocol", "nu-protocol",
"nu-system", "nu-system",
"nu-utils",
"serde", "serde",
"typetag", "typetag",
"windows 0.54.0", "windows 0.54.0",
@ -3339,6 +3346,7 @@ dependencies = [
"convert_case", "convert_case",
"fancy-regex", "fancy-regex",
"indexmap", "indexmap",
"log",
"lru", "lru",
"miette", "miette",
"nix", "nix",
@ -3765,9 +3773,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]] [[package]]
name = "open" name = "open"
version = "5.2.0" version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d2c909a3fce3bd80efef4cd1c6c056bd9376a8fe06fcfdbebaf32cb485a7e37" checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3"
dependencies = [ dependencies = [
"is-wsl", "is-wsl",
"libc", "libc",
@ -3828,6 +3836,12 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]] [[package]]
name = "ordered-multimap" name = "ordered-multimap"
version = "0.7.3" version = "0.7.3"
@ -4988,8 +5002,7 @@ dependencies = [
[[package]] [[package]]
name = "reedline" name = "reedline"
version = "0.32.0" version = "0.32.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "git+https://github.com/nushell/reedline?branch=main#480059a3f52cf919341cda88e8c544edd846bc73"
checksum = "abf59e4c97b5049ba96b052cdb652368305a2eddcbce9bf1c16f9d003139eeea"
dependencies = [ dependencies = [
"arboard", "arboard",
"chrono", "chrono",
@ -5203,9 +5216,9 @@ dependencies = [
[[package]] [[package]]
name = "rust-embed" name = "rust-embed"
version = "8.4.0" version = "8.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19549741604902eb99a7ed0ee177a0663ee1eda51a29f71401f166e47e77806a" checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
dependencies = [ dependencies = [
"rust-embed-impl", "rust-embed-impl",
"rust-embed-utils", "rust-embed-utils",
@ -6466,9 +6479,9 @@ checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]] [[package]]
name = "ureq" name = "ureq"
version = "2.9.7" version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" checksum = "72139d247e5f97a3eff96229a7ae85ead5328a39efe76f8bf5a06313d505b6ea"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"encoding_rs", "encoding_rs",
@ -6625,9 +6638,9 @@ checksum = "425a23c7b7145bc7620c9c445817c37b1f78b6790aee9f208133f3c028975b60"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.9.1" version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5de17fd2f7da591098415cff336e12965a28061ddace43b59cb3c430179c9439" checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"serde", "serde",

View File

@ -83,7 +83,7 @@ ctrlc = "3.4"
deunicode = "1.6.0" deunicode = "1.6.0"
dialoguer = { default-features = false, version = "0.11" } dialoguer = { default-features = false, version = "0.11" }
digest = { default-features = false, version = "0.10" } digest = { default-features = false, version = "0.10" }
dirs-next = "2.0" dirs = "5.0"
dtparse = "2.0" dtparse = "2.0"
encoding_rs = "0.8" encoding_rs = "0.8"
fancy-regex = "0.13" fancy-regex = "0.13"
@ -120,7 +120,7 @@ num-format = "0.4"
num-traits = "0.2" num-traits = "0.2"
omnipath = "0.1" omnipath = "0.1"
once_cell = "1.18" once_cell = "1.18"
open = "5.2" open = "5.3"
os_pipe = { version = "1.2", features = ["io_safety"] } os_pipe = { version = "1.2", features = ["io_safety"] }
pathdiff = "0.2" pathdiff = "0.2"
percent-encoding = "2" percent-encoding = "2"
@ -145,7 +145,7 @@ ropey = "1.6.1"
roxmltree = "0.19" roxmltree = "0.19"
rstest = { version = "0.18", default-features = false } rstest = { version = "0.18", default-features = false }
rusqlite = "0.31" rusqlite = "0.31"
rust-embed = "8.4.0" rust-embed = "8.5.0"
same-file = "1.0" same-file = "1.0"
serde = { version = "1.0", default-features = false } serde = { version = "1.0", default-features = false }
serde_json = "1.0" serde_json = "1.0"
@ -164,7 +164,7 @@ trash = "3.3"
umask = "2.1" umask = "2.1"
unicode-segmentation = "1.11" unicode-segmentation = "1.11"
unicode-width = "0.1" unicode-width = "0.1"
ureq = { version = "2.9", default-features = false } ureq = { version = "2.10", default-features = false }
url = "2.2" url = "2.2"
uu_cp = "0.0.27" uu_cp = "0.0.27"
uu_mkdir = "0.0.27" uu_mkdir = "0.0.27"
@ -173,7 +173,7 @@ uu_mv = "0.0.27"
uu_whoami = "0.0.27" uu_whoami = "0.0.27"
uu_uname = "0.0.27" uu_uname = "0.0.27"
uucore = "0.0.27" uucore = "0.0.27"
uuid = "1.9.1" uuid = "1.10.0"
v_htmlescape = "0.15.0" v_htmlescape = "0.15.0"
wax = "0.6" wax = "0.6"
which = "6.0.0" which = "6.0.0"
@ -197,12 +197,11 @@ nu-protocol = { path = "./crates/nu-protocol", version = "0.95.1" }
nu-std = { path = "./crates/nu-std", version = "0.95.1" } nu-std = { path = "./crates/nu-std", version = "0.95.1" }
nu-system = { path = "./crates/nu-system", version = "0.95.1" } nu-system = { path = "./crates/nu-system", version = "0.95.1" }
nu-utils = { path = "./crates/nu-utils", version = "0.95.1" } nu-utils = { path = "./crates/nu-utils", version = "0.95.1" }
reedline = { workspace = true, features = ["bashisms", "sqlite"] } reedline = { workspace = true, features = ["bashisms", "sqlite"] }
crossterm = { workspace = true } crossterm = { workspace = true }
ctrlc = { workspace = true } ctrlc = { workspace = true }
dirs-next = { workspace = true } dirs = { workspace = true }
log = { workspace = true } log = { workspace = true }
miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] } miette = { workspace = true, features = ["fancy-no-backtrace", "fancy"] }
mimalloc = { version = "0.1.42", default-features = false, optional = true } mimalloc = { version = "0.1.42", default-features = false, optional = true }
@ -230,9 +229,10 @@ nu-test-support = { path = "./crates/nu-test-support", version = "0.95.1" }
nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.95.1" } nu-plugin-protocol = { path = "./crates/nu-plugin-protocol", version = "0.95.1" }
nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.95.1" } nu-plugin-core = { path = "./crates/nu-plugin-core", version = "0.95.1" }
assert_cmd = "2.0" assert_cmd = "2.0"
dirs-next = { workspace = true } dirs = { workspace = true }
tango-bench = "0.5" tango-bench = "0.5"
pretty_assertions = { workspace = true } pretty_assertions = { workspace = true }
regex = { workspace = true }
rstest = { workspace = true, default-features = false } rstest = { workspace = true, default-features = false }
serial_test = "3.1" serial_test = "3.1"
tempfile = { workspace = true } tempfile = { workspace = true }
@ -304,7 +304,7 @@ bench = false
# To use a development version of a dependency please use a global override here # 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 # changing versions in each sub-crate of the workspace is tedious
[patch.crates-io] [patch.crates-io]
# reedline = { git = "https://github.com/nushell/reedline", branch = "main" } reedline = { git = "https://github.com/nushell/reedline", branch = "main" }
# nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"} # nu-ansi-term = {git = "https://github.com/nushell/nu-ansi-term.git", branch = "main"}
# Run all benchmarks with `cargo bench` # Run all benchmarks with `cargo bench`

View File

@ -4,11 +4,14 @@ use nu_plugin_protocol::{PluginCallResponse, PluginOutput};
use nu_protocol::{ use nu_protocol::{
engine::{EngineState, Stack}, engine::{EngineState, Stack},
PipelineData, Span, Spanned, Value, PipelineData, Signals, Span, Spanned, Value,
}; };
use nu_std::load_standard_library; use nu_std::load_standard_library;
use nu_utils::{get_default_config, get_default_env}; use nu_utils::{get_default_config, get_default_env};
use std::rc::Rc; use std::{
rc::Rc,
sync::{atomic::AtomicBool, Arc},
};
use std::hint::black_box; use std::hint::black_box;
@ -42,6 +45,10 @@ fn setup_stack_and_engine_from_command(command: &str) -> (Stack, EngineState) {
}; };
let mut stack = Stack::new(); let mut stack = Stack::new();
// Support running benchmarks with IR mode
stack.use_ir = std::env::var_os("NU_USE_IR").is_some();
evaluate_commands( evaluate_commands(
&commands, &commands,
&mut engine, &mut engine,
@ -248,14 +255,12 @@ fn bench_eval_interleave(n: i32) -> impl IntoBenchmarks {
) )
} }
fn bench_eval_interleave_with_ctrlc(n: i32) -> impl IntoBenchmarks { fn bench_eval_interleave_with_interrupt(n: i32) -> impl IntoBenchmarks {
let mut engine = setup_engine(); let mut engine = setup_engine();
engine.ctrlc = Some(std::sync::Arc::new(std::sync::atomic::AtomicBool::new( engine.set_signals(Signals::new(Arc::new(AtomicBool::new(false))));
false,
)));
let stack = Stack::new(); let stack = Stack::new();
bench_command( bench_command(
&format!("eval_interleave_with_ctrlc_{n}"), &format!("eval_interleave_with_interrupt_{n}"),
&format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"), &format!("seq 1 {n} | wrap a | interleave {{ seq 1 {n} | wrap b }} | ignore"),
stack, stack,
engine, engine,
@ -443,9 +448,9 @@ tango_benchmarks!(
bench_eval_interleave(100), bench_eval_interleave(100),
bench_eval_interleave(1_000), bench_eval_interleave(1_000),
bench_eval_interleave(10_000), bench_eval_interleave(10_000),
bench_eval_interleave_with_ctrlc(100), bench_eval_interleave_with_interrupt(100),
bench_eval_interleave_with_ctrlc(1_000), bench_eval_interleave_with_interrupt(1_000),
bench_eval_interleave_with_ctrlc(10_000), bench_eval_interleave_with_interrupt(10_000),
// For // For
bench_eval_for(1), bench_eval_for(1),
bench_eval_for(10), bench_eval_for(10),

7
crates/nu-cli/README.md Normal file
View File

@ -0,0 +1,7 @@
This crate implements the core functionality of the interactive Nushell REPL and interfaces with `reedline`.
Currently implements the syntax highlighting and completions logic.
Furthermore includes a few commands that are specific to `reedline`
## Internal Nushell crate
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.

View File

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

View File

@ -49,22 +49,24 @@ impl Command for KeybindingsList {
fn run( fn run(
&self, &self,
_engine_state: &EngineState, engine_state: &EngineState,
_stack: &mut Stack, stack: &mut Stack,
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let records = if call.named_len() == 0 {
let all_options = ["modifiers", "keycodes", "edits", "modes", "events"]; let all_options = ["modifiers", "keycodes", "edits", "modes", "events"];
all_options
let presence = all_options
.iter() .iter()
.flat_map(|argument| get_records(argument, call.head)) .map(|option| call.has_flag(engine_state, stack, option))
.collect() .collect::<Result<Vec<_>, ShellError>>()?;
} else {
call.named_iter() let records = all_options
.flat_map(|(argument, _, _)| get_records(argument.item.as_str(), call.head)) .iter()
.collect() .zip(presence)
}; .filter(|(_, present)| *present)
.flat_map(|(option, _)| get_records(option, call.head))
.collect();
Ok(Value::list(records, call.head).into_pipeline_data()) Ok(Value::list(records, call.head).into_pipeline_data())
} }

View File

@ -229,6 +229,8 @@ impl Completer for VariableCompletion {
} }
} }
output = sort_suggestions(&prefix_str, output, SortBy::Ascending);
output.dedup(); // TODO: Removes only consecutive duplicates, is it intended? output.dedup(); // TODO: Removes only consecutive duplicates, is it intended?
output output

View File

@ -53,9 +53,8 @@ pub fn evaluate_commands(
// Parse the source code // Parse the source code
let (block, delta) = { let (block, delta) = {
if let Some(ref t_mode) = table_mode { if let Some(ref t_mode) = table_mode {
let mut config = engine_state.get_config().clone(); Arc::make_mut(&mut engine_state.config).table_mode =
config.table_mode = t_mode.coerce_str()?.parse().unwrap_or_default(); t_mode.coerce_str()?.parse().unwrap_or_default();
engine_state.set_config(config);
} }
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);
@ -70,6 +69,11 @@ pub fn evaluate_commands(
std::process::exit(1); std::process::exit(1);
} }
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
(output, working_set.render()) (output, working_set.render())
}; };

View File

@ -76,12 +76,21 @@ pub fn evaluate_file(
trace!("parsing file: {}", file_path_str); trace!("parsing file: {}", file_path_str);
let block = parse(&mut working_set, Some(file_path_str), &file, false); let block = parse(&mut working_set, Some(file_path_str), &file, false);
if let Some(warning) = working_set.parse_warnings.first() {
report_error(&working_set, warning);
}
// If any parse errors were found, report the first error and exit. // If any parse errors were found, report the first error and exit.
if let Some(err) = working_set.parse_errors.first() { if let Some(err) = working_set.parse_errors.first() {
report_error(&working_set, err); report_error(&working_set, err);
std::process::exit(1); std::process::exit(1);
} }
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
// Look for blocks whose name starts with "main" and replace it with the filename. // Look for blocks whose name starts with "main" and replace it with the filename.
for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) { for block in working_set.delta.blocks.iter_mut().map(Arc::make_mut) {
if block.signature.name == "main" { if block.signature.name == "main" {

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod commands; mod commands;
mod completions; mod completions;
mod config_files; mod config_files;

View File

@ -1,25 +1,31 @@
use nu_engine::documentation::get_flags_section; use nu_engine::documentation::get_flags_section;
use nu_protocol::{engine::EngineState, levenshtein_distance}; use nu_protocol::{engine::EngineState, levenshtein_distance, Config};
use nu_utils::IgnoreCaseExt; use nu_utils::IgnoreCaseExt;
use reedline::{Completer, Suggestion}; use reedline::{Completer, Suggestion};
use std::{fmt::Write, sync::Arc}; use std::{fmt::Write, sync::Arc};
pub struct NuHelpCompleter(Arc<EngineState>); pub struct NuHelpCompleter {
engine_state: Arc<EngineState>,
config: Arc<Config>,
}
impl NuHelpCompleter { impl NuHelpCompleter {
pub fn new(engine_state: Arc<EngineState>) -> Self { pub fn new(engine_state: Arc<EngineState>, config: Arc<Config>) -> Self {
Self(engine_state) Self {
engine_state,
config,
}
} }
fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> { fn completion_helper(&self, line: &str, pos: usize) -> Vec<Suggestion> {
let folded_line = line.to_folded_case(); let folded_line = line.to_folded_case();
let mut commands = self let mut commands = self
.0 .engine_state
.get_decls_sorted(false) .get_decls_sorted(false)
.into_iter() .into_iter()
.filter_map(|(_, decl_id)| { .filter_map(|(_, decl_id)| {
let decl = self.0.get_decl(decl_id); let decl = self.engine_state.get_decl(decl_id);
(decl.name().to_folded_case().contains(&folded_line) (decl.name().to_folded_case().contains(&folded_line)
|| decl.usage().to_folded_case().contains(&folded_line) || decl.usage().to_folded_case().contains(&folded_line)
|| decl || decl
@ -54,9 +60,12 @@ impl NuHelpCompleter {
let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature()); let _ = write!(long_desc, "Usage:\r\n > {}\r\n", sig.call_signature());
if !sig.named.is_empty() { if !sig.named.is_empty() {
long_desc.push_str(&get_flags_section(Some(&*self.0.clone()), &sig, |v| { long_desc.push_str(&get_flags_section(
v.to_parsable_string(", ", &self.0.config) Some(&self.engine_state),
})) Some(&self.config),
&sig,
|v| v.to_parsable_string(", ", &self.config),
))
} }
if !sig.required_positional.is_empty() if !sig.required_positional.is_empty()
@ -71,7 +80,7 @@ impl NuHelpCompleter {
let opt_suffix = if let Some(value) = &positional.default_value { let opt_suffix = if let Some(value) = &positional.default_value {
format!( format!(
" (optional, default: {})", " (optional, default: {})",
&value.to_parsable_string(", ", &self.0.config), &value.to_parsable_string(", ", &self.config),
) )
} else { } else {
(" (optional)").to_string() (" (optional)").to_string()
@ -138,7 +147,8 @@ mod test {
) { ) {
let engine_state = let engine_state =
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context()); nu_command::add_shell_command_context(nu_cmd_lang::create_default_context());
let mut completer = NuHelpCompleter::new(engine_state.into()); let config = engine_state.get_config().clone();
let mut completer = NuHelpCompleter::new(engine_state.into(), config);
let suggestions = completer.complete(line, end); let suggestions = completer.complete(line, end);
assert_eq!( assert_eq!(

View File

@ -1,3 +1,5 @@
use std::sync::Arc;
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use reedline::{Highlighter, StyledText}; use reedline::{Highlighter, StyledText};
@ -32,14 +34,11 @@ impl Command for NuHighlight {
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let head = call.head; let head = call.head;
let ctrlc = engine_state.ctrlc.clone(); let signals = engine_state.signals();
let engine_state = std::sync::Arc::new(engine_state.clone());
let config = engine_state.get_config().clone();
let highlighter = crate::NuHighlighter { let highlighter = crate::NuHighlighter {
engine_state, engine_state: Arc::new(engine_state.clone()),
stack: std::sync::Arc::new(stack.clone()), stack: Arc::new(stack.clone()),
config,
}; };
input.map( input.map(
@ -50,7 +49,7 @@ impl Command for NuHighlight {
} }
Err(err) => Value::error(err, head), Err(err) => Value::error(err, head),
}, },
ctrlc, signals,
) )
} }

View File

@ -77,13 +77,19 @@ pub(crate) fn add_menus(
mut line_editor: Reedline, mut line_editor: Reedline,
engine_state_ref: Arc<EngineState>, engine_state_ref: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
//log::trace!("add_menus: config: {:#?}", &config); //log::trace!("add_menus: config: {:#?}", &config);
line_editor = line_editor.clear_menus(); line_editor = line_editor.clear_menus();
for menu in &config.menus { for menu in &config.menus {
line_editor = add_menu(line_editor, menu, engine_state_ref.clone(), stack, config)? line_editor = add_menu(
line_editor,
menu,
engine_state_ref.clone(),
stack,
config.clone(),
)?
} }
// Checking if the default menus have been added from the config file // Checking if the default menus have been added from the config file
@ -100,7 +106,7 @@ pub(crate) fn add_menus(
if !config if !config
.menus .menus
.iter() .iter()
.any(|menu| menu.name.to_expanded_string("", config) == name) .any(|menu| menu.name.to_expanded_string("", &config) == name)
{ {
let (block, delta) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(&engine_state); let mut working_set = StateWorkingSet::new(&engine_state);
@ -137,7 +143,7 @@ pub(crate) fn add_menus(
&menu, &menu,
new_engine_state_ref.clone(), new_engine_state_ref.clone(),
stack, stack,
config, config.clone(),
)?; )?;
} }
} }
@ -151,27 +157,27 @@ fn add_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let span = menu.menu_type.span(); let span = menu.menu_type.span();
if let Value::Record { val, .. } = &menu.menu_type { if let Value::Record { val, .. } = &menu.menu_type {
let layout = extract_value("layout", val, span)?.to_expanded_string("", config); let layout = extract_value("layout", val, span)?.to_expanded_string("", &config);
match layout.as_str() { match layout.as_str() {
"columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, config), "columnar" => add_columnar_menu(line_editor, menu, engine_state, stack, &config),
"list" => add_list_menu(line_editor, menu, engine_state, stack, config), "list" => add_list_menu(line_editor, menu, engine_state, stack, config),
"ide" => add_ide_menu(line_editor, menu, engine_state, stack, config), "ide" => add_ide_menu(line_editor, menu, engine_state, stack, config),
"description" => add_description_menu(line_editor, menu, engine_state, stack, config), "description" => add_description_menu(line_editor, menu, engine_state, stack, config),
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "columnar, list, ide or description".to_string(), expected: "columnar, list, ide or description".to_string(),
value: menu.menu_type.to_abbreviated_string(config), value: menu.menu_type.to_abbreviated_string(&config),
span: menu.menu_type.span(), span: menu.menu_type.span(),
}), }),
} }
} else { } else {
Err(ShellError::UnsupportedConfigValue { Err(ShellError::UnsupportedConfigValue {
expected: "only record type".to_string(), expected: "only record type".to_string(),
value: menu.menu_type.to_abbreviated_string(config), value: menu.menu_type.to_abbreviated_string(&config),
span: menu.menu_type.span(), span: menu.menu_type.span(),
}) })
} }
@ -282,9 +288,9 @@ pub(crate) fn add_list_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let name = menu.name.to_expanded_string("", config); let name = menu.name.to_expanded_string("", &config);
let mut list_menu = ListMenu::default().with_name(&name); let mut list_menu = ListMenu::default().with_name(&name);
let span = menu.menu_type.span(); let span = menu.menu_type.span();
@ -311,7 +317,7 @@ pub(crate) fn add_list_menu(
} }
} }
let marker = menu.marker.to_expanded_string("", config); let marker = menu.marker.to_expanded_string("", &config);
list_menu = list_menu.with_marker(&marker); list_menu = list_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?; let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -337,7 +343,7 @@ pub(crate) fn add_list_menu(
} }
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "block or omitted value".to_string(), expected: "block or omitted value".to_string(),
value: menu.source.to_abbreviated_string(config), value: menu.source.to_abbreviated_string(&config),
span: menu.source.span(), span: menu.source.span(),
}), }),
} }
@ -349,10 +355,10 @@ pub(crate) fn add_ide_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let span = menu.menu_type.span(); let span = menu.menu_type.span();
let name = menu.name.to_expanded_string("", config); let name = menu.name.to_expanded_string("", &config);
let mut ide_menu = IdeMenu::default().with_name(&name); let mut ide_menu = IdeMenu::default().with_name(&name);
if let Value::Record { val, .. } = &menu.menu_type { if let Value::Record { val, .. } = &menu.menu_type {
@ -417,7 +423,7 @@ pub(crate) fn add_ide_menu(
} else { } else {
return Err(ShellError::UnsupportedConfigValue { return Err(ShellError::UnsupportedConfigValue {
expected: "bool or record".to_string(), expected: "bool or record".to_string(),
value: border.to_abbreviated_string(config), value: border.to_abbreviated_string(&config),
span: border.span(), span: border.span(),
}); });
} }
@ -441,7 +447,7 @@ pub(crate) fn add_ide_menu(
_ => { _ => {
return Err(ShellError::UnsupportedConfigValue { return Err(ShellError::UnsupportedConfigValue {
expected: "\"left\", \"right\" or \"prefer_right\"".to_string(), expected: "\"left\", \"right\" or \"prefer_right\"".to_string(),
value: description_mode.to_abbreviated_string(config), value: description_mode.to_abbreviated_string(&config),
span: description_mode.span(), span: description_mode.span(),
}); });
} }
@ -509,7 +515,7 @@ pub(crate) fn add_ide_menu(
} }
} }
let marker = menu.marker.to_expanded_string("", config); let marker = menu.marker.to_expanded_string("", &config);
ide_menu = ide_menu.with_marker(&marker); ide_menu = ide_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?; let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -535,7 +541,7 @@ pub(crate) fn add_ide_menu(
} }
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "block or omitted value".to_string(), expected: "block or omitted value".to_string(),
value: menu.source.to_abbreviated_string(config), value: menu.source.to_abbreviated_string(&config),
span, span,
}), }),
} }
@ -547,9 +553,9 @@ pub(crate) fn add_description_menu(
menu: &ParsedMenu, menu: &ParsedMenu,
engine_state: Arc<EngineState>, engine_state: Arc<EngineState>,
stack: &Stack, stack: &Stack,
config: &Config, config: Arc<Config>,
) -> Result<Reedline, ShellError> { ) -> Result<Reedline, ShellError> {
let name = menu.name.to_expanded_string("", config); let name = menu.name.to_expanded_string("", &config);
let mut description_menu = DescriptionMenu::default().with_name(&name); let mut description_menu = DescriptionMenu::default().with_name(&name);
let span = menu.menu_type.span(); let span = menu.menu_type.span();
@ -608,7 +614,7 @@ pub(crate) fn add_description_menu(
} }
} }
let marker = menu.marker.to_expanded_string("", config); let marker = menu.marker.to_expanded_string("", &config);
description_menu = description_menu.with_marker(&marker); description_menu = description_menu.with_marker(&marker);
let only_buffer_difference = menu.only_buffer_difference.as_bool()?; let only_buffer_difference = menu.only_buffer_difference.as_bool()?;
@ -617,7 +623,7 @@ pub(crate) fn add_description_menu(
let span = menu.source.span(); let span = menu.source.span();
match &menu.source { match &menu.source {
Value::Nothing { .. } => { Value::Nothing { .. } => {
let completer = Box::new(NuHelpCompleter::new(engine_state)); let completer = Box::new(NuHelpCompleter::new(engine_state, config));
Ok(line_editor.with_menu(ReedlineMenu::WithCompleter { Ok(line_editor.with_menu(ReedlineMenu::WithCompleter {
menu: Box::new(description_menu), menu: Box::new(description_menu),
completer, completer,
@ -638,7 +644,7 @@ pub(crate) fn add_description_menu(
} }
_ => Err(ShellError::UnsupportedConfigValue { _ => Err(ShellError::UnsupportedConfigValue {
expected: "closure or omitted value".to_string(), expected: "closure or omitted value".to_string(),
value: menu.source.to_abbreviated_string(config), value: menu.source.to_abbreviated_string(&config),
span: menu.source.span(), span: menu.source.span(),
}), }),
} }

View File

@ -43,7 +43,7 @@ use std::{
io::{self, IsTerminal, Write}, io::{self, IsTerminal, Write},
panic::{catch_unwind, AssertUnwindSafe}, panic::{catch_unwind, AssertUnwindSafe},
path::{Path, PathBuf}, path::{Path, PathBuf},
sync::{atomic::Ordering, Arc}, sync::Arc,
time::{Duration, Instant}, time::{Duration, Instant},
}; };
use sysinfo::System; use sysinfo::System;
@ -268,14 +268,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
if let Err(err) = engine_state.merge_env(&mut stack, cwd) { if let Err(err) = engine_state.merge_env(&mut stack, cwd) {
report_error_new(engine_state, &err); report_error_new(engine_state, &err);
} }
// Check whether $env.NU_USE_IR is set, so that the user can change it in the REPL
// Temporary while IR eval is optional
stack.use_ir = stack.has_env_var(engine_state, "NU_USE_IR");
perf!("merge env", start_time, use_color); perf!("merge env", start_time, use_color);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Reset the ctrl-c handler engine_state.reset_signals();
if let Some(ctrlc) = &mut engine_state.ctrlc { perf!("reset signals", start_time, use_color);
ctrlc.store(false, Ordering::SeqCst);
}
perf!("reset ctrlc", start_time, use_color);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Right before we start our prompt and take input from the user, // Right before we start our prompt and take input from the user,
@ -297,7 +297,7 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
perf!("env-change hook", start_time, use_color); perf!("env-change hook", start_time, use_color);
let engine_reference = Arc::new(engine_state.clone()); let engine_reference = Arc::new(engine_state.clone());
let config = engine_state.get_config(); let config = stack.get_config(engine_state);
start_time = std::time::Instant::now(); start_time = std::time::Instant::now();
// Find the configured cursor shapes for each mode // Find the configured cursor shapes for each mode
@ -323,7 +323,6 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
engine_state: engine_reference.clone(), engine_state: engine_reference.clone(),
// STACK-REFERENCE 1 // STACK-REFERENCE 1
stack: stack_arc.clone(), stack: stack_arc.clone(),
config: config.clone(),
})) }))
.with_validator(Box::new(NuValidator { .with_validator(Box::new(NuValidator {
engine_state: engine_reference.clone(), engine_state: engine_reference.clone(),
@ -336,6 +335,14 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) {
.with_quick_completions(config.quick_completions) .with_quick_completions(config.quick_completions)
.with_partial_completions(config.partial_completions) .with_partial_completions(config.partial_completions)
.with_ansi_colors(config.use_ansi_coloring) .with_ansi_colors(config.use_ansi_coloring)
.with_cwd(Some(
engine_state
.cwd(None)
.map(|cwd| cwd.into_std_path_buf())
.unwrap_or_default()
.to_string_lossy()
.to_string(),
))
.with_cursor_config(cursor_config); .with_cursor_config(cursor_config);
perf!("reedline builder", start_time, use_color); perf!("reedline builder", start_time, use_color);
@ -666,13 +673,14 @@ fn prepare_history_metadata(
line_editor: &mut Reedline, line_editor: &mut Reedline,
) { ) {
if !s.is_empty() && line_editor.has_last_command_context() { if !s.is_empty() && line_editor.has_last_command_context() {
#[allow(deprecated)]
let result = line_editor let result = line_editor
.update_last_command_context(&|mut c| { .update_last_command_context(&|mut c| {
c.start_timestamp = Some(chrono::Utc::now()); c.start_timestamp = Some(chrono::Utc::now());
c.hostname = hostname.map(str::to_string); c.hostname = hostname.map(str::to_string);
c.cwd = engine_state
c.cwd = Some(StateWorkingSet::new(engine_state).get_cwd()); .cwd(None)
.ok()
.map(|path| path.to_string_lossy().to_string());
c c
}) })
.into_diagnostic(); .into_diagnostic();

View File

@ -6,7 +6,7 @@ use nu_parser::{flatten_block, parse, FlatShape};
use nu_protocol::{ use nu_protocol::{
ast::{Block, Expr, Expression, PipelineRedirection, RecordItem}, ast::{Block, Expr, Expression, PipelineRedirection, RecordItem},
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
Config, Span, Span,
}; };
use reedline::{Highlighter, StyledText}; use reedline::{Highlighter, StyledText};
use std::sync::Arc; use std::sync::Arc;
@ -14,15 +14,14 @@ use std::sync::Arc;
pub struct NuHighlighter { pub struct NuHighlighter {
pub engine_state: Arc<EngineState>, pub engine_state: Arc<EngineState>,
pub stack: Arc<Stack>, pub stack: Arc<Stack>,
pub config: Config,
} }
impl Highlighter for NuHighlighter { impl Highlighter for NuHighlighter {
fn highlight(&self, line: &str, _cursor: usize) -> StyledText { fn highlight(&self, line: &str, _cursor: usize) -> StyledText {
trace!("highlighting: {}", line); trace!("highlighting: {}", line);
let highlight_resolved_externals = let config = self.stack.get_config(&self.engine_state);
self.engine_state.get_config().highlight_resolved_externals; let highlight_resolved_externals = config.highlight_resolved_externals;
let mut working_set = StateWorkingSet::new(&self.engine_state); let mut working_set = StateWorkingSet::new(&self.engine_state);
let block = parse(&mut working_set, None, line.as_bytes(), false); let block = parse(&mut working_set, None, line.as_bytes(), false);
let (shapes, global_span_offset) = { let (shapes, global_span_offset) = {
@ -88,7 +87,7 @@ impl Highlighter for NuHighlighter {
.to_string(); .to_string();
let mut add_colored_token = |shape: &FlatShape, text: String| { let mut add_colored_token = |shape: &FlatShape, text: String| {
output.push((get_shape_color(shape.as_str(), &self.config), text)); output.push((get_shape_color(shape.as_str(), &config), text));
}; };
match shape.1 { match shape.1 {
@ -128,9 +127,9 @@ impl Highlighter for NuHighlighter {
let start = part.start - span.start; let start = part.start - span.start;
let end = part.end - span.start; let end = part.end - span.start;
let text = next_token[start..end].to_string(); let text = next_token[start..end].to_string();
let mut style = get_shape_color(shape.as_str(), &self.config); let mut style = get_shape_color(shape.as_str(), &config);
if highlight { if highlight {
style = get_matching_brackets_style(style, &self.config); style = get_matching_brackets_style(style, &config);
} }
output.push((style, text)); output.push((style, text));
} }

View File

@ -262,6 +262,11 @@ fn evaluate_source(
return Ok(Some(1)); return Ok(Some(1));
} }
if let Some(err) = working_set.compile_errors.first() {
report_error(&working_set, err);
// Not a fatal error, for now
}
(output, working_set.render()) (output, working_set.render())
}; };

View File

@ -107,7 +107,7 @@ fn subcommand_completer() -> NuCompleter {
} }
#[test] #[test]
fn variables_dollar_sign_with_varialblecompletion() { fn variables_dollar_sign_with_variablecompletion() {
let (_, _, engine, stack) = new_engine(); let (_, _, engine, stack) = new_engine();
let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack));
@ -721,7 +721,7 @@ fn file_completion_quoted() {
"`te#st.txt`".to_string(), "`te#st.txt`".to_string(),
"`te'st.txt`".to_string(), "`te'st.txt`".to_string(),
"`te(st).txt`".to_string(), "`te(st).txt`".to_string(),
format!("`{}`", folder("test dir".into())), format!("`{}`", folder("test dir")),
]; ];
match_suggestions(expected_paths, suggestions); match_suggestions(expected_paths, suggestions);
@ -907,6 +907,11 @@ fn variables_completions() {
// Match results // Match results
match_suggestions(expected, suggestions); match_suggestions(expected, suggestions);
let suggestions = completer.complete("$", 1);
let expected: Vec<String> = vec!["$actor".into(), "$env".into(), "$in".into(), "$nu".into()];
match_suggestions(expected, suggestions);
} }
#[test] #[test]

View File

@ -1,5 +1,6 @@
use nu_engine::eval_block; use nu_engine::eval_block;
use nu_parser::parse; use nu_parser::parse;
use nu_path::{AbsolutePathBuf, PathBuf};
use nu_protocol::{ use nu_protocol::{
debugger::WithoutDebug, debugger::WithoutDebug,
engine::{EngineState, Stack, StateWorkingSet}, engine::{EngineState, Stack, StateWorkingSet},
@ -7,14 +8,14 @@ use nu_protocol::{
}; };
use nu_test_support::fs; use nu_test_support::fs;
use reedline::Suggestion; use reedline::Suggestion;
use std::path::{PathBuf, MAIN_SEPARATOR}; use std::path::MAIN_SEPARATOR;
fn create_default_context() -> EngineState { fn create_default_context() -> EngineState {
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context()) nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
} }
// creates a new engine with the current path into the completions fixtures folder // creates a new engine with the current path into the completions fixtures folder
pub fn new_engine() -> (PathBuf, String, EngineState, Stack) { pub fn new_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("completions"); let dir = fs::fixtures().join("completions");
let dir_str = dir let dir_str = dir
@ -69,7 +70,7 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) {
} }
// creates a new engine with the current path into the completions fixtures folder // creates a new engine with the current path into the completions fixtures folder
pub fn new_dotnu_engine() -> (PathBuf, String, EngineState, Stack) { pub fn new_dotnu_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("dotnu_completions"); let dir = fs::fixtures().join("dotnu_completions");
let dir_str = dir let dir_str = dir
@ -114,7 +115,7 @@ pub fn new_dotnu_engine() -> (PathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack) (dir, dir_str, engine_state, stack)
} }
pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) { pub fn new_quote_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("quoted_completions"); let dir = fs::fixtures().join("quoted_completions");
let dir_str = dir let dir_str = dir
@ -149,7 +150,7 @@ pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) {
(dir, dir_str, engine_state, stack) (dir, dir_str, engine_state, stack)
} }
pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) { pub fn new_partial_engine() -> (AbsolutePathBuf, String, EngineState, Stack) {
// Target folder inside assets // Target folder inside assets
let dir = fs::fixtures().join("partial_completions"); let dir = fs::fixtures().join("partial_completions");
let dir_str = dir let dir_str = dir
@ -205,16 +206,15 @@ pub fn match_suggestions(expected: Vec<String>, suggestions: Vec<Suggestion>) {
} }
// append the separator to the converted path // append the separator to the converted path
pub fn folder(path: PathBuf) -> String { pub fn folder(path: impl Into<PathBuf>) -> String {
let mut converted_path = file(path); let mut converted_path = file(path);
converted_path.push(MAIN_SEPARATOR); converted_path.push(MAIN_SEPARATOR);
converted_path converted_path
} }
// convert a given path to string // convert a given path to string
pub fn file(path: PathBuf) -> String { pub fn file(path: impl Into<PathBuf>) -> String {
path.into_os_string().into_string().unwrap_or_default() path.into().into_os_string().into_string().unwrap()
} }
// merge_input executes the given input into the engine // merge_input executes the given input into the engine
@ -223,7 +223,7 @@ pub fn merge_input(
input: &[u8], input: &[u8],
engine_state: &mut EngineState, engine_state: &mut EngineState,
stack: &mut Stack, stack: &mut Stack,
dir: PathBuf, dir: AbsolutePathBuf,
) -> Result<(), ShellError> { ) -> Result<(), ShellError> {
let (block, delta) = { let (block, delta) = {
let mut working_set = StateWorkingSet::new(engine_state); let mut working_set = StateWorkingSet::new(engine_state);

View File

@ -0,0 +1,5 @@
Utilities used by the different `nu-command`/`nu-cmd-*` crates, should not contain any full `Command` implementations.
## Internal Nushell crate
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.

View File

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

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
pub mod formats; pub mod formats;
pub mod hook; pub mod hook;
pub mod input_handler; pub mod input_handler;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -239,7 +239,7 @@ fn to_html(
let partial = call.has_flag(engine_state, stack, "partial")?; let partial = call.has_flag(engine_state, stack, "partial")?;
let list = call.has_flag(engine_state, stack, "list")?; let list = call.has_flag(engine_state, stack, "list")?;
let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?; let theme: Option<Spanned<String>> = call.get_flag(engine_state, stack, "theme")?;
let config = engine_state.get_config(); let config = &stack.get_config(engine_state);
let vec_of_values = input.into_iter().collect::<Vec<Value>>(); let vec_of_values = input.into_iter().collect::<Vec<Value>>();
let headers = merge_descriptors(&vec_of_values); let headers = merge_descriptors(&vec_of_values);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,5 @@
use nu_engine::command_prelude::*; use nu_engine::command_prelude::*;
use nu_protocol::{ast::PathMember, engine::StateWorkingSet, ListStream}; use nu_protocol::{ast::PathMember, engine::StateWorkingSet, Config, ListStream};
#[derive(Clone)] #[derive(Clone)]
pub struct FormatPattern; pub struct FormatPattern;
@ -43,6 +43,8 @@ impl Command for FormatPattern {
let it_id = working_set.add_variable(b"$it".to_vec(), call.head, Type::Any, false); let it_id = working_set.add_variable(b"$it".to_vec(), call.head, Type::Any, false);
stack.add_var(it_id, input_val.clone()); stack.add_var(it_id, input_val.clone());
let config = stack.get_config(engine_state);
match specified_pattern { match specified_pattern {
Err(e) => Err(e), Err(e) => Err(e),
Ok(pattern) => { Ok(pattern) => {
@ -56,7 +58,7 @@ impl Command for FormatPattern {
string_span.start + 1, string_span.start + 1,
)?; )?;
format(input_val, &ops, engine_state, call.head) format(input_val, &ops, engine_state, &config, call.head)
} }
} }
} }
@ -181,33 +183,30 @@ fn format(
input_data: Value, input_data: Value,
format_operations: &[FormatOperation], format_operations: &[FormatOperation],
engine_state: &EngineState, engine_state: &EngineState,
config: &Config,
head_span: Span, head_span: Span,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let data_as_value = input_data; let data_as_value = input_data;
// We can only handle a Record or a List of Records // We can only handle a Record or a List of Records
match data_as_value { match data_as_value {
Value::Record { .. } => { Value::Record { .. } => match format_record(format_operations, &data_as_value, config) {
match format_record(format_operations, &data_as_value, engine_state) {
Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)), Ok(value) => Ok(PipelineData::Value(Value::string(value, head_span), None)),
Err(value) => Err(value), Err(value) => Err(value),
} },
}
Value::List { vals, .. } => { Value::List { vals, .. } => {
let mut list = vec![]; let mut list = vec![];
for val in vals.iter() { for val in vals.iter() {
match val { match val {
Value::Record { .. } => { Value::Record { .. } => match format_record(format_operations, val, config) {
match format_record(format_operations, val, engine_state) {
Ok(value) => { Ok(value) => {
list.push(Value::string(value, head_span)); list.push(Value::string(value, head_span));
} }
Err(value) => { Err(value) => {
return Err(value); return Err(value);
} }
} },
}
Value::Error { error, .. } => return Err(*error.clone()), Value::Error { error, .. } => return Err(*error.clone()),
_ => { _ => {
return Err(ShellError::OnlySupportsThisInputType { return Err(ShellError::OnlySupportsThisInputType {
@ -220,7 +219,7 @@ fn format(
} }
} }
Ok(ListStream::new(list.into_iter(), head_span, engine_state.ctrlc.clone()).into()) Ok(ListStream::new(list.into_iter(), head_span, engine_state.signals().clone()).into())
} }
// Unwrapping this ShellError is a bit unfortunate. // Unwrapping this ShellError is a bit unfortunate.
// Ideally, its Span would be preserved. // Ideally, its Span would be preserved.
@ -237,9 +236,8 @@ fn format(
fn format_record( fn format_record(
format_operations: &[FormatOperation], format_operations: &[FormatOperation],
data_as_value: &Value, data_as_value: &Value,
engine_state: &EngineState, config: &Config,
) -> Result<String, ShellError> { ) -> Result<String, ShellError> {
let config = engine_state.get_config();
let mut output = String::new(); let mut output = String::new();
for op in format_operations { for op in format_operations {

View File

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

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod example_test; mod example_test;
pub mod extra; pub mod extra;
pub use extra::*; pub use extra::*;

View File

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

View File

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

View File

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

View File

@ -46,6 +46,9 @@ impl Command for Const {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let var_id = if let Some(id) = call.positional_nth(0).and_then(|pos| pos.as_var()) { let var_id = if let Some(id) = call.positional_nth(0).and_then(|pos| pos.as_var()) {
id id
} else { } else {

View File

@ -81,6 +81,10 @@ impl Command for Do {
bind_args_to(&mut callee_stack, &block.signature, rest, head)?; bind_args_to(&mut callee_stack, &block.signature, rest, head)?;
let eval_block_with_early_return = get_eval_block_with_early_return(engine_state); let eval_block_with_early_return = get_eval_block_with_early_return(engine_state);
// Applies to all block evaluation once set true
callee_stack.use_ir = caller_stack.has_env_var(engine_state, "NU_USE_IR");
let result = eval_block_with_early_return(engine_state, &mut callee_stack, block, input); let result = eval_block_with_early_return(engine_state, &mut callee_stack, block, input);
if has_env { if has_env {

View File

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

View File

@ -1,5 +1,5 @@
use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression}; use nu_engine::{command_prelude::*, get_eval_block, get_eval_expression};
use nu_protocol::engine::CommandType; use nu_protocol::{engine::CommandType, Signals};
#[derive(Clone)] #[derive(Clone)]
pub struct For; pub struct For;
@ -48,6 +48,9 @@ impl Command for For {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head; let head = call.head;
let var_id = call let var_id = call
.positional_nth(0) .positional_nth(0)
@ -72,7 +75,6 @@ impl Command for For {
let value = eval_expression(engine_state, stack, keyword_expr)?; let value = eval_expression(engine_state, stack, keyword_expr)?;
let ctrlc = engine_state.ctrlc.clone();
let engine_state = engine_state.clone(); let engine_state = engine_state.clone();
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);
@ -82,9 +84,7 @@ impl Command for For {
match value { match value {
Value::List { vals, .. } => { Value::List { vals, .. } => {
for x in vals.into_iter() { for x in vals.into_iter() {
if nu_utils::ctrl_c::was_pressed(&ctrlc) { engine_state.signals().check(head)?;
break;
}
// with_env() is used here to ensure that each iteration uses // with_env() is used here to ensure that each iteration uses
// a different set of environment variables. // a different set of environment variables.
@ -116,7 +116,8 @@ impl Command for For {
} }
} }
Value::Range { val, .. } => { Value::Range { val, .. } => {
for x in val.into_range_iter(span, ctrlc) { for x in val.into_range_iter(span, Signals::empty()) {
engine_state.signals().check(head)?;
stack.add_var(var_id, x); stack.add_var(var_id, x);
match eval_block(&engine_state, stack, block, PipelineData::empty()) { match eval_block(&engine_state, stack, block, PipelineData::empty()) {

View File

@ -60,6 +60,9 @@ impl Command for If {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let cond = call.positional_nth(0).expect("checked through parser"); let cond = call.positional_nth(0).expect("checked through parser");
let then_block = call let then_block = call
.positional_nth(1) .positional_nth(1)
@ -99,6 +102,9 @@ impl Command for If {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let cond = call.positional_nth(0).expect("checked through parser"); let cond = call.positional_nth(0).expect("checked through parser");
let then_block = call let then_block = call
.positional_nth(1) .positional_nth(1)

View File

@ -46,6 +46,9 @@ impl Command for Let {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let var_id = call let var_id = call
.positional_nth(0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")

View File

@ -37,6 +37,10 @@ impl Command for Loop {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head;
let block_id = call let block_id = call
.positional_nth(0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")
@ -49,9 +53,7 @@ impl Command for Loop {
let stack = &mut stack.push_redirection(None, None); let stack = &mut stack.push_redirection(None, None);
loop { loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) { engine_state.signals().check(head)?;
break;
}
match eval_block(engine_state, stack, block, PipelineData::empty()) { match eval_block(engine_state, stack, block, PipelineData::empty()) {
Err(ShellError::Break { .. }) => { Err(ShellError::Break { .. }) => {

View File

@ -43,6 +43,9 @@ impl Command for Match {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let value: Value = call.req(engine_state, stack, 0)?; let value: Value = call.req(engine_state, stack, 0)?;
let matches = call let matches = call
.positional_nth(1) .positional_nth(1)

View File

@ -46,6 +46,9 @@ impl Command for Mut {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let var_id = call let var_id = call
.positional_nth(0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")

View File

@ -65,9 +65,9 @@ impl Command for OverlayUse {
name_arg.item = trim_quotes_str(&name_arg.item).to_string(); name_arg.item = trim_quotes_str(&name_arg.item).to_string();
let maybe_origin_module_id = let maybe_origin_module_id =
if let Some(overlay_expr) = call.get_parser_info("overlay_expr") { if let Some(overlay_expr) = call.get_parser_info(caller_stack, "overlay_expr") {
if let Expr::Overlay(module_id) = &overlay_expr.expr { if let Expr::Overlay(module_id) = &overlay_expr.expr {
module_id *module_id
} else { } else {
return Err(ShellError::NushellFailedSpanned { return Err(ShellError::NushellFailedSpanned {
msg: "Not an overlay".to_string(), msg: "Not an overlay".to_string(),
@ -110,7 +110,7 @@ impl Command for OverlayUse {
// a) adding a new overlay // a) adding a new overlay
// b) refreshing an active overlay (the origin module changed) // b) refreshing an active overlay (the origin module changed)
let module = engine_state.get_module(*module_id); let module = engine_state.get_module(module_id);
// Evaluate the export-env block (if any) and keep its environment // Evaluate the export-env block (if any) and keep its environment
if let Some(block_id) = module.env_block { if let Some(block_id) = module.env_block {
@ -118,7 +118,7 @@ impl Command for OverlayUse {
&name_arg.item, &name_arg.item,
engine_state, engine_state,
caller_stack, caller_stack,
get_dirs_var_from_call(call), get_dirs_var_from_call(caller_stack, call),
)?; )?;
let block = engine_state.get_block(block_id); let block = engine_state.get_block(block_id);

View File

@ -47,6 +47,9 @@ impl Command for Try {
call: &Call, call: &Call,
input: PipelineData, input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let try_block = call let try_block = call
.positional_nth(0) .positional_nth(0)
.expect("checked through parser") .expect("checked through parser")

View File

@ -57,7 +57,7 @@ This command is a parser keyword. For details, check:
let Some(Expression { let Some(Expression {
expr: Expr::ImportPattern(import_pattern), expr: Expr::ImportPattern(import_pattern),
.. ..
}) = call.get_parser_info("import_pattern") }) = call.get_parser_info(caller_stack, "import_pattern")
else { else {
return Err(ShellError::GenericError { return Err(ShellError::GenericError {
error: "Unexpected import".into(), error: "Unexpected import".into(),
@ -68,6 +68,9 @@ This command is a parser keyword. For details, check:
}); });
}; };
// Necessary so that we can modify the stack.
let import_pattern = import_pattern.clone();
if let Some(module_id) = import_pattern.head.id { if let Some(module_id) = import_pattern.head.id {
// Add constants // Add constants
for var_id in &import_pattern.constants { for var_id in &import_pattern.constants {
@ -99,7 +102,7 @@ This command is a parser keyword. For details, check:
&module_arg_str, &module_arg_str,
engine_state, engine_state,
caller_stack, caller_stack,
get_dirs_var_from_call(call), get_dirs_var_from_call(caller_stack, call),
)?; )?;
let maybe_parent = maybe_file_path let maybe_parent = maybe_file_path
.as_ref() .as_ref()

View File

@ -46,6 +46,10 @@ impl Command for While {
call: &Call, call: &Call,
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
// This is compiled specially by the IR compiler. The code here is never used when
// running in IR mode.
let call = call.assert_ast_call()?;
let head = call.head;
let cond = call.positional_nth(0).expect("checked through parser"); let cond = call.positional_nth(0).expect("checked through parser");
let block_id = call let block_id = call
.positional_nth(1) .positional_nth(1)
@ -59,9 +63,7 @@ impl Command for While {
let stack = &mut stack.push_redirection(None, None); let stack = &mut stack.push_redirection(None, None);
loop { loop {
if nu_utils::ctrl_c::was_pressed(&engine_state.ctrlc) { engine_state.signals().check(head)?;
break;
}
let result = eval_expression(engine_state, stack, cond)?; let result = eval_expression(engine_state, stack, cond)?;

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod core_commands; mod core_commands;
mod default_context; mod default_context;
pub mod example_support; pub mod example_support;

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,5 @@
Logic to resolve colors for syntax highlighting and output formatting
## Internal Nushell crate
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.

View File

@ -1,3 +1,4 @@
#![doc = include_str!("../README.md")]
mod color_config; mod color_config;
mod matching_brackets_style; mod matching_brackets_style;
mod nu_style; mod nu_style;

View File

@ -1,6 +1,6 @@
use crate::{color_record_to_nustyle, lookup_ansi_color_style, text_style::Alignment, TextStyle}; use crate::{color_record_to_nustyle, lookup_ansi_color_style, text_style::Alignment, TextStyle};
use nu_ansi_term::{Color, Style}; use nu_ansi_term::{Color, Style};
use nu_engine::{env::get_config, ClosureEvalOnce}; use nu_engine::ClosureEvalOnce;
use nu_protocol::{ use nu_protocol::{
cli_error::CliError, cli_error::CliError,
engine::{Closure, EngineState, Stack, StateWorkingSet}, engine::{Closure, EngineState, Stack, StateWorkingSet},
@ -114,7 +114,7 @@ impl<'a> StyleComputer<'a> {
// The main constructor. // The main constructor.
pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> { pub fn from_config(engine_state: &'a EngineState, stack: &'a Stack) -> StyleComputer<'a> {
let config = get_config(engine_state, stack); let config = stack.get_config(engine_state);
// Create the hashmap // Create the hashmap
#[rustfmt::skip] #[rustfmt::skip]

View File

@ -140,7 +140,7 @@ trash-support = ["trash"]
nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.95.1" } nu-cmd-lang = { path = "../nu-cmd-lang", version = "0.95.1" }
nu-test-support = { path = "../nu-test-support", version = "0.95.1" } nu-test-support = { path = "../nu-test-support", version = "0.95.1" }
dirs-next = { workspace = true } dirs = { workspace = true }
mockito = { workspace = true, default-features = false } mockito = { workspace = true, default-features = false }
quickcheck = { workspace = true } quickcheck = { workspace = true }
quickcheck_macros = { workspace = true } quickcheck_macros = { workspace = true }

View File

@ -0,0 +1,7 @@
This crate contains the majority of our commands
We allow ourselves to move some of the commands in `nu-command` to `nu-cmd-*` crates as needed.
## Internal Nushell crate
This crate implements components of Nushell and is not designed to support plugin authors or other users directly.

View File

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

View File

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

View File

@ -49,10 +49,8 @@ impl Command for BytesBuild {
_input: PipelineData, _input: PipelineData,
) -> Result<PipelineData, ShellError> { ) -> Result<PipelineData, ShellError> {
let mut output = vec![]; let mut output = vec![];
for val in call.rest_iter_flattened(0, |expr| {
let eval_expression = get_eval_expression(engine_state); let eval_expression = get_eval_expression(engine_state);
eval_expression(engine_state, stack, expr) for val in call.rest_iter_flattened(engine_state, stack, eval_expression, 0)? {
})? {
let val_span = val.span(); let val_span = val.span();
match val { match val {
Value::Binary { mut val, .. } => output.append(&mut val), Value::Binary { mut val, .. } => output.append(&mut val),

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -141,7 +141,7 @@ impl Command for SubCommand {
zone_options, zone_options,
cell_paths, cell_paths,
}; };
operate(action, args, input, call.head, engine_state.ctrlc.clone()) operate(action, args, input, call.head, engine_state.signals())
} }
} }

View File

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

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