mirror of
https://github.com/nushell/nushell
synced 2024-12-26 13:03:07 +00:00
Added help externs
command (#8403)
# Description `help externs` - command, which list external commands Closes https://github.com/nushell/nushell/issues/8301 # User-Facing Changes ```nu $ help externs ╭───┬──────────────┬─────────────┬───────────────────────────────────────────────────╮ │ # │ name │ module_name │ usage │ ├───┼──────────────┼─────────────┼───────────────────────────────────────────────────┤ │ 0 │ git push │ completions │ Push changes │ │ │ │ │ │ │ 1 │ git fetch │ completions │ Download objects and refs from another repository │ │ │ │ │ │ │ 2 │ git checkout │ completions │ Check out git branches and files │ │ │ │ │ │ ╰───┴──────────────┴─────────────┴───────────────────────────────────────────────────╯ ``` # Tests + Formatting Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass # After Submitting If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date.
This commit is contained in:
parent
f34ac9be62
commit
4de0347fdc
4 changed files with 201 additions and 0 deletions
152
crates/nu-cmd-lang/src/core_commands/help_externs.rs
Normal file
152
crates/nu-cmd-lang/src/core_commands/help_externs.rs
Normal file
|
@ -0,0 +1,152 @@
|
|||
use crate::help::highlight_search_in_table;
|
||||
use nu_color_config::StyleComputer;
|
||||
use nu_engine::{get_full_help, scope::ScopeData, CallExt};
|
||||
use nu_protocol::{
|
||||
ast::Call,
|
||||
engine::{Command, EngineState, Stack},
|
||||
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
||||
ShellError, Signature, Span, Spanned, SyntaxShape, Type, Value,
|
||||
};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct HelpExterns;
|
||||
|
||||
impl Command for HelpExterns {
|
||||
fn name(&self) -> &str {
|
||||
"help externs"
|
||||
}
|
||||
|
||||
fn usage(&self) -> &str {
|
||||
"Show help on nushell externs."
|
||||
}
|
||||
|
||||
fn signature(&self) -> Signature {
|
||||
Signature::build("help externs")
|
||||
.category(Category::Core)
|
||||
.rest(
|
||||
"rest",
|
||||
SyntaxShape::String,
|
||||
"the name of extern to get help on",
|
||||
)
|
||||
.named(
|
||||
"find",
|
||||
SyntaxShape::String,
|
||||
"string to find in extern names and usage",
|
||||
Some('f'),
|
||||
)
|
||||
.input_output_types(vec![(Type::Nothing, Type::Table(vec![]))])
|
||||
.allow_variants_without_examples(true)
|
||||
}
|
||||
|
||||
fn examples(&self) -> Vec<Example> {
|
||||
vec![
|
||||
Example {
|
||||
description: "show all externs",
|
||||
example: "help externs",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "show help for single extern",
|
||||
example: "help externs smth",
|
||||
result: None,
|
||||
},
|
||||
Example {
|
||||
description: "search for string in extern names and usages",
|
||||
example: "help externs --find smth",
|
||||
result: None,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
fn run(
|
||||
&self,
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
_input: PipelineData,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
help_externs(engine_state, stack, call)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn help_externs(
|
||||
engine_state: &EngineState,
|
||||
stack: &mut Stack,
|
||||
call: &Call,
|
||||
) -> Result<PipelineData, ShellError> {
|
||||
let head = call.head;
|
||||
let find: Option<Spanned<String>> = call.get_flag(engine_state, stack, "find")?;
|
||||
let rest: Vec<Spanned<String>> = call.rest(engine_state, stack, 0)?;
|
||||
|
||||
// 🚩The following two-lines are copied from filters/find.rs:
|
||||
let style_computer = StyleComputer::from_config(engine_state, stack);
|
||||
// Currently, search results all use the same style.
|
||||
// Also note that this sample string is passed into user-written code (the closure that may or may not be
|
||||
// defined for "string").
|
||||
let string_style = style_computer.compute("string", &Value::string("search result", head));
|
||||
|
||||
if let Some(f) = find {
|
||||
let all_cmds_vec = build_help_externs(engine_state, stack, head);
|
||||
let found_cmds_vec =
|
||||
highlight_search_in_table(all_cmds_vec, &f.item, &["name", "usage"], &string_style)?;
|
||||
|
||||
return Ok(found_cmds_vec
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()));
|
||||
}
|
||||
|
||||
if rest.is_empty() {
|
||||
let found_cmds_vec = build_help_externs(engine_state, stack, head);
|
||||
|
||||
Ok(found_cmds_vec
|
||||
.into_iter()
|
||||
.into_pipeline_data(engine_state.ctrlc.clone()))
|
||||
} else {
|
||||
let mut name = String::new();
|
||||
|
||||
for r in &rest {
|
||||
if !name.is_empty() {
|
||||
name.push(' ');
|
||||
}
|
||||
name.push_str(&r.item);
|
||||
}
|
||||
|
||||
let output = engine_state
|
||||
.get_signatures_with_examples(false)
|
||||
.iter()
|
||||
.filter(|(signature, _, _, _, _)| signature.name == name)
|
||||
.map(|(signature, examples, _, _, is_parser_keyword)| {
|
||||
get_full_help(signature, examples, engine_state, stack, *is_parser_keyword)
|
||||
})
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
if !output.is_empty() {
|
||||
Ok(Value::String {
|
||||
val: output.join("======================\n\n"),
|
||||
span: call.head,
|
||||
}
|
||||
.into_pipeline_data())
|
||||
} else {
|
||||
Err(ShellError::CommandNotFound(span(&[
|
||||
rest[0].span,
|
||||
rest[rest.len() - 1].span,
|
||||
])))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_help_externs(engine_state: &EngineState, stack: &Stack, span: Span) -> Vec<Value> {
|
||||
let mut scope = ScopeData::new(engine_state, stack);
|
||||
scope.populate_all();
|
||||
scope.collect_externs(span)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#[test]
|
||||
fn test_examples() {
|
||||
use super::HelpExterns;
|
||||
use crate::test_examples;
|
||||
test_examples(HelpExterns {})
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ mod for_;
|
|||
pub mod help;
|
||||
pub mod help_aliases;
|
||||
pub mod help_commands;
|
||||
pub mod help_externs;
|
||||
pub mod help_modules;
|
||||
mod help_operators;
|
||||
mod hide;
|
||||
|
@ -59,6 +60,7 @@ pub use for_::For;
|
|||
pub use help::Help;
|
||||
pub use help_aliases::HelpAliases;
|
||||
pub use help_commands::HelpCommands;
|
||||
pub use help_externs::HelpExterns;
|
||||
pub use help_modules::HelpModules;
|
||||
pub use help_operators::HelpOperators;
|
||||
pub use hide::Hide;
|
||||
|
|
|
@ -39,6 +39,7 @@ pub fn create_default_context() -> EngineState {
|
|||
HelpAliases,
|
||||
HelpCommands,
|
||||
HelpModules,
|
||||
HelpExterns,
|
||||
HelpOperators,
|
||||
Hide,
|
||||
HideEnv,
|
||||
|
|
|
@ -463,6 +463,52 @@ impl<'e, 's> ScopeData<'e, 's> {
|
|||
sig_records
|
||||
}
|
||||
|
||||
pub fn collect_externs(&self, span: Span) -> Vec<Value> {
|
||||
let mut externals = vec![];
|
||||
for ((command_name, _), decl_id) in &self.commands_map {
|
||||
let decl = self.engine_state.get_decl(**decl_id);
|
||||
|
||||
if decl.is_known_external() {
|
||||
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,
|
||||
});
|
||||
|
||||
externals.push(Value::Record { cols, vals, span })
|
||||
}
|
||||
}
|
||||
|
||||
externals
|
||||
}
|
||||
|
||||
pub fn collect_aliases(&self, span: Span) -> Vec<Value> {
|
||||
let mut aliases = vec![];
|
||||
for (alias_name, alias_id) in &self.aliases_map {
|
||||
|
|
Loading…
Reference in a new issue