The data types for the right-click context menu, with no behavior yet
(nothing opens a menu until the dispatch wiring lands).
- `MenuRegistry` / `MenuItem` / `MenuError` (Q#CM2): the Lua-facing item
registry, mirroring `CommandRegistry`. Items carry id / label / command
/ context tag / predicate / group / order; `context` is validated
against a known vocabulary (typo -> hard error, R50-style), and a
matching `id` replaces in place so config reloads and user overrides
are idempotent.
- `MenuState` / `MenuRow` / `SharedMenu` (Q#CM1): the open-menu runtime
state, plus the self-suppressing TUI `MenuView` overlay (the
`SearchView` pattern). `MenuRow` is Item|Separator; navigation and
hit-testing skip separators.
Pure additions behind `pub mod menu` — the lib still builds with the
module unused.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014TXbAwk27agwhrNNrhLi2U
The per-buffer in-buffer-search store, mirroring DiagnosticStore:
keyed Arc<Mutex>, sorted match ranges + active index, next/prev
stepping with wrap, focus-from-cursor, and stale-on-edit tracking
(M11.8 model — an edit suppresses matches at pre-edit byte
positions until re-search). find_all is smart-case substring
(case-insensitive unless the query has an uppercase char), ASCII
case-folded to keep byte offsets exact, non-overlapping matches.
Pure core, no wiring yet. 8 unit tests (case folding, overlap,
wrap, focus, stale, active-clamp-on-reset).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
LSP data layer only — independent of the M11 semantic-render
protocol (semantic_render.rs / semantic_client.rs, tree-sitter →
frontend wire families). No InstanceMessage family added; wiring
LSP tokens into styling is a separate rendering milestone. Same
shape as every sibling LSP feature: typed store + async request
+ Lua surface + command + modeline summary.
- src/semantic_tokens.rs: decode the 5-int relative encoding
(deltaLine, deltaStartChar, length, tokenType, tokenModifiers)
into absolute SemanticToken{line,start,length,token_type,
token_modifiers}, with the same-line-vs-new-line deltaStartChar
rule and defensive truncation of a malformed trailing group.
SemanticTokensLegend::from_capabilities parses
semanticTokensProvider.legend and resolves type index / modifier
bitset to names. Store keyed (server, uri). 6 unit tests.
- src/lsp.rs: store + accessor, ResponseRoute::SemanticTokens +
absorb, request_semantic_tokens (/full; v1 no range/delta),
textDocument.semanticTokens client capability (full-only,
formats=[relative], standard LSP legend).
- src/lua_bindings.rs: _request_semantic_tokens_raw,
pmacs.semantic_tokens.{tokens, legend, clear} (legend reads the
per-server initialize capabilities).
- pmacs_fake_lsp.rs: semanticTokensProvider.legend in initialize;
textDocument/semanticTokens/full arm with relative-encoded data.
- builtin/runtime/lsp.lua: pmacs.lsp.semantic_tokens() requests
full, stores, modeline summary (first token's type resolved via
legend); lsp.semantic-tokens command + C-c y.
- tests/m4_acceptance.rs: m4_17 drives the request via the Lua
surface, asserts decoded absolute tokens (incl. deltaLine!=0 ⇒
absolute startChar) and legend index→name resolution.
Gates: lib 1285/0, m4 72/0, m8_1 10/0, m8_9 26/0, m8_10 19/0,
m9_1 18/0, m5_8 5/0, m11_5 (--features crdt) 2/0 (confirms no
collision with the M11 render protocol); fmt + clippy clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Independent LSP feature (not part of the L1-L4 cross-file arc),
shipped in the same shape as every sibling: typed store + async
request + Lua surface + command + modeline summary, with the
inline renderer deferred as its own milestone.
- src/inlay_hint.rs: parse InlayHint[]|null — position, label
(string OR InlayHintLabelPart[] flattened), kind (type/
parameter), paddingLeft/Right, tooltip (string|MarkupContent).
Store keyed (server, uri). 5 unit tests.
- src/lsp.rs: inlay_hint_store + accessor, ResponseRoute::InlayHint
+ absorb, request_inlay_hint (range params), textDocument.
inlayHint client capability (no resolveSupport/refreshSupport —
full hints, on-demand re-query is the v1 model).
- src/lua_bindings.rs: _request_inlay_hint_raw,
pmacs.inlay_hint.{hints,clear}.
- pmacs_fake_lsp.rs: textDocument/inlayHint arm returning a
string-label type hint and a label-parts parameter hint.
- builtin/runtime/lsp.lua: pmacs.lsp.inlay_hints() requests over
the whole-buffer range, stores, modeline summary;
lsp.inlay-hints command + C-c i; scope header notes the inline
renderer is a later milestone.
- tests/m4_acceptance.rs: m4_16 drives the request via the Lua
surface, asserts both label shapes / kinds / padding parsed.
Deferred (scoping, not a regression): inline virtual-text
rendering. The VirtualCellOverlay model only overwrites existing
cells; rendering hints inline needs a column-inserting/reflowing
renderer — a rendering milestone, not an LSP task — staged like
the hover panel / references list. pmacs.inlay_hint is the data
surface a future render layer subscribes to.
Gates: lib 1279/0, m4 71/0, m8_1 10/0, m8_9 26/0, m8_10 19/0,
m9_1 18/0, m5_8 5/0, m11_5 (--features crdt) 2/0; fmt + clippy
clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Third cross-file layer: code actions and the server→client edit
channel that executeCommand-driven actions depend on.
- src/code_action.rs: normalise (Command | CodeAction)[] into one
CodeActionItem (bare-Command vs nested-Command disambiguated by
whether top-level `command` is a string; inline `edit` reuses
rename::WorkspaceEditResponse). Store keyed (server, uri). 5 tests.
- src/lsp.rs: code_action_store + accessor, ResponseRoute::CodeAction
+ absorb, request_code_action, request_execute_command (awaiter
only — effect arrives out of band). Capabilities: codeAction
(+codeActionLiteralSupport), workspace.executeCommand, and
workspace.applyEdit flipped to true.
- src/lua_bindings.rs: _request_code_action_raw,
_request_execute_command_raw, _parse_workspace_edit (any raw
WorkspaceEdit JSON -> applier input shape), pmacs.code_action.*.
- pmacs_fake_lsp.rs: textDocument/codeAction arm (command action
first, inline-edit action second) + workspace/executeCommand arm
that emits a server→client workspace/applyEdit before responding.
- builtin/runtime/lsp.lua: pmacs.lsp.code_actions (apply first
action: inline edit and/or executeCommand); the applyEdit pump
(chained on pmacs._async.tick, drains only attachment-server
events, snapshots server ids before applying since find_or_open
can mutate `attachments`, replies { applied }); lsp.code-actions
command + C-c a keybind.
- tests/m4_acceptance.rs: m4_14 drives the full
codeAction→executeCommand→applyEdit chain end to end.
Gates: lib 1273/0, m4 69/0, m8_1 10/0, m8_9 26/0, m8_10 19/0,
m9_1 18/0, m11_5 (--features crdt) 2/0; fmt + clippy clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Second cross-file layer on the L1 foundations: a multi-file
WorkspaceEdit applier and an LSP rename UX driving it.
- src/rename.rs: parse a WorkspaceEdit (both `changes` map and
`documentChanges`, the latter preferred per spec; AnnotatedTextEdit
handled; create/rename/delete resource ops counted into
`unsupported_ops` for L4) into per-file TextEdit lists. RenameStore
keyed by the request's origin URI. 6 unit tests.
- src/lsp.rs: rename_store + accessor, ResponseRoute::Rename,
request_rename, and the textDocument.rename client capability
(prepareSupport=false — L2 renames from the cursor position).
- src/lua_bindings.rs: _request_rename_raw + pmacs.rename.{file_edits,
unsupported,clear}.
- pmacs_fake_lsp.rs: textDocument/rename arm; `rename` mode returns a
2-file documentChanges plus a create resource op.
- builtin/runtime/lsp.lua: apply_workspace_edit (preflight rejects
unresolvable URIs before mutating anything; per-file reverse-sorted
application; origin buffer restored), pmacs.lsp.rename with a
minibuffer prompt, lsp.rename command, C-c r keybind.
- tests/m4_acceptance.rs: m4_13 drives rename end-to-end through the
minibuffer and asserts both files mutated + origin restored.
Gates: lib 1268/0, m4 68/0, m8_1 10/0, m8_9 26/0, m8_10 19/0,
m9_1 18/0, m11_5 (--features crdt) 2/0; fmt + clippy clean.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Completes the read-only LSP feature set (everything except edits).
Same request→store→Lua pattern as the nav batch; these three need
new parsing (new response shapes), so they were deliberately split
from the Location-family PR (#16).
- src/symbol.rs: one flat Symbol type for both symbol requests.
from_lsp_value handles BOTH LSP shapes — hierarchical
DocumentSymbol[] (flattened with depth + parent chain) and flat
SymbolInformation[]/WorkspaceSymbol[] (location.uri, range
optional for WorkspaceSymbol). Scope-keyed (Document(uri) vs
Workspace(query)) so an outline and a query don't collide.
- src/document_highlight.rs: range + DocumentHighlightKind (absent
defaults to Text=1 per spec), (server,uri)-keyed.
- lsp.rs: three ResponseRoute variants + absorb arms + request
methods. documentSymbol/documentHighlight ranges convert via the
requested-doc codec; workspace/symbol results are cross-file →
route uri "" → non-destructive passthrough (same rule as
cross-file definition).
- Lua: raw bindings + pmacs.document_symbol / .workspace_symbol /
.document_highlight read surfaces (the new LSP Symbol is aliased
to avoid the pre-existing project_index::Symbol name clash);
lsp.lua wrappers + an lsp.document-symbols command on C-c o
(modeline summary; outline buffer is future UX).
- Tests: 6 parser unit tests (hierarchical depth/parent, flat
SymbolInformation, range-less WorkspaceSymbol, highlight kind
default, scope non-collision) + an e2e driving all three through
the async bridge asserting shape correctness.
Also includes a pre-existing rustfmt normalization of the #15
semantic-frontend files (protocol.rs / semantic_client.rs /
semantic_render.rs) — main was not rustfmt-clean there after the #15
merge; bundled here per operator decision so the fmt gate is green.
Gate: fmt clean; clippy --all-targets -D warnings clean; lib
1257/0; m4_acceptance 66/0; m9_1 18/0; m8_1/m8_9/m8_10 green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The "cheap batch" subset that is genuine template-fill: these four
return the exact `Location | Location[] | LocationLink[] | null`
shape `textDocument/definition` already parses, so no new parsing —
only a kind discriminator so they don't collide on (server, uri).
- src/locations.rs: a (server, uri, kind)-keyed store whose value
type is the reused crate::definition::DefinitionResponse. The
proven definition store + Lua API are untouched.
- lsp.rs: ResponseRoute::Locations { uri, kind }; one absorb arm; a
DRY request_locations helper + request_references /
request_declaration / request_type_definition /
request_implementation. references sends
context.includeDeclaration. Supersede keys derive from each
kind's distinct method, so the four don't cancel each other.
- Lua: _request_*_raw bindings + install_locations exposing
pmacs.references / .declaration / .type_definition /
.implementation ({ locations, clear }, mirroring pmacs.definition,
reusing definition_response_to_lua). lsp.lua Handle wrappers + a
lsp.find-references command bound to M-? (modeline summary;
references-list buffer is future UX, like the hover panel).
- Tests: locations.rs unit tests (kind labels distinct; keys don't
collide); e2e driving all four through the async bridge and
asserting each routes to its own kind slot (fake returns distinct
lines 11/21/31/41).
Scoped: documentSymbol / workspaceSymbol / documentHighlight return
different shapes (new parsing) — a separate follow-up, not crammed
in here.
Gate: fmt clean; clippy --all-targets -D warnings clean; lib
1232/0; m4_acceptance 65/0; m9_1 18/0; m8_1/m8_9/m8_10 green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Completes the M11 arc with the consumer side. pmacs has no GUI
toolkit, so per the design note's testability strategy the
deliverable is the bounded testable glue, not a GPU renderer.
- src/semantic_client.rs (crdt-gated): headless SemanticClient
composing the BufferMirror rope replica (M10.10) with a tile-based
SemanticModel that reconstructs styling/decorations from the full +
dirty-segment deltas (M11.4). Emits FrontendEvent::Viewport;
read-back accessors (text / effective_style_at /
decoration_kinds_at / tile ranges). The M11.4 contract (segments
carry every current item intersecting their range) makes tiles
self-contained → incremental apply is per-tile replacement with
edge-clipping, no cross-span surgery. 7 unit tests.
- tests/m11_5_semantic_acceptance.rs: (a) reconstruction-equivalence
— incrementally-driven client asserted byte-for-byte identical to
a fresh full projection across a scripted viewport/edit/selection
sequence incl. a viewport jump (golden discipline, no snapshot
crate); (b) end-to-end — a real daemon routes StyleSpans/
Decorations to a semantic session (after it declares a Viewport)
and never to a grid session, CellDelta vice versa, validating the
M11.2 per-session projection through the socket.
Lib (1404 crdt / 1242 non-crdt) + integration green on both feature
flavors; clippy -D warnings clean on both.
M11 arc complete (M11.1–M11.5). Inline/Block/Fold/ResourceOffer
remain honest stubs pending their source features.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The first real producer of the semantic-frontend arc. The instance
projects syntax styling to semantic_render sessions without
rasterizing to a cell grid.
- src/semantic_render.rs: SemanticRenderState, sibling of
instance_render::RenderState. Reads the same EditorState, emits
InstanceMessage::StyleSpans (tree-sitter spans via the active
Theme), scoped + clipped to the FrontendEvent::Viewport byte range.
Emits nothing until a viewport is declared; suppresses
byte-identical frames (per-span delta encoding deferred to M11.4).
- CrdtState::version_scalar(): oplog version vector summed to a
monotonic non-decreasing u64 — the StyleSpans.generation anchor.
- daemon dispatcher: semantic_states map parallel to render_states;
projection selected per session. Semantic sessions get StyleSpans +
CursorByte + BufferSnapshot + CrdtOp + presence, never CellDelta /
grid Cursor. FrontendEvent::Viewport consumed (routed by
authenticated source). SessionEstablished body extracted to
handle_session_established (clippy 100-line ceiling). Grid-less
sessions no longer panic the _ => apply_event arm.
- InstanceCapabilities default semantic_render flipped to
cfg!(feature = "crdt") — the "M11.2 enables semantic" moment,
analogous to the M10.8 Day-4 flip. M11.1 negotiation test comment
updated for the flip (frontend-side default still false keeps M10.7
outcomes unperturbed).
Lib (1392 crdt / 1237 non-crdt) + integration suites green on both
feature flavors; clippy -D warnings clean on both.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Land the optimistic local-edit-application layer on top of the M10 CRDT
foundation: frontend-side rope replica with local edit application,
daemon-authoritative broadcast, and bidirectional cursor reconciliation.
Keystrokes feel instantaneous because the local replica answers next-render
queries before the daemon round-trip completes, while the daemon remains
the single source of truth for conflict resolution and broadcast to remote
replicas.
Architecture beats:
- BufferMirror (src/buffer_mirror.rs) holds a per-frontend rope replica
with explicit cursor-staleness tracking. Every event that may move the
active cursor or swap the active buffer marks the mirror stale; the
next CursorByte from the daemon clears it.
- CrdtOpOrigin {OptimisticReplica(FrontendId), DaemonKey} routes broadcast.
OptimisticReplica skips re-application on the originating frontend
(already applied locally); DaemonKey broadcasts to all replicas including
source -- covers Lua-driven and generated-buffer edits that bypass the
optimistic path.
- Generated buffers (*help*, *workers*, *pmacs-instance*, *errors*) funnel
apply_edit output through queue_daemon_origin_crdt_op so post-attach
CRDT upgrades don't drop their edits.
- forbid(unsafe_code) preserved throughout; loro 1.12 added as the CRDT
engine.
Audit posture: M10.10 shipped through six post-audit review rounds with
twenty-eight cumulative findings, most categorized as "incomplete
application of a prior round's mechanism." The audit doc records
grep-driven exhaustiveness as the standing countermeasure.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Land the Model Context Protocol (MCP) integration as a transport binding,
not a built-in feature. Six Lua functions plus userdata methods expose
the substance of three MCP feature areas (resources, tools, prompts), a
notification dispatcher, and a non-trivial AI-assistance example package
that meets the architectural ship gate (spec/pmacs-spec.tex:1572): zero
direct calls into the Rust core, zero special-cased MCP handling outside
the public API, source under 2000 lines of Lua.
The M9.5 -> M9.6 -> M9.7 -> M9.8 layered composition validates the claim
"AI is a transport binding, not a feature" -- pmacs-mcp-ai composes with
pmacs-mcp-prompts.render and inherits notification handling transitively
through M9.7's package, demonstrating that the AI domain is a layer
above MCP, not a thread woven through the core.
Subtask shape:
M9.1 stdio transport + initialize handshake + restart policy
M9.2 resources with in-flight + settled cache and per-uri invalidation
M9.3 tools with isError-vs-JSON-RPC-error semantics + cancellation
M9.4 prompts with required-argument validation
M9.5 notification dispatcher (on_notification, off_notification)
M9.6 tools-as-commands fixture package + 12 audit findings disposed
M9.7 prompts-as-result-buffers fixture package + tree-sitter-md grammar
M9.8 AI-assistance fixture package (363+ LoC; 17/17 acceptance tests)
M9.9 formal package audit -- PASS on all three criteria
M9.10 release: TRANSITION-M9.md + MCP-for-package-authors guide
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>