diff --git a/src/run.rs b/src/run.rs index ffd0fb8fc..a20a73095 100644 --- a/src/run.rs +++ b/src/run.rs @@ -6,10 +6,103 @@ use crate::{ }; #[cfg(feature = "plugin")] use nu_cli::read_plugin_file; -use nu_cli::{evaluate_commands, evaluate_file, evaluate_repl}; -use nu_protocol::PipelineData; +use nu_cli::{evaluate_commands, evaluate_file, evaluate_repl, report_error}; +use nu_parser::{parse, parse_module_block}; +use nu_protocol::{engine::StateWorkingSet, Module, PipelineData, ShellError, Span}; use nu_utils::utils::perf; +fn get_standard_library() -> &'static str { + include_str!("../crates/nu-utils/standard_library/std.nu") +} + +fn load_prelude(working_set: &mut StateWorkingSet, prelude: Vec<(&str, &str)>, module: &Module) { + let mut decls = Vec::new(); + let mut errs = Vec::new(); + for (name, search_name) in prelude { + if let Some(id) = module.decls.get(&search_name.as_bytes().to_vec()) { + let decl = (name.as_bytes().to_vec(), id.to_owned()); + decls.push(decl); + } else { + errs.push(ShellError::GenericError( + format!("could not load `{}` from `std`.", search_name), + String::new(), + None, + None, + Vec::new(), + )); + } + } + + if !errs.is_empty() { + report_error( + working_set, + &ShellError::GenericError( + "Unable to load the prelude of the standard library.".into(), + String::new(), + None, + Some("this is a bug: please file an issue in the [issue tracker](https://github.com/nushell/nushell/issues/new/choose)".to_string()), + errs, + ), + ); + } + + working_set.use_decls(decls); +} + +fn load_standard_library( + engine_state: &mut nu_protocol::engine::EngineState, +) -> Result<(), miette::ErrReport> { + let delta = { + let name = "std".to_string(); + let content = get_standard_library().as_bytes(); + + let mut working_set = StateWorkingSet::new(engine_state); + + let start = working_set.next_span_start(); + working_set.add_file(name.clone(), content); + let end = working_set.next_span_start(); + + let (_, module, comments, parse_error) = parse_module_block( + &mut working_set, + Span::new(start, end), + name.as_bytes(), + &[], + ); + + if let Some(err) = parse_error { + report_error(&working_set, &err); + } + + let (_, parse_error) = parse(&mut working_set, Some(&name), content, true, &[]); + + if let Some(err) = parse_error { + report_error(&working_set, &err); + } + + // TODO: change this when #8505 is merged + // NOTE: remove the assert and uncomment the `help`s + let prelude = vec![ + ("assert", "assert"), + // ("help", "help"), + // ("help commands", "help commands"), + // ("help aliases", "help aliases"), + // ("help modules", "help modules"), + // ("help externs", "help externs"), + // ("help operators", "help operators"), + ]; + + load_prelude(&mut working_set, prelude, &module); + + working_set.add_module(&name, module, comments); + + working_set.render() + }; + + engine_state.merge_delta(delta)?; + + Ok(()) +} + pub(crate) fn run_commands( engine_state: &mut nu_protocol::engine::EngineState, parsed_nu_cli_args: command::NushellCliArgs, @@ -70,6 +163,8 @@ pub(crate) fn run_commands( use_color, ); + load_standard_library(engine_state)?; + // Before running commands, set up the startup time engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64); let start_time = std::time::Instant::now(); @@ -157,6 +252,8 @@ pub(crate) fn run_file( use_color, ); + load_standard_library(engine_state)?; + let start_time = std::time::Instant::now(); let ret_val = evaluate_file( script_name, @@ -227,6 +324,8 @@ pub(crate) fn run_repl( use_color, ); + load_standard_library(engine_state)?; + let start_time = std::time::Instant::now(); let ret_val = evaluate_repl( engine_state, diff --git a/src/tests.rs b/src/tests.rs index 20db9d5e0..4d5f21325 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -16,6 +16,7 @@ mod test_parser; mod test_ranges; mod test_regex; mod test_signatures; +mod test_stdlib; mod test_strings; mod test_table_operations; mod test_type_check; diff --git a/src/tests/test_stdlib.rs b/src/tests/test_stdlib.rs new file mode 100644 index 000000000..c44f6b1fd --- /dev/null +++ b/src/tests/test_stdlib.rs @@ -0,0 +1,35 @@ +use crate::tests::{fail_test, run_test, TestResult}; + +#[test] +fn library_loaded() -> TestResult { + run_test( + "help std | lines | first 1 | to text", + "std.nu, `used` to load all standard library components", + ) +} + +#[test] +fn prelude_loaded() -> TestResult { + run_test( + "help assert | lines | first 1 | to text", + "Universal assert command", + ) +} + +#[test] +fn prelude_run() -> TestResult { + run_test("assert true; print 'it works'", "it works") +} + +#[test] +fn not_loaded() -> TestResult { + fail_test("help log info", "") +} + +#[test] +fn use_command() -> TestResult { + run_test( + "use std 'log info'; log info 'this is some information'", + "", + ) +}