diff --git a/src/help.rs b/src/help.rs index e62c028..82ed7bd 100644 --- a/src/help.rs +++ b/src/help.rs @@ -4,17 +4,17 @@ //! no way to split it: `help_heading` is an argument setting, and //! `subcommand_help_heading` renames the single section rather than adding a //! second one. A dozen verbs in one alphabetical run read as a dozen -//! unrelated things, when there are really three kinds of them — what atgc is -//! and who you are, what it does to a repo, and the odds and ends. +//! unrelated things, when a reader arrives with a task and wants the two or +//! three lines it could be in. //! //! So the section is rendered here instead: the same two-space, one-column -//! shape clap would have printed, split under three headings, handed to the -//! root command as its `before_help`, and put back where `Commands:` was by -//! a help template that drops clap's own list. Only the grouping is written -//! down — every name, its column and its line of help still come out of the -//! command tree, so a command whose `about` changes says the new thing here -//! without anyone touching this file, and the one thing that can go stale -//! (which group a command is in) is what this module's tests hold. +//! shape clap would have printed, split under headings, handed to the root +//! command as its `before_help`, and put back where `Commands:` was by a help +//! template that drops clap's own list. Only the grouping is written down — +//! every name, its column and its line of help still come out of the command +//! tree, so a command whose `about` changes says the new thing here without +//! anyone touching this file, and the one thing that can go stale (which +//! group a command is in) is what this module's tests hold. use clap::builder::{StyledStr, Styles}; use clap::{Command, CommandFactory, FromArgMatches}; @@ -23,27 +23,36 @@ use std::fmt::Write as _; /// The top-level commands, in the groups and the order `--help` shows them. /// /// Every visible subcommand belongs to exactly one group, `help` included — -/// it is clap's own and is listed with the rest of the odds and ends rather -/// than left to trail the list unexplained. The order inside a group is not -/// alphabetical: it is the order somebody meets the commands in, so `repo` -/// comes before the pull requests that are opened against it. +/// it is clap's own and is listed rather than left to trail the list +/// unexplained. The order inside a group is not alphabetical: it is the order +/// somebody meets the commands in, so `repo` comes before the pull requests +/// that are opened against it. /// -/// `status` is in the first group and not the second, which is the whole -/// point of it existing: the second group is what atgc does *to the repo you -/// are in*, and `status` is the family that is deliberately not scoped to -/// one. It sits after `doctor` because they are the two commands that report -/// rather than act, and because the questions run in that order — whether -/// this machine is set up, then what is going on. +/// The cut is by subject, because a reader arrives with a task rather than +/// with a taxonomy: what atgc is, who you are, what you are doing to a repo, +/// and what to do when it does not work. That is also why `report` is in the +/// troubleshooting group despite being the only command there that writes +/// anything — it is where a `doctor` row and a `logs` line end up, and +/// somebody looking for it is already in that group. +/// +/// `Other` holds the two commands no subject fits, and they resist for +/// opposite reasons. `completion` is run once, at install, and never again; +/// `api` is authenticated access to the whole lexicon and is the most +/// powerful thing here. What they share is only that no heading above is +/// true of them, which is what the word means. Forcing either into a group +/// that nearly fits would cost a reader more than the honest label does. const GROUPS: &[(&str, &[&str])] = &[ - ( - "Meta and identity", - &["about", "agent", "auth", "key", "doctor"], - ), + ("Meta", &["about", "agent", "help"]), + ("Account management", &["auth", "key"]), ( "Repository operations", &["repo", "browse", "search", "issue", "pr", "stack"], ), - ("Other", &["api", "logs", "completion", "report", "help"]), + ( + "Checking and troubleshooting", + &["doctor", "logs", "report"], + ), + ("Other", &["api", "completion"]), ]; /// Parse argv, with the grouped list standing in for clap's `Commands:`. @@ -179,14 +188,18 @@ mod tests { let rendered = command_list(&cmd).ansi().to_string(); // Styling is on in this rendering; the assertions read the text // around the escapes rather than trying to reproduce them. - assert!(rendered.contains("Meta and identity:")); - assert!(rendered.contains("Repository operations:")); - assert!(rendered.contains("Other:")); - assert!( - rendered.find("Meta and identity:") < rendered.find("Repository operations:") - && rendered.find("Repository operations:") < rendered.find("Other:"), - "the groups are out of order:\n{rendered}" - ); + // + // Every heading, in the order the const declares them: a group that + // renders in the wrong place reads as a different taxonomy, and the + // headings are the whole of what this module adds. + let mut at = 0; + for (heading, _) in GROUPS { + let found = rendered + .find(&format!("{heading}:")) + .unwrap_or_else(|| panic!("no `{heading}:` heading in:\n{rendered}")); + assert!(found >= at, "`{heading}` is out of order:\n{rendered}"); + at = found; + } for name in GROUPS.iter().flat_map(|(_, names)| *names) { assert!(rendered.contains(name), "`{name}` is not in the list"); } diff --git a/src/main.rs b/src/main.rs index 77434ee..40b4ee5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -157,7 +157,7 @@ enum Command { /// atgc search --all typo --author permadeath.com --json #[command(verbatim_doc_comment)] Search(cmd::search::SearchArgs), - /// Call an XRPC method directly, and print what came back + /// Call an XRPC method directly, as you, and print what came back /// /// The escape hatch. Tangled's lexicon is much larger than the part atgc /// has commands for — issues, labels, stars, follows, collaborators,