feat(commandline): autocomplete suggestions + inventory-based registry

Type-as-you-go suggestion popup above the input that filters known
command names by substring (so `LEADER` surfaces both LEADER and
MLEADER). Up / Down navigates the list, Enter dispatches the
highlighted entry, clicking a row dispatches directly. Esc on a
non-empty input clears the buffer instead of escalating to
command-cancel.

The name corpus comes from `inventory::submit!` blocks placed next
to each `CadCommand` impl rather than a hand-maintained static list.
Adding a new interactive command means dropping one `inventory::submit!`
line in its own module file — the autocomplete picks it up at the
next compile. 98 submissions seeded across 60 module files;
`command::all_registered_command_names()` flattens them at runtime.

One-shot dispatch arms in `app/commands.rs` (NEW, OPEN, SAVE, UNDO,
…) don't have a `CadCommand` impl and are therefore absent from
autocomplete by design.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.7 committed 2026-05-26 22:45:54 +03:00
1 parent 4124cb2689
commit 05ea31aa2a
67 files changed
+507 -2

No files matched your search

+13
View File
@@ -1102,3 +1102,16 @@ impl CadCommand for ArcCSLCommand {
}
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_3P"] }); // Arc3PCommand
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_CSA"] }); // ArcCSACommand
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_CSL"] }); // ArcCSLCommand
inventory::submit!(crate::command::CommandRegistration { names: &["A", "ARC"] }); // ArcCommand
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SCA"] }); // ArcSCACommand
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SCE"] }); // ArcSCECommand
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SCL"] }); // ArcSCLCommand
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SEA"] }); // ArcSEACommand
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SED"] }); // ArcSEDCommand
inventory::submit!(crate::command::CommandRegistration { names: &["ARC_SER"] }); // ArcSERCommand
+4
View File
@@ -184,3 +184,7 @@ impl CadCommand for AttdefCommand {
})
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["ATTDEF"] }); // AttdefCommand
+9
View File
@@ -910,3 +910,12 @@ impl CadCommand for CircleTTTCommand {
None
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_2P"] }); // Circle2PCommand
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_3P"] }); // Circle3PCommand
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_CD"] }); // CircleCDCommand
inventory::submit!(crate::command::CommandRegistration { names: &["C", "CIRCLE"] }); // CircleCommand
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_TTR"] }); // CircleTTRCommand
inventory::submit!(crate::command::CommandRegistration { names: &["CIRCLE_TTT"] }); // CircleTTTCommand
+4
View File
@@ -132,3 +132,7 @@ fn make_donut(cx: f64, cy: f64, inner_r: f64, outer_r: f64) -> EntityType {
p.vertices = vec![v0, v1];
EntityType::LwPolyline(p)
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DO", "DONUT"] }); // DonutCommand
+6
View File
@@ -532,3 +532,9 @@ fn line_wire(from: Vec3, to: Vec3) -> WireModel {
fill_tris: vec![],
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["ELLIPSE_ARC"] }); // EllipseArcCommand
inventory::submit!(crate::command::CommandRegistration { names: &["ELLIPSE_AXIS"] }); // EllipseAxisCommand
inventory::submit!(crate::command::CommandRegistration { names: &["EL", "ELLIPSE"] }); // EllipseCommand
+6
View File
@@ -423,3 +423,9 @@ impl CadCommand for BoundaryCommand {
CmdResult::Cancel
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["BOUNDARY"] }); // BoundaryCommand
inventory::submit!(crate::command::CommandRegistration { names: &["GRADIENT"] }); // GradientCommand
inventory::submit!(crate::command::CommandRegistration { names: &["H", "HATCH"] }); // HatchCommand
+4
View File
@@ -161,3 +161,7 @@ impl CadCommand for HatcheditCommand {
CmdResult::Cancel
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["HATCHEDIT", "HE"] }); // HatcheditCommand
+4
View File
@@ -94,3 +94,7 @@ impl CadCommand for LineCommand {
})
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["L", "LINE"] }); // LineCommand
+4
View File
@@ -157,3 +157,7 @@ fn build_mline(pts: &[Vec3], scale: f64, closed: bool, style_name: &str) -> Enti
mline.style_name = style_name.to_string();
EntityType::MLine(mline)
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["ML", "MLINE"] }); // MlineCommand
+4
View File
@@ -55,3 +55,7 @@ impl CadCommand for PointCommand {
None
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["PO", "POINT"] }); // PointCommand
+4
View File
@@ -321,3 +321,7 @@ impl CadCommand for PlineCommand {
))
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["PL", "PLINE"] }); // PlineCommand
+5
View File
@@ -173,3 +173,8 @@ impl CadCommand for XLineCommand {
})
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["RAY"] }); // RayCommand
inventory::submit!(crate::command::CommandRegistration { names: &["CONSTRUCTIONLINE", "XL", "XLINE"] }); // XLineCommand
+4
View File
@@ -133,3 +133,7 @@ fn make_revcloud(pts: &[Vec3], arc_len: f64) -> EntityType {
p.vertices = vertices;
EntityType::LwPolyline(p)
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["REVCLOUD"] }); // RevCloudCommand
+9
View File
@@ -633,3 +633,12 @@ fn edge_poly_params(a: Vec3, b: Vec3, sides: u32) -> Option<(Vec3, f32, f32)> {
let sa = angle_xy(center, a);
Some((center, vr, sa))
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["POLY_C"] }); // PolyCCommand
inventory::submit!(crate::command::CommandRegistration { names: &["POL", "POLY", "POLYGON"] }); // PolyCommand
inventory::submit!(crate::command::CommandRegistration { names: &["POLY_E"] }); // PolyECommand
inventory::submit!(crate::command::CommandRegistration { names: &["RECT_CEN"] }); // RectCenCommand
inventory::submit!(crate::command::CommandRegistration { names: &["REC", "RECT", "RECTANG"] }); // RectCommand
inventory::submit!(crate::command::CommandRegistration { names: &["RECT_ROT"] }); // RectRotCommand
+4
View File
@@ -118,3 +118,7 @@ impl CadCommand for SplineCommand {
))
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["SPL", "SPLINE"] }); // SplineCommand