forked from 1b-agent/opencadstudio-upstream
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>
118 lines
4.9 KiB
Rust
118 lines
4.9 KiB
Rust
//! Host-side IPC request handler.
|
|
|
|
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
|
|
/// command; the host should install an adapter that sends
|
|
/// `HostRequest::InteractiveEvent` back to the plugin process.
|
|
pub fn handle_plugin_request(
|
|
host: &mut dyn HostApi,
|
|
req: PluginRequest,
|
|
on_start_interactive: &mut dyn FnMut(u64),
|
|
) -> PluginResponse {
|
|
use PluginRequest::*;
|
|
match req {
|
|
PushInfo(msg) => {
|
|
host.push_info(&msg);
|
|
PluginResponse::Ok
|
|
}
|
|
PushOutput(msg) => {
|
|
host.push_output(&msg);
|
|
PluginResponse::Ok
|
|
}
|
|
PushError(msg) => {
|
|
host.push_error(&msg);
|
|
PluginResponse::Ok
|
|
}
|
|
AddEntity(entity) => PluginResponse::Handle(host.add_entity(entity)),
|
|
AddEntities(entities) => PluginResponse::Handles(host.add_entities(entities)),
|
|
UpdateEntity(entity) => PluginResponse::Bool(host.update_entity(entity)),
|
|
RemoveEntity { handle } => PluginResponse::Bool(host.remove_entity(handle)),
|
|
BumpGeometry => {
|
|
host.bump_geometry();
|
|
PluginResponse::Ok
|
|
}
|
|
ReadRecord { handle, app_name } => {
|
|
PluginResponse::Record(host.read_record(handle, &app_name).cloned())
|
|
}
|
|
WriteRecord { handle, record } => PluginResponse::Bool(host.write_record(handle, record)),
|
|
RemoveRecord { handle, app_name } => {
|
|
PluginResponse::Bool(host.remove_record(handle, &app_name))
|
|
}
|
|
PushUndo { label } => {
|
|
host.push_undo(&label);
|
|
PluginResponse::Ok
|
|
}
|
|
SetDirty => {
|
|
host.set_dirty();
|
|
PluginResponse::Ok
|
|
}
|
|
StartInteractive { command_id } => {
|
|
on_start_interactive(command_id);
|
|
PluginResponse::Ok
|
|
}
|
|
DocumentSnapshot => PluginResponse::Document(Box::new(addressable_document_snapshot(host))),
|
|
OpenDocumentView => match host.document_view() {
|
|
Some(info) => PluginResponse::DocumentView {
|
|
path: info.path,
|
|
version: info.version,
|
|
},
|
|
None => PluginResponse::Error("shared document view unavailable".to_string()),
|
|
},
|
|
OpenDocumentViewV4 { tab_id } => match host.document_view_v4(tab_id) {
|
|
Some(info) => PluginResponse::DocumentViewV4 {
|
|
path: info.path,
|
|
version: info.version,
|
|
},
|
|
None => PluginResponse::Error("V4 shared document view unavailable".to_string()),
|
|
},
|
|
CloseDocumentViewV4 { tab_id } => {
|
|
host.close_document_view_v4(tab_id);
|
|
PluginResponse::Ok
|
|
}
|
|
GetTabId => PluginResponse::TabId(host.tab_id()),
|
|
DocumentPath { tab_id } => PluginResponse::DocumentPath(
|
|
host.document_path(tab_id).map(|path| path.into_os_string()),
|
|
),
|
|
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)),
|
|
}
|
|
}
|