diff --git a/crates/nu-cmd-lang/src/core_commands/scope/aliases.rs b/crates/nu-cmd-lang/src/core_commands/scope/aliases.rs index 9a874f228e..ac5e55dae0 100644 --- a/crates/nu-cmd-lang/src/core_commands/scope/aliases.rs +++ b/crates/nu-cmd-lang/src/core_commands/scope/aliases.rs @@ -35,7 +35,7 @@ impl Command for ScopeAliases { let ctrlc = engine_state.ctrlc.clone(); let mut scope_data = ScopeData::new(engine_state, stack); - scope_data.populate_all(); + scope_data.populate_decls(); Ok(scope_data.collect_aliases(span).into_pipeline_data(ctrlc)) } diff --git a/crates/nu-cmd-lang/src/core_commands/scope/commands.rs b/crates/nu-cmd-lang/src/core_commands/scope/commands.rs index 8247b93bd6..cfbd14cab8 100644 --- a/crates/nu-cmd-lang/src/core_commands/scope/commands.rs +++ b/crates/nu-cmd-lang/src/core_commands/scope/commands.rs @@ -35,7 +35,7 @@ impl Command for ScopeCommands { let ctrlc = engine_state.ctrlc.clone(); let mut scope_data = ScopeData::new(engine_state, stack); - scope_data.populate_all(); + scope_data.populate_decls(); Ok(scope_data.collect_commands(span).into_pipeline_data(ctrlc)) } 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 index 5cbbf09450..8da0da83c6 100644 --- a/crates/nu-cmd-lang/src/core_commands/scope/engine_stats.rs +++ b/crates/nu-cmd-lang/src/core_commands/scope/engine_stats.rs @@ -31,8 +31,7 @@ impl Command for ScopeEngineStats { ) -> Result { let span = call.head; - let mut scope_data = ScopeData::new(engine_state, stack); - scope_data.populate_all(); + let scope_data = ScopeData::new(engine_state, stack); Ok(scope_data.collect_engine_state(span).into_pipeline_data()) } diff --git a/crates/nu-cmd-lang/src/core_commands/scope/externs.rs b/crates/nu-cmd-lang/src/core_commands/scope/externs.rs new file mode 100644 index 0000000000..437b64940c --- /dev/null +++ b/crates/nu-cmd-lang/src/core_commands/scope/externs.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 ScopeExterns; + +impl Command for ScopeExterns { + fn name(&self) -> &str { + "scope externs" + } + + fn signature(&self) -> Signature { + Signature::build("scope externs") + .input_output_types(vec![(Type::Nothing, Type::Any)]) + .allow_variants_without_examples(true) + .category(Category::Filters) + } + + fn usage(&self) -> &str { + "Output info on the known externals 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_decls(); + + Ok(scope_data.collect_externs(span).into_pipeline_data(ctrlc)) + } + + fn examples(&self) -> Vec { + vec![Example { + description: "Show the known externals in the current scope", + example: "scope externs", + result: None, + }] + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_examples() { + use crate::test_examples; + + test_examples(ScopeExterns {}) + } +} diff --git a/crates/nu-cmd-lang/src/core_commands/scope/mod.rs b/crates/nu-cmd-lang/src/core_commands/scope/mod.rs index ca82cb8995..1a65812e35 100644 --- a/crates/nu-cmd-lang/src/core_commands/scope/mod.rs +++ b/crates/nu-cmd-lang/src/core_commands/scope/mod.rs @@ -2,6 +2,7 @@ mod aliases; mod command; mod commands; mod engine_stats; +mod externs; mod modules; mod variables; @@ -9,5 +10,6 @@ pub use aliases::*; pub use command::*; pub use commands::*; pub use engine_stats::*; +pub use externs::*; pub use modules::*; pub use variables::*; diff --git a/crates/nu-cmd-lang/src/core_commands/scope/variables.rs b/crates/nu-cmd-lang/src/core_commands/scope/variables.rs index 017352ef44..d509c0b119 100644 --- a/crates/nu-cmd-lang/src/core_commands/scope/variables.rs +++ b/crates/nu-cmd-lang/src/core_commands/scope/variables.rs @@ -35,7 +35,7 @@ impl Command for ScopeVariables { let ctrlc = engine_state.ctrlc.clone(); let mut scope_data = ScopeData::new(engine_state, stack); - scope_data.populate_all(); + scope_data.populate_vars(); Ok(scope_data.collect_vars(span).into_pipeline_data(ctrlc)) } diff --git a/crates/nu-cmd-lang/src/default_context.rs b/crates/nu-cmd-lang/src/default_context.rs index 25f90360ce..daf6a5ecbe 100644 --- a/crates/nu-cmd-lang/src/default_context.rs +++ b/crates/nu-cmd-lang/src/default_context.rs @@ -58,6 +58,7 @@ pub fn create_default_context() -> EngineState { ScopeAliases, ScopeCommands, ScopeEngineStats, + ScopeExterns, ScopeModules, ScopeVariables, Try, diff --git a/crates/nu-command/src/help/help_aliases.rs b/crates/nu-command/src/help/help_aliases.rs index a1c9f5e7ca..17f9b62844 100644 --- a/crates/nu-command/src/help/help_aliases.rs +++ b/crates/nu-command/src/help/help_aliases.rs @@ -169,7 +169,7 @@ pub fn help_aliases( fn build_help_aliases(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec { let mut scope_data = ScopeData::new(engine_state, stack); - scope_data.populate_all(); + scope_data.populate_decls(); scope_data.collect_aliases(span) } diff --git a/crates/nu-command/src/help/help_externs.rs b/crates/nu-command/src/help/help_externs.rs index b15f39e215..29bda80a96 100644 --- a/crates/nu-command/src/help/help_externs.rs +++ b/crates/nu-command/src/help/help_externs.rs @@ -144,7 +144,7 @@ pub fn help_externs( fn build_help_externs(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec { let mut scope = ScopeData::new(engine_state, stack); - scope.populate_all(); + scope.populate_decls(); scope.collect_externs(span) } diff --git a/crates/nu-command/tests/commands/help.rs b/crates/nu-command/tests/commands/help.rs index 32e01c1c0f..b23f8e322c 100644 --- a/crates/nu-command/tests/commands/help.rs +++ b/crates/nu-command/tests/commands/help.rs @@ -2,6 +2,8 @@ use nu_test_support::fs::Stub::FileWithContent; use nu_test_support::playground::Playground; use nu_test_support::{nu, nu_repl_code, pipeline}; +// Note: These tests might slightly overlap with tests/scope/mod.rs + #[test] fn help_commands_length() { let actual = nu!("help commands | length"); @@ -22,7 +24,6 @@ fn help_shows_signature() { assert!(!actual.out.contains("Input/output types")); } -#[ignore = "TODO: Need to decide how to do help messages of new aliases"] #[test] fn help_aliases() { let code = &[ @@ -34,7 +35,6 @@ fn help_aliases() { assert_eq!(actual.out, "1"); } -#[ignore = "TODO: Need to decide how to do help messages of new aliases"] #[test] fn help_alias_usage_1() { Playground::setup("help_alias_usage_1", |dirs, sandbox| { @@ -56,7 +56,6 @@ fn help_alias_usage_1() { }) } -#[ignore = "TODO: Need to decide how to do help messages of new aliases"] #[test] fn help_alias_usage_2() { let code = &[ @@ -68,7 +67,6 @@ fn help_alias_usage_2() { assert_eq!(actual.out, "line2"); } -#[ignore = "TODO: Need to decide how to do help messages of new aliases"] #[test] fn help_alias_usage_3() { Playground::setup("help_alias_usage_3", |dirs, sandbox| { @@ -91,7 +89,6 @@ fn help_alias_usage_3() { }) } -#[ignore = "TODO: Need to decide how to do help messages of new aliases"] #[test] fn help_alias_name() { Playground::setup("help_alias_name", |dirs, sandbox| { @@ -113,7 +110,6 @@ fn help_alias_name() { }) } -#[ignore = "TODO: Need to decide how to do help messages of new aliases"] #[test] fn help_alias_name_f() { Playground::setup("help_alias_name_f", |dirs, sandbox| { @@ -133,7 +129,6 @@ fn help_alias_name_f() { }) } -#[ignore = "TODO: Need to decide how to do help messages of new aliases"] #[test] fn help_export_alias_name_single_word() { Playground::setup("help_export_alias_name_single_word", |dirs, sandbox| { @@ -155,7 +150,6 @@ fn help_export_alias_name_single_word() { }) } -#[ignore = "TODO: Need to decide how to do help messages of new aliases"] #[test] fn help_export_alias_name_multi_word() { Playground::setup("help_export_alias_name_multi_word", |dirs, sandbox| { @@ -302,7 +296,6 @@ fn help_usage_extra_usage_command() { }) } -#[ignore = "TODO: Need to decide how to do help messages of new aliases"] #[test] fn help_usage_extra_usage_alias() { Playground::setup("help_usage_extra_usage_alias", |dirs, sandbox| { @@ -360,7 +353,7 @@ fn help_modules_main_2() { r#"module spam { export def main [] { 'foo' }; }"#, - "help modules | where name == spam | get 0.commands.0", + "help modules | where name == spam | get 0.commands.0.name", ]; let actual = nu!(pipeline(&inp.join("; "))); @@ -368,19 +361,6 @@ fn help_modules_main_2() { assert_eq!(actual.out, "spam"); } -#[ignore = "TODO: Can't have alias with the same name as command"] -#[test] -fn help_alias_before_command() { - let code = &[ - "alias SPAM = echo 'spam'", - "def SPAM [] { 'spam' }", - "help SPAM", - ]; - let actual = nu!(nu_repl_code(code)); - - assert!(actual.out.contains("Alias")); -} - #[test] fn nothing_type_annotation() { let actual = nu!(pipeline( diff --git a/crates/nu-engine/src/scope.rs b/crates/nu-engine/src/scope.rs index 1961ff7a78..512d3176b7 100644 --- a/crates/nu-engine/src/scope.rs +++ b/crates/nu-engine/src/scope.rs @@ -1,53 +1,10 @@ use nu_protocol::{ engine::{Command, EngineState, Stack, Visibility}, - ShellError, Signature, Span, SyntaxShape, Type, Value, + ModuleId, Signature, Span, SyntaxShape, Type, Value, }; -use std::borrow::Borrow; use std::cmp::Ordering; use std::collections::HashMap; -pub fn create_scope( - engine_state: &EngineState, - stack: &Stack, - span: Span, -) -> Result { - let mut scope_data = ScopeData::new(engine_state, stack); - - scope_data.populate_all(); - - let mut cols = vec![]; - let mut vals = vec![]; - - cols.push("vars".to_string()); - vals.push(Value::List { - vals: scope_data.collect_vars(span), - span, - }); - - cols.push("commands".to_string()); - vals.push(Value::List { - vals: scope_data.collect_commands(span), - span, - }); - - cols.push("aliases".to_string()); - vals.push(Value::List { - vals: scope_data.collect_aliases(span), - span, - }); - - cols.push("modules".to_string()); - vals.push(Value::List { - vals: scope_data.collect_modules(span), - span, - }); - - cols.push("engine_state".to_string()); - vals.push(scope_data.collect_engine_state(span)); - - Ok(Value::Record { cols, vals, span }) -} - pub struct ScopeData<'e, 's> { engine_state: &'e EngineState, stack: &'s Stack, @@ -69,11 +26,16 @@ impl<'e, 's> ScopeData<'e, 's> { } } - pub fn populate_all(&mut self) { + pub fn populate_vars(&mut self) { for overlay_frame in self.engine_state.active_overlays(&[]) { self.vars_map.extend(&overlay_frame.vars); + } + } + + // decls include all commands, i.e., normal commands, aliases, and externals + pub fn populate_decls(&mut self) { + for overlay_frame in self.engine_state.active_overlays(&[]) { self.decls_map.extend(&overlay_frame.decls); - self.modules_map.extend(&overlay_frame.modules); self.visibility.merge_with(overlay_frame.visibility.clone()); } } @@ -86,20 +48,28 @@ impl<'e, 's> ScopeData<'e, 's> { pub fn collect_vars(&self, span: Span) -> Vec { let mut vars = vec![]; - for var in &self.vars_map { - let var_name = Value::string(String::from_utf8_lossy(var.0).to_string(), span); - let var_type = Value::string(self.engine_state.get_var(**var.1).ty.to_string(), span); + for (var_name, var_id) in &self.vars_map { + let var_name = Value::string(String::from_utf8_lossy(var_name).to_string(), span); - let var_value = if let Ok(val) = self.stack.get_var(**var.1, span) { + let var_type = Value::string(self.engine_state.get_var(**var_id).ty.to_string(), span); + + let var_value = if let Ok(val) = self.stack.get_var(**var_id, span) { val } else { Value::nothing(span) }; + let var_id_val = Value::int(**var_id as i64, span); + vars.push(Value::Record { - cols: vec!["name".to_string(), "type".to_string(), "value".to_string()], - vals: vec![var_name, var_type, var_value], + cols: vec![ + "name".to_string(), + "type".to_string(), + "value".to_string(), + "var_id".to_string(), + ], + vals: vec![var_name, var_type, var_value, var_id_val], span, }) } @@ -108,6 +78,7 @@ impl<'e, 's> ScopeData<'e, 's> { pub fn collect_commands(&self, span: Span) -> Vec { let mut commands = vec![]; + for (command_name, decl_id) in &self.decls_map { if self.visibility.is_decl_id_visible(decl_id) && !self.engine_state.get_decl(**decl_id).is_alias() @@ -115,27 +86,12 @@ impl<'e, 's> ScopeData<'e, 's> { let mut cols = vec![]; let mut vals = vec![]; - let mut module_commands = vec![]; - for module in &self.modules_map { - let module_name = String::from_utf8_lossy(module.0).to_string(); - let module_id = self.engine_state.find_module(module.0, &[]); - if let Some(module_id) = module_id { - let module = self.engine_state.get_module(module_id); - if module.has_decl(command_name) { - module_commands.push(module_name); - } - } - } - cols.push("name".into()); vals.push(Value::String { val: String::from_utf8_lossy(command_name).to_string(), span, }); - cols.push("module_name".into()); - vals.push(Value::string(module_commands.join(", "), span)); - let decl = self.engine_state.get_decl(**decl_id); let signature = decl.signature(); @@ -238,6 +194,9 @@ impl<'e, 's> ScopeData<'e, 's> { span, }); + cols.push("decl_id".into()); + vals.push(Value::int(**decl_id as i64, span)); + commands.push(Value::Record { cols, vals, span }) } } @@ -488,6 +447,7 @@ impl<'e, 's> ScopeData<'e, 's> { pub fn collect_externs(&self, span: Span) -> Vec { let mut externals = vec![]; + for (command_name, decl_id) in &self.decls_map { let decl = self.engine_state.get_decl(**decl_id); @@ -495,36 +455,21 @@ impl<'e, 's> ScopeData<'e, 's> { let mut cols = vec![]; let mut vals = vec![]; - let mut module_commands = vec![]; - for module in &self.modules_map { - let module_name = String::from_utf8_lossy(module.0).to_string(); - let module_id = self.engine_state.find_module(module.0, &[]); - if let Some(module_id) = module_id { - let module = self.engine_state.get_module(module_id); - if module.has_decl(command_name) { - module_commands.push(module_name); - } - } - } - cols.push("name".into()); vals.push(Value::String { val: String::from_utf8_lossy(command_name).to_string(), span, }); - cols.push("module_name".into()); - vals.push(Value::String { - val: module_commands.join(", "), - span, - }); - cols.push("usage".to_string()); vals.push(Value::String { val: decl.usage().into(), span, }); + cols.push("decl_id".into()); + vals.push(Value::int(**decl_id as i64, span)); + externals.push(Value::Record { cols, vals, span }) } } @@ -534,17 +479,23 @@ impl<'e, 's> ScopeData<'e, 's> { pub fn collect_aliases(&self, span: Span) -> Vec { let mut aliases = vec![]; - for (_, decl_id) in self.engine_state.get_decls_sorted(false) { + + for (decl_name, decl_id) in self.engine_state.get_decls_sorted(false) { if self.visibility.is_decl_id_visible(&decl_id) { let decl = self.engine_state.get_decl(decl_id); if let Some(alias) = decl.as_alias() { - let sig = decl.signature().update_from_command(decl.borrow()); - let key = sig.name; - aliases.push(Value::Record { - cols: vec!["name".into(), "expansion".into(), "usage".into()], + cols: vec![ + "name".into(), + "expansion".into(), + "usage".into(), + "decl_id".into(), + ], vals: vec![ - Value::String { val: key, span }, + Value::String { + val: String::from_utf8_lossy(&decl_name).to_string(), + span, + }, Value::String { val: String::from_utf8_lossy( self.engine_state.get_span_contents(alias.wrapped_call.span), @@ -553,7 +504,11 @@ impl<'e, 's> ScopeData<'e, 's> { span, }, Value::String { - val: alias.signature().usage, + val: alias.usage().to_string(), + span, + }, + Value::Int { + val: decl_id as i64, span, }, ], @@ -567,70 +522,156 @@ impl<'e, 's> ScopeData<'e, 's> { aliases } + fn collect_module(&self, module_name: &[u8], module_id: &ModuleId, span: Span) -> Value { + let module = self.engine_state.get_module(*module_id); + + let all_decls = module.decls(); + + let export_commands: Vec = all_decls + .iter() + .filter_map(|(name_bytes, decl_id)| { + let decl = self.engine_state.get_decl(*decl_id); + + if !decl.is_alias() && !decl.is_known_external() { + Some(Value::record( + vec!["name".into(), "decl_id".into()], + vec![ + Value::string(String::from_utf8_lossy(name_bytes), span), + Value::int(*decl_id as i64, span), + ], + span, + )) + } else { + None + } + }) + .collect(); + + let export_aliases: Vec = all_decls + .iter() + .filter_map(|(name_bytes, decl_id)| { + let decl = self.engine_state.get_decl(*decl_id); + + if decl.is_alias() { + Some(Value::record( + vec!["name".into(), "decl_id".into()], + vec![ + Value::string(String::from_utf8_lossy(name_bytes), span), + Value::int(*decl_id as i64, span), + ], + span, + )) + } else { + None + } + }) + .collect(); + + let export_externs: Vec = all_decls + .iter() + .filter_map(|(name_bytes, decl_id)| { + let decl = self.engine_state.get_decl(*decl_id); + + if decl.is_known_external() { + Some(Value::record( + vec!["name".into(), "decl_id".into()], + vec![ + Value::string(String::from_utf8_lossy(name_bytes), span), + Value::int(*decl_id as i64, span), + ], + span, + )) + } else { + None + } + }) + .collect(); + + let export_submodules: Vec = module + .submodules() + .iter() + .map(|(name_bytes, submodule_id)| self.collect_module(name_bytes, submodule_id, span)) + .collect(); + + let export_consts: Vec = module + .vars() + .iter() + .map(|(name_bytes, var_id)| { + Value::record( + vec!["name".into(), "type".into(), "var_id".into()], + vec![ + Value::string(String::from_utf8_lossy(name_bytes), span), + Value::string(self.engine_state.get_var(*var_id).ty.to_string(), span), + Value::int(*var_id as i64, span), + ], + span, + ) + }) + .collect(); + + let export_env_block = module.env_block.map_or_else( + || Value::nothing(span), + |block_id| Value::Block { + val: block_id, + span, + }, + ); + + let module_usage = self + .engine_state + .build_module_usage(*module_id) + .map(|(usage, _)| usage) + .unwrap_or_default(); + + Value::Record { + cols: vec![ + "name".into(), + "commands".into(), + "aliases".into(), + "externs".into(), + "submodules".into(), + "constants".into(), + "env_block".into(), + "usage".into(), + "module_id".into(), + ], + vals: vec![ + Value::string(String::from_utf8_lossy(module_name), span), + Value::List { + vals: export_commands, + span, + }, + Value::List { + vals: export_aliases, + span, + }, + Value::List { + vals: export_externs, + span, + }, + Value::List { + vals: export_submodules, + span, + }, + Value::List { + vals: export_consts, + span, + }, + export_env_block, + Value::string(module_usage, span), + Value::int(*module_id as i64, span), + ], + span, + } + } + pub fn collect_modules(&self, span: Span) -> Vec { let mut modules = vec![]; for (module_name, module_id) in &self.modules_map { - let module = self.engine_state.get_module(**module_id); - - let export_commands: Vec = module - .decls() - .iter() - .filter(|(_, id)| { - self.visibility.is_decl_id_visible(id) - && !self.engine_state.get_decl(*id).is_alias() - }) - .map(|(bytes, _)| Value::string(String::from_utf8_lossy(bytes), span)) - .collect(); - - let export_aliases: Vec = module - .decls() - .iter() - .filter(|(_, id)| { - self.visibility.is_decl_id_visible(id) - && self.engine_state.get_decl(*id).is_alias() - }) - .map(|(bytes, _)| Value::string(String::from_utf8_lossy(bytes), span)) - .collect(); - - let export_env_block = module.env_block.map_or_else( - || Value::nothing(span), - |block_id| Value::Block { - val: block_id, - span, - }, - ); - - let module_usage = self - .engine_state - .build_module_usage(**module_id) - .map(|(usage, _)| usage) - .unwrap_or_default(); - - modules.push(Value::Record { - cols: vec![ - "name".into(), - "commands".into(), - "aliases".into(), - "env_block".into(), - "usage".into(), - ], - vals: vec![ - Value::string(String::from_utf8_lossy(module_name), span), - Value::List { - vals: export_commands, - span, - }, - Value::List { - vals: export_aliases, - span, - }, - export_env_block, - Value::string(module_usage, span), - ], - span, - }); + modules.push(self.collect_module(module_name, module_id, span)); } + modules.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)); modules } @@ -660,6 +701,7 @@ impl<'e, 's> ScopeData<'e, 's> { span, ), ]; + Value::Record { cols: engine_state_cols, vals: engine_state_vals, diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 3eca9eb7dc..50c72e30f5 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -749,6 +749,7 @@ pub fn parse_alias( if let Some(decl_id) = working_set.find_decl(b"alias") { let (command_spans, rest_spans) = spans.split_at(split_id); + let (usage, extra_usage) = working_set.build_usage(&lite_command.comments); let original_starting_error_count = working_set.parse_errors.len(); @@ -906,6 +907,8 @@ pub fn parse_alias( name: alias_name, command, wrapped_call, + usage, + extra_usage, }; working_set.add_decl(Box::new(decl)); diff --git a/crates/nu-protocol/src/alias.rs b/crates/nu-protocol/src/alias.rs index e8c668922a..712a017956 100644 --- a/crates/nu-protocol/src/alias.rs +++ b/crates/nu-protocol/src/alias.rs @@ -3,15 +3,16 @@ use crate::PipelineData; use crate::{ ast::{Call, Expression}, engine::Command, - BlockId, Example, ShellError, Signature, + ShellError, Signature, }; -use std::path::PathBuf; #[derive(Clone)] pub struct Alias { pub name: String, pub command: Option>, // None if external call pub wrapped_call: Expression, + pub usage: String, + pub extra_usage: String, } impl Command for Alias { @@ -28,19 +29,11 @@ impl Command for Alias { } fn usage(&self) -> &str { - if let Some(cmd) = &self.command { - cmd.usage() - } else { - "This alias wraps an unknown external command." - } + &self.usage } fn extra_usage(&self) -> &str { - if let Some(cmd) = &self.command { - cmd.extra_usage() - } else { - "" - } + &self.extra_usage } fn run( @@ -57,30 +50,6 @@ impl Command for Alias { }) } - fn examples(&self) -> Vec { - if let Some(cmd) = &self.command { - cmd.examples() - } else { - vec![] - } - } - - fn is_builtin(&self) -> bool { - if let Some(cmd) = &self.command { - cmd.is_builtin() - } else { - false - } - } - - fn is_known_external(&self) -> bool { - if let Some(cmd) = &self.command { - cmd.is_known_external() - } else { - false - } - } - fn is_alias(&self) -> bool { true } @@ -88,54 +57,4 @@ impl Command for Alias { fn as_alias(&self) -> Option<&Alias> { Some(self) } - - fn is_custom_command(&self) -> bool { - if let Some(cmd) = &self.command { - cmd.is_custom_command() - } else if self.get_block_id().is_some() { - true - } else { - self.is_known_external() - } - } - - fn is_sub(&self) -> bool { - if let Some(cmd) = &self.command { - cmd.is_sub() - } else { - self.name().contains(' ') - } - } - - fn is_parser_keyword(&self) -> bool { - if let Some(cmd) = &self.command { - cmd.is_parser_keyword() - } else { - false - } - } - - fn is_plugin(&self) -> Option<(&PathBuf, &Option)> { - if let Some(cmd) = &self.command { - cmd.is_plugin() - } else { - None - } - } - - fn get_block_id(&self) -> Option { - if let Some(cmd) = &self.command { - cmd.get_block_id() - } else { - None - } - } - - fn search_terms(&self) -> Vec<&str> { - if let Some(cmd) = &self.command { - cmd.search_terms() - } else { - vec![] - } - } } diff --git a/tests/scope/mod.rs b/tests/scope/mod.rs index 1841a0a8ac..7e7e77e112 100644 --- a/tests/scope/mod.rs +++ b/tests/scope/mod.rs @@ -1,7 +1,10 @@ +use nu_test_support::fs::Stub::FileWithContent; use nu_test_support::nu; +use nu_test_support::playground::Playground; use pretty_assertions::assert_eq; -#[ignore = "TODO: This shows old-style aliases. New aliases are under commands"] +// Note: These tests might slightly overlap with crates/nu-command/tests/commands/help.rs + #[test] fn scope_shows_alias() { let actual = nu!("alias xaz = echo alias1 @@ -71,7 +74,7 @@ fn scope_doesnt_show_hidden_command() { } // same problem as 'which' command -#[ignore] +#[ignore = "See https://github.com/nushell/nushell/issues/4837"] #[test] fn correctly_report_of_shadowed_alias() { let actual = nu!("alias xaz = echo alias1 @@ -83,3 +86,163 @@ fn correctly_report_of_shadowed_alias() { assert_eq!(actual.out, "echo alias2"); } + +#[test] +fn correct_scope_modules_fields() { + let module_setup = r#" + # nice spam + + export module eggs { + export module bacon { + export def sausage [] { 'sausage' } + } + } + + export def main [] { 'foo' }; + export alias xaz = print + export extern git [] + export const X = 4 + + export-env { $env.SPAM = 'spam' } + "#; + + Playground::setup("correct_scope_modules_fields", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent("spam.nu", module_setup)]); + + let inp = &[ + "use spam.nu", + "scope modules | where name == spam | get 0.name", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "spam"); + + let inp = &[ + "use spam.nu", + "scope modules | where name == spam | get 0.usage", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "nice spam"); + + let inp = &[ + "use spam.nu", + "scope modules | where name == spam | get 0.env_block | is-empty", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "false"); + + let inp = &[ + "use spam.nu", + "scope modules | where name == spam | get 0.commands.0.name", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "spam"); + + let inp = &[ + "use spam.nu", + "scope modules | where name == spam | get 0.aliases.0.name", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "xaz"); + + let inp = &[ + "use spam.nu", + "scope modules | where name == spam | get 0.externs.0.name", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "git"); + + let inp = &[ + "use spam.nu", + "scope modules | where name == spam | get 0.constants.0.name", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "X"); + + let inp = &[ + "use spam.nu", + "scope modules | where name == spam | get 0.submodules.0.submodules.0.name", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "bacon"); + + let inp = &[ + "use spam.nu", + "scope modules | where name == spam | get 0.submodules.0.submodules.0.commands.0.name", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "sausage"); + }) +} + +#[test] +fn correct_scope_aliases_fields() { + let module_setup = r#" + # nice alias + export alias xaz = print + "#; + + Playground::setup("correct_scope_aliases_fields", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent("spam.nu", module_setup)]); + + let inp = &[ + "use spam.nu", + "scope aliases | where name == 'spam xaz' | get 0.name", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "spam xaz"); + + let inp = &[ + "use spam.nu", + "scope aliases | where name == 'spam xaz' | get 0.expansion", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "print"); + + let inp = &[ + "use spam.nu", + "scope aliases | where name == 'spam xaz' | get 0.usage", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "nice alias"); + + let inp = &[ + "use spam.nu", + "scope aliases | where name == 'spam xaz' | get 0.decl_id | is-empty", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "false"); + }) +} + +#[test] +fn correct_scope_externs_fields() { + let module_setup = r#" + # nice extern + export extern git [] + "#; + + Playground::setup("correct_scope_aliases_fields", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent("spam.nu", module_setup)]); + + let inp = &[ + "use spam.nu", + "scope externs | where name == 'spam git' | get 0.name", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "spam git"); + + let inp = &[ + "use spam.nu", + "scope externs | where name == 'spam git' | get 0.usage", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "nice extern"); + + let inp = &[ + "use spam.nu", + "scope externs | where name == 'spam git' | get 0.decl_id | is-empty", + ]; + let actual = nu!(cwd: dirs.test(), &inp.join("; ")); + assert_eq!(actual.out, "false"); + }) +}