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

+1 -1
View File
@@ -7,7 +7,7 @@ pub mod host;
pub mod marketplace;
pub mod registry;
pub use registry::{all_ribbon_modules, ribbon_modules_enabled};
pub use registry::{all_ribbon_modules, plugin_command_names, ribbon_modules_enabled};
pub(crate) use registry::try_dispatch;
/// Run a plugin entry point under a panic guard so a buggy external plugin
+18
View File
@@ -31,6 +31,24 @@ pub fn ribbon_modules_enabled(
core
}
/// Command names contributed by loaded external plugins whose id is **not** in
/// `disabled` — every ribbon tool's command id plus manifest command prefixes.
/// The host merges these into command-line autocomplete so plugin commands are
/// discoverable by typing, not only via ribbon buttons (#272).
pub fn plugin_command_names(disabled: &rustc_hash::FxHashSet<String>) -> Vec<String> {
#[cfg(not(target_arch = "wasm32"))]
{
return crate::plugin::external::with_manager(|manager| {
manager.command_names(|id| disabled.contains(id))
});
}
#[cfg(target_arch = "wasm32")]
{
let _ = disabled;
Vec::new()
}
}
/// Dispatch `cmd` to a loaded external plugin (skipping disabled ones).
/// Returns true if one handled it.
pub(crate) fn try_dispatch(app: &mut OpenCADStudio, tab: usize, cmd: &str) -> bool {