feat(plugin): surface loaded plugin commands in command-line autocomplete

Autocomplete suggestions came only from all_registered_command_names() —
the compile-time inventory registry returning &'static str — so
runtime-loaded plugin commands could never appear. Typing a plugin's
command prefix (e.g. `LS_`) showed nothing even with the plugin loaded
and its ribbon tab active; dispatch worked, only discovery was blind.

Collect each enabled plugin's ribbon ToolDef command ids plus its
manifest command_prefixes into a dynamic candidate pool
(OwnedRibbonGroup::command_ids → PluginManager::command_names →
plugin_command_names), refreshed in rebuild_ribbon_modules on startup
load, settings reload, and every enable/disable toggle. ranked_matches
now merges this pool with the static registry and returns owned strings.

A plugin.toml `commands = [...]` list for sub-verbs with no ribbon button
(e.g. LS_AUTOLABEL) stays a follow-up — it's a plugin-API surface change.

Closes #272

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-07-04 23:01:53 +03:00
1 parent 60872da62a
commit 77c0c5a9d0
8 files changed
+173 -20

No files matched your search

+7 -4
View File
@@ -148,12 +148,15 @@ impl OpenCADStudio {
// command completes on Enter (`BAC` → `BACKGROUND`). The verb's own
// input was already cleared, so rank against `cmd` directly.
if allow_suggest {
if let Some(top) = crate::ui::command_line::ranked_matches(cmd)
.first()
.copied()
if let Some(top) = crate::ui::command_line::ranked_matches(
cmd,
&self.command_line.dynamic_commands,
)
.into_iter()
.next()
{
if !top.eq_ignore_ascii_case(cmd) {
return self.dispatch_command_inner(top, false);
return self.dispatch_command_inner(&top, false);
}
}
}
+1 -2
View File
@@ -194,8 +194,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
// rather than the partial text actually in the buffer.
let i_tab = self.active_tab;
if self.tabs[i_tab].active_cmd.is_none() {
if let Some(picked) = self.command_line.selected_suggestion() {
let cmd = picked.to_string();
if let Some(cmd) = self.command_line.selected_suggestion() {
self.command_line.input.clear();
self.command_line.autocomplete_cursor = None;
return self.dispatch_command(&cmd);
+5
View File
@@ -158,6 +158,11 @@ impl OpenCADStudio {
let modules =
crate::plugin::ribbon_modules_enabled(&self.disabled_plugins);
self.ribbon.set_modules(modules);
// Refresh the command-line autocomplete pool so a newly enabled plugin's
// commands become typeable (and a disabled one's drop out). This runs on
// startup load, settings reload, and every enable/disable toggle (#272).
self.command_line.dynamic_commands =
crate::plugin::plugin_command_names(&self.disabled_plugins);
}
/// Snapshot of disabled plugin ids — lets the registry skip them while it