From 193c4cc6d562c5ef043a08f10683bf717ccc76ba Mon Sep 17 00:00:00 2001 From: Shaurya Date: Sun, 20 Sep 2020 02:07:47 +0530 Subject: [PATCH] Include subcommands in `help commands` (#2575) * Add minor fixes to comments * Include subcommands in `help commands` --- crates/nu-cli/src/commands/help.rs | 154 +++++++++++++++++++---------- crates/nu-cli/src/documentation.rs | 2 +- crates/nu-protocol/src/value.rs | 1 + 3 files changed, 104 insertions(+), 53 deletions(-) diff --git a/crates/nu-cli/src/commands/help.rs b/crates/nu-cli/src/commands/help.rs index 50cc09758f..3a12abe374 100644 --- a/crates/nu-cli/src/commands/help.rs +++ b/crates/nu-cli/src/commands/help.rs @@ -63,64 +63,114 @@ async fn help(args: CommandArgs, registry: &CommandRegistry) -> Result, _>(|cmd_name| cmd_name.contains(' ')); - // Internal only commands shouldn't be displayed - let command = match registry.get_command(&cmd_name) { - Some(c) => c, - None => return None, - }; - if command.is_internal() { - return None; - }; + fn process_name( + dict: &mut TaggedDictBuilder, + cmd_name: &str, + registry: CommandRegistry, + rest: Vec>, + name: Tag, + ) -> Result<(), ShellError> { + let document_tag = rest[0].tag.clone(); + let value = command_dict( + registry.get_command(&cmd_name).ok_or_else(|| { + ShellError::labeled_error( + format!("Could not load {}", cmd_name), + "could not load command", + document_tag, + ) + })?, + name, + ); - let mut short_desc = TaggedDictBuilder::new(name.clone()); - let document_tag = rest[0].tag.clone(); - let value = command_dict( - match registry.get_command(&cmd_name).ok_or_else(|| { + dict.insert_untagged("name", cmd_name); + dict.insert_untagged( + "description", + get_data_by_key(&value, "usage".spanned_unknown()) + .ok_or_else(|| { ShellError::labeled_error( - format!("Could not load {}", cmd_name), - "could not load command", - document_tag, + "Expected a usage key", + "expected a 'usage' key", + &value.tag, ) - }) { - Ok(ok) => ok, - Err(err) => return Some(Err(err)), - }, - name.clone(), - ); + })? + .as_string()?, + ); - short_desc.insert_untagged("name", cmd_name); - short_desc.insert_untagged( - "description", - match match get_data_by_key(&value, "usage".spanned_unknown()).ok_or_else( - || { - ShellError::labeled_error( - "Expected a usage key", - "expected a 'usage' key", - &value.tag, - ) - }, - ) { - Ok(ok) => ok, - Err(err) => return Some(Err(err)), - } - .as_string() - { - Ok(ok) => ok, - Err(err) => return Some(Err(err)), - }, - ); + //ReturnSuccess::value(dict.into_value()) + Ok(()) + } - Some(ReturnSuccess::value(short_desc.into_value())) - })) - .to_output_stream(), - ) + fn make_subcommands_table( + subcommand_names: &mut Vec, + cmd_name: &str, + registry: CommandRegistry, + rest: Vec>, + name: Tag, + ) -> Result { + let (matching, not_matching) = subcommand_names + .drain(..) + .partition(|subcommand_name| subcommand_name.starts_with(cmd_name)); + *subcommand_names = not_matching; + Ok(if !matching.is_empty() { + UntaggedValue::table( + &(matching + .into_iter() + .map(|cmd_name: String| -> Result<_, ShellError> { + let mut short_desc = TaggedDictBuilder::new(name.clone()); + process_name( + &mut short_desc, + &cmd_name, + registry.clone(), + rest.clone(), + name.clone(), + )?; + Ok(short_desc.into_value()) + }) + .collect::, _>>()?[..]), + ) + .into_value(name) + } else { + UntaggedValue::nothing().into_value(name) + }) + } + + let iterator = + command_names + .into_iter() + .map(move |cmd_name| -> Result<_, ShellError> { + let mut short_desc = TaggedDictBuilder::new(name.clone()); + process_name( + &mut short_desc, + &cmd_name, + registry.clone(), + rest.clone(), + name.clone(), + )?; + short_desc.insert_value( + "subcommands", + make_subcommands_table( + &mut subcommand_names, + &cmd_name, + registry.clone(), + rest.clone(), + name.clone(), + )?, + ); + ReturnSuccess::value(short_desc.into_value()) + }); + + Ok(futures::stream::iter(iterator).to_output_stream()) } else if rest[0].item == "generate_docs" { Ok(OutputStream::one(ReturnSuccess::value(generate_docs( ®istry, diff --git a/crates/nu-cli/src/documentation.rs b/crates/nu-cli/src/documentation.rs index 6ff1e79d12..e2b915b028 100644 --- a/crates/nu-cli/src/documentation.rs +++ b/crates/nu-cli/src/documentation.rs @@ -77,7 +77,7 @@ pub fn generate_docs(registry: &CommandRegistry) -> Value { }; } // Return documentation for each command - // Sub-commands are nested under there respective parent commands + // Sub-commands are nested under their respective parent commands let mut table = Vec::new(); for name in sorted_names.iter() { // Must be a sub-command, skip since it's being handled underneath when we hit the parent command diff --git a/crates/nu-protocol/src/value.rs b/crates/nu-protocol/src/value.rs index 9945de4066..314e7a32c1 100644 --- a/crates/nu-protocol/src/value.rs +++ b/crates/nu-protocol/src/value.rs @@ -83,6 +83,7 @@ impl UntaggedValue { matches!(self, UntaggedValue::Primitive(Primitive::Filesize(_))) } + /// Returns true if this value represents a duration pub fn is_duration(&self) -> bool { matches!(self, UntaggedValue::Primitive(Primitive::Duration(_))) }