From fbf3f7cf1ccb09565b1fd63ba5c72ff3c171d763 Mon Sep 17 00:00:00 2001 From: JT <547158+jntrnr@users.noreply.github.com> Date: Wed, 21 Jun 2023 09:33:01 +1200 Subject: [PATCH] split $nu variable into scope commands and simpler $nu (#9487) # Description This splits off `scope` from `$nu`, creating a set of `scope` commands for the various types of scope you might be interested in. This also simplifies the `$nu` variable a bit. # User-Facing Changes This changes `$nu` to be a bit simpler and introduces a set of `scope` subcommands. # Tests + Formatting # After Submitting --- crates/nu-cli/tests/completions.rs | 84 +----- crates/nu-cmd-lang/src/core_commands/mod.rs | 2 + .../src/core_commands/scope/aliases.rs | 62 ++++ .../src/core_commands/scope/command.rs | 50 ++++ .../src/core_commands/scope/commands.rs | 62 ++++ .../src/core_commands/scope/engine_stats.rs | 59 ++++ .../src/core_commands/scope/mod.rs | 13 + .../src/core_commands/scope/modules.rs | 62 ++++ .../src/core_commands/scope/variables.rs | 62 ++++ crates/nu-cmd-lang/src/default_context.rs | 6 + crates/nu-command/tests/commands/alias.rs | 4 +- crates/nu-command/tests/commands/headers.rs | 2 +- crates/nu-engine/src/eval.rs | 225 ++++++++++++++- crates/nu-engine/src/lib.rs | 1 - crates/nu-engine/src/nu_variable.rs | 267 ------------------ crates/nu-engine/src/scope.rs | 2 +- crates/nu-std/std/help.nu | 18 +- .../src/sample_config/default_config.nu | 6 +- src/tests/test_engine.rs | 4 +- src/tests/test_stdlib.rs | 2 +- tests/scope/mod.rs | 14 +- 21 files changed, 620 insertions(+), 387 deletions(-) create mode 100644 crates/nu-cmd-lang/src/core_commands/scope/aliases.rs create mode 100644 crates/nu-cmd-lang/src/core_commands/scope/command.rs create mode 100644 crates/nu-cmd-lang/src/core_commands/scope/commands.rs create mode 100644 crates/nu-cmd-lang/src/core_commands/scope/engine_stats.rs create mode 100644 crates/nu-cmd-lang/src/core_commands/scope/mod.rs create mode 100644 crates/nu-cmd-lang/src/core_commands/scope/modules.rs create mode 100644 crates/nu-cmd-lang/src/core_commands/scope/variables.rs delete mode 100644 crates/nu-engine/src/nu_variable.rs diff --git a/crates/nu-cli/tests/completions.rs b/crates/nu-cli/tests/completions.rs index d6608975f4..1acc797aff 100644 --- a/crates/nu-cli/tests/completions.rs +++ b/crates/nu-cli/tests/completions.rs @@ -538,7 +538,7 @@ fn variables_completions() { "loginshell-path".into(), "os-info".into(), "pid".into(), - "scope".into(), + "plugin-path".into(), "startup-time".into(), "temp-path".into(), ]; @@ -568,88 +568,6 @@ fn variables_completions() { // Match results match_suggestions(expected, suggestions); - // Test completions for $nu.scope - let suggestions = completer.complete("$nu.scope.", 10); - assert_eq!(5, suggestions.len()); - let expected: Vec = vec![ - "aliases".into(), - "commands".into(), - "engine_state".into(), - "modules".into(), - "vars".into(), - ]; - // Match results - match_suggestions(expected, suggestions); - - // Test completions for $nu.scope.commands - let suggestions = completer.complete("$nu.scope.commands.", 19); - assert_eq!(15, suggestions.len()); - let expected: Vec = vec![ - "category".into(), - "creates_scope".into(), - "examples".into(), - "extra_usage".into(), - "is_builtin".into(), - "is_custom".into(), - "is_extern".into(), - "is_keyword".into(), - "is_plugin".into(), - "is_sub".into(), - "module_name".into(), - "name".into(), - "search_terms".into(), - "signatures".into(), - "usage".into(), - ]; - // Match results - match_suggestions(expected, suggestions); - - // Test completions for $nu.scope.commands.signatures - let suggestions = completer.complete("$nu.scope.commands.signatures.", 30); - assert_eq!(17, suggestions.len()); - let expected: Vec = vec![ - "any".into(), - "binary".into(), - "bool".into(), - "datetime".into(), - "duration".into(), - "filesize".into(), - "int".into(), - "list".into(), - "list".into(), - "list".into(), - "list".into(), - "nothing".into(), - "number".into(), - "range".into(), - "record".into(), - "string".into(), - "table".into(), - ]; - // Match results - match_suggestions(expected, suggestions); - - // Test completions for $nu.scope.engine_state - let suggestions = completer.complete("$nu.scope.engine_state.", 23); - assert_eq!(6, suggestions.len()); - let expected: Vec = vec![ - "num_blocks".into(), - "num_decls".into(), - "num_env_vars".into(), - "num_modules".into(), - "num_vars".into(), - "source_bytes".into(), - ]; - // Match results - match_suggestions(expected, suggestions); - - // Test completions for $nu.scope.vars - let suggestions = completer.complete("$nu.scope.vars.", 15); - assert_eq!(3, suggestions.len()); - let expected: Vec = vec!["name".into(), "type".into(), "value".into()]; - // Match results - match_suggestions(expected, suggestions); - // Test completions for custom var let suggestions = completer.complete("$actor.", 7); diff --git a/crates/nu-cmd-lang/src/core_commands/mod.rs b/crates/nu-cmd-lang/src/core_commands/mod.rs index ee05d6d576..902c16f834 100644 --- a/crates/nu-cmd-lang/src/core_commands/mod.rs +++ b/crates/nu-cmd-lang/src/core_commands/mod.rs @@ -30,6 +30,7 @@ mod module; mod mut_; pub(crate) mod overlay; mod return_; +mod scope; mod try_; mod use_; mod version; @@ -67,6 +68,7 @@ pub use module::Module; pub use mut_::Mut; pub use overlay::*; pub use return_::Return; +pub use scope::*; pub use try_::Try; pub use use_::Use; pub use version::Version; diff --git a/crates/nu-cmd-lang/src/core_commands/scope/aliases.rs b/crates/nu-cmd-lang/src/core_commands/scope/aliases.rs new file mode 100644 index 0000000000..9a874f228e --- /dev/null +++ b/crates/nu-cmd-lang/src/core_commands/scope/aliases.rs @@ -0,0 +1,62 @@ +use nu_engine::scope::ScopeData; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, +}; + +#[derive(Clone)] +pub struct ScopeAliases; + +impl Command for ScopeAliases { + fn name(&self) -> &str { + "scope aliases" + } + + fn signature(&self) -> Signature { + Signature::build("scope aliases") + .input_output_types(vec![(Type::Nothing, Type::Any)]) + .allow_variants_without_examples(true) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Output info on the aliases in the current scope." + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + let span = call.head; + let ctrlc = engine_state.ctrlc.clone(); + + let mut scope_data = ScopeData::new(engine_state, stack); + scope_data.populate_all(); + + Ok(scope_data.collect_aliases(span).into_pipeline_data(ctrlc)) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Show the aliases in the current scope", + example: "scope aliases", + result: None, + }] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(ScopeAliases {}) + } +} diff --git a/crates/nu-cmd-lang/src/core_commands/scope/command.rs b/crates/nu-cmd-lang/src/core_commands/scope/command.rs new file mode 100644 index 0000000000..9baa09aa4d --- /dev/null +++ b/crates/nu-cmd-lang/src/core_commands/scope/command.rs @@ -0,0 +1,50 @@ +use nu_engine::get_full_help; +use nu_protocol::{ + ast::Call, + engine::{Command, EngineState, Stack}, + Category, IntoPipelineData, PipelineData, ShellError, Signature, Type, Value, +}; + +#[derive(Clone)] +pub struct Scope; + +impl Command for Scope { + fn name(&self) -> &str { + "scope" + } + + fn signature(&self) -> Signature { + Signature::build("scope") + .category(Category::Core) + .input_output_types(vec![(Type::Nothing, Type::String)]) + .allow_variants_without_examples(true) + } + + fn usage(&self) -> &str { + "Commands for getting info about what is in scope." + } + + fn is_parser_keyword(&self) -> bool { + true + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + Ok(Value::String { + val: get_full_help( + &Scope.signature(), + &[], + engine_state, + stack, + self.is_parser_keyword(), + ), + span: call.head, + } + .into_pipeline_data()) + } +} diff --git a/crates/nu-cmd-lang/src/core_commands/scope/commands.rs b/crates/nu-cmd-lang/src/core_commands/scope/commands.rs new file mode 100644 index 0000000000..8247b93bd6 --- /dev/null +++ b/crates/nu-cmd-lang/src/core_commands/scope/commands.rs @@ -0,0 +1,62 @@ +use nu_engine::scope::ScopeData; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, +}; + +#[derive(Clone)] +pub struct ScopeCommands; + +impl Command for ScopeCommands { + fn name(&self) -> &str { + "scope commands" + } + + fn signature(&self) -> Signature { + Signature::build("scope commands") + .input_output_types(vec![(Type::Nothing, Type::Any)]) + .allow_variants_without_examples(true) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Output info on the commands in the current scope." + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + let span = call.head; + let ctrlc = engine_state.ctrlc.clone(); + + let mut scope_data = ScopeData::new(engine_state, stack); + scope_data.populate_all(); + + Ok(scope_data.collect_commands(span).into_pipeline_data(ctrlc)) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Show the commands in the current scope", + example: "scope commands", + result: None, + }] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(ScopeCommands {}) + } +} diff --git a/crates/nu-cmd-lang/src/core_commands/scope/engine_stats.rs b/crates/nu-cmd-lang/src/core_commands/scope/engine_stats.rs new file mode 100644 index 0000000000..5cbbf09450 --- /dev/null +++ b/crates/nu-cmd-lang/src/core_commands/scope/engine_stats.rs @@ -0,0 +1,59 @@ +use nu_engine::scope::ScopeData; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{Category, Example, IntoPipelineData, PipelineData, ShellError, Signature, Type}; + +#[derive(Clone)] +pub struct ScopeEngineStats; + +impl Command for ScopeEngineStats { + fn name(&self) -> &str { + "scope engine-stats" + } + + fn signature(&self) -> Signature { + Signature::build("scope engine-stats") + .input_output_types(vec![(Type::Nothing, Type::Any)]) + .allow_variants_without_examples(true) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Output stats on the engine in the current state." + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + let span = call.head; + + let mut scope_data = ScopeData::new(engine_state, stack); + scope_data.populate_all(); + + Ok(scope_data.collect_engine_state(span).into_pipeline_data()) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Show the stats on the current engine state", + example: "scope engine-stats", + result: None, + }] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(ScopeEngineStats {}) + } +} diff --git a/crates/nu-cmd-lang/src/core_commands/scope/mod.rs b/crates/nu-cmd-lang/src/core_commands/scope/mod.rs new file mode 100644 index 0000000000..ca82cb8995 --- /dev/null +++ b/crates/nu-cmd-lang/src/core_commands/scope/mod.rs @@ -0,0 +1,13 @@ +mod aliases; +mod command; +mod commands; +mod engine_stats; +mod modules; +mod variables; + +pub use aliases::*; +pub use command::*; +pub use commands::*; +pub use engine_stats::*; +pub use modules::*; +pub use variables::*; diff --git a/crates/nu-cmd-lang/src/core_commands/scope/modules.rs b/crates/nu-cmd-lang/src/core_commands/scope/modules.rs new file mode 100644 index 0000000000..cf96ebc2c4 --- /dev/null +++ b/crates/nu-cmd-lang/src/core_commands/scope/modules.rs @@ -0,0 +1,62 @@ +use nu_engine::scope::ScopeData; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, +}; + +#[derive(Clone)] +pub struct ScopeModules; + +impl Command for ScopeModules { + fn name(&self) -> &str { + "scope modules" + } + + fn signature(&self) -> Signature { + Signature::build("scope modules") + .input_output_types(vec![(Type::Nothing, Type::Any)]) + .allow_variants_without_examples(true) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Output info on the modules in the current scope." + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + let span = call.head; + let ctrlc = engine_state.ctrlc.clone(); + + let mut scope_data = ScopeData::new(engine_state, stack); + scope_data.populate_modules(); + + Ok(scope_data.collect_modules(span).into_pipeline_data(ctrlc)) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Show the modules in the current scope", + example: "scope modules", + result: None, + }] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(ScopeModules {}) + } +} diff --git a/crates/nu-cmd-lang/src/core_commands/scope/variables.rs b/crates/nu-cmd-lang/src/core_commands/scope/variables.rs new file mode 100644 index 0000000000..017352ef44 --- /dev/null +++ b/crates/nu-cmd-lang/src/core_commands/scope/variables.rs @@ -0,0 +1,62 @@ +use nu_engine::scope::ScopeData; +use nu_protocol::ast::Call; +use nu_protocol::engine::{Command, EngineState, Stack}; +use nu_protocol::{ + Category, Example, IntoInterruptiblePipelineData, PipelineData, ShellError, Signature, Type, +}; + +#[derive(Clone)] +pub struct ScopeVariables; + +impl Command for ScopeVariables { + fn name(&self) -> &str { + "scope variables" + } + + fn signature(&self) -> Signature { + Signature::build("scope variables") + .input_output_types(vec![(Type::Nothing, Type::Any)]) + .allow_variants_without_examples(true) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Output info on the variables in the current scope." + } + + fn run( + &self, + engine_state: &EngineState, + stack: &mut Stack, + call: &Call, + _input: PipelineData, + ) -> Result { + let span = call.head; + let ctrlc = engine_state.ctrlc.clone(); + + let mut scope_data = ScopeData::new(engine_state, stack); + scope_data.populate_all(); + + Ok(scope_data.collect_vars(span).into_pipeline_data(ctrlc)) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Show the variables in the current scope", + example: "scope variables", + result: None, + }] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(ScopeVariables {}) + } +} diff --git a/crates/nu-cmd-lang/src/default_context.rs b/crates/nu-cmd-lang/src/default_context.rs index 7f76cbe600..e8377fd5d1 100644 --- a/crates/nu-cmd-lang/src/default_context.rs +++ b/crates/nu-cmd-lang/src/default_context.rs @@ -52,6 +52,12 @@ pub fn create_default_context() -> EngineState { Module, Mut, Return, + Scope, + ScopeAliases, + ScopeCommands, + ScopeEngineStats, + ScopeModules, + ScopeVariables, Try, Use, Version, diff --git a/crates/nu-command/tests/commands/alias.rs b/crates/nu-command/tests/commands/alias.rs index 997a9ef94d..97af0ccda9 100644 --- a/crates/nu-command/tests/commands/alias.rs +++ b/crates/nu-command/tests/commands/alias.rs @@ -24,7 +24,7 @@ fn alias_hiding_1() { cwd: "tests/fixtures/formats", pipeline( r#" overlay use ./activate-foo.nu; - $nu.scope.aliases | find deactivate-foo | length + scope aliases | find deactivate-foo | length "# )); @@ -39,7 +39,7 @@ fn alias_hiding_2() { r#" overlay use ./activate-foo.nu; deactivate-foo; - $nu.scope.aliases | find deactivate-foo | length + scope aliases | find deactivate-foo | length "# )); diff --git a/crates/nu-command/tests/commands/headers.rs b/crates/nu-command/tests/commands/headers.rs index c7c9725f83..88cc557866 100644 --- a/crates/nu-command/tests/commands/headers.rs +++ b/crates/nu-command/tests/commands/headers.rs @@ -49,7 +49,7 @@ fn headers_invalid_column_type_record() { let actual = nu!( cwd: "tests/fixtures/formats", pipeline( r#" - [[a b]; [1 ($nu.scope)] [2 2]] + [[a b]; [1 (scope aliases)] [2 2]] | headers"# )); diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 8e0fadf8dd..a2f22f8fff 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -1,4 +1,4 @@ -use crate::{current_dir_str, get_full_help, nu_variable::NuVariable}; +use crate::{current_dir_str, get_full_help}; use nu_path::expand_path_with; use nu_protocol::{ ast::{ @@ -9,8 +9,9 @@ use nu_protocol::{ DataSource, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData, PipelineMetadata, Range, ShellError, Span, Spanned, Unit, Value, VarId, ENV_VARIABLE_ID, }; -use std::collections::HashMap; use std::time::Instant; +use std::{collections::HashMap, path::PathBuf}; +use sysinfo::SystemExt; pub fn eval_operator(op: &Expression) -> Result { match op { @@ -1202,6 +1203,217 @@ pub fn eval_subexpression( Ok(input) } +pub fn eval_nu_variable(engine_state: &EngineState, span: Span) -> Result { + fn canonicalize_path(engine_state: &EngineState, path: &PathBuf) -> PathBuf { + let cwd = engine_state.current_work_dir(); + + if path.exists() { + match nu_path::canonicalize_with(path, cwd) { + Ok(canon_path) => canon_path, + Err(_) => path.clone(), + } + } else { + path.clone() + } + } + + let mut cols = vec![]; + let mut vals = vec![]; + + cols.push("default-config-dir".to_string()); + if let Some(mut path) = nu_path::config_dir() { + path.push("nushell"); + vals.push(Value::String { + val: path.to_string_lossy().to_string(), + span, + }) + } else { + vals.push(Value::Error { + error: Box::new(ShellError::IOError("Could not get config directory".into())), + }) + } + + cols.push("config-path".to_string()); + if let Some(path) = engine_state.get_config_path("config-path") { + let canon_config_path = canonicalize_path(engine_state, path); + vals.push(Value::String { + val: canon_config_path.to_string_lossy().to_string(), + span, + }) + } else if let Some(mut path) = nu_path::config_dir() { + path.push("nushell"); + path.push("config.nu"); + vals.push(Value::String { + val: path.to_string_lossy().to_string(), + span, + }) + } else { + vals.push(Value::Error { + error: Box::new(ShellError::IOError("Could not get config directory".into())), + }) + } + + cols.push("env-path".to_string()); + if let Some(path) = engine_state.get_config_path("env-path") { + let canon_env_path = canonicalize_path(engine_state, path); + vals.push(Value::String { + val: canon_env_path.to_string_lossy().to_string(), + span, + }) + } else if let Some(mut path) = nu_path::config_dir() { + path.push("nushell"); + path.push("env.nu"); + vals.push(Value::String { + val: path.to_string_lossy().to_string(), + span, + }) + } else { + vals.push(Value::Error { + error: Box::new(ShellError::IOError( + "Could not find environment path".into(), + )), + }) + } + + cols.push("history-path".to_string()); + if let Some(mut path) = nu_path::config_dir() { + path.push("nushell"); + match engine_state.config.history_file_format { + nu_protocol::HistoryFileFormat::Sqlite => { + path.push("history.sqlite3"); + } + nu_protocol::HistoryFileFormat::PlainText => { + path.push("history.txt"); + } + } + let canon_hist_path = canonicalize_path(engine_state, &path); + vals.push(Value::String { + val: canon_hist_path.to_string_lossy().to_string(), + span, + }) + } else { + vals.push(Value::Error { + error: Box::new(ShellError::IOError("Could not find history path".into())), + }) + } + + cols.push("loginshell-path".to_string()); + if let Some(mut path) = nu_path::config_dir() { + path.push("nushell"); + path.push("login.nu"); + let canon_login_path = canonicalize_path(engine_state, &path); + vals.push(Value::String { + val: canon_login_path.to_string_lossy().to_string(), + span, + }) + } else { + vals.push(Value::Error { + error: Box::new(ShellError::IOError( + "Could not find login shell path".into(), + )), + }) + } + + #[cfg(feature = "plugin")] + { + cols.push("plugin-path".to_string()); + + if let Some(path) = &engine_state.plugin_signatures { + let canon_plugin_path = canonicalize_path(engine_state, path); + vals.push(Value::String { + val: canon_plugin_path.to_string_lossy().to_string(), + span, + }) + } else { + vals.push(Value::Error { + error: Box::new(ShellError::IOError( + "Could not get plugin signature location".into(), + )), + }) + } + } + + cols.push("home-path".to_string()); + if let Some(path) = nu_path::home_dir() { + let canon_home_path = canonicalize_path(engine_state, &path); + vals.push(Value::String { + val: canon_home_path.to_string_lossy().into(), + span, + }) + } else { + vals.push(Value::Error { + error: Box::new(ShellError::IOError("Could not get home path".into())), + }) + } + + cols.push("temp-path".to_string()); + let canon_temp_path = canonicalize_path(engine_state, &std::env::temp_dir()); + vals.push(Value::String { + val: canon_temp_path.to_string_lossy().into(), + span, + }); + + cols.push("pid".to_string()); + vals.push(Value::int(std::process::id().into(), span)); + + cols.push("os-info".to_string()); + let sys = sysinfo::System::new(); + let ver = match sys.kernel_version() { + Some(v) => v, + None => "unknown".into(), + }; + let os_record = Value::Record { + cols: vec![ + "name".into(), + "arch".into(), + "family".into(), + "kernel_version".into(), + ], + vals: vec![ + Value::string(std::env::consts::OS, span), + Value::string(std::env::consts::ARCH, span), + Value::string(std::env::consts::FAMILY, span), + Value::string(ver, span), + ], + span, + }; + vals.push(os_record); + + cols.push("startup-time".to_string()); + vals.push(Value::Duration { + val: engine_state.get_startup_time(), + span, + }); + + cols.push("is-interactive".to_string()); + vals.push(Value::Bool { + val: engine_state.is_interactive, + span, + }); + + cols.push("is-login".to_string()); + vals.push(Value::Bool { + val: engine_state.is_login, + span, + }); + + cols.push("current-exe".to_string()); + if let Ok(current_exe) = std::env::current_exe() { + vals.push(Value::String { + val: current_exe.to_string_lossy().into(), + span, + }); + } else { + vals.push(Value::Error { + error: Box::new(ShellError::IOError( + "Could not get current executable path".to_string(), + )), + }) + } + + Ok(Value::Record { cols, vals, span }) +} + pub fn eval_variable( engine_state: &EngineState, stack: &Stack, @@ -1210,14 +1422,7 @@ pub fn eval_variable( ) -> Result { match var_id { // $nu - nu_protocol::NU_VARIABLE_ID => Ok(Value::LazyRecord { - val: Box::new(NuVariable { - engine_state: engine_state.clone(), - stack: stack.clone(), - span, - }), - span, - }), + nu_protocol::NU_VARIABLE_ID => eval_nu_variable(engine_state, span), ENV_VARIABLE_ID => { let env_vars = stack.get_env_vars(engine_state); let env_columns = env_vars.keys(); diff --git a/crates/nu-engine/src/lib.rs b/crates/nu-engine/src/lib.rs index 657dc77635..2853f9b891 100644 --- a/crates/nu-engine/src/lib.rs +++ b/crates/nu-engine/src/lib.rs @@ -4,7 +4,6 @@ pub mod documentation; pub mod env; mod eval; mod glob_from; -mod nu_variable; pub mod scope; pub use call_ext::CallExt; diff --git a/crates/nu-engine/src/nu_variable.rs b/crates/nu-engine/src/nu_variable.rs deleted file mode 100644 index 72c1f92a44..0000000000 --- a/crates/nu-engine/src/nu_variable.rs +++ /dev/null @@ -1,267 +0,0 @@ -use crate::scope::create_scope; -use core::fmt; -use nu_protocol::{ - engine::{EngineState, Stack}, - LazyRecord, ShellError, Span, Value, -}; -use std::path::PathBuf; -use sysinfo::SystemExt; - -// NuVariable: a LazyRecord for the special $nu variable -// $nu used to be a plain old Record, but LazyRecord lets us load different fields/columns lazily. This is important for performance; -// collecting all the information in $nu is expensive and unnecessary if you just want a subset of the data - -// Note: NuVariable is not meaningfully serializable, this #[derive] is a lie to satisfy the type checker. -// Make sure to collect() the record before serializing it -#[derive(Clone)] -pub struct NuVariable { - pub engine_state: EngineState, - pub stack: Stack, - pub span: Span, -} - -impl<'a> LazyRecord<'a> for NuVariable { - fn column_names(&self) -> Vec<&'static str> { - let mut cols = vec![ - "default-config-dir", - "config-path", - "env-path", - "history-path", - "loginshell-path", - ]; - - #[cfg(feature = "plugin")] - if self.engine_state.plugin_signatures.is_some() { - cols.push("plugin-path"); - } - - cols.push("scope"); - cols.push("home-path"); - cols.push("temp-path"); - cols.push("pid"); - cols.push("os-info"); - cols.push("startup-time"); - - cols.push("is-interactive"); - cols.push("is-login"); - - cols.push("current-exe"); - - cols - } - - fn get_column_value(&self, column: &str) -> Result { - let err = |message: &str| -> Result { - Err(ShellError::LazyRecordAccessFailed { - message: message.into(), - column_name: column.to_string(), - span: self.span, - }) - }; - - fn canonicalize_path(engine_state: &EngineState, path: &PathBuf) -> PathBuf { - let cwd = engine_state.current_work_dir(); - - if path.exists() { - match nu_path::canonicalize_with(path, cwd) { - Ok(canon_path) => canon_path, - Err(_) => path.clone(), - } - } else { - path.clone() - } - } - - match column { - "default-config-dir" => { - if let Some(mut path) = nu_path::config_dir() { - path.push("nushell"); - Ok(Value::String { - val: path.to_string_lossy().to_string(), - span: self.span, - }) - } else { - err("Could not get config directory") - } - } - "config-path" => { - if let Some(path) = self.engine_state.get_config_path("config-path") { - let canon_config_path = canonicalize_path(&self.engine_state, path); - Ok(Value::String { - val: canon_config_path.to_string_lossy().to_string(), - span: self.span, - }) - } else if let Some(mut path) = nu_path::config_dir() { - path.push("nushell"); - path.push("config.nu"); - Ok(Value::String { - val: path.to_string_lossy().to_string(), - span: self.span, - }) - } else { - err("Could not get config directory") - } - } - "env-path" => { - if let Some(path) = self.engine_state.get_config_path("env-path") { - let canon_env_path = canonicalize_path(&self.engine_state, path); - Ok(Value::String { - val: canon_env_path.to_string_lossy().to_string(), - span: self.span, - }) - } else if let Some(mut path) = nu_path::config_dir() { - path.push("nushell"); - path.push("env.nu"); - Ok(Value::String { - val: path.to_string_lossy().to_string(), - span: self.span, - }) - } else { - err("Could not get config directory") - } - } - "history-path" => { - if let Some(mut path) = nu_path::config_dir() { - path.push("nushell"); - match self.engine_state.config.history_file_format { - nu_protocol::HistoryFileFormat::Sqlite => { - path.push("history.sqlite3"); - } - nu_protocol::HistoryFileFormat::PlainText => { - path.push("history.txt"); - } - } - let canon_hist_path = canonicalize_path(&self.engine_state, &path); - Ok(Value::String { - val: canon_hist_path.to_string_lossy().to_string(), - span: self.span, - }) - } else { - err("Could not get config directory") - } - } - "loginshell-path" => { - if let Some(mut path) = nu_path::config_dir() { - path.push("nushell"); - path.push("login.nu"); - let canon_login_path = canonicalize_path(&self.engine_state, &path); - Ok(Value::String { - val: canon_login_path.to_string_lossy().to_string(), - span: self.span, - }) - } else { - err("Could not get config directory") - } - } - "plugin-path" => { - #[cfg(feature = "plugin")] - { - if let Some(path) = &self.engine_state.plugin_signatures { - let canon_plugin_path = canonicalize_path(&self.engine_state, path); - Ok(Value::String { - val: canon_plugin_path.to_string_lossy().to_string(), - span: self.span, - }) - } else { - err("Could not get plugin signature location") - } - } - - #[cfg(not(feature = "plugin"))] - { - err("Plugin feature not enabled") - } - } - "scope" => Ok(create_scope(&self.engine_state, &self.stack, self.span())?), - "home-path" => { - if let Some(path) = nu_path::home_dir() { - let canon_home_path = canonicalize_path(&self.engine_state, &path); - Ok(Value::String { - val: canon_home_path.to_string_lossy().into(), - span: self.span(), - }) - } else { - err("Could not get home path") - } - } - "temp-path" => { - let canon_temp_path = canonicalize_path(&self.engine_state, &std::env::temp_dir()); - Ok(Value::String { - val: canon_temp_path.to_string_lossy().into(), - span: self.span(), - }) - } - "pid" => Ok(Value::int(std::process::id().into(), self.span())), - "os-info" => { - let sys = sysinfo::System::new(); - let ver = match sys.kernel_version() { - Some(v) => v, - None => "unknown".into(), - }; - - let os_record = Value::Record { - cols: vec![ - "name".into(), - "arch".into(), - "family".into(), - "kernel_version".into(), - ], - vals: vec![ - Value::string(std::env::consts::OS, self.span()), - Value::string(std::env::consts::ARCH, self.span()), - Value::string(std::env::consts::FAMILY, self.span()), - Value::string(ver, self.span()), - ], - span: self.span(), - }; - - Ok(os_record) - } - "is-interactive" => Ok(Value::Bool { - val: self.engine_state.is_interactive, - span: self.span, - }), - "is-login" => Ok(Value::Bool { - val: self.engine_state.is_login, - span: self.span, - }), - "startup-time" => Ok(Value::Duration { - val: self.engine_state.get_startup_time(), - span: self.span(), - }), - "current-exe" => { - let exe = std::env::current_exe().map_err(|_| { - err("Could not get current executable path") - .expect_err("did not get err from err function") - })?; - - let canon_exe = canonicalize_path(&self.engine_state, &exe); - - Ok(Value::String { - val: canon_exe.to_string_lossy().into(), - span: self.span(), - }) - } - _ => err(&format!("Could not find column '{column}'")), - } - } - - fn span(&self) -> Span { - self.span - } - - fn clone_value(&self, span: Span) -> Value { - Value::LazyRecord { - val: Box::new((*self).clone()), - span, - } - } -} - -// manually implemented so we can skip engine_state which doesn't implement Debug -// FIXME: find a better way -impl fmt::Debug for NuVariable { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("NuVariable").finish() - } -} diff --git a/crates/nu-engine/src/scope.rs b/crates/nu-engine/src/scope.rs index 78e98ca863..af887a7a6e 100644 --- a/crates/nu-engine/src/scope.rs +++ b/crates/nu-engine/src/scope.rs @@ -283,7 +283,7 @@ impl<'e, 's> ScopeData<'e, 's> { .collect::>(); // Until we allow custom commands to have input and output types, let's just - // make them Type::Any Type::Any so they can show up in our $nu.scope.commands + // make them Type::Any Type::Any so they can show up in our `scope commands` // a little bit better. If sigs is empty, we're pretty sure that we're dealing // with a custom command. if sigs.is_empty() { diff --git a/crates/nu-std/std/help.nu b/crates/nu-std/std/help.nu index af3e9f2366..a379901acc 100644 --- a/crates/nu-std/std/help.nu +++ b/crates/nu-std/std/help.nu @@ -74,11 +74,11 @@ def get-all-operators [] { return [ ]} def "nu-complete list-aliases" [] { - $nu.scope.aliases | select name usage | rename value description + scope aliases | select name usage | rename value description } def "nu-complete list-modules" [] { - $nu.scope.modules | select name usage | rename value description + scope modules | select name usage | rename value description } def "nu-complete list-operators" [] { @@ -91,11 +91,11 @@ def "nu-complete list-operators" [] { } def "nu-complete list-commands" [] { - $nu.scope.commands | select name usage | rename value description + scope commands | select name usage | rename value description } def "nu-complete list-externs" [] { - $nu.scope.commands | where is_extern | select name usage | rename value description + scope commands | where is_extern | select name usage | rename value description } def build-help-header [ @@ -239,7 +239,7 @@ export def modules [ ...module: string@"nu-complete list-modules" # the name of module to get help on --find (-f): string # string to find in module names ] { - let modules = $nu.scope.modules + let modules = (scope modules) if not ($find | is-empty) { $modules | find $find --columns [name usage] @@ -345,7 +345,7 @@ export def aliases [ ...alias: string@"nu-complete list-aliases" # the name of alias to get help on --find (-f): string # string to find in alias names ] { - let aliases = ($nu.scope.aliases | sort-by name) + let aliases = (scope aliases | sort-by name) if not ($find | is-empty) { $aliases | find $find --columns [name usage] @@ -382,7 +382,7 @@ export def externs [ --find (-f): string # string to find in extern names ] { let externs = ( - $nu.scope.commands + scope commands | where is_extern | select name module_name usage | sort-by name @@ -557,7 +557,7 @@ def build-command-page [command: record] { ] } else { [] }) - let subcommands = ($nu.scope.commands | where name =~ $"^($command.name) " | select name usage) + let subcommands = (scope commands | where name =~ $"^($command.name) " | select name usage) let subcommands = (if not ($subcommands | is-empty) {[ (build-help-header "Subcommands") ($subcommands | each {|subcommand | @@ -677,7 +677,7 @@ export def commands [ ...command: string@"nu-complete list-commands" # the name of command to get help on --find (-f): string # string to find in command names and usage ] { - let commands = ($nu.scope.commands | where not is_extern | reject is_extern | sort-by name) + let commands = (scope commands | where not is_extern | reject is_extern | sort-by name) if not ($find | is-empty) { # TODO: impl find for external commands diff --git a/crates/nu-utils/src/sample_config/default_config.nu b/crates/nu-utils/src/sample_config/default_config.nu index 523ae3321f..6131788440 100644 --- a/crates/nu-utils/src/sample_config/default_config.nu +++ b/crates/nu-utils/src/sample_config/default_config.nu @@ -390,7 +390,7 @@ let-env config = { description_text: yellow } source: { |buffer, position| - $nu.scope.commands + scope commands | where name =~ $buffer | each { |it| {value: $it.name description: $it.usage} } } @@ -409,7 +409,7 @@ let-env config = { description_text: yellow } source: { |buffer, position| - $nu.scope.vars + scope variables | where name =~ $buffer | sort-by name | each { |it| {value: $it.name description: $it.type} } @@ -433,7 +433,7 @@ let-env config = { description_text: yellow } source: { |buffer, position| - $nu.scope.commands + scope commands | where name =~ $buffer | each { |it| {value: $it.name description: $it.usage} } } diff --git a/src/tests/test_engine.rs b/src/tests/test_engine.rs index b510e65262..7c88e57ef8 100644 --- a/src/tests/test_engine.rs +++ b/src/tests/test_engine.rs @@ -60,7 +60,7 @@ fn help_works_with_missing_requirements() -> TestResult { #[test] fn scope_variable() -> TestResult { run_test( - r#"let x = 3; $nu.scope.vars | where name == "$x" | get type.0"#, + r#"let x = 3; scope variables | where name == "$x" | get type.0"#, "int", ) } @@ -74,7 +74,7 @@ fn scope_command_defaults(#[case] var: &str, #[case] exp_result: &str) -> TestRe run_test( &format!( r#"def t1 [a:int b?:float=1.23 --flag1:string --flag2:float=4.56] {{ true }}; - let rslt = ($nu.scope.commands | where name == 't1' | get signatures.0.any | where parameter_name == '{var}' | get parameter_default.0); + let rslt = (scope commands | where name == 't1' | get signatures.0.any | where parameter_name == '{var}' | get parameter_default.0); $"<($rslt)> ($rslt | describe)""# ), exp_result, diff --git a/src/tests/test_stdlib.rs b/src/tests/test_stdlib.rs index 3a7a4b226b..3be252ef77 100644 --- a/src/tests/test_stdlib.rs +++ b/src/tests/test_stdlib.rs @@ -2,7 +2,7 @@ use crate::tests::{fail_test, run_test_std, TestResult}; #[test] fn library_loaded() -> TestResult { - run_test_std("$nu.scope.modules | where name == 'std' | length", "1") + run_test_std("scope modules | where name == 'std' | length", "1") } #[test] diff --git a/tests/scope/mod.rs b/tests/scope/mod.rs index c21787ed08..f044292041 100644 --- a/tests/scope/mod.rs +++ b/tests/scope/mod.rs @@ -7,7 +7,7 @@ fn scope_shows_alias() { let actual = nu!( cwd: ".", r#"alias xaz = echo alias1 - $nu.scope.aliases | find xaz | length + scope aliases | find xaz | length "# ); @@ -20,7 +20,7 @@ fn scope_shows_command() { let actual = nu!( cwd: ".", r#"def xaz [] { echo xaz } - $nu.scope.commands | find xaz | length + scope commands | find xaz | length "# ); @@ -35,7 +35,7 @@ fn scope_doesnt_show_scoped_hidden_alias() { r#"alias xaz = echo alias1 do { hide xaz - $nu.scope.aliases | find xaz | length + scope aliases | find xaz | length } "# ); @@ -50,7 +50,7 @@ fn scope_doesnt_show_hidden_alias() { cwd: ".", r#"alias xaz = echo alias1 hide xaz - $nu.scope.aliases | find xaz | length + scope aliases | find xaz | length "# ); @@ -65,7 +65,7 @@ fn scope_doesnt_show_scoped_hidden_command() { r#"def xaz [] { echo xaz } do { hide xaz - $nu.scope.commands | find xaz | length + scope commands | find xaz | length } "# ); @@ -80,7 +80,7 @@ fn scope_doesnt_show_hidden_command() { cwd: ".", r#"def xaz [] { echo xaz } hide xaz - $nu.scope.commands | find xaz | length + scope commands | find xaz | length "# ); @@ -97,7 +97,7 @@ fn correctly_report_of_shadowed_alias() { r#"alias xaz = echo alias1 def helper [] { alias xaz = echo alias2 - $nu.scope.aliases + scope aliases } helper | where alias == xaz | get expansion.0"# );