mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 09:25:38 +02:00
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:
@ -700,7 +700,7 @@ impl EngineState {
|
||||
pub fn get_signatures_with_examples(
|
||||
&self,
|
||||
include_hidden: bool,
|
||||
) -> Vec<(Signature, Vec<Example>, bool, bool)> {
|
||||
) -> Vec<(Signature, Vec<Example>, bool, bool, bool)> {
|
||||
self.get_decl_ids_sorted(include_hidden)
|
||||
.map(|id| {
|
||||
let decl = self.get_decl(id);
|
||||
@ -712,6 +712,7 @@ impl EngineState {
|
||||
decl.examples(),
|
||||
decl.is_plugin().is_some(),
|
||||
decl.get_block_id().is_some(),
|
||||
decl.is_parser_keyword(),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
|
@ -122,6 +122,72 @@ pub struct Signature {
|
||||
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 {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.name == other.name
|
||||
|
@ -107,24 +107,36 @@ impl Display for Type {
|
||||
Type::Float => write!(f, "float"),
|
||||
Type::Int => write!(f, "int"),
|
||||
Type::Range => write!(f, "range"),
|
||||
Type::Record(fields) => write!(
|
||||
f,
|
||||
"record<{}>",
|
||||
fields
|
||||
.iter()
|
||||
.map(|(x, y)| format!("{}: {}", x, y))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
),
|
||||
Type::Table(columns) => write!(
|
||||
f,
|
||||
"table<{}>",
|
||||
columns
|
||||
.iter()
|
||||
.map(|(x, y)| format!("{}: {}", x, y))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
),
|
||||
Type::Record(fields) => {
|
||||
if fields.is_empty() {
|
||||
write!(f, "record")
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"record<{}>",
|
||||
fields
|
||||
.iter()
|
||||
.map(|(x, y)| format!("{}: {}", x, y))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
)
|
||||
}
|
||||
}
|
||||
Type::Table(columns) => {
|
||||
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::Nothing => write!(f, "nothing"),
|
||||
Type::Number => write!(f, "number"),
|
||||
|
Reference in New Issue
Block a user