forked from extern/nushell
add signature information when get help on one command (#7079)
* add signature information when help on one command * tell user that one command support operated on cell paths Also, make type output to be more friendly, like `record<>` should just be `record` And the same to `table<>`, which should be `table` * simplify code * don't show signatures for parser keyword * update comment * output arg syntax shape as type, so it's the same as describe command * fix string when no positional args * update signature body * update * add help signature test * fix arg output format for composed data type like list or record * fix clippy * add comment
This commit is contained in:
parent
a896892ac9
commit
d01ccd5a54
@ -17,7 +17,7 @@ impl NuHelpCompleter {
|
|||||||
//Vec<(Signature, Vec<Example>, bool, bool)> {
|
//Vec<(Signature, Vec<Example>, bool, bool)> {
|
||||||
let mut commands = full_commands
|
let mut commands = full_commands
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(sig, _, _, _)| {
|
.filter(|(sig, _, _, _, _)| {
|
||||||
sig.name.to_lowercase().contains(&line.to_lowercase())
|
sig.name.to_lowercase().contains(&line.to_lowercase())
|
||||||
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
|| sig.usage.to_lowercase().contains(&line.to_lowercase())
|
||||||
|| sig
|
|| sig
|
||||||
@ -31,7 +31,7 @@ impl NuHelpCompleter {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
commands.sort_by(|(a, _, _, _), (b, _, _, _)| {
|
commands.sort_by(|(a, _, _, _, _), (b, _, _, _, _)| {
|
||||||
let a_distance = levenshtein_distance(line, &a.name);
|
let a_distance = levenshtein_distance(line, &a.name);
|
||||||
let b_distance = levenshtein_distance(line, &b.name);
|
let b_distance = levenshtein_distance(line, &b.name);
|
||||||
a_distance.cmp(&b_distance)
|
a_distance.cmp(&b_distance)
|
||||||
@ -39,7 +39,7 @@ impl NuHelpCompleter {
|
|||||||
|
|
||||||
commands
|
commands
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(sig, examples, _, _)| {
|
.map(|(sig, examples, _, _, _)| {
|
||||||
let mut long_desc = String::new();
|
let mut long_desc = String::new();
|
||||||
|
|
||||||
let usage = &sig.usage;
|
let usage = &sig.usage;
|
||||||
|
@ -29,7 +29,13 @@ impl Command for Bits {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Bits.signature(), &Bits.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Bits.signature(),
|
||||||
|
&Bits.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -29,7 +29,13 @@ impl Command for Bytes {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Bytes.signature(), &Bytes.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Bytes.signature(),
|
||||||
|
&Bytes.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -29,7 +29,13 @@ impl Command for Into {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Into.signature(), &[], engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Into.signature(),
|
||||||
|
&[],
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -45,6 +45,7 @@ impl Command for ExportCommand {
|
|||||||
&ExportCommand.examples(),
|
&ExportCommand.examples(),
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
),
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use fancy_regex::Regex;
|
use fancy_regex::Regex;
|
||||||
use itertools::Itertools;
|
|
||||||
use nu_ansi_term::{
|
use nu_ansi_term::{
|
||||||
Color::{Default, Red, White},
|
Color::{Default, Red, White},
|
||||||
Style,
|
Style,
|
||||||
@ -105,6 +104,7 @@ fn help(
|
|||||||
let mut vals = vec![];
|
let mut vals = vec![];
|
||||||
let decl = engine_state.get_decl(decl_id);
|
let decl = engine_state.get_decl(decl_id);
|
||||||
let sig = decl.signature().update_from_command(decl.borrow());
|
let sig = decl.signature().update_from_command(decl.borrow());
|
||||||
|
let signatures = sig.to_string();
|
||||||
let key = sig.name;
|
let key = sig.name;
|
||||||
let usage = sig.usage;
|
let usage = sig.usage;
|
||||||
let search_terms = sig.search_terms;
|
let search_terms = sig.search_terms;
|
||||||
@ -154,11 +154,11 @@ fn help(
|
|||||||
|
|
||||||
cols.push("signatures".into());
|
cols.push("signatures".into());
|
||||||
vals.push(Value::String {
|
vals.push(Value::String {
|
||||||
val: sig
|
val: if decl.is_parser_keyword() {
|
||||||
.input_output_types
|
"".to_string()
|
||||||
.iter()
|
} else {
|
||||||
.map(|(i, o)| format!("{:?} => {:?}", i.to_shape(), o.to_shape()))
|
signatures
|
||||||
.join("\n"),
|
},
|
||||||
span: head,
|
span: head,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -219,6 +219,7 @@ fn help(
|
|||||||
let decl = engine_state.get_decl(decl_id);
|
let decl = engine_state.get_decl(decl_id);
|
||||||
let sig = decl.signature().update_from_command(decl.borrow());
|
let sig = decl.signature().update_from_command(decl.borrow());
|
||||||
|
|
||||||
|
let signatures = sig.to_string();
|
||||||
let key = sig.name;
|
let key = sig.name;
|
||||||
let usage = sig.usage;
|
let usage = sig.usage;
|
||||||
let search_terms = sig.search_terms;
|
let search_terms = sig.search_terms;
|
||||||
@ -249,11 +250,11 @@ fn help(
|
|||||||
|
|
||||||
cols.push("signatures".into());
|
cols.push("signatures".into());
|
||||||
vals.push(Value::String {
|
vals.push(Value::String {
|
||||||
val: sig
|
val: if decl.is_parser_keyword() {
|
||||||
.input_output_types
|
"".to_string()
|
||||||
.iter()
|
} else {
|
||||||
.map(|(i, o)| format!("{:?} => {:?}", i.to_shape(), o.to_shape()))
|
signatures
|
||||||
.join("\n"),
|
},
|
||||||
span: head,
|
span: head,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -290,9 +291,9 @@ fn help(
|
|||||||
let output = engine_state
|
let output = engine_state
|
||||||
.get_signatures_with_examples(false)
|
.get_signatures_with_examples(false)
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|(signature, _, _, _)| signature.name == name)
|
.filter(|(signature, _, _, _, _)| signature.name == name)
|
||||||
.map(|(signature, examples, _, _)| {
|
.map(|(signature, examples, _, _, is_parser_keyword)| {
|
||||||
get_full_help(signature, examples, engine_state, stack)
|
get_full_help(signature, examples, engine_state, stack, *is_parser_keyword)
|
||||||
})
|
})
|
||||||
.collect::<Vec<String>>();
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
@ -38,7 +38,13 @@ impl Command for Overlay {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Overlay.signature(), &[], engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Overlay.signature(),
|
||||||
|
&[],
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -53,7 +53,13 @@ fn date(
|
|||||||
let head = call.head;
|
let head = call.head;
|
||||||
|
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Date.signature(), &Date.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Date.signature(),
|
||||||
|
&Date.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
false,
|
||||||
|
),
|
||||||
span: head,
|
span: head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
1
crates/nu-command/src/env/config/config_.rs
vendored
1
crates/nu-command/src/env/config/config_.rs
vendored
@ -34,6 +34,7 @@ impl Command for ConfigMeta {
|
|||||||
&ConfigMeta.examples(),
|
&ConfigMeta.examples(),
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
),
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,13 @@ impl Command for Roll {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Roll.signature(), &Roll.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Roll.signature(),
|
||||||
|
&Roll.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -27,7 +27,13 @@ impl Command for From {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&From.signature(), &From.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&From.signature(),
|
||||||
|
&From.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -27,7 +27,13 @@ impl Command for To {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, ShellError> {
|
) -> Result<nu_protocol::PipelineData, ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&To.signature(), &To.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&To.signature(),
|
||||||
|
&To.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -27,7 +27,13 @@ impl Command for Hash {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<PipelineData, ShellError> {
|
) -> Result<PipelineData, ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Self.signature(), &Self.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Self.signature(),
|
||||||
|
&Self.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -34,6 +34,7 @@ impl Command for MathCommand {
|
|||||||
&MathCommand.examples(),
|
&MathCommand.examples(),
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
),
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,13 @@ impl Command for Url {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Url.signature(), &Url.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Url.signature(),
|
||||||
|
&Url.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -49,6 +49,7 @@ the path literal."#
|
|||||||
&PathCommand.examples(),
|
&PathCommand.examples(),
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
),
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ impl Command for Keybindings {
|
|||||||
&Keybindings.examples(),
|
&Keybindings.examples(),
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
),
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ impl Command for RandomCommand {
|
|||||||
&RandomCommand.examples(),
|
&RandomCommand.examples(),
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
),
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
|
@ -34,6 +34,7 @@ impl Command for SplitCommand {
|
|||||||
&SplitCommand.examples(),
|
&SplitCommand.examples(),
|
||||||
engine_state,
|
engine_state,
|
||||||
stack,
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
),
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,13 @@ impl Command for Str {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Str.signature(), &Str.examples(), engine_state, stack),
|
val: get_full_help(
|
||||||
|
&Str.signature(),
|
||||||
|
&Str.examples(),
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
self.is_parser_keyword(),
|
||||||
|
),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -14,3 +14,15 @@ fn help_commands_length() {
|
|||||||
let is_positive = output_int.is_positive();
|
let is_positive = output_int.is_positive();
|
||||||
assert!(is_positive);
|
assert!(is_positive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn help_shows_signature() {
|
||||||
|
let actual = nu!(cwd: ".", pipeline("help str distance"));
|
||||||
|
assert!(actual
|
||||||
|
.out
|
||||||
|
.contains("<string> | str distance <string> -> <int>"));
|
||||||
|
|
||||||
|
// don't show signature for parser keyword
|
||||||
|
let actual = nu!(cwd: ".", pipeline("help alias"));
|
||||||
|
assert!(!actual.out.contains("Signatures"));
|
||||||
|
}
|
||||||
|
@ -92,7 +92,7 @@ fn reject_record_from_raw_eval() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
assert!(actual.out.contains("record<>"));
|
assert!(actual.out.contains("record"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -10,6 +10,7 @@ pub fn get_full_help(
|
|||||||
examples: &[Example],
|
examples: &[Example],
|
||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
|
is_parser_keyword: bool,
|
||||||
) -> String {
|
) -> String {
|
||||||
let config = engine_state.get_config();
|
let config = engine_state.get_config();
|
||||||
let doc_config = DocumentationConfig {
|
let doc_config = DocumentationConfig {
|
||||||
@ -17,7 +18,14 @@ pub fn get_full_help(
|
|||||||
no_color: !config.use_ansi_coloring,
|
no_color: !config.use_ansi_coloring,
|
||||||
brief: false,
|
brief: false,
|
||||||
};
|
};
|
||||||
get_documentation(sig, examples, engine_state, stack, &doc_config)
|
get_documentation(
|
||||||
|
sig,
|
||||||
|
examples,
|
||||||
|
engine_state,
|
||||||
|
stack,
|
||||||
|
&doc_config,
|
||||||
|
is_parser_keyword,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
@ -34,6 +42,7 @@ fn get_documentation(
|
|||||||
engine_state: &EngineState,
|
engine_state: &EngineState,
|
||||||
stack: &mut Stack,
|
stack: &mut Stack,
|
||||||
config: &DocumentationConfig,
|
config: &DocumentationConfig,
|
||||||
|
is_parser_keyword: bool,
|
||||||
) -> String {
|
) -> String {
|
||||||
// Create ansi colors
|
// Create ansi colors
|
||||||
const G: &str = "\x1b[32m"; // green
|
const G: &str = "\x1b[32m"; // green
|
||||||
@ -89,6 +98,18 @@ fn get_documentation(
|
|||||||
long_desc.push_str(&get_flags_section(sig))
|
long_desc.push_str(&get_flags_section(sig))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !is_parser_keyword && !sig.input_output_types.is_empty() {
|
||||||
|
if sig.operates_on_cell_paths() {
|
||||||
|
let _ = writeln!(
|
||||||
|
long_desc,
|
||||||
|
"\n{}Signatures(Cell paths are supported){}:\n{}",
|
||||||
|
G, RESET, sig
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let _ = writeln!(long_desc, "\n{}Signatures{}:\n{}", G, RESET, sig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !sig.required_positional.is_empty()
|
if !sig.required_positional.is_empty()
|
||||||
|| !sig.optional_positional.is_empty()
|
|| !sig.optional_positional.is_empty()
|
||||||
|| sig.rest_positional.is_some()
|
|| sig.rest_positional.is_some()
|
||||||
|
@ -43,7 +43,13 @@ pub fn eval_call(
|
|||||||
signature.usage = decl.usage().to_string();
|
signature.usage = decl.usage().to_string();
|
||||||
signature.extra_usage = decl.extra_usage().to_string();
|
signature.extra_usage = decl.extra_usage().to_string();
|
||||||
|
|
||||||
let full_help = get_full_help(&signature, &decl.examples(), engine_state, caller_stack);
|
let full_help = get_full_help(
|
||||||
|
&signature,
|
||||||
|
&decl.examples(),
|
||||||
|
engine_state,
|
||||||
|
caller_stack,
|
||||||
|
decl.is_parser_keyword(),
|
||||||
|
);
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: full_help,
|
val: full_help,
|
||||||
span: call.head,
|
span: call.head,
|
||||||
|
@ -700,7 +700,7 @@ impl EngineState {
|
|||||||
pub fn get_signatures_with_examples(
|
pub fn get_signatures_with_examples(
|
||||||
&self,
|
&self,
|
||||||
include_hidden: bool,
|
include_hidden: bool,
|
||||||
) -> Vec<(Signature, Vec<Example>, bool, bool)> {
|
) -> Vec<(Signature, Vec<Example>, bool, bool, bool)> {
|
||||||
self.get_decl_ids_sorted(include_hidden)
|
self.get_decl_ids_sorted(include_hidden)
|
||||||
.map(|id| {
|
.map(|id| {
|
||||||
let decl = self.get_decl(id);
|
let decl = self.get_decl(id);
|
||||||
@ -712,6 +712,7 @@ impl EngineState {
|
|||||||
decl.examples(),
|
decl.examples(),
|
||||||
decl.is_plugin().is_some(),
|
decl.is_plugin().is_some(),
|
||||||
decl.get_block_id().is_some(),
|
decl.get_block_id().is_some(),
|
||||||
|
decl.is_parser_keyword(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -122,6 +122,72 @@ pub struct Signature {
|
|||||||
pub category: Category,
|
pub category: Category,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fromat argumet type for user readable output.
|
||||||
|
///
|
||||||
|
/// In general:
|
||||||
|
/// if argument type is a simple type(like string), we'll wrapped with `<>`, the result will be `<string>`
|
||||||
|
/// if argument type is already contains `<>`, like `list<any>`, the result will be `list<any>`.
|
||||||
|
fn fmt_type(arg_type: &Type, optional: bool) -> String {
|
||||||
|
let arg_type = arg_type.to_string();
|
||||||
|
if arg_type.contains('<') && arg_type.contains('>') {
|
||||||
|
if optional {
|
||||||
|
format!("{arg_type}?")
|
||||||
|
} else {
|
||||||
|
arg_type
|
||||||
|
}
|
||||||
|
} else if optional {
|
||||||
|
format!("<{arg_type}?>")
|
||||||
|
} else {
|
||||||
|
format!("<{arg_type}>")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// in general, a commands signature should looks like this:
|
||||||
|
//
|
||||||
|
// <string> | <string>, <int?> => string
|
||||||
|
//
|
||||||
|
// More detail explaination:
|
||||||
|
// the first one is the input from previous command, aka, pipeline input
|
||||||
|
// then followed by `|`, then positional arguments type
|
||||||
|
// then optional arguments type, which ends with `?`
|
||||||
|
// Then followed by `->`
|
||||||
|
// Finally output type.
|
||||||
|
//
|
||||||
|
// If a command contains multiple input/output types, separate them in different lines.
|
||||||
|
impl std::fmt::Display for Signature {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut args = self
|
||||||
|
.required_positional
|
||||||
|
.iter()
|
||||||
|
.map(|p| fmt_type(&p.shape.to_type(), false))
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
args.append(
|
||||||
|
&mut self
|
||||||
|
.optional_positional
|
||||||
|
.iter()
|
||||||
|
.map(|p| fmt_type(&p.shape.to_type(), true))
|
||||||
|
.collect::<Vec<String>>(),
|
||||||
|
);
|
||||||
|
let args = args.join(", ");
|
||||||
|
|
||||||
|
let mut signatures = vec![];
|
||||||
|
for (input_type, output_type) in self.input_output_types.iter() {
|
||||||
|
// ident with two spaces for user friendly output.
|
||||||
|
let input_type = fmt_type(input_type, false);
|
||||||
|
let output_type = fmt_type(output_type, false);
|
||||||
|
if args.is_empty() {
|
||||||
|
signatures.push(format!(" {input_type} | {} -> {output_type}", self.name))
|
||||||
|
} else {
|
||||||
|
signatures.push(format!(
|
||||||
|
" {input_type} | {} {args} -> {output_type}",
|
||||||
|
self.name
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
write!(f, "{}", signatures.join("\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl PartialEq for Signature {
|
impl PartialEq for Signature {
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.name == other.name
|
self.name == other.name
|
||||||
|
@ -107,24 +107,36 @@ impl Display for Type {
|
|||||||
Type::Float => write!(f, "float"),
|
Type::Float => write!(f, "float"),
|
||||||
Type::Int => write!(f, "int"),
|
Type::Int => write!(f, "int"),
|
||||||
Type::Range => write!(f, "range"),
|
Type::Range => write!(f, "range"),
|
||||||
Type::Record(fields) => write!(
|
Type::Record(fields) => {
|
||||||
f,
|
if fields.is_empty() {
|
||||||
"record<{}>",
|
write!(f, "record")
|
||||||
fields
|
} else {
|
||||||
.iter()
|
write!(
|
||||||
.map(|(x, y)| format!("{}: {}", x, y))
|
f,
|
||||||
.collect::<Vec<String>>()
|
"record<{}>",
|
||||||
.join(", "),
|
fields
|
||||||
),
|
.iter()
|
||||||
Type::Table(columns) => write!(
|
.map(|(x, y)| format!("{}: {}", x, y))
|
||||||
f,
|
.collect::<Vec<String>>()
|
||||||
"table<{}>",
|
.join(", "),
|
||||||
columns
|
)
|
||||||
.iter()
|
}
|
||||||
.map(|(x, y)| format!("{}: {}", x, y))
|
}
|
||||||
.collect::<Vec<String>>()
|
Type::Table(columns) => {
|
||||||
.join(", ")
|
if columns.is_empty() {
|
||||||
),
|
write!(f, "table")
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"table<{}>",
|
||||||
|
columns
|
||||||
|
.iter()
|
||||||
|
.map(|(x, y)| format!("{}: {}", x, y))
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Type::List(l) => write!(f, "list<{}>", l),
|
Type::List(l) => write!(f, "list<{}>", l),
|
||||||
Type::Nothing => write!(f, "nothing"),
|
Type::Nothing => write!(f, "nothing"),
|
||||||
Type::Number => write!(f, "number"),
|
Type::Number => write!(f, "number"),
|
||||||
|
19
src/main.rs
19
src/main.rs
@ -563,8 +563,13 @@ fn parse_commandline_args(
|
|||||||
let help = call.has_flag("help");
|
let help = call.has_flag("help");
|
||||||
|
|
||||||
if help {
|
if help {
|
||||||
let full_help =
|
let full_help = get_full_help(
|
||||||
get_full_help(&Nu.signature(), &Nu.examples(), engine_state, &mut stack);
|
&Nu.signature(),
|
||||||
|
&Nu.examples(),
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
let _ = std::panic::catch_unwind(move || stdout_write_all_and_flush(full_help));
|
let _ = std::panic::catch_unwind(move || stdout_write_all_and_flush(full_help));
|
||||||
|
|
||||||
@ -600,7 +605,13 @@ fn parse_commandline_args(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Just give the help and exit if the above fails
|
// Just give the help and exit if the above fails
|
||||||
let full_help = get_full_help(&Nu.signature(), &Nu.examples(), engine_state, &mut stack);
|
let full_help = get_full_help(
|
||||||
|
&Nu.signature(),
|
||||||
|
&Nu.examples(),
|
||||||
|
engine_state,
|
||||||
|
&mut stack,
|
||||||
|
true,
|
||||||
|
);
|
||||||
print!("{}", full_help);
|
print!("{}", full_help);
|
||||||
std::process::exit(1);
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
@ -731,7 +742,7 @@ impl Command for Nu {
|
|||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
Ok(Value::String {
|
Ok(Value::String {
|
||||||
val: get_full_help(&Nu.signature(), &Nu.examples(), engine_state, stack),
|
val: get_full_help(&Nu.signature(), &Nu.examples(), engine_state, stack, true),
|
||||||
span: call.head,
|
span: call.head,
|
||||||
}
|
}
|
||||||
.into_pipeline_data())
|
.into_pipeline_data())
|
||||||
|
@ -53,7 +53,7 @@ fn in_and_if_else() -> TestResult {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn help_works_with_missing_requirements() -> TestResult {
|
fn help_works_with_missing_requirements() -> TestResult {
|
||||||
run_test(r#"each --help | lines | length"#, "37")
|
run_test(r#"each --help | lines | length"#, "40")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
Loading…
Reference in New Issue
Block a user