Refactor/fix tests affecting the whole command set (#15073)

# Description
Pre-cratification of `nu-command` we added tests that covered the whole
command set to ensure consistent documentation style choices and that
the search terms which are added are not uselessly redundant. These
tests are now moved into the suite of the main binary to truly cover all
commands.

- **Move parser quickcheck "fuzz" to `nu-cmd-lang`**
- **Factor out creation of full engine state for tests**
- **Move all-command tests to main context creation**
- **Fix all descriptions**
- **Fix search term duplicate**

# User-Facing Changes
As a result I had to fix a few command argument descriptions. (Doesn't
mean I fully stand behind this choice, but) positionals
(rest/required/optional) and top level descriptions should start with a
capital letter and end with a period. This is not enforced for flags.

# Tests + Formatting
Furthermore I moved our poor-peoples-fuzzer that runs in CI with
`quicktest` over the parser to `nu-cmd-lang` reducing its command set to
just the keywords (similar to
https://github.com/nushell/nushell/pull/15036). Thus this should also
run slightly faster (maybe a slight parallel build cost due to earlier
dependency on quicktest)
This commit is contained in:
Stefan Holderbach 2025-02-11 11:36:36 +01:00 committed by GitHub
parent 2a3d5a9d42
commit a58d9b0b3a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 299 additions and 291 deletions

4
Cargo.lock generated
View File

@ -3570,6 +3570,8 @@ dependencies = [
"nu-parser",
"nu-protocol",
"nu-utils",
"quickcheck",
"quickcheck_macros",
"shadow-rs",
]
@ -3666,8 +3668,6 @@ dependencies = [
"print-positions",
"procfs",
"quick-xml 0.37.1",
"quickcheck",
"quickcheck_macros",
"rand",
"rand_chacha",
"rayon",

View File

@ -29,7 +29,7 @@ impl Command for SubCommand {
.required(
"str",
SyntaxShape::String,
"the string to perform the operation with",
"The string to perform the operation with.",
)
.category(Category::Core)
}

View File

@ -18,7 +18,7 @@ impl Command for SubCommand {
"set the current cursor position to the end of the buffer",
Some('e'),
)
.optional("pos", SyntaxShape::Int, "Cursor position to be set")
.optional("pos", SyntaxShape::Int, "Cursor position to be set.")
.category(Category::Core)
}

View File

@ -21,7 +21,7 @@ impl Command for HistoryImport {
}
fn description(&self) -> &str {
"Import command line history"
"Import command line history."
}
fn extra_description(&self) -> &str {

View File

@ -26,7 +26,7 @@ impl Command for BitsAnd {
.required(
"target",
SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]),
"right-hand side of the operation",
"Right-hand side of the operation.",
)
.named(
"endian",

View File

@ -26,7 +26,7 @@ impl Command for BitsInto {
.rest(
"rest",
SyntaxShape::CellPath,
"for a data structure input, convert data at the given cell paths",
"For a data structure input, convert data at the given cell paths.",
)
.category(Category::Deprecated)
}

View File

@ -27,7 +27,7 @@ impl Command for BitsOr {
.required(
"target",
SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]),
"right-hand side of the operation",
"Right-hand side of the operation.",
)
.named(
"endian",

View File

@ -37,7 +37,7 @@ impl Command for BitsRol {
),
])
.allow_variants_without_examples(true)
.required("bits", SyntaxShape::Int, "number of bits to rotate left")
.required("bits", SyntaxShape::Int, "Number of bits to rotate left.")
.switch(
"signed",
"always treat input number as a signed number",

View File

@ -37,7 +37,7 @@ impl Command for BitsRor {
),
])
.allow_variants_without_examples(true)
.required("bits", SyntaxShape::Int, "number of bits to rotate right")
.required("bits", SyntaxShape::Int, "Number of bits to rotate right.")
.switch(
"signed",
"always treat input number as a signed number",

View File

@ -40,7 +40,7 @@ impl Command for BitsShl {
),
])
.allow_variants_without_examples(true)
.required("bits", SyntaxShape::Int, "number of bits to shift left")
.required("bits", SyntaxShape::Int, "Number of bits to shift left.")
.switch(
"signed",
"always treat input number as a signed number",

View File

@ -37,7 +37,7 @@ impl Command for BitsShr {
),
])
.allow_variants_without_examples(true)
.required("bits", SyntaxShape::Int, "number of bits to shift right")
.required("bits", SyntaxShape::Int, "Number of bits to shift right.")
.switch(
"signed",
"always treat input number as a signed number",

View File

@ -27,7 +27,7 @@ impl Command for BitsXor {
.required(
"target",
SyntaxShape::OneOf(vec![SyntaxShape::Binary, SyntaxShape::Int]),
"right-hand side of the operation",
"Right-hand side of the operation.",
)
.named(
"endian",

View File

@ -26,7 +26,7 @@ impl Command for EachWhile {
.required(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
"the closure to run",
"The closure to run.",
)
.category(Category::Filters)
}

View File

@ -20,7 +20,7 @@ impl Command for Rotate {
.rest(
"rest",
SyntaxShape::String,
"the names to give columns once rotated",
"The names to give columns once rotated.",
)
.category(Category::Filters)
.allow_variants_without_examples(true)

View File

@ -16,7 +16,7 @@ impl Command for UpdateCells {
.required(
"closure",
SyntaxShape::Closure(Some(vec![SyntaxShape::Any])),
"the closure to run an update for each cell",
"The closure to run an update for each cell.",
)
.named(
"columns",

View File

@ -38,7 +38,7 @@ impl Command for SubCommand {
.rest(
"cell path",
SyntaxShape::CellPath,
"for a data structure input, add a gradient to strings at the given cell paths",
"For a data structure input, add a gradient to strings at the given cell paths.",
)
.input_output_types(vec![
(Type::String, Type::String),

View File

@ -40,7 +40,7 @@ impl Command for FormatBits {
.rest(
"rest",
SyntaxShape::CellPath,
"for a data structure input, convert data at the given cell paths",
"For a data structure input, convert data at the given cell paths.",
)
.category(Category::Conversions)
}

View File

@ -18,7 +18,7 @@ impl Command for FormatPattern {
.required(
"pattern",
SyntaxShape::String,
"the pattern to output. e.g.) \"{foo}: {bar}\"",
"The pattern to output. e.g.) \"{foo}: {bar}\".",
)
.allow_variants_without_examples(true)
.category(Category::Strings)

View File

@ -20,7 +20,7 @@ impl Command for FormatNumber {
}
fn search_terms(&self) -> Vec<&str> {
vec!["display", "render", "format"]
vec!["display", "render", "fmt"]
}
fn examples(&self) -> Vec<Example> {

View File

@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest(
"rest",
SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths",
"For a data structure input, convert strings at the given cell paths.",
)
.category(Category::Strings)
}

View File

@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest(
"rest",
SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths",
"For a data structure input, convert strings at the given cell paths.",
)
.category(Category::Strings)
}

View File

@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest(
"rest",
SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths",
"For a data structure input, convert strings at the given cell paths.",
)
.category(Category::Strings)
}

View File

@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest(
"rest",
SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths",
"For a data structure input, convert strings at the given cell paths.",
)
.category(Category::Strings)
}

View File

@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest(
"rest",
SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths",
"For a data structure input, convert strings at the given cell paths.",
)
.category(Category::Strings)
}

View File

@ -25,7 +25,7 @@ impl Command for SubCommand {
.rest(
"rest",
SyntaxShape::CellPath,
"For a data structure input, convert strings at the given cell paths",
"For a data structure input, convert strings at the given cell paths.",
)
.category(Category::Strings)
}

View File

@ -26,6 +26,10 @@ shadow-rs = { version = "0.38", default-features = false }
[build-dependencies]
shadow-rs = { version = "0.38", default-features = false }
[dev-dependencies]
quickcheck = { workspace = true }
quickcheck_macros = { workspace = true }
[features]
default = ["os"]
os = [
@ -42,4 +46,4 @@ mimalloc = []
trash-support = []
sqlite = []
static-link-openssl = []
system-clipboard = []
system-clipboard = []

View File

@ -4,6 +4,8 @@ mod core_commands;
mod default_context;
pub mod example_support;
mod example_test;
#[cfg(test)]
mod parse_const_test;
pub use core_commands::*;
pub use default_context::*;

View File

@ -0,0 +1,19 @@
use nu_protocol::{engine::StateWorkingSet, Span};
use quickcheck_macros::quickcheck;
#[quickcheck]
fn quickcheck_parse(data: String) -> bool {
let (tokens, err) = nu_parser::lex(data.as_bytes(), 0, b"", b"", true);
if err.is_none() {
let context = crate::create_default_context();
{
let mut working_set = StateWorkingSet::new(&context);
let _ = working_set.add_file("quickcheck".into(), data.as_bytes());
let _ =
nu_parser::parse_block(&mut working_set, &tokens, Span::new(0, 0), false, false);
}
}
true
}

View File

@ -33,7 +33,7 @@ impl Command for PluginAdd {
.required(
"filename",
SyntaxShape::String,
"Path to the executable for the plugin",
"Path to the executable for the plugin.",
)
.category(Category::Plugin)
}

View File

@ -28,7 +28,7 @@ impl Command for PluginRm {
.required(
"name",
SyntaxShape::String,
"The name, or filename, of the plugin to remove",
"The name, or filename, of the plugin to remove.",
)
.category(Category::Plugin)
}

View File

@ -16,7 +16,7 @@ impl Command for PluginStop {
.required(
"name",
SyntaxShape::String,
"The name, or filename, of the plugin to stop",
"The name, or filename, of the plugin to stop.",
)
.category(Category::Plugin)
}

View File

@ -25,7 +25,7 @@ impl Command for PluginUse {
.required(
"name",
SyntaxShape::String,
"The name, or filename, of the plugin to load",
"The name, or filename, of the plugin to load.",
)
.category(Category::Plugin)
}

View File

@ -195,8 +195,6 @@ nu-test-support = { path = "../nu-test-support", version = "0.102.1" }
dirs = { workspace = true }
mockito = { workspace = true, default-features = false }
quickcheck = { workspace = true }
quickcheck_macros = { workspace = true }
rstest = { workspace = true, default-features = false }
rstest_reuse = { workspace = true }
pretty_assertions = { workspace = true }

View File

@ -1,247 +1,3 @@
use nu_protocol::{
engine::{EngineState, StateWorkingSet},
Category, PositionalArg, Span,
};
use quickcheck_macros::quickcheck;
mod commands;
mod format_conversions;
mod sort_utils;
fn create_default_context() -> EngineState {
nu_command::add_shell_command_context(nu_cmd_lang::create_default_context())
}
#[quickcheck]
fn quickcheck_parse(data: String) -> bool {
let (tokens, err) = nu_parser::lex(data.as_bytes(), 0, b"", b"", true);
if err.is_none() {
let context = create_default_context();
{
let mut working_set = StateWorkingSet::new(&context);
let _ = working_set.add_file("quickcheck".into(), data.as_bytes());
let _ =
nu_parser::parse_block(&mut working_set, &tokens, Span::new(0, 0), false, false);
}
}
true
}
#[test]
fn arguments_end_period() {
fn ends_period(cmd_name: &str, ty: &str, arg: PositionalArg, failures: &mut Vec<String>) {
let arg_name = arg.name;
let desc = arg.desc;
if !desc.ends_with('.') {
failures.push(format!(
"{cmd_name} {ty} argument \"{arg_name}\": \"{desc}\""
));
}
}
let ctx = crate::create_default_context();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let signature = cmd.signature();
for arg in signature.required_positional {
ends_period(&cmd_name, "required", arg, &mut failures);
}
for arg in signature.optional_positional {
ends_period(&cmd_name, "optional", arg, &mut failures);
}
if let Some(arg) = signature.rest_positional {
ends_period(&cmd_name, "rest", arg, &mut failures);
}
}
assert!(
failures.is_empty(),
"Command argument description does not end with a period:\n{}",
failures.join("\n")
);
}
#[test]
fn arguments_start_uppercase() {
fn starts_uppercase(cmd_name: &str, ty: &str, arg: PositionalArg, failures: &mut Vec<String>) {
let arg_name = arg.name;
let desc = arg.desc;
// Check lowercase to allow usage to contain syntax like:
//
// "`as` keyword …"
if desc.starts_with(|u: char| u.is_lowercase()) {
failures.push(format!(
"{cmd_name} {ty} argument \"{arg_name}\": \"{desc}\""
));
}
}
let ctx = crate::create_default_context();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let signature = cmd.signature();
for arg in signature.required_positional {
starts_uppercase(&cmd_name, "required", arg, &mut failures);
}
for arg in signature.optional_positional {
starts_uppercase(&cmd_name, "optional", arg, &mut failures);
}
if let Some(arg) = signature.rest_positional {
starts_uppercase(&cmd_name, "rest", arg, &mut failures);
}
}
assert!(
failures.is_empty(),
"Command argument description does not end with a period:\n{}",
failures.join("\n")
);
}
#[test]
fn signature_name_matches_command_name() {
let ctx = create_default_context();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let sig_name = cmd.signature().name;
let category = cmd.signature().category;
if cmd_name != sig_name {
failures.push(format!(
"{cmd_name} ({category:?}): Signature name \"{sig_name}\" is not equal to the command name \"{cmd_name}\""
));
}
}
assert!(
failures.is_empty(),
"Name mismatch:\n{}",
failures.join("\n")
);
}
#[test]
fn commands_declare_input_output_types() {
let ctx = create_default_context();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (_, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let sig_name = cmd.signature().name;
let category = cmd.signature().category;
if matches!(category, Category::Removed) {
// Deprecated/Removed commands don't have to conform
continue;
}
if cmd.signature().input_output_types.is_empty() {
failures.push(format!(
"{sig_name} ({category:?}): No pipeline input/output type signatures found"
));
}
}
assert!(
failures.is_empty(),
"Command missing type annotations:\n{}",
failures.join("\n")
);
}
#[test]
fn no_search_term_duplicates() {
let ctx = crate::create_default_context();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let search_terms = cmd.search_terms();
let category = cmd.signature().category;
for search_term in search_terms {
if cmd_name.contains(search_term) {
failures.push(format!("{cmd_name} ({category:?}): Search term \"{search_term}\" is substring of command name \"{cmd_name}\""));
}
}
}
assert!(
failures.is_empty(),
"Duplication in search terms:\n{}",
failures.join("\n")
);
}
#[test]
fn description_end_period() {
let ctx = crate::create_default_context();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let description = cmd.description();
if !description.ends_with('.') {
failures.push(format!("{cmd_name}: \"{description}\""));
}
}
assert!(
failures.is_empty(),
"Command description does not end with a period:\n{}",
failures.join("\n")
);
}
#[test]
fn description_start_uppercase() {
let ctx = crate::create_default_context();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let description = cmd.description();
// Check lowercase to allow description to contain syntax like:
//
// "`$env.FOO = ...`"
if description.starts_with(|u: char| u.is_lowercase()) {
failures.push(format!("{cmd_name}: \"{description}\""));
}
}
assert!(
failures.is_empty(),
"Command description does not start with an uppercase letter:\n{}",
failures.join("\n")
);
}

239
src/command_context.rs Normal file
View File

@ -0,0 +1,239 @@
use nu_protocol::engine::EngineState;
pub(crate) fn get_engine_state() -> EngineState {
let engine_state = nu_cmd_lang::create_default_context();
#[cfg(feature = "plugin")]
let engine_state = nu_cmd_plugin::add_plugin_command_context(engine_state);
let engine_state = nu_command::add_shell_command_context(engine_state);
let engine_state = nu_cmd_extra::add_extra_command_context(engine_state);
let engine_state = nu_cli::add_cli_context(engine_state);
nu_explore::add_explore_context(engine_state)
}
#[cfg(test)]
mod tests {
use super::*;
use nu_protocol::{Category, PositionalArg};
#[test]
fn arguments_end_period() {
fn ends_period(cmd_name: &str, ty: &str, arg: PositionalArg, failures: &mut Vec<String>) {
let arg_name = arg.name;
let desc = arg.desc;
if !desc.ends_with('.') {
failures.push(format!(
"{cmd_name} {ty} argument \"{arg_name}\": \"{desc}\""
));
}
}
let ctx = get_engine_state();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let signature = cmd.signature();
for arg in signature.required_positional {
ends_period(&cmd_name, "required", arg, &mut failures);
}
for arg in signature.optional_positional {
ends_period(&cmd_name, "optional", arg, &mut failures);
}
if let Some(arg) = signature.rest_positional {
ends_period(&cmd_name, "rest", arg, &mut failures);
}
}
assert!(
failures.is_empty(),
"Command argument description does not end with a period:\n{}",
failures.join("\n")
);
}
#[test]
fn arguments_start_uppercase() {
fn starts_uppercase(
cmd_name: &str,
ty: &str,
arg: PositionalArg,
failures: &mut Vec<String>,
) {
let arg_name = arg.name;
let desc = arg.desc;
// Check lowercase to allow usage to contain syntax like:
//
// "`as` keyword …"
if desc.starts_with(|u: char| u.is_lowercase()) {
failures.push(format!(
"{cmd_name} {ty} argument \"{arg_name}\": \"{desc}\""
));
}
}
let ctx = get_engine_state();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let signature = cmd.signature();
for arg in signature.required_positional {
starts_uppercase(&cmd_name, "required", arg, &mut failures);
}
for arg in signature.optional_positional {
starts_uppercase(&cmd_name, "optional", arg, &mut failures);
}
if let Some(arg) = signature.rest_positional {
starts_uppercase(&cmd_name, "rest", arg, &mut failures);
}
}
assert!(
failures.is_empty(),
"Command argument description does not end with a period:\n{}",
failures.join("\n")
);
}
#[test]
fn signature_name_matches_command_name() {
let ctx = get_engine_state();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let sig_name = cmd.signature().name;
let category = cmd.signature().category;
if cmd_name != sig_name {
failures.push(format!(
"{cmd_name} ({category:?}): Signature name \"{sig_name}\" is not equal to the command name \"{cmd_name}\""
));
}
}
assert!(
failures.is_empty(),
"Name mismatch:\n{}",
failures.join("\n")
);
}
#[test]
fn commands_declare_input_output_types() {
let ctx = get_engine_state();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (_, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let sig_name = cmd.signature().name;
let category = cmd.signature().category;
if matches!(category, Category::Removed) {
// Deprecated/Removed commands don't have to conform
continue;
}
if cmd.signature().input_output_types.is_empty() {
failures.push(format!(
"{sig_name} ({category:?}): No pipeline input/output type signatures found"
));
}
}
assert!(
failures.is_empty(),
"Command missing type annotations:\n{}",
failures.join("\n")
);
}
#[test]
fn no_search_term_duplicates() {
let ctx = get_engine_state();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let search_terms = cmd.search_terms();
let category = cmd.signature().category;
for search_term in search_terms {
if cmd_name.contains(search_term) {
failures.push(format!("{cmd_name} ({category:?}): Search term \"{search_term}\" is substring of command name \"{cmd_name}\""));
}
}
}
assert!(
failures.is_empty(),
"Duplication in search terms:\n{}",
failures.join("\n")
);
}
#[test]
fn description_end_period() {
let ctx = get_engine_state();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let description = cmd.description();
if !description.ends_with('.') {
failures.push(format!("{cmd_name}: \"{description}\""));
}
}
assert!(
failures.is_empty(),
"Command description does not end with a period:\n{}",
failures.join("\n")
);
}
#[test]
fn description_start_uppercase() {
let ctx = get_engine_state();
let decls = ctx.get_decls_sorted(true);
let mut failures = Vec::new();
for (name_bytes, decl_id) in decls {
let cmd = ctx.get_decl(decl_id);
let cmd_name = String::from_utf8_lossy(&name_bytes);
let description = cmd.description();
// Check lowercase to allow description to contain syntax like:
//
// "`$env.FOO = ...`"
if description.starts_with(|u: char| u.is_lowercase()) {
failures.push(format!("{cmd_name}: \"{description}\""));
}
}
assert!(
failures.is_empty(),
"Command description does not start with an uppercase letter:\n{}",
failures.join("\n")
);
}
}

View File

@ -1,4 +1,5 @@
mod command;
mod command_context;
mod config_files;
mod ide;
mod logger;
@ -25,9 +26,8 @@ use nu_engine::convert_env_values;
use nu_lsp::LanguageServer;
use nu_path::canonicalize_with;
use nu_protocol::{
engine::{EngineState, Stack},
record, report_shell_error, ByteStream, Config, IntoValue, PipelineData, ShellError, Span,
Spanned, Type, Value,
engine::Stack, record, report_shell_error, ByteStream, Config, IntoValue, PipelineData,
ShellError, Span, Spanned, Type, Value,
};
use nu_std::load_standard_library;
use nu_utils::perf;
@ -35,16 +35,6 @@ use run::{run_commands, run_file, run_repl};
use signals::ctrlc_protection;
use std::{path::PathBuf, str::FromStr, sync::Arc};
fn get_engine_state() -> EngineState {
let engine_state = nu_cmd_lang::create_default_context();
#[cfg(feature = "plugin")]
let engine_state = nu_cmd_plugin::add_plugin_command_context(engine_state);
let engine_state = nu_command::add_shell_command_context(engine_state);
let engine_state = nu_cmd_extra::add_extra_command_context(engine_state);
let engine_state = nu_cli::add_cli_context(engine_state);
nu_explore::add_explore_context(engine_state)
}
/// Get the directory where the Nushell executable is located.
fn current_exe_directory() -> PathBuf {
let mut path = std::env::current_exe().expect("current_exe() should succeed");
@ -76,7 +66,7 @@ fn main() -> Result<()> {
miette_hook(x);
}));
let mut engine_state = get_engine_state();
let mut engine_state = command_context::get_engine_state();
// Get the current working directory from the environment.
let init_cwd = current_dir_from_environment();