Merge pull request #1716 from schoeller/xrecord_crud

Xrecord crud
This commit is contained in:
Hakan Seven committed 2026-10-12 00:21:14 +03:00
commit e917c23b46
13 files changed
+1365 -4

No files matched your search

+23
View File
@@ -339,3 +339,26 @@ plugin gate (60 real-IPC host tests, 115 plugin API, 20 plugin crate, 16 Python
`--no-fail-fast` (1,805 passed, 1 failed: `fonts_parse_and_resolve`, which fails on pristine `main` on this Mac too; `arc_grips_drive_center_start_and_end_but_not_midpoint`
now passes, fixed upstream). The change is 72 files, +30,705 / -178 against current `main` (fork-only workflow excluded).
### 2026-10-11: XRecord objects (API v8, no Python surface yet)
New additive v8 requests `CreateXRecord` / `ReadXRecord` / `UpdateXRecord` / `DeleteXRecord` / `ListXRecords`
give plugins a non-graphical record carrier: name-addressed typed `XRecordEntry` pairs under the drawing's
named-object dictionary, with no carrier entity. `dictionary_path` is walked from the NOD root (an empty path
is the root), creating missing dictionaries on create only — the `ensure_image_dictionary` bootstrap — so the
first call seeds a namespace; `create` refuses an existing name and `update` refuses a missing one, so neither
can silently overwrite or create. Names and path fragments are symbol-validated, entry group codes are checked
(1..=369, never the handle codes 5/105), and every refusal is decided before the undo step is recorded. Update
replaces the entries atomically (handle, owner and name stay, the record's verbatim raw payloads are dropped, so
saves emit the new content); delete returns the removed record; list reports only the keys that address
XRecords, in dictionary order. Undo: objects-map mutations are captured by the pending history's full structure
snapshot, so each mutating call is one undo step whose undo restores the objects map byte-exactly, the
auto-created dictionaries included. Undo composition follows the entity-write contract: a mutation inside a
plugin-opened `push_undo` scope joins that scope (a multi-write operation — entities and XRecords — is a single
undo step under the plugin's label); only a scope-less mutation opens its own step. Evidence:
`xrecord_crud_round_trips_refusals_and_list`,
`xrecord_mutations_are_single_undo_steps_that_restore_objects` (four steps, refusal paths record none),
`xrecord_writes_join_a_plugin_initiated_undo_scope` (3 creates + update + delete + a refusal + an entity write
in one scope = one step, undone and redone whole),
`xrecords_survive_dxf_and_dwg_round_trips` (typed entries reload from both writers), the IPC round-trips
`xrecord_request_and_response_roundtrip` and `xrecord_requests_await_their_typed_responses`. The API is
host-level and stands on its own; a Python mapping (`doc.xrecords`-style) is future work.
+20
View File
@@ -26,6 +26,7 @@ without it keeps working, and the default `HostApi` methods refuse.
| Selection | `selection`, `set_selection` | Ordered, tab-scoped, exact (missing or duplicate handles reject the request). |
| Solids | `solid_operation` | Kernel-backed create, transform, booleans, extrude, region and surface from a profile, picture embedding. Every result is verified to lift back from its ACIS payload without loss; a refusal leaves the operands untouched. |
| Tables | `table_operation` | Layers, text and dimension styles, blocks and their contents, linetypes, layouts (see below). |
| Objects | `create_xrecord`, `read_xrecord`, `update_xrecord`, `delete_xrecord`, `list_xrecords` | Name-addressed XRecords under the drawing's named-object dictionary (API v8, see below): a non-graphical record carrier with strict create-vs-update semantics and one undo step per change. |
| Commands | `run_command` | Drives the real OCS command one step at a time (see below). |
| Settings | `system_variable`, `set_system_variable` | `CLAYER`, `SNAPANG`, and a read-only `CTAB` (the current layout). |
@@ -85,6 +86,25 @@ recorded, so a failure changes nothing and leaves the history alone.
Style and layout operations act on the active document, because they use the
application's shared style and view machinery.
## XRecords (API v8)
`create_xrecord`, `read_xrecord`, `update_xrecord`, `delete_xrecord` and
`list_xrecords` address XRecords by name under a dictionary path walked from the
named-object dictionary root (an empty path is the root itself); missing
dictionaries are created on create only, so the first call seeds a plugin's
namespace. The payload is the codec's typed `XRecordEntry` pairs — the host puts
no opinion on the content. Names and path fragments are symbol-validated, and
dictionary addressing follows the codec's case-insensitive lookup. Create never
overwrites and update never creates; each refusal is decided before the undo
scope is touched, so a failure changes nothing, and each change joins a
plugin-opened `push_undo` scope — the entity-write contract — so an operation
composed of several writes (entities and XRecords) is a single undo step; with
no scope open a change is one undo step of its own, whose undo restores the
objects map byte-exactly, the auto-created dictionaries included.
`list_xrecords` reports only the keys that address XRecords, in the
dictionary's own order, so audits need no prior knowledge. Not yet mapped to
Python.
## Commands
`run_command` takes a `CommandRequest` (`Run`, `Start`, `Point`, `Text`, `Token`,