feat(help): open a generated command reference instead of Discussions (#1049)

HELP now opens docs/command-reference.md on GitHub — a 600-command
reference grouped by category with default shortcuts and selection
behavior, generated from the in-repo command registry. A staleness
test fails when the registry and the page drift apart; regenerate
with UPDATE_COMMAND_REFERENCE=1 cargo test command_reference. The
command-line notice is localized in all 21 languages.
This commit is contained in:
Afonsop committed 2026-09-17 13:14:06 +01:00
1 parent 334960fcf3
commit ebee7e5489
25 files changed
+742 -2

No files matched your search

+4 -2
View File
@@ -78,9 +78,11 @@ impl OpenCADStudio {
"HELP" => {
self.command_line
.push_info(crate::t!("Opening OCS Discussions for help and questions...").as_ref());
.push_info(crate::t!("Opening the command reference...").as_ref());
return Some(crate::sys::open_url(
"https://github.com/HakanSeven12/OpenCADStudio/discussions",
// Generated from the in-repo command registry; the
// staleness test in app::control keeps it current.
"https://github.com/HakanSeven12/OpenCADStudio/blob/main/docs/command-reference.md",
self.main_window,
));
}
+74
View File
@@ -1086,6 +1086,80 @@ pub(super) fn action_names() -> &'static [&'static str] {
#[cfg(test)]
mod tests {
use super::*;
/// Renders `docs/command-reference.md` from the live command registry,
/// the category table, selection policies, and the default key bindings
/// (HELP opens this page). Regenerate with
/// `UPDATE_COMMAND_REFERENCE=1 cargo test command_reference`.
fn render_command_reference() -> String {
let mut names = crate::command::all_registered_command_names();
names.sort_unstable();
names.dedup();
// First sorted binding wins; Ctrl/Cmd is normalized so the
// generated file does not differ by generating platform.
let mut shortcut_for: std::collections::BTreeMap<String, String> = Default::default();
for (key, cmd) in crate::app::shortcuts::default_bindings() {
let key = key.replace("CMD+", "Ctrl+").replace("CTRL+", "Ctrl+");
shortcut_for.entry(cmd).or_insert(key);
}
let mut by_category: std::collections::BTreeMap<&str, Vec<&str>> = Default::default();
for name in &names {
by_category
.entry(command_category(name))
.or_default()
.push(name);
}
let mut out = String::new();
out.push_str("# Open CAD Studio \u{2014} Command Reference\n\n");
out.push_str(
"Generated from the in-repo command registry; manual edits are overwritten. \
Regenerate with `UPDATE_COMMAND_REFERENCE=1 cargo test command_reference`.\n\n",
);
for (category, commands) in &by_category {
let title: String = category
.char_indices()
.map(|(i, c)| if i == 0 { c.to_ascii_uppercase() } else { c })
.collect::<String>();
out.push_str(&format!("## {title} ({})\n\n", commands.len()));
out.push_str("| Command | Default shortcut | Selection |\n|---|---|---|\n");
for cmd in commands {
let shortcut = shortcut_for.get(*cmd).map(String::as_str).unwrap_or("");
let selection = match command_selection_policy(cmd) {
"current_selection_or_prompt" => "current selection or prompt",
"command_defined" => "command-driven",
other => other,
};
out.push_str(&format!("| `{cmd}` | {shortcut} | {selection} |\n"));
}
out.push('\n');
}
out.push_str(&format!(
"_{} commands across {} categories._\n",
names.len(),
by_category.len()
));
out
}
#[test]
fn command_reference_is_up_to_date() {
let generated = render_command_reference();
let path = concat!(env!("CARGO_MANIFEST_DIR"), "/docs/command-reference.md");
if std::env::var("UPDATE_COMMAND_REFERENCE").is_ok() {
std::fs::write(path, generated).expect("write command reference");
return;
}
let committed = std::fs::read_to_string(path).unwrap_or_default();
assert_eq!(
committed, generated,
"docs/command-reference.md is stale: run UPDATE_COMMAND_REFERENCE=1 cargo test \
command_reference_is_up_to_date and commit the result"
);
}
fn request(app: &mut OpenCADStudio, mut req: Value) -> Value {
let state = app.control_state();
req["protocol"] = json!(1);
+1
View File
@@ -1884,6 +1884,7 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"Opening \"{name}\" ({size_label})…" => Some(("common", "opening-name-size-label")),
"Opening OCS Discussions for help and questions..." => Some(("view", "opening-ocs-discussions-for-help-and-questions")),
"Opening OCS Web..." => Some(("view", "opening-ocs-web")),
"Opening the command reference..." => Some(("view", "opening-the-command-reference")),
"Opening Patreon page..." => Some(("view", "opening-patreon-page")),
"Opening feedback page..." => Some(("view", "opening-feedback-page")),
"Opening file" => Some(("ui", "opening-file")),