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 <[email protected]>
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

+4
View File
@@ -149,3 +149,7 @@ fn preview_aligned(p1: Vec3, p2: Vec3, dim_pt: Vec3) -> WireModel {
fill_tris: vec![],
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DAL", "DIMALIGNED"] }); // AlignedDimensionCommand
+4
View File
@@ -173,3 +173,7 @@ fn angular_preview(vertex: Vec3, first: Vec3, second: Vec3, arc_pt: Vec3) -> Vec
}
points
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DIMANGULAR"] }); // AngularDimensionCommand
+4
View File
@@ -112,3 +112,7 @@ fn preview_line(a: Vec3, b: Vec3) -> WireModel {
fill_tris: vec![],
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DDI", "DIMDIAMETER"] }); // DiameterDimensionCommand
+4
View File
@@ -177,3 +177,7 @@ impl CadCommand for DimBaselineCommand {
fn v3(p: Vec3) -> Vector3 {
Vector3::new(p.x as f64, p.y as f64, p.z as f64)
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DBA", "DIMBASELINE"] }); // DimBaselineCommand
+4
View File
@@ -158,3 +158,7 @@ impl CadCommand for DimContinueCommand {
fn v3(p: Vec3) -> Vector3 {
Vector3::new(p.x as f64, p.y as f64, p.z as f64)
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DCO", "DIMCONTINUE"] }); // DimContinueCommand
+4
View File
@@ -97,3 +97,7 @@ impl CadCommand for DimBreakCommand {
CmdResult::Cancel
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DBR", "DIMBREAK"] }); // DimBreakCommand
+4
View File
@@ -85,3 +85,7 @@ impl CadCommand for DimEditCommand {
CmdResult::Cancel
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DED", "DIMEDIT"] }); // DimEditCommand
+4
View File
@@ -108,3 +108,7 @@ impl CadCommand for DimJogLineCommand {
})
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DIMJOGLINE", "DJL"] }); // DimJogLineCommand
+4
View File
@@ -125,3 +125,7 @@ impl CadCommand for DimSpaceCommand {
}
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DIMSPACE", "DSPACE"] }); // DimSpaceCommand
+4
View File
@@ -128,3 +128,7 @@ impl CadCommand for DimTeditCommand {
}
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DIMTED", "DIMTEDIT"] }); // DimTeditCommand
+4
View File
@@ -252,3 +252,7 @@ pub fn arrowhead_wings(tip: Vec3, next: Vec3, size: f32) -> [Vec3; 2] {
),
]
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["LE", "LEADER"] }); // LeaderCommand
+4
View File
@@ -163,3 +163,7 @@ fn linear_text_pos(first: Vec3, second: Vec3, def: Vec3) -> Vec3 {
let d2 = second + perp * offset;
(d1 + d2) * 0.5 + perp * 0.15
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DIMLINEAR"] }); // LinearDimensionCommand
+4
View File
@@ -199,3 +199,7 @@ fn arrowhead_wings(tip: Vec3, next: Vec3, size: f32) -> [Vec3; 2] {
),
]
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["MLD", "MLEADER"] }); // MLeaderCommand
+7
View File
@@ -476,3 +476,10 @@ fn preview_wire(pts: &[Vec3]) -> WireModel {
// Silence unused-import warning for MultiLeader and LeaderLine if not used in all paths
fn _uses_ml_types(_ml: &MultiLeader, _ll: &LeaderLine) {}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["MLA", "MLEADERADD"] }); // MLeaderAddCommand
inventory::submit!(crate::command::CommandRegistration { names: &["MLAL", "MLEADERALIGN"] }); // MLeaderAlignCommand
inventory::submit!(crate::command::CommandRegistration { names: &["MLC", "MLEADERCOLLECT"] }); // MLeaderCollectCommand
inventory::submit!(crate::command::CommandRegistration { names: &["MLEADERREMOVE", "MLR"] }); // MLeaderRemoveCommand
+4
View File
@@ -91,3 +91,7 @@ impl CadCommand for MTextCommand {
None
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["MT", "MTEXT"] }); // MTextCommand
+4
View File
@@ -86,3 +86,7 @@ impl CadCommand for OrdinateDimCommand {
fn v3(p: Vec3) -> Vector3 {
Vector3::new(p.x as f64, 0.0, p.z as f64)
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DIMORDINATE", "DOR"] }); // OrdinateDimCommand
+4
View File
@@ -128,3 +128,7 @@ pub fn make_linear_dim(p1: Vec3, p2: Vec3, dim_pt: Vec3) -> EntityType {
dim.base.actual_measurement = dim.measurement();
EntityType::Dimension(Dimension::Linear(dim))
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["QDIM"] }); // QdimCommand
+4
View File
@@ -117,3 +117,7 @@ fn preview_wire(points: Vec<Vec3>) -> WireModel {
fill_tris: vec![],
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DIMRADIUS"] }); // RadiusDimensionCommand
+4
View File
@@ -169,3 +169,7 @@ impl CadCommand for TableCommand {
}
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["TABLE"] }); // TableCommand
+4
View File
@@ -86,3 +86,7 @@ impl CadCommand for TextCommand {
None
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["DT", "T", "TEXT"] }); // TextCommand
+4
View File
@@ -108,3 +108,7 @@ impl CadCommand for ToleranceCommand {
})
}
}
// ── Autocomplete registry ─────────────────────────────────
inventory::submit!(crate::command::CommandRegistration { names: &["TOL", "TOLERANCE"] }); // ToleranceCommand