Merge pull request #1423 from felixriestra:pr/python-host-model

The bundled `opencad.python` plugin drives OCS through plugin API v7: all
43 canvas entity kinds, the drawing tables, and OCS's own commands run
step by step. RustPython stays in the plugin process — the plugin crate is
outside the workspace with its own lock file, so the application's own
dependencies are unchanged.

Reconciled with the plugin command work that landed as #1425, which the
branch was written before:

- Both added `impl Drop for PluginProcessInteractiveAdapter`, which git
  merges silently into two impls that do not compile. One body now
  cancels the command and then drops it from the runner, in that order: a
  plugin waiting on a step has to be released before the runner forgets
  the command.
- `execute_command` (#1425) and `run_command` (#1423) are both routes from
  a script to the command line, and only the second was guarded. The
  SCRIPTCOMMANDS switch and the denylist are shared now, so neither route
  is the unguarded one.
- `run_command` dispatched with plugin dispatch still enabled, so a script
  naming a plugin command re-entered the plugin that was blocked waiting
  for it. It sets the suppression flag for the length of the dispatch, as
  `execute_command` does.

The denylist read the command as typed, while the dispatcher resolves
aliases: a user-defined `Q` for `QUIT` (ALIASEDIT writes those) walked
past it. It resolves the alias first.

Also: the active-command comparison ran on every message, a mouse move
included, allocating two strings for names that are already `&'static
str`; SCRIPTCOMMANDS' own messages go through the translation macros; and
`docs/.DS_Store` (now ignored) and an unrelated RTF note about constraint
commands are out of the tree.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hakan SevenandClaude Opus 5 committed 2026-09-22 23:26:30 +03:00
commit 41ecc0c458
72 files changed
+30614 -91

No files matched your search

+34 -1
View File
@@ -3,6 +3,27 @@
use crate::host::HostApi;
use crate::ipc::protocol::{PluginRequest, PluginResponse};
fn addressable_document_snapshot(host: &dyn HostApi) -> acadrust::CadDocument {
let mut snapshot = host.document().clone();
let attributes = host
.document()
.entities()
.filter_map(|entity| match entity {
acadrust::EntityType::Insert(insert) => Some(insert.attributes.clone()),
_ => None,
})
.flatten()
.map(acadrust::EntityType::AttributeEntity)
.collect::<Vec<_>>();
for attribute in attributes {
// Attribute entities remain canonical children of INSERT in the host
// document. The IPC snapshot also indexes them by handle so plugin
// document models can address a child exactly like any other entity.
let _ = snapshot.add_entity(attribute);
}
snapshot
}
/// Apply one plugin request to the host's `HostApi` implementation.
///
/// `on_start_interactive` is called when the plugin starts an interactive
@@ -54,7 +75,7 @@ pub fn handle_plugin_request(
on_start_interactive(command_id);
PluginResponse::Ok
}
DocumentSnapshot => PluginResponse::Document(Box::new(host.document().clone())),
DocumentSnapshot => PluginResponse::Document(Box::new(addressable_document_snapshot(host))),
OpenDocumentView => match host.document_view() {
Some(info) => PluginResponse::DocumentView {
path: info.path,
@@ -80,5 +101,17 @@ pub fn handle_plugin_request(
AddLayer(config) => PluginResponse::OptHandle(host.add_layer(config)),
ModifyLayer(config) => PluginResponse::Bool(host.modify_layer(config)),
ExecuteCommand(cmd) => PluginResponse::Bool(host.execute_command(&cmd)),
GetSystemVariable { name } => PluginResponse::SystemVariable(host.system_variable(&name)),
SetSystemVariable { name, value } => {
PluginResponse::SystemVariableResult(host.set_system_variable(&name, value))
}
UpdateEntitiesTransaction { label, entities } => PluginResponse::EntityTransactionResult(
host.update_entities_transaction(&label, entities),
),
GetSelection => PluginResponse::Selection(host.selection()),
SetSelection { handles } => PluginResponse::SelectionResult(host.set_selection(&handles)),
SolidOperation { operation } => PluginResponse::SolidResult(host.solid_operation(operation)),
TableOperation { operation } => PluginResponse::TableResult(host.table_operation(operation)),
RunCommand { request } => PluginResponse::CommandResult(host.run_command(request)),
}
}