just playing with tangled
at ig/vimdiffwarn 153 lines 5.0 kB view raw
1// Copyright 2024 The Jujutsu Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15use std::fmt::Write as _; 16use std::io::Write as _; 17 18use clap::builder::PossibleValue; 19use clap::builder::StyledStr; 20use crossterm::style::Stylize as _; 21use itertools::Itertools as _; 22use tracing::instrument; 23 24use crate::cli_util::CommandHelper; 25use crate::command_error; 26use crate::command_error::CommandError; 27use crate::ui::Ui; 28 29/// Print this message or the help of the given subcommand(s) 30#[derive(clap::Args, Clone, Debug)] 31pub(crate) struct HelpArgs { 32 /// Print help for the subcommand(s) 33 pub(crate) command: Vec<String>, 34 /// Show help for keywords instead of commands 35 #[arg( 36 long, 37 short = 'k', 38 conflicts_with = "command", 39 value_parser = KEYWORDS 40 .iter() 41 .map(|k| PossibleValue::new(k.name).help(k.description)) 42 .collect_vec() 43 )] 44 pub(crate) keyword: Option<String>, 45} 46 47#[instrument(skip_all)] 48pub(crate) fn cmd_help( 49 ui: &mut Ui, 50 command: &CommandHelper, 51 args: &HelpArgs, 52) -> Result<(), CommandError> { 53 if let Some(name) = &args.keyword { 54 let keyword = find_keyword(name).expect("clap should check this with `value_parser`"); 55 ui.request_pager(); 56 write!(ui.stdout(), "{}", keyword.content)?; 57 58 return Ok(()); 59 } 60 61 let bin_name = command 62 .string_args() 63 .first() 64 .map_or(command.app().get_name(), |name| name.as_ref()); 65 let mut args_to_show_help = vec![bin_name]; 66 args_to_show_help.extend(args.command.iter().map(|s| s.as_str())); 67 args_to_show_help.push("--help"); 68 69 // TODO: `help log -- -r` will give a cryptic error, ideally, it should state 70 // that the subcommand `log -r` doesn't exist. 71 let help_err = command 72 .app() 73 .clone() 74 .subcommand_required(true) 75 .try_get_matches_from(args_to_show_help) 76 .expect_err("Clap library should return a DisplayHelp error in this context"); 77 78 Err(command_error::cli_error(help_err)) 79} 80 81#[derive(Clone)] 82struct Keyword { 83 name: &'static str, 84 description: &'static str, 85 content: &'static str, 86} 87 88// TODO: Add all documentation to keywords 89// 90// Maybe adding some code to build.rs to find all the docs files and build the 91// `KEYWORDS` at compile time. 92// 93// It would be cool to follow the docs hierarchy somehow. 94// 95// One of the problems would be `config.md`, as it has the same name as a 96// subcommand. 97// 98// TODO: Find a way to render markdown using ANSI escape codes. 99// 100// Maybe we can steal some ideas from https://github.com/jj-vcs/jj/pull/3130 101const KEYWORDS: &[Keyword] = &[ 102 Keyword { 103 name: "bookmarks", 104 description: "Named pointers to revisions (similar to Git's branches)", 105 content: include_str!(concat!("../../", env!("JJ_DOCS_DIR"), "bookmarks.md")), 106 }, 107 Keyword { 108 name: "config", 109 description: "How and where to set configuration options", 110 content: include_str!(concat!("../../", env!("JJ_DOCS_DIR"), "config.md")), 111 }, 112 Keyword { 113 name: "filesets", 114 description: "A functional language for selecting a set of files", 115 content: include_str!(concat!("../../", env!("JJ_DOCS_DIR"), "filesets.md")), 116 }, 117 Keyword { 118 name: "glossary", 119 description: "Definitions of various terms", 120 content: include_str!(concat!("../../", env!("JJ_DOCS_DIR"), "glossary.md")), 121 }, 122 Keyword { 123 name: "revsets", 124 description: "A functional language for selecting a set of revision", 125 content: include_str!(concat!("../../", env!("JJ_DOCS_DIR"), "revsets.md")), 126 }, 127 Keyword { 128 name: "templates", 129 description: "A functional language to customize command output", 130 content: include_str!(concat!("../../", env!("JJ_DOCS_DIR"), "templates.md")), 131 }, 132 Keyword { 133 name: "tutorial", 134 description: "Show a tutorial to get started with jj", 135 content: include_str!(concat!("../../", env!("JJ_DOCS_DIR"), "tutorial.md")), 136 }, 137]; 138 139fn find_keyword(name: &str) -> Option<&Keyword> { 140 KEYWORDS.iter().find(|keyword| keyword.name == name) 141} 142 143pub fn show_keyword_hint_after_help() -> StyledStr { 144 let mut ret = StyledStr::new(); 145 writeln!( 146 ret, 147 "{} lists available keywords. Use {} to show help for one of these keywords.", 148 "'jj help --help'".bold(), 149 "'jj help -k'".bold(), 150 ) 151 .unwrap(); 152 ret 153}