forked from extern/nushell
306 lines
9.0 KiB
Rust
306 lines
9.0 KiB
Rust
use nu_protocol::{
|
|
ast::Call,
|
|
engine::{Command, EngineState, Stack},
|
|
span, Category, Example, IntoInterruptiblePipelineData, IntoPipelineData, PipelineData,
|
|
ShellError, Signature, Spanned, SyntaxShape, Value,
|
|
};
|
|
|
|
use nu_engine::{get_full_help, CallExt};
|
|
|
|
use std::borrow::Borrow;
|
|
|
|
#[derive(Clone)]
|
|
pub struct Help;
|
|
|
|
impl Command for Help {
|
|
fn name(&self) -> &str {
|
|
"help"
|
|
}
|
|
|
|
fn signature(&self) -> Signature {
|
|
Signature::build("help")
|
|
.rest(
|
|
"rest",
|
|
SyntaxShape::String,
|
|
"the name of command to get help on",
|
|
)
|
|
.named(
|
|
"find",
|
|
SyntaxShape::String,
|
|
"string to find in command names, usage, and search terms",
|
|
Some('f'),
|
|
)
|
|
.category(Category::Core)
|
|
}
|
|
|
|
fn usage(&self) -> &str {
|
|
"Display help information about commands."
|
|
}
|
|
|
|
fn run(
|
|
&self,
|
|
engine_state: &EngineState,
|
|
stack: &mut Stack,
|
|
call: &Call,
|
|
_input: PipelineData,
|
|
) -> Result<PipelineData, ShellError> {
|
|
help(engine_state, stack, call)
|
|
}
|
|
|
|
fn examples(&self) -> Vec<Example> {
|
|
vec![
|
|
Example {
|
|
description: "show all commands and sub-commands",
|
|
example: "help commands",
|
|
result: None,
|
|
},
|
|
Example {
|
|
description: "show help for single command",
|
|
example: "help match",
|
|
result: None,
|
|
},
|
|
Example {
|
|
description: "show help for single sub-command",
|
|
example: "help str lpad",
|
|
result: None,
|
|
},
|
|
Example {
|
|
description: "search for string in command names, usage and search terms",
|
|
example: "help --find char",
|
|
result: None,
|
|
},
|
|
]
|
|
}
|
|
}
|
|
|
|
fn help(
|
|
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)?;
|
|
|
|
let commands = engine_state.get_decl_ids_sorted(false);
|
|
|
|
if let Some(f) = find {
|
|
let search_string = f.item.to_lowercase();
|
|
let mut found_cmds_vec = Vec::new();
|
|
|
|
for decl_id in commands {
|
|
let mut cols = vec![];
|
|
let mut vals = vec![];
|
|
|
|
let decl = engine_state.get_decl(decl_id);
|
|
let sig = decl.signature().update_from_command(decl.borrow());
|
|
|
|
let key = sig.name;
|
|
let usage = sig.usage;
|
|
let search_terms = sig.search_terms;
|
|
let matches_term = if !search_terms.is_empty() {
|
|
search_terms
|
|
.iter()
|
|
.any(|term| term.to_lowercase().contains(&search_string))
|
|
} else {
|
|
false
|
|
};
|
|
|
|
if key.to_lowercase().contains(&search_string)
|
|
|| usage.to_lowercase().contains(&search_string)
|
|
|| matches_term
|
|
{
|
|
cols.push("name".into());
|
|
vals.push(Value::String {
|
|
val: key,
|
|
span: head,
|
|
});
|
|
|
|
cols.push("category".into());
|
|
vals.push(Value::String {
|
|
val: sig.category.to_string(),
|
|
span: head,
|
|
});
|
|
|
|
cols.push("is_plugin".into());
|
|
vals.push(Value::Bool {
|
|
val: decl.is_plugin().is_some(),
|
|
span: head,
|
|
});
|
|
|
|
cols.push("is_custom".into());
|
|
vals.push(Value::Bool {
|
|
val: decl.get_block_id().is_some(),
|
|
span: head,
|
|
});
|
|
|
|
cols.push("is_keyword".into());
|
|
vals.push(Value::Bool {
|
|
val: decl.is_parser_keyword(),
|
|
span: head,
|
|
});
|
|
|
|
cols.push("usage".into());
|
|
vals.push(Value::String {
|
|
val: usage,
|
|
span: head,
|
|
});
|
|
|
|
cols.push("search_terms".into());
|
|
vals.push(if search_terms.is_empty() {
|
|
Value::nothing(head)
|
|
} else {
|
|
Value::String {
|
|
val: search_terms.join(", "),
|
|
span: head,
|
|
}
|
|
});
|
|
|
|
found_cmds_vec.push(Value::Record {
|
|
cols,
|
|
vals,
|
|
span: head,
|
|
});
|
|
}
|
|
}
|
|
|
|
return Ok(found_cmds_vec
|
|
.into_iter()
|
|
.into_pipeline_data(engine_state.ctrlc.clone()));
|
|
}
|
|
|
|
if !rest.is_empty() {
|
|
let mut found_cmds_vec = Vec::new();
|
|
|
|
if rest[0].item == "commands" {
|
|
for decl_id in commands {
|
|
let mut cols = vec![];
|
|
let mut vals = vec![];
|
|
|
|
let decl = engine_state.get_decl(decl_id);
|
|
let sig = decl.signature().update_from_command(decl.borrow());
|
|
|
|
let key = sig.name;
|
|
let usage = sig.usage;
|
|
let search_terms = sig.search_terms;
|
|
|
|
cols.push("name".into());
|
|
vals.push(Value::String {
|
|
val: key,
|
|
span: head,
|
|
});
|
|
|
|
cols.push("category".into());
|
|
vals.push(Value::String {
|
|
val: sig.category.to_string(),
|
|
span: head,
|
|
});
|
|
|
|
cols.push("is_plugin".into());
|
|
vals.push(Value::Bool {
|
|
val: decl.is_plugin().is_some(),
|
|
span: head,
|
|
});
|
|
|
|
cols.push("is_custom".into());
|
|
vals.push(Value::Bool {
|
|
val: decl.get_block_id().is_some(),
|
|
span: head,
|
|
});
|
|
|
|
cols.push("is_keyword".into());
|
|
vals.push(Value::Bool {
|
|
val: decl.is_parser_keyword(),
|
|
span: head,
|
|
});
|
|
|
|
cols.push("usage".into());
|
|
vals.push(Value::String {
|
|
val: usage,
|
|
span: head,
|
|
});
|
|
|
|
cols.push("search_terms".into());
|
|
vals.push(if search_terms.is_empty() {
|
|
Value::nothing(head)
|
|
} else {
|
|
Value::String {
|
|
val: search_terms.join(", "),
|
|
span: head,
|
|
}
|
|
});
|
|
|
|
found_cmds_vec.push(Value::Record {
|
|
cols,
|
|
vals,
|
|
span: 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, _, _)| {
|
|
get_full_help(signature, examples, engine_state, stack)
|
|
})
|
|
.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,
|
|
])))
|
|
}
|
|
}
|
|
} else {
|
|
let msg = r#"Welcome to Nushell.
|
|
|
|
Here are some tips to help you get started.
|
|
* help commands - list all available commands
|
|
* help <command name> - display help about a particular command
|
|
* help --find <text to search> - search through all of help
|
|
|
|
Nushell works on the idea of a "pipeline". Pipelines are commands connected with the '|' character.
|
|
Each stage in the pipeline works together to load, parse, and display information to you.
|
|
|
|
[Examples]
|
|
|
|
List the files in the current directory, sorted by size:
|
|
ls | sort-by size
|
|
|
|
Get information about the current system:
|
|
sys | get host
|
|
|
|
Get the processes on your system actively using CPU:
|
|
ps | where cpu > 0
|
|
|
|
You can also learn more at https://www.nushell.sh/book/"#;
|
|
|
|
Ok(Value::String {
|
|
val: msg.into(),
|
|
span: head,
|
|
}
|
|
.into_pipeline_data())
|
|
}
|
|
}
|