From 7024fe6c7de099a925085f6e38d49dc81c8c200a Mon Sep 17 00:00:00 2001 From: Arthur Grillo Date: Sat, 12 Oct 2024 11:12:38 -0300 Subject: [PATCH] help: Add a category feature It would be nice to not need to go the documentation website. This aims to solve that by introducing the concept of categories to the help command. Basically, categories are things that we want to add help messages to, but they don't necessarily have an associated subcommand. For now we only have two categories: - `revsets`: Shows the docs for revsets - `tutorial`: Shows the tutorial that is on the documentation You get the category content by tipping `jj help revsets`. It would be nice to have all the documentation on the categories, maybe a next commit could do it. --- CHANGELOG.md | 2 + cli/src/commands/help.rs | 83 ++++++++++++++++++++++++++++++++++ cli/tests/test_help_command.rs | 14 ++++-- 3 files changed, 94 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b5a2dc8b5c..b67636a3df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). * `jj squash` now supports `-f/-t` shorthands for `--from/--[in]to`. +* `jj help` now can give help for some categories, like `jj help revsets`. + ### Fixed bugs * Error on `trunk()` revset resolution is now handled gracefully. diff --git a/cli/src/commands/help.rs b/cli/src/commands/help.rs index 364cbe7fca..1400472275 100644 --- a/cli/src/commands/help.rs +++ b/cli/src/commands/help.rs @@ -12,6 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::collections::HashMap; +use std::fmt::Write; + +use clap::builder::StyledStr; +use itertools::Itertools; use tracing::instrument; use crate::cli_util::CommandHelper; @@ -26,21 +31,99 @@ pub(crate) struct HelpArgs { pub(crate) command: Vec, } +struct Category { + description: &'static str, + content: &'static str, +} + #[instrument(skip_all)] pub(crate) fn cmd_help( _ui: &mut Ui, command: &CommandHelper, args: &HelpArgs, ) -> Result<(), CommandError> { + // TODO: Add all documentation to categories + // + // Maybe adding some code to build.rs to find all the docs files and build the + // HashMap at compile time. + // + // It would be cool to follow the docs hierarchy somehow. + // + // One of the problems would be `config.md`, as it has the same name as a + // subcommand. + let categories = HashMap::from([ + ( + "revsets", + Category { + description: "Documentation about revsets", + content: include_str!("../../../docs/revsets.md"), + }, + ), + ( + "tutorial", + Category { + description: "Show a tutorial to get started with jj", + content: include_str!("../../../docs/tutorial.md"), + }, + ), + ]); + + if let Some(category) = categories.get(args.command.join(" ").as_str()) { + let mut help_str = StyledStr::new(); + let _ = help_str.write_str(category.content); + + let help_err = clap::Command::new("stub") + .override_help(help_str) + .try_get_matches_from(vec!["stub", "--help"]) + .unwrap_err(); + + return Err(command_error::cli_error(help_err)); + } + let mut args_to_show_help = vec![command.app().get_name()]; args_to_show_help.extend(args.command.iter().map(|s| s.as_str())); args_to_show_help.push("--help"); + let help_template_before_categories = "\ + {about-with-newline}\n{usage-heading} {usage}\n\n\x1b[1m\x1b[4mCommands:\x1b[0m\n{subcommands} + "; + + let subcommand_max_len = command + .app() + .get_subcommands() + .map(|cmd| cmd.get_name().len()) + .max() + .unwrap(); + + let help_template_categories = format!( + "\x1b[1m\x1b[4mCategories:\x1b[0m\n{}", + &categories + .iter() + .map(|(&name, category)| format!( + "{{tab}}\x1b[1m{: