feat: H3 help <command> per-command detail + general reference

HELP node takes an optional single-word topic (BarePath);
AppCommand::Help { topic }. note_help_topic renders the help
block(s) of every command sharing that entry word (so `help
create` covers both create forms), plus `help types` and a
friendly "no help for X" pointer for unknown topics. Full help
gains a detail-hint footer. Catalogued help.detail_hint /
help.unknown_topic; parse-error matrix updated (help now takes a
topic, so the near-miss is the multi-word case). 9 integration
tests in tests/it/help_command.rs. Mark H3 [x].
This commit is contained in:
claude@clouddev1
2026-06-07 13:32:18 +00:00
parent 8dec784080
commit 757711f2bf
12 changed files with 247 additions and 26 deletions
+48 -2
View File
@@ -1321,8 +1321,11 @@ impl App {
use crate::dsl::{AppCommand, MessagesValue, ModeValue};
match cmd {
AppCommand::Quit => vec![Action::Quit],
AppCommand::Help => {
self.note_help();
AppCommand::Help { topic } => {
match &topic {
Some(t) => self.note_help_topic(t),
None => self.note_help(),
}
Vec::new()
}
AppCommand::Rebuild => vec![Action::PrepareRebuild],
@@ -2405,6 +2408,49 @@ impl App {
.lines()
.map(str::to_string),
);
// H3: point at the focused per-command form.
lines.push(crate::t!("help.detail_hint"));
for line in lines {
self.note_system(line);
}
}
/// Focused per-command help (H3): `help <topic>`, where `topic`
/// is a command entry word (`insert`, `create`, `show`, …) or
/// the special `types`. Renders the help block(s) of every
/// command sharing that entry word — so `help create` covers
/// both the DSL and SQL create forms — or a friendly pointer
/// back to `help` when nothing matches.
fn note_help_topic(&mut self, topic: &str) {
use crate::dsl::grammar::REGISTRY;
let topic = topic.trim();
// `help types` re-shows just the type reference.
if topic.eq_ignore_ascii_case("types") {
for line in crate::t!("help.types_reference").lines() {
self.note_system(line.to_string());
}
return;
}
let mut lines: Vec<String> = Vec::new();
for (command, _category) in REGISTRY {
let Some(help_id) = command.help_id else {
continue;
};
if command.entry.matches(topic) {
let key = format!("help.{help_id}");
let body = crate::friendly::translate(&key, &[]);
lines.extend(body.lines().map(str::to_string));
}
}
if lines.is_empty() {
// No command owns that entry word — name it and point
// back at the full list rather than failing silently.
self.note_system(crate::t!("help.unknown_topic", topic = topic));
return;
}
for line in lines {
self.note_system(line);
}