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:
WindSoilder
2022-11-20 21:22:42 +08:00
committed by GitHub
parent a896892ac9
commit d01ccd5a54
29 changed files with 258 additions and 55 deletions

View File

@ -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()

View File

@ -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

View File

@ -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"),