Merge pull request #128 from levineuwirth/handoff-2026-07-20
docs: refresh cross-machine continuity
This commit is contained in:
commit
f1a2f755a5
|
|
@ -0,0 +1,35 @@
|
||||||
|
# pmacs agent instructions
|
||||||
|
|
||||||
|
**Start here: read `docs/agent-handoff.md`, then
|
||||||
|
`docs/active-work.md`, before taking on any work.** The handoff carries
|
||||||
|
durable project state, working method, substrate invariants, and the
|
||||||
|
standing backlog. The active-work ledger carries volatile branches,
|
||||||
|
checkpoints, verification, and exact cross-machine recovery commands.
|
||||||
|
Keep both updated according to their own update protocols.
|
||||||
|
|
||||||
|
Always true, independent of the handoff:
|
||||||
|
|
||||||
|
- Rust core + Lua runtime (`builtin/runtime/*.lua`), TUI + GPU
|
||||||
|
(`pmacs-gpu`) frontends over a versioned semantic protocol
|
||||||
|
(`pmacs-protocol`). `#![forbid(unsafe_code)]`.
|
||||||
|
- Workflow: framing doc in `docs/` -> user approval -> branch -> implement
|
||||||
|
-> full gate suite -> PR -> user review rounds -> user says when to
|
||||||
|
merge. Never merge unprompted. One feature, one branch, one PR.
|
||||||
|
- Gates before any PR: `cargo fmt --check`; `cargo clippy --workspace
|
||||||
|
--all-targets -- -D warnings` (as its own step); `cargo test --lib`;
|
||||||
|
`cargo test --lib --features crdt`; the touched acceptance suites;
|
||||||
|
`cargo test --test m4_acceptance -- --skip basedpyright`;
|
||||||
|
`PMACS_REQUIRE_GPU=1 cargo test -p pmacs-gpu`; `git diff --check`.
|
||||||
|
- The checkout may be shared with the user: check `git status` for
|
||||||
|
foreign uncommitted work before stash, checkout, or branch operations,
|
||||||
|
and never delete untracked files you did not create.
|
||||||
|
- The canonical development URL is
|
||||||
|
`https://github.com/levineuwirth/pmacs.git`; recovery docs normalize
|
||||||
|
it to the local alias `githubsucks`. Remote names such as `origin` are
|
||||||
|
machine-local and carry no authority by themselves. Bootstrap/verify
|
||||||
|
the alias via `docs/active-work.md` before basing new work.
|
||||||
|
- Work is portable only after it is committed and pushed. Uncommitted
|
||||||
|
worktree changes, untracked files, and `/tmp` dependencies do not
|
||||||
|
travel to another machine.
|
||||||
|
- Write commit messages with `git commit -F <file>`. Never use
|
||||||
|
`git add .`.
|
||||||
33
CLAUDE.md
33
CLAUDE.md
|
|
@ -1,18 +1,19 @@
|
||||||
# pmacs — agent instructions
|
# pmacs agent instructions
|
||||||
|
|
||||||
**Start here: read `docs/agent-handoff.md` before taking on any work.**
|
**Start here: read `docs/agent-handoff.md`, then
|
||||||
It carries current project state, the working method, substrate
|
`docs/active-work.md`, before taking on any work.** The handoff carries
|
||||||
invariants, and the standing backlog — it is the continuity bridge
|
durable project state, working method, substrate invariants, and the
|
||||||
between development machines. Keep it updated as part of your work
|
standing backlog. The active-work ledger carries volatile branches,
|
||||||
(update protocol is in the file itself).
|
checkpoints, verification, and exact cross-machine recovery commands.
|
||||||
|
Keep both updated according to their own update protocols.
|
||||||
|
|
||||||
Always true, independent of the handoff:
|
Always true, independent of the handoff:
|
||||||
|
|
||||||
- Rust core + Lua runtime (`builtin/runtime/*.lua`), TUI + GPU
|
- Rust core + Lua runtime (`builtin/runtime/*.lua`), TUI + GPU
|
||||||
(`pmacs-gpu`) frontends over a versioned semantic protocol
|
(`pmacs-gpu`) frontends over a versioned semantic protocol
|
||||||
(`pmacs-protocol`). `#![forbid(unsafe_code)]`.
|
(`pmacs-protocol`). `#![forbid(unsafe_code)]`.
|
||||||
- Workflow: framing doc in `docs/` → user approval → branch → implement
|
- Workflow: framing doc in `docs/` -> user approval -> branch -> implement
|
||||||
→ full gate suite → PR → user's review rounds → user says when to
|
-> full gate suite -> PR -> user review rounds -> user says when to
|
||||||
merge. Never merge unprompted. One feature, one branch, one PR.
|
merge. Never merge unprompted. One feature, one branch, one PR.
|
||||||
- Gates before any PR: `cargo fmt --check`; `cargo clippy --workspace
|
- Gates before any PR: `cargo fmt --check`; `cargo clippy --workspace
|
||||||
--all-targets -- -D warnings` (as its own step); `cargo test --lib`;
|
--all-targets -- -D warnings` (as its own step); `cargo test --lib`;
|
||||||
|
|
@ -20,7 +21,15 @@ Always true, independent of the handoff:
|
||||||
`cargo test --test m4_acceptance -- --skip basedpyright`;
|
`cargo test --test m4_acceptance -- --skip basedpyright`;
|
||||||
`PMACS_REQUIRE_GPU=1 cargo test -p pmacs-gpu`; `git diff --check`.
|
`PMACS_REQUIRE_GPU=1 cargo test -p pmacs-gpu`; `git diff --check`.
|
||||||
- The checkout may be shared with the user: check `git status` for
|
- The checkout may be shared with the user: check `git status` for
|
||||||
foreign uncommitted work before stash/checkout/branch operations, and
|
foreign uncommitted work before stash, checkout, or branch operations,
|
||||||
never delete untracked files you didn't create.
|
and never delete untracked files you did not create.
|
||||||
- Commit messages via `git commit -F <file>`, ending with the Claude
|
- The canonical development URL is
|
||||||
co-author line; PR bodies end with the Claude Code attribution.
|
`https://github.com/levineuwirth/pmacs.git`; recovery docs normalize
|
||||||
|
it to the local alias `githubsucks`. Remote names such as `origin` are
|
||||||
|
machine-local and carry no authority by themselves. Bootstrap/verify
|
||||||
|
the alias via `docs/active-work.md` before basing new work.
|
||||||
|
- Work is portable only after it is committed and pushed. Uncommitted
|
||||||
|
worktree changes, untracked files, and `/tmp` dependencies do not
|
||||||
|
travel to another machine.
|
||||||
|
- Write commit messages with `git commit -F <file>`. Never use
|
||||||
|
`git add .`.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,137 @@
|
||||||
|
# Active work — cross-machine resume ledger
|
||||||
|
|
||||||
|
**Snapshot: 2026-07-21.** This file records volatile work that has not
|
||||||
|
landed on `main`. Read it after `docs/agent-handoff.md`. Remove completed
|
||||||
|
entries when their PR merges; do not let this become a second permanent
|
||||||
|
backlog.
|
||||||
|
|
||||||
|
## Repository authority
|
||||||
|
|
||||||
|
- Canonical development URL:
|
||||||
|
`https://github.com/levineuwirth/pmacs.git`. This ledger uses the
|
||||||
|
normalized local alias `githubsucks` so its refs and recovery commands
|
||||||
|
are identical on every machine. Remote names are otherwise
|
||||||
|
machine-local: `origin` may name this canonical URL, a release mirror,
|
||||||
|
or something else, and therefore has no authority by name alone.
|
||||||
|
- Canonical base at this snapshot:
|
||||||
|
`githubsucks/main` @ `2e37c04` (#127 merged; protocol v18).
|
||||||
|
- On the transfer source, `origin/main` named a release mirror at
|
||||||
|
`d3fa632` and lagged badly. On the current destination, `origin` names
|
||||||
|
the canonical URL. This difference is why all recovery begins by
|
||||||
|
verifying URLs and normalizing `githubsucks` rather than trusting
|
||||||
|
`origin/main`.
|
||||||
|
- The shared desktop checkout contained unrelated uncommitted work. The
|
||||||
|
branches below were prepared in isolated worktrees; never clean or
|
||||||
|
overwrite the shared checkout to recover them.
|
||||||
|
|
||||||
|
Start on another machine by inspecting its remotes:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git remote -v
|
||||||
|
git remote get-url githubsucks
|
||||||
|
```
|
||||||
|
|
||||||
|
If the second command says the alias is absent, add it; if it prints a
|
||||||
|
different URL, stop and resolve that collision rather than overwriting an
|
||||||
|
unknown remote:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git remote add githubsucks https://github.com/levineuwirth/pmacs.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Then recover current refs:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git fetch githubsucks --prune
|
||||||
|
git log -1 --oneline githubsucks/main
|
||||||
|
git worktree list
|
||||||
|
git status --short --branch
|
||||||
|
```
|
||||||
|
|
||||||
|
The first command must expose `2e37c04` or a newer intentional main.
|
||||||
|
If it does not, stop and repair the remote/fetch configuration.
|
||||||
|
|
||||||
|
|
||||||
|
## Vterm Stage 2 framing lane
|
||||||
|
|
||||||
|
- Portable branch: `githubsucks/vterm-framing`
|
||||||
|
- Approved framing head: `fb4f8f0`
|
||||||
|
- Base: canonical `main` @ `643d1e1` (Vterm Stage 1 / PR #126 merged).
|
||||||
|
`main` has since advanced to `2e37c04` (config registry #127, no
|
||||||
|
runtime overlap with vterm); cut the Stage 2 lane from current `main`,
|
||||||
|
not from `643d1e1`.
|
||||||
|
- State: `docs/vterm-framing.md` Revision 7 is framing-only, reviewed, and
|
||||||
|
approved for implementation. It closes the final `at_bottom`, terminal
|
||||||
|
`C-c` binding-reachability, and context-implicit Lua failure-mode findings.
|
||||||
|
There is no Stage 2 runtime implementation or PR yet.
|
||||||
|
- Next lane: create `pmacs-vterm-tui` / `vterm-tui` from current canonical
|
||||||
|
`main`, carry the approved framing as its first commit, then implement and
|
||||||
|
gate Stage 2. Do not implement on `vterm-framing`.
|
||||||
|
|
||||||
|
Recovery worktree on a machine that does not already own the branch:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git worktree add --track \
|
||||||
|
-b vterm-framing \
|
||||||
|
../pmacs-vterm-framing \
|
||||||
|
githubsucks/vterm-framing
|
||||||
|
```
|
||||||
|
|
||||||
|
## Parked lane: kill-ring browser + persistence
|
||||||
|
|
||||||
|
- Portable branch: `githubsucks/kill-ring-browser`
|
||||||
|
- Parked framing head: `503c489`
|
||||||
|
- State: framing only, revision 2; no implementation and no PR.
|
||||||
|
- Status: explicitly parked by the user on 2026-07-20.
|
||||||
|
- Its original scout was based on `0efb5cd`. The preserved framing marks
|
||||||
|
this ground truth stale and requires a complete re-scout against the
|
||||||
|
then-current `githubsucks/main` before implementation.
|
||||||
|
- Compile-mode has merged since the original scout, so old
|
||||||
|
“compile-mode in flight” keybinding/touch-set assumptions are not
|
||||||
|
authoritative.
|
||||||
|
|
||||||
|
Recovery worktree, only when the user un-parks it:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git worktree add --track \
|
||||||
|
-b kill-ring-browser \
|
||||||
|
../pmacs-kill-ring-browser \
|
||||||
|
githubsucks/kill-ring-browser
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation lane
|
||||||
|
|
||||||
|
- Portable branch: `githubsucks/handoff-2026-07-20`
|
||||||
|
- Carries synchronized `AGENTS.md` / `CLAUDE.md`, this ledger, the
|
||||||
|
durable handoff refresh, and the keybinding reference correction.
|
||||||
|
- It changes no runtime code.
|
||||||
|
- Review and merge this documentation branch separately; it must not be
|
||||||
|
folded into a feature framing branch.
|
||||||
|
- Now also absorbs both landed arcs: Vterm Stage 1 (#126) and the config
|
||||||
|
registry (#127). Canonical `main` is merged into it up to `2e37c04`,
|
||||||
|
so its diff against `main` is documentation only.
|
||||||
|
|
||||||
|
## Closed since the last snapshot
|
||||||
|
|
||||||
|
- **Config registry — MERGED as #127** (`main` @ `2e37c04`). Its lane
|
||||||
|
(`config-registry`, worktree `../pmacs-config-registry`) is done; the
|
||||||
|
branch is kept but carries nothing unmerged. Durable substrate facts
|
||||||
|
moved to `docs/agent-handoff.md` §1 per rule 3 below.
|
||||||
|
- Both this and Vterm Stage 1 ran as **concurrent lanes in sibling
|
||||||
|
worktrees off `main`**, with the shared files (`src/editor.rs`,
|
||||||
|
`src/lua_bindings/mod.rs`, `src/lib.rs`) assigned to one lane each in
|
||||||
|
advance. The rebase of the second lane onto the first had **zero
|
||||||
|
conflicts** — worth repeating for future parallel work, along with its
|
||||||
|
precondition: agree the file split before either lane starts, and keep
|
||||||
|
each lane's footprint in the other's files to a single line.
|
||||||
|
|
||||||
|
## Update protocol
|
||||||
|
|
||||||
|
Whenever a listed lane changes materially:
|
||||||
|
|
||||||
|
1. update its public branch and head/state here;
|
||||||
|
2. record new verification and remove superseded caveats;
|
||||||
|
3. keep durable architecture in `docs/agent-handoff.md`, not here;
|
||||||
|
4. remove the lane after merge or abandonment;
|
||||||
|
5. verify every recovery command from a clean worktree before calling
|
||||||
|
the transfer complete.
|
||||||
|
|
@ -1,18 +1,66 @@
|
||||||
# Agent handoff — cross-machine continuity
|
# Agent handoff — cross-machine continuity
|
||||||
|
|
||||||
**Last updated: 2026-07-21, after Vterm Stage 1 review round 2 was addressed
|
**Last updated: 2026-07-21, after the config registry (#127) and Vterm
|
||||||
and fully gated on `vterm-core` (awaiting merge authorization; not merged).
|
Stage 1 terminal core (#126) both landed on `main`, atop completed
|
||||||
Vterm Stages 2 and 3 are not implemented.** This file is the bridge between development
|
Themes Arc 4 (#120/#124/#125). Vterm Stages 2 and 3 are not
|
||||||
machines. If you are an agent reading on a fresh clone: this document
|
implemented.**
|
||||||
plus the `docs/*-framing.md` files ARE your memory. Read this fully
|
This file is the
|
||||||
before taking on work, seed persistent memory from it, and **update this
|
bridge between development machines. If you are an agent reading
|
||||||
file (and commit it) whenever project state changes materially** — the
|
this on a fresh clone: this document plus the `docs/*-framing.md`
|
||||||
next machine reads it the way you just did.
|
files ARE your memory. Read this fully before taking on work, seed
|
||||||
|
your persistent memory from it, and **update this file (and commit
|
||||||
|
it) whenever project state changes materially** — the next machine
|
||||||
|
reads it the way you just did.
|
||||||
|
|
||||||
|
For volatile branches, checkpoints, verification, and recovery
|
||||||
|
commands, read `docs/active-work.md` immediately after this file.
|
||||||
|
|
||||||
## 1. Where the project stands (2026-07-21)
|
## 1. Where the project stands (2026-07-21)
|
||||||
|
|
||||||
- Canonical `main` @ `7bc0c61` (#125 merged), protocol
|
- `main` @ `2e37c04` (config registry #127), protocol **v18**
|
||||||
**v18** (`SUPPORTED=[6..18]`).
|
(`SUPPORTED=[6..18]`; v16 = `ThemeFacts`, v17 = `FontFacts`, v18 =
|
||||||
|
`StatuslineSegments`).
|
||||||
|
- **Config registry LANDED — #127** (`docs/config-registry-framing.md`
|
||||||
|
rev 3; merge `2e37c04`; two review rounds). `pmacs.config` is the
|
||||||
|
typed, introspectable options registry the backlog ranked first, and
|
||||||
|
it closes the "config-registry-blocked" deferrals below. It was built
|
||||||
|
as a PARALLEL LANE alongside vterm in a sibling worktree; the files
|
||||||
|
were assigned per-lane up front and the rebase had zero conflicts.
|
||||||
|
- **Third registry** beside `CommandRegistry`/`HookRegistry`
|
||||||
|
(`src/config_registry.rs`), same R42/R50/duplicate-rejection/
|
||||||
|
`SourceLocation` vocabulary; Lua surface in
|
||||||
|
`src/lua_bindings/config.rs`. **No protocol change (still v18) and
|
||||||
|
ZERO changes to `src/editor.rs`.**
|
||||||
|
- **An override is ALWAYS stored**, even when equal to the value it
|
||||||
|
shadows; only `value_epoch` and listener dispatch key on effective
|
||||||
|
change. The "equal-value set is a no-op" reading silently voids a
|
||||||
|
buffer-local pin: nothing is stored, and a later global `set` flips
|
||||||
|
the very buffer the user pinned.
|
||||||
|
- **Two scopes: global and buffer-local.** `get(name, buf)` resolves
|
||||||
|
local → global → default; **`get(name)` resolves the GLOBAL CHAIN
|
||||||
|
ONLY** and never consults an ambient buffer. Per-language and
|
||||||
|
per-project are *patterns* (a hook calling `set_local`), not scopes
|
||||||
|
the registry knows about. Mode scope is impossible until the mode
|
||||||
|
system is wired — every editor `KeymapStack::resolve` passes `&[]`.
|
||||||
|
- Buffer-locals live in a registry side table purged at
|
||||||
|
`after_buffer_removed`, beside the keymap purge.
|
||||||
|
- Listeners: commit → snapshot → **drop the borrow** → re-enter Lua;
|
||||||
|
a raising listener is logged without blocking the rest or rolling
|
||||||
|
back; a depth bound turns a cycle into a pointed error. **Explicit
|
||||||
|
dispose only** — there is no `MetaMethod::Gc` anywhere in the
|
||||||
|
codebase, and GC timing differs between the two Lua backends.
|
||||||
|
- `StartupOnly` freezes off the existing `InitCompleteFlag` at write
|
||||||
|
time (which is why no `editor.rs` call was needed). In `--lib`
|
||||||
|
builds `set_init_complete` never runs, so a post-freeze test must
|
||||||
|
flip the flag explicitly or it passes vacuously.
|
||||||
|
- Adopters own their own `define`, so `SourceLocation` names the
|
||||||
|
owning module: `editing.auto-pair` (pair.lua, read against the
|
||||||
|
typed edit's SOURCE buffer), `editing.trim-on-save` (editops.lua,
|
||||||
|
read against the buffer being saved), `autosave.interval-ms`
|
||||||
|
(autosave.lua, re-read per tick). **The migration wrappers keep
|
||||||
|
their legacy coercion** — the registry is strict, the legacy setters
|
||||||
|
stay lenient (`trim_on_save("yes")`, `interval_ms(1500.7)`).
|
||||||
|
- `M-x describe-setting` renders into `*help*`.
|
||||||
- **Syntax-highlight / language-detection side-quest (#114–#118)
|
- **Syntax-highlight / language-detection side-quest (#114–#118)
|
||||||
LANDED** — a one-shot arc built in sibling worktrees off main while
|
LANDED** — a one-shot arc built in sibling worktrees off main while
|
||||||
the user's themes lane (`theme-faces`) ran concurrently in the shared
|
the user's themes lane (`theme-faces`) ran concurrently in the shared
|
||||||
|
|
@ -69,6 +117,16 @@ next machine reads it the way you just did.
|
||||||
`inline` node; matches tree-sitter-md's own splitter), and the wire
|
`inline` node; matches tree-sitter-md's own splitter), and the wire
|
||||||
flattener runs over the WHOLE buffer via the file-style summary, so it
|
flattener runs over the WHOLE buffer via the file-style summary, so it
|
||||||
must be an event sweep, not O(spans²).
|
must be an event sweep, not O(spans²).
|
||||||
|
- **JSON + YAML grammars and language servers (#123) LANDED** — bundled
|
||||||
|
ABI-current `tree-sitter-json` / `tree-sitter-yaml` cover `.json`,
|
||||||
|
`.yaml`, and `.yml`; the existing injection engine now highlights YAML
|
||||||
|
frontmatter and JSON/YAML fences. Default external LSP configs are the
|
||||||
|
pinned `vscode-json-language-server` provider and
|
||||||
|
`yaml-language-server`; configured settings are pushed after
|
||||||
|
`initialized`, which also supports push-model servers. The fake-server
|
||||||
|
delivery proof and PATH-gated live JSON/YAML provider smokes cover the
|
||||||
|
configuration contract. `.jsonc` / `.json5` remain a deliberate
|
||||||
|
follow-up because the JSON grammar is strict.
|
||||||
- **Compile-mode (Arc 5 stage 1, #113) LANDED** (2026-07-14, 7 rounds;
|
- **Compile-mode (Arc 5 stage 1, #113) LANDED** (2026-07-14, 7 rounds;
|
||||||
framing `docs/compile-mode-framing.md` rev 13). `compile.run` streams
|
framing `docs/compile-mode-framing.md` rev 13). `compile.run` streams
|
||||||
`/bin/sh -c "exec 2>&1; <cmd>"` into an intercept-read-only
|
`/bin/sh -c "exec 2>&1; <cmd>"` into an intercept-read-only
|
||||||
|
|
@ -120,13 +178,12 @@ next machine reads it the way you just did.
|
||||||
workspace sweep 2,718 passed across 78 suites (19 ignored,
|
workspace sweep 2,718 passed across 78 suites (19 ignored,
|
||||||
`basedpyright` filtered); `git diff --check` clean. Stage 3 landed
|
`basedpyright` filtered); `git diff --check` clean. Stage 3 landed
|
||||||
as #125 and completed Arc 4 on `main`.
|
as #125 and completed Arc 4 on `main`.
|
||||||
- **Vterm Stage 1 terminal core IMPLEMENTED ON `vterm-core`, FULLY GATED,
|
- **Vterm Stage 1 terminal core LANDED ON `main` — #126**
|
||||||
AWAITING MERGE AUTHORIZATION, NOT MERGED** (`docs/vterm-framing.md` rev 5).
|
(`docs/vterm-framing.md` rev 5; merge `643d1e1`).
|
||||||
- Implementation commits: `bbc1f33` (Stage 1), `962944b` (Darwin signal
|
- Implementation commits: `bbc1f33` (Stage 1), `962944b` (Darwin signal
|
||||||
normalization), first-review fixes `f0a235f`, `28f2e6c`, `bf972a7`, and
|
normalization), first-review fixes `f0a235f`, `28f2e6c`, `bf972a7`, and
|
||||||
second-review hardening `9797ada`; pull request: #126,
|
second-review hardening `9797ada`; reviewed feature head `fc4e0ce` merged
|
||||||
<https://github.com/levineuwirth/pmacs/pull/126> (open, non-draft,
|
through PR #126, <https://github.com/levineuwirth/pmacs/pull/126>.
|
||||||
targeting `main`).
|
|
||||||
- `AnsiParserProfile::{LineOriented, FullScreen}` preserves compile/REPL
|
- `AnsiParserProfile::{LineOriented, FullScreen}` preserves compile/REPL
|
||||||
behavior while terminal PTYs emit the full cursor/mode/device operation
|
behavior while terminal PTYs emit the full cursor/mode/device operation
|
||||||
set. `src/terminal/{screen,input,session}.rs` owns the state machine,
|
set. `src/terminal/{screen,input,session}.rs` owns the state machine,
|
||||||
|
|
@ -174,6 +231,10 @@ next machine reads it the way you just did.
|
||||||
authenticated source routing, protocol-owned wire types/limits, and a
|
authenticated source routing, protocol-owned wire types/limits, and a
|
||||||
deliberate complete-frame limit decision: 16 MiB is insufficient; use a
|
deliberate complete-frame limit decision: 16 MiB is insufficient; use a
|
||||||
measured legal-worst cap or aggregate bound, never silent chunking.
|
measured legal-worst cap or aggregate bound, never silent chunking.
|
||||||
|
- **PARKED: kill-ring browser + persistence.** Revision 2 framing is
|
||||||
|
preserved on branch `kill-ring-browser`, but its `0efb5cd` scout is stale
|
||||||
|
and must be repeated before implementation. No PR or implementation is
|
||||||
|
active.
|
||||||
- Roadmap: `docs/roadmap-2026-07.md` (ranked arcs). Position:
|
- Roadmap: `docs/roadmap-2026-07.md` (ranked arcs). Position:
|
||||||
- **Arc 1 (LSP utility surface) COMPLETE** — completion popup
|
- **Arc 1 (LSP utility surface) COMPLETE** — completion popup
|
||||||
(#92/#93), panels/references/outline/hover (#94–#96), plus
|
(#92/#93), panels/references/outline/hover (#94–#96), plus
|
||||||
|
|
@ -184,6 +245,16 @@ next machine reads it the way you just did.
|
||||||
- **Arc 3 (persistence) COMPLETE** — saveplace/recentf (#98),
|
- **Arc 3 (persistence) COMPLETE** — saveplace/recentf (#98),
|
||||||
desktop-save (#99), autosave/crash-recovery (#100), save-clobber
|
desktop-save (#99), autosave/crash-recovery (#100), save-clobber
|
||||||
fix (#101).
|
fix (#101).
|
||||||
|
- **Arc 4 (themes + extensibility) COMPLETE** — named UI faces (#120),
|
||||||
|
live GPU font preferences (#124), statusline providers (#125).
|
||||||
|
- **Arc 5 terminal stage ACTIVE** — compile mode (#113) and Vterm terminal
|
||||||
|
core (#126) landed; Vterm TUI is the next formal stage.
|
||||||
|
- **Config registry COMPLETE (#127)** — not a numbered arc; it was the
|
||||||
|
cross-cutting substrate ranked first on
|
||||||
|
`docs/side-quest-backlog.md`'s north star, and it unblocks the
|
||||||
|
editing/indent/comment items that were config-blocked.
|
||||||
|
- Remaining ranked arcs: 6 folding, 7 DAP, 8 GPU splits, plus the
|
||||||
|
`.ipynb` arc (its JSON-grammar prerequisite shipped in #123).
|
||||||
|
|
||||||
## 2. How we work (the part that must not drift)
|
## 2. How we work (the part that must not drift)
|
||||||
|
|
||||||
|
|
@ -292,8 +363,10 @@ buffer owns a path's recovery slot; only recover/discard release
|
||||||
unclaimed crash data; adopt clears the old owner's skip cache.
|
unclaimed crash data; adopt clears the old owner's skip cache.
|
||||||
|
|
||||||
**Protocol** — encoding-breaking bumps are deliberate and versioned
|
**Protocol** — encoding-breaking bumps are deliberate and versioned
|
||||||
(`SUPPORTED=[6..15]`). v15 = `CompletionPopup` + `StatusFacts.message`.
|
(`SUPPORTED=[6..18]`). v15 = `CompletionPopup` +
|
||||||
New wire surface ⇒ bump + both-frontends support + acceptance.
|
`StatusFacts.message`; v16 = `ThemeFacts`; v17 = `FontFacts`; v18 =
|
||||||
|
`StatuslineSegments`. New wire surface ⇒ bump + both-frontends support +
|
||||||
|
acceptance.
|
||||||
|
|
||||||
**Fake LSP** (`src/bin/pmacs_fake_lsp.rs`) modes: `fullonly`,
|
**Fake LSP** (`src/bin/pmacs_fake_lsp.rs`) modes: `fullonly`,
|
||||||
`rangeonly`, `rangeonly16` (UTF-16 + fail-closed bounds validation),
|
`rangeonly`, `rangeonly16` (UTF-16 + fail-closed bounds validation),
|
||||||
|
|
@ -337,6 +410,28 @@ New wire surface ⇒ bump + both-frontends support + acceptance.
|
||||||
in per-session baselines; and any daemon-side reset needs its
|
in per-session baselines; and any daemon-side reset needs its
|
||||||
frontend mirror audited in the same round (the GPU snapshot arm
|
frontend mirror audited in the same round (the GPU snapshot arm
|
||||||
missed search/menu/status the first time).
|
missed search/menu/status the first time).
|
||||||
|
- **Tab width is a rendering-parity bug, NOT a config gap** (scouted at
|
||||||
|
`7bc0c61` while framing #127; still true). There are FIVE tab-width
|
||||||
|
sites across TWO crates with TWO different values: `TAB_WIDTH = 8` in
|
||||||
|
`src/text_view.rs`, `src/highlight.rs`, `src/diag.rs` and
|
||||||
|
`src/completion.rs`, versus `advance_minimap_col` in
|
||||||
|
`pmacs-gpu/src/main.rs` expanding to **4** — and the GPU's main text
|
||||||
|
path expands tabs *not at all* (buffer bytes reach the frontend raw,
|
||||||
|
so a literal `\t` is shaped by the font). `editor.tab-width` is
|
||||||
|
therefore the obvious-looking first config adopter and is not one:
|
||||||
|
defining the setting cannot make the GPU honor it. Doing it properly
|
||||||
|
needs frontend tab expansion plus a wire-or-frontend-local decision.
|
||||||
|
Deferred from #127 on exactly these grounds; don't re-plan it as a
|
||||||
|
config task.
|
||||||
|
- **A test that never runs passes.** Two #127 review-round tests passed
|
||||||
|
vacuously at first: `pmacs.editor.save()` is the RAW save, while
|
||||||
|
`buffer.before-save` fires inside the `buffer.save` COMMAND
|
||||||
|
(`builtin/commands/default.lua`), and `save()` no-ops on an
|
||||||
|
unmodified buffer — so a fixture that opens a file and saves it
|
||||||
|
asserts on bytes nothing rewrote. Dirty the buffer with a real edit
|
||||||
|
and go through `pmacs.command.invoke("buffer.save")`. Caught only
|
||||||
|
because the *other* case failed and the cause was chased instead of
|
||||||
|
the assertion adjusted.
|
||||||
|
|
||||||
## 6. Named deferrals (the standing backlog, consolidated)
|
## 6. Named deferrals (the standing backlog, consolidated)
|
||||||
|
|
||||||
|
|
@ -347,8 +442,9 @@ mid-line comment spans, comment-dwim append-at-EOL, per-language
|
||||||
comment padding. Pairing (framing "Deferred"): wrap-region on opener,
|
comment padding. Pairing (framing "Deferred"): wrap-region on opener,
|
||||||
pair-aware backspace, RET-inside-pair closer-on-own-line,
|
pair-aware backspace, RET-inside-pair closer-on-own-line,
|
||||||
in-string/in-comment inhibit (needs node-at-byte `pmacs.parse`),
|
in-string/in-comment inhibit (needs node-at-byte `pmacs.parse`),
|
||||||
undo amalgamation (pair = one step), balance-aware quotes,
|
undo amalgamation (pair = one step), balance-aware quotes
|
||||||
per-buffer toggle (config-registry-blocked). Editops deferrals (full
|
(the per-buffer toggle SHIPPED in #127 as `editing.auto-pair`).
|
||||||
|
Editops deferrals (full
|
||||||
list in its framing): recenter (blocked on viewport facts — the GPU
|
list in its framing): recenter (blocked on viewport facts — the GPU
|
||||||
never consumes daemon `view_top`), Unicode case/word classes,
|
never consumes daemon `view_top`), Unicode case/word classes,
|
||||||
region-spanning move/duplicate, locale collation for sort-lines,
|
region-spanning move/duplicate, locale collation for sort-lines,
|
||||||
|
|
@ -364,7 +460,20 @@ origin-pinned `buffer.after-edit` fan-out (a context-switching
|
||||||
intercept changes what later callbacks — LSP, completion — observe).
|
intercept changes what later callbacks — LSP, completion — observe).
|
||||||
LSP/persistence: hidden-buffer LSP attach, daemon desktop-restore, the
|
LSP/persistence: hidden-buffer LSP attach, daemon desktop-restore, the
|
||||||
*warning* half of external-change detection (verify-visited-file-
|
*warning* half of external-change detection (verify-visited-file-
|
||||||
modtime), config registry (no unified config surface yet).
|
modtime).
|
||||||
|
Config registry (SHIPPED #127; these are its own named deferrals):
|
||||||
|
persistence of settings and the `custom-file` split-brain question,
|
||||||
|
`M-x list-settings` as a listview panel, a settings completion source
|
||||||
|
for the minibuffer (`minibuffer.read`'s `source` is a fixed Rust-side
|
||||||
|
vocabulary), table-valued settings (so `pmacs.lsp.config`,
|
||||||
|
`pmacs.pair.sets`, `pmacs.comment.strings` and the `pmacs.parse.*`
|
||||||
|
write-through proxies stay raw Lua), migrating the remaining scalar
|
||||||
|
setters (`async_config` ×2, `killring.max`, the `enable` booleans) and
|
||||||
|
`pmacs.gpu.set_font`, pending-set staging for names defined after
|
||||||
|
`init.lua` runs, and a `scope = "global"` define flag — `set_local` is
|
||||||
|
currently accepted for `autosave.interval-ms`, where a per-buffer
|
||||||
|
value is meaningless.
|
||||||
|
**Tab width is NOT a config gap** — see §5.
|
||||||
Highlight/detection (from the #114–#118 side-quest + injections #122):
|
Highlight/detection (from the #114–#118 side-quest + injections #122):
|
||||||
locals-query processing (run each grammar's LOCALS_QUERY so
|
locals-query processing (run each grammar's LOCALS_QUERY so
|
||||||
`#is?`/`#is-not? local` is honored instead of the current fail-closed
|
`#is?`/`#is-not? local` is honored instead of the current fail-closed
|
||||||
|
|
@ -377,18 +486,11 @@ runtime/Lua-registered languages (v1 resolves only against
|
||||||
`BUILTIN_LANGUAGES`), and the next injection *consumers* gated on new
|
`BUILTIN_LANGUAGES`), and the next injection *consumers* gated on new
|
||||||
grammars — HTML/CSS/GraphQL/SQL (`<script>`/`<style>`, JS/TS template
|
grammars — HTML/CSS/GraphQL/SQL (`<script>`/`<style>`, JS/TS template
|
||||||
literals, doc-comment code); modeline detection as a 5th layer
|
literals, doc-comment code); modeline detection as a 5th layer
|
||||||
(`-*- mode: … -*-` / `# vim: ft=…`);
|
(`-*- mode: … -*-` / `# vim: ft=…`);
|
||||||
byte-accurate multibyte cursor placement in `move_active_cursor_to`
|
byte-accurate multibyte cursor placement in `move_active_cursor_to`
|
||||||
(still steps one codepoint per LSP byte column). **JSON + YAML PR #123
|
(still steps one codepoint per LSP byte column). A full Jupyter `.ipynb`
|
||||||
OPEN** (grammar-gap style, `tree-sitter-json`/`-yaml`; LSP configs
|
setup (reader → editable → kernel execution) now has its JSON grammar
|
||||||
`vscode-json-language-server` with provider pin
|
prerequisite, but remains a real arc, not a one-shot.
|
||||||
`@t1ckbase/vscode-langservers-extracted@2.0.2`, plus
|
|
||||||
`yaml-language-server`). The public and checkpoint branches are both at
|
|
||||||
fully gated `5c202c5`, rebased onto `f8096ff`; the JSON and YAML
|
|
||||||
PATH-gated pmacs smokes both passed, and the PR awaits user review.
|
|
||||||
Once merged, YAML `---` and TOML `+++` markdown frontmatter highlight via
|
|
||||||
the #122 engine and the Jupyter reader → editable → kernel arc has both
|
|
||||||
grammar prerequisites.
|
|
||||||
GPU: auto-reconnect after daemon restart, splits/multi-buffer, gutter
|
GPU: auto-reconnect after daemon restart, splits/multi-buffer, gutter
|
||||||
riders (whitespace guides, folding, git markers).
|
riders (whitespace guides, folding, git markers).
|
||||||
Themes (full list in theme-faces framing rev 9 "Deferred (named)"):
|
Themes (full list in theme-faces framing rev 9 "Deferred (named)"):
|
||||||
|
|
@ -397,7 +499,8 @@ popup/menu/dropdown bg + selected-row faces, `ui.background` /
|
||||||
palette (+`ui.selection` for peer rects), `ui.inlay_hint` (needs the
|
palette (+`ui.selection` for peer rects), `ui.inlay_hint` (needs the
|
||||||
epoch treatment on its producer), wire alpha, `Indexed` palette
|
epoch treatment on its producer), wire alpha, `Indexed` palette
|
||||||
unification, named-theme registry / light theme / persistence
|
unification, named-theme registry / light theme / persistence
|
||||||
(config-registry-blocked), grid-vs-wire `default_style` asymmetry,
|
(the registry exists now, #127; theme persistence still waits on
|
||||||
|
settings persistence), grid-vs-wire `default_style` asymmetry,
|
||||||
mask widening (gutter bg, wash glyph recolor, statusline bg echo
|
mask widening (gutter bg, wash glyph recolor, statusline bg echo
|
||||||
surface, chrome bold/italic/underline re-shaping).
|
surface, chrome bold/italic/underline re-shaping).
|
||||||
Housekeeping: F-016 `lua_bindings/mod.rs` split paused mid-way
|
Housekeeping: F-016 `lua_bindings/mod.rs` split paused mid-way
|
||||||
|
|
@ -419,5 +522,7 @@ Don't expect them in a clone; on the desktop, never delete them.
|
||||||
When a PR merges, an arc opens/closes, or a decision lands: edit the
|
When a PR merges, an arc opens/closes, or a decision lands: edit the
|
||||||
snapshot (§1), append lessons (§5) and deferrals (§6) as they arise,
|
snapshot (§1), append lessons (§5) and deferrals (§6) as they arise,
|
||||||
bump the date line at the top, and commit — usually riding the same PR
|
bump the date line at the top, and commit — usually riding the same PR
|
||||||
as the work. Keep it under ~250 lines: this is a briefing, not a log;
|
as the work. Keep durable architecture here; put branch hashes,
|
||||||
prune sections that stop being true.
|
machine-local tools, incomplete verification, and recovery commands in
|
||||||
|
`docs/active-work.md`. This is a briefing, not a log: prune sections
|
||||||
|
that stop being true.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,379 @@
|
||||||
|
# pmacs keybindings — reference
|
||||||
|
|
||||||
|
**Last verified against `main` @ `f8096ff` (2026-07-20).** This is a
|
||||||
|
snapshot, not generated output — when a PR adds, removes, or rebinds a
|
||||||
|
key, update this file in the same PR (see §6). If you're an agent and
|
||||||
|
this file looks stale against the code it cites, trust the code.
|
||||||
|
|
||||||
|
pmacs keys come from two independent places:
|
||||||
|
|
||||||
|
- **The Lua keymap** (§1–2) — `pmacs.keymap.bind{...}` calls, resolved
|
||||||
|
by the Rust dispatcher against whatever `init.lua` has bound at
|
||||||
|
runtime. Fully user-rebindable: unbind or rebind any of these from
|
||||||
|
init.lua (§5).
|
||||||
|
- **Rust-hardcoded modal shadows** (§3) — isearch, query-replace,
|
||||||
|
the minibuffer/prompt, the completion popup, and the context menu
|
||||||
|
each shadow the Lua keymap while active: `EditorInstance::dispatch_key`
|
||||||
|
(`src/editor.rs:658-733`) checks these modes, highest-priority first,
|
||||||
|
before a key ever reaches the Lua dispatcher. **Not user-configurable**
|
||||||
|
— there is no `pmacs.keymap` surface for them; changing one means
|
||||||
|
editing the mode's `from_chord` decoder in Rust.
|
||||||
|
|
||||||
|
Notation matches what `pmacs.keymap.bind` accepts: `C-` = Ctrl, `M-` =
|
||||||
|
Alt/Meta, `S-` = Shift, bare letters/punctuation self-insert when
|
||||||
|
unmodified. Named keys are angle-bracketed (`<left>`, `<up>`, `<home>`)
|
||||||
|
or all-caps (`RET`, `BS`/Backspace, `DEL`/Delete, `TAB`, `SPC`).
|
||||||
|
Sequences separated by spaces (`C-x C-s`) are chords typed in order.
|
||||||
|
|
||||||
|
## 1. Global keymap
|
||||||
|
|
||||||
|
Source: `builtin/keymaps/default.lua` unless noted. All bound at
|
||||||
|
`scope = "global"`.
|
||||||
|
|
||||||
|
### Cursor motion
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `C-a` / `<home>` | `cursor.line-start` |
|
||||||
|
| `C-e` / `<end>` | `cursor.line-end` |
|
||||||
|
| `C-f` / `<right>` | `cursor.right` |
|
||||||
|
| `C-b` / `<left>` | `cursor.left` |
|
||||||
|
| `C-n` / `<down>` | `cursor.down` |
|
||||||
|
| `C-p` / `<up>` | `cursor.up` |
|
||||||
|
| `C-<left>` / `M-b` | `cursor.word-left` |
|
||||||
|
| `C-<right>` / `M-f` | `cursor.word-right` |
|
||||||
|
| `C-<up>` / `M-{` | `cursor.paragraph-up` |
|
||||||
|
| `C-<down>` / `M-}` | `cursor.paragraph-down` |
|
||||||
|
| `<pageup>` / `M-v` | `cursor.page-up` |
|
||||||
|
| `<pagedown>` / `C-v` | `cursor.page-down` |
|
||||||
|
| `M-g g` / `M-g M-g` | `cursor.goto-line` (`builtin/runtime/editops.lua`) |
|
||||||
|
|
||||||
|
### Selection (CUA shift-select)
|
||||||
|
|
||||||
|
Plain motion preserves an existing selection instead of dropping it
|
||||||
|
(Emacs-flavored default, not strict CUA).
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `S-<left>` / `S-<right>` | `cursor.select-left` / `cursor.select-right` |
|
||||||
|
| `S-<up>` / `S-<down>` | `cursor.select-up` / `cursor.select-down` |
|
||||||
|
| `S-<home>` / `S-<end>` | `cursor.select-line-start` / `cursor.select-line-end` |
|
||||||
|
| `C-S-<left>` / `C-S-<right>` | `cursor.select-word-left` / `cursor.select-word-right` |
|
||||||
|
| `C-S-<up>` / `C-S-<down>` | `cursor.select-paragraph-up` / `cursor.select-paragraph-down` |
|
||||||
|
|
||||||
|
### Editing
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `BS` | `buffer.delete-backward` |
|
||||||
|
| `DEL` / `C-d` | `buffer.delete-forward` |
|
||||||
|
| `RET` | `edit.newline-and-indent` |
|
||||||
|
| `TAB` | `buffer.tab` |
|
||||||
|
| `C-BS` / `C-h` | `buffer.delete-word-backward` (see §4 for the `C-h` rationale) |
|
||||||
|
| `M-BS` | `buffer.delete-word-backward` |
|
||||||
|
| `C-DEL` | `buffer.delete-word-forward` |
|
||||||
|
| `M-d` | `buffer.delete-word-forward` |
|
||||||
|
| `M-u` | `edit.upcase` (`editops.lua`) |
|
||||||
|
| `M-l` | `edit.downcase` (`editops.lua`) |
|
||||||
|
| `M-c` | `edit.capitalize` (`editops.lua`) |
|
||||||
|
| `C-t` | `edit.transpose-chars` (`editops.lua`) |
|
||||||
|
| `M-t` | `edit.transpose-words` (`editops.lua`) |
|
||||||
|
| `M-z` | `edit.zap-to-char` (`editops.lua`) |
|
||||||
|
| `M-<up>` / `M-<down>` | `edit.move-line-up` / `edit.move-line-down` (`editops.lua`) |
|
||||||
|
| `M-^` | `edit.join-line` (`editops.lua`) |
|
||||||
|
| `M-;` | `edit.toggle-comment` (`builtin/runtime/comment.lua`) |
|
||||||
|
|
||||||
|
> `M-d` / `M-BS` currently plain-delete the word — they are **not**
|
||||||
|
> kill-ring members yet (a named deferral; see `docs/agent-handoff.md`
|
||||||
|
> §6, "word kills"). `edit.kill-line` (below) is the only word/line
|
||||||
|
> kill wired into the ring so far.
|
||||||
|
|
||||||
|
### Clipboard & kill ring
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `M-w` | `edit.copy` |
|
||||||
|
| `C-w` | `edit.cut` |
|
||||||
|
| `C-y` | `edit.paste` |
|
||||||
|
| `C-x h` | `edit.select-all` (Emacs `mark-whole-buffer`) |
|
||||||
|
| `C-k` | `edit.kill-line` (`builtin/runtime/killring.lua`) |
|
||||||
|
| `M-y` | `edit.yank-pop` — replace the just-yanked text with the previous kill, immediately after `C-y` (`killring.lua`) |
|
||||||
|
|
||||||
|
### Undo / redo
|
||||||
|
|
||||||
|
Multiple bindings exist because terminals disagree on how `Ctrl+/`
|
||||||
|
encodes; see §4.
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `C-/` / `C-_` / `C-4` / `C-x u` | `buffer.undo` |
|
||||||
|
| `C-?` / `C-S-_` / `C-x r` | `buffer.redo` |
|
||||||
|
|
||||||
|
### Search & replace
|
||||||
|
|
||||||
|
Once a search is running, `C-s`/`C-r` step to the next/previous match
|
||||||
|
and `M-r` toggles literal↔regex — those are Rust-hardcoded isearch
|
||||||
|
keys, not Lua bindings (§3).
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `C-s` | `search.forward` (starts isearch) |
|
||||||
|
| `C-r` | `search.backward` (starts isearch) |
|
||||||
|
| `C-M-s` | `search.forward-regex` |
|
||||||
|
| `C-M-r` | `search.backward-regex` |
|
||||||
|
| `M-%` | `query-replace` (starts an interactive replace session, §3) |
|
||||||
|
| `C-M-%` | `query-replace-regexp` |
|
||||||
|
|
||||||
|
### Multi-key (`C-x`) chords
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `C-x C-s` | `buffer.save` |
|
||||||
|
| `C-x C-c` | `editor.quit` |
|
||||||
|
| `C-x 2` | `window.split-horizontal` |
|
||||||
|
| `C-x 3` | `window.split-vertical` |
|
||||||
|
| `C-x o` / `C-x O` | `window.focus-next` / `window.focus-prev` |
|
||||||
|
| `C-x 0` | `window.close` |
|
||||||
|
| `C-x 1` | `window.close-others` |
|
||||||
|
| `C-x b` | `editor.switch-buffer` |
|
||||||
|
| `C-x C-b` | `editor.list-buffers` (opens the `*buffer-list*` panel, §2) |
|
||||||
|
| `C-x <right>` / `C-x <left>` | `editor.next-buffer` / `editor.previous-buffer` |
|
||||||
|
| `C-x C-r` | `recent-files` (`builtin/runtime/recentf.lua`) |
|
||||||
|
|
||||||
|
### Command palette & cancellation
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `M-x` | `editor.execute-command` — prompts (via the minibuffer, §3) for any command by name |
|
||||||
|
| `C-g` | `editor.cancel` — resets the dispatcher / clears an unfinished prefix |
|
||||||
|
|
||||||
|
### Completion
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `C-M-i` | `completion.at-point` (`builtin/runtime/completion.lua`) — opens the popup; popup navigation is Rust-hardcoded (§3) |
|
||||||
|
|
||||||
|
### LSP
|
||||||
|
|
||||||
|
Source: `builtin/runtime/lsp.lua`. `M-.` follows the cross-editor
|
||||||
|
go-to-definition convention; the rest sit on the `C-c` prefix to keep
|
||||||
|
printable letters free for self-insert.
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `M-.` | `lsp.go-to-definition` |
|
||||||
|
| `M-?` | `lsp.find-references` (opens `*references*` panel, §2) |
|
||||||
|
| `M-,` | `lsp.jump-back` (unwind the cross-file jump ring) |
|
||||||
|
| `C-c o` | `lsp.document-symbols` (opens `*outline*` panel, §2) |
|
||||||
|
| `C-c r` | `lsp.rename` |
|
||||||
|
| `C-c a` | `lsp.code-actions` |
|
||||||
|
| `C-c i` | `lsp.inlay-hints` |
|
||||||
|
| `C-c y` | `lsp.semantic-tokens` |
|
||||||
|
| `C-c h` | `lsp.hover` |
|
||||||
|
| `C-c H` | `lsp.hover-doc` (opens `*lsp-help*` panel, §2) |
|
||||||
|
| `C-c s` | `lsp.signature-help` |
|
||||||
|
| `C-c f` | `lsp.format-buffer` |
|
||||||
|
|
||||||
|
`builtin/runtime/lsp.lua` initially binds `M-g n` / `M-g p` to
|
||||||
|
diagnostic navigation. `compile.lua` loads afterward and deliberately
|
||||||
|
replaces them with the unified error dispatcher below.
|
||||||
|
|
||||||
|
### Compile, shell command, and unified errors
|
||||||
|
|
||||||
|
Source: `builtin/runtime/compile.lua`.
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `M-g n` / `M-g p` | `error.next` / `error.previous` — compile/grep errors when that source has claimed navigation, otherwise LSP diagnostics |
|
||||||
|
| `` C-x ` `` | `error.next` |
|
||||||
|
| `M-!` | `shell.command` — asynchronous output in `*shell-command*` |
|
||||||
|
|
||||||
|
`compile.run` and `compile.recompile` are available through `M-x`; no
|
||||||
|
global key is assigned to them.
|
||||||
|
|
||||||
|
## 2. Buffer-local panel keymaps
|
||||||
|
|
||||||
|
Read-only panel buffers built on `pmacs.listview.open` (buffer scope
|
||||||
|
`{ scope = "buffer", buffer = <id> }`; see `builtin/runtime/listview.lua`)
|
||||||
|
all share one keymap:
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|---|---|
|
||||||
|
| `RET` / `SPC` | `listview.visit` — act on the item under the cursor |
|
||||||
|
| `n` / `<down>` | `cursor.down` |
|
||||||
|
| `p` / `<up>` | `cursor.up` |
|
||||||
|
| `g` | `listview.refresh` — re-run the data source and re-render |
|
||||||
|
| `q` | `listview.quit` — restore the buffer that was active before the panel opened |
|
||||||
|
|
||||||
|
Panels currently built on this: `*references*`, `*outline*`,
|
||||||
|
`*lsp-help*` (hover docs). Header text always spells out the same
|
||||||
|
`RET`/`n`/`p`/`g`/`q` legend inline.
|
||||||
|
|
||||||
|
`*buffer-list*` (`editor.list-buffers`, `C-x C-b`) uses its own
|
||||||
|
keymap, layered on the same idiom, in `builtin/commands/default.lua`:
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `RET` / `SPC` | `editor.buffer-list-visit` |
|
||||||
|
| `n` / `<down>` | `cursor.down` |
|
||||||
|
| `p` / `<up>` | `cursor.up` |
|
||||||
|
| `d` | `editor.buffer-list-mark-delete` |
|
||||||
|
| `u` | `editor.buffer-list-unmark` |
|
||||||
|
| `x` | `editor.buffer-list-execute` — kill every marked buffer |
|
||||||
|
| `k` | `editor.buffer-list-kill-now` |
|
||||||
|
| `g` | `editor.buffer-list-refresh` |
|
||||||
|
| `q` | `editor.buffer-list-quit` |
|
||||||
|
|
||||||
|
One-off buffer-local bindings, each scoped to a single generated
|
||||||
|
buffer:
|
||||||
|
|
||||||
|
| Buffer | Key | Command |
|
||||||
|
|---|---|---|
|
||||||
|
| `*workers*` (`editor.list-workers`) | `C-c C-k` | `workers.cancel-at-point` (`builtin/runtime/async.lua`) |
|
||||||
|
| `*pmacs-instance*` (`editor.describe-instance-buffer`) | `q` | `buffer.kill-this` (`commands/default.lua`) |
|
||||||
|
| `*help*` (`editor.describe-command`) | `q` | `buffer.kill-this` |
|
||||||
|
| REPL buffers (`builtin/packages/repl/init.lua`) | `RET` | `pmacs.repl.submit-current` |
|
||||||
|
| REPL buffers | `C-c` | `pmacs.repl.send-sigint-current` |
|
||||||
|
| REPL buffers | `C-d` | `pmacs.repl.send-eof-current` — closes stdin on an empty line, else deletes forward |
|
||||||
|
|
||||||
|
Compile-mode generated buffers (`*compilation*` and
|
||||||
|
`*shell-command*`) have their own buffer-local map:
|
||||||
|
|
||||||
|
| Key | Command |
|
||||||
|
|---|---|
|
||||||
|
| `RET` | `compile.visit-error` |
|
||||||
|
| `n` / `p` | `compile.next-error-line` / `compile.previous-error-line` |
|
||||||
|
| `q` | `compile.quit` |
|
||||||
|
| `C-c C-k` | `compile.kill` |
|
||||||
|
| `g` | `compile.recompile` (`*compilation*` only) |
|
||||||
|
| every shipped undo/redo chord | `compile.undo-noop` — generated output is intercept-read-only |
|
||||||
|
|
||||||
|
The REPL package (`builtin/packages/repl/`) is shipped but opt-in —
|
||||||
|
loaded via `require`, not part of the always-on `builtin/runtime`
|
||||||
|
lane. Its bindings only exist in a buffer created by a REPL session.
|
||||||
|
|
||||||
|
## 3. Rust-hardcoded modal keys
|
||||||
|
|
||||||
|
These live in `src/editor.rs` (and `src/minibuffer.rs` for the
|
||||||
|
prompt) as small `from_chord(chord) -> Action` decoders, one per mode,
|
||||||
|
checked in priority order by `EditorInstance::dispatch_key`
|
||||||
|
(`src/editor.rs:658-733`, highest first): **context menu → isearch →
|
||||||
|
query-replace → minibuffer → completion popup → normal Lua dispatch.**
|
||||||
|
Each decoder's rustdoc names its own key list; this table mirrors
|
||||||
|
those. They are not reachable through `pmacs.keymap` — there is
|
||||||
|
deliberately no Lua surface for them (keeps the set curated; see the
|
||||||
|
`R51` rationale cited in `lib.rs`/`lua_bindings/mod.rs`).
|
||||||
|
|
||||||
|
**Isearch** (`SearchKey`, `editor.rs:1858-1919`) — active after
|
||||||
|
`C-s`/`C-r`/`C-M-s`/`C-M-r`:
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|---|---|
|
||||||
|
| `C-s` / `<down>` | next match |
|
||||||
|
| `C-r` / `<up>` | previous match |
|
||||||
|
| `RET` / `C-m` | accept — keep cursor + highlights |
|
||||||
|
| `C-g` / `Esc` | cancel — restore the origin cursor |
|
||||||
|
| `BS` / `C-h` | shorten the query by one character |
|
||||||
|
| `M-r` | toggle literal ↔ regex |
|
||||||
|
| any printable char | extend the query |
|
||||||
|
|
||||||
|
**Query-replace** (`QueryReplaceKey`, `editor.rs:1926-1961`) — active
|
||||||
|
after `M-%`/`C-M-%`:
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|---|---|
|
||||||
|
| `y` / `SPC` | replace this match, advance |
|
||||||
|
| `n` / `BS` / `Delete` | skip this match, advance |
|
||||||
|
| `!` | replace this and every remaining match, no more prompts |
|
||||||
|
| `.` | replace this match, then quit |
|
||||||
|
| `q` / `RET` / `Esc` / `C-g` | quit (replacements already made are kept) |
|
||||||
|
|
||||||
|
**Minibuffer / prompt** (`MinibufferAction`, `minibuffer.rs:418-527`)
|
||||||
|
— backs every `pmacs.minibuffer.read` call: `M-x`, query-replace's
|
||||||
|
from/to prompts, `find-file`, etc.:
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|---|---|
|
||||||
|
| `RET` / `C-m` | accept |
|
||||||
|
| `C-g` | cancel |
|
||||||
|
| `TAB` / `C-i` | complete to the selected candidate |
|
||||||
|
| `<up>` / `<down>` | prev/next candidate if a dropdown is showing, else history navigation |
|
||||||
|
| `C-p` / `C-n` | history prev/next, unconditionally |
|
||||||
|
| `<left>` / `C-b`, `<right>` / `C-f` | cursor move |
|
||||||
|
| `<home>` / `C-a`, `<end>` / `C-e` | line start/end |
|
||||||
|
| `BS` | delete backward |
|
||||||
|
| `Delete` / `C-d` | delete forward |
|
||||||
|
| `M-n` / `M-p` | scroll the selected candidate forward/back |
|
||||||
|
| any other printable char | self-insert |
|
||||||
|
|
||||||
|
**In-buffer completion popup** (`CompletionPopupKey`,
|
||||||
|
`editor.rs:2019-2059`) — active after `C-M-i` or an LSP-triggered
|
||||||
|
popup. Unlike the others this is a **partial** shadow: only the keys
|
||||||
|
below are intercepted; everything else (typing, motion) falls through
|
||||||
|
to normal dispatch, so typing keeps self-inserting while the popup is
|
||||||
|
open.
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|---|---|
|
||||||
|
| `<down>` / `C-n` | next candidate |
|
||||||
|
| `<up>` / `C-p` | previous candidate |
|
||||||
|
| `TAB` / `RET` | accept the highlighted candidate |
|
||||||
|
| `Esc` / `C-g` | dismiss |
|
||||||
|
|
||||||
|
**Context menu** (`MenuKey`, `editor.rs:1966-2009`) — opened by
|
||||||
|
right-click, not a keybinding itself, but shadows the keymap while
|
||||||
|
open:
|
||||||
|
|
||||||
|
| Key | Action |
|
||||||
|
|---|---|
|
||||||
|
| `<down>` / `C-n` | next item |
|
||||||
|
| `<up>` / `C-p` | previous item |
|
||||||
|
| `RET` | invoke the highlighted item |
|
||||||
|
| `Esc` / `C-g` | cancel |
|
||||||
|
| any other key | dismiss (click-away semantics) |
|
||||||
|
|
||||||
|
**Frontend detach** — `F12` (any modifiers) detaches an attached
|
||||||
|
frontend from the daemon (`src/attach.rs:997-1006`, checked at
|
||||||
|
`attach.rs:818`). Not a UI mode inside the editor core, but another
|
||||||
|
literal-`KeyCode` interception outside the Lua keymap; tentative for
|
||||||
|
v0.1 per the comment there (chosen because F12 is rarely bound to
|
||||||
|
anything else).
|
||||||
|
|
||||||
|
## 4. Terminal-compatibility caveats
|
||||||
|
|
||||||
|
- **`C-h` doubles as `C-BS`.** Most terminals without the kitty
|
||||||
|
keyboard protocol can't disambiguate `Ctrl+Backspace` from
|
||||||
|
`Ctrl+H` — both legacy paths send byte `0x08`. `C-h` is bound to
|
||||||
|
`buffer.delete-word-backward` alongside `C-BS` so the shortcut works
|
||||||
|
on legacy terminals. pmacs does not use `C-h` as an Emacs-style help
|
||||||
|
prefix; a user who wants that can rebind it.
|
||||||
|
- **Undo/redo have redundant bindings** (`C-/`, `C-_`, `C-4` for undo;
|
||||||
|
`C-?`, `C-S-_` for redo) because terminals encode `Ctrl+/` several
|
||||||
|
different ways. Kitty's keyboard protocol routes most cleanly
|
||||||
|
through `C-/`; the alternates keep legacy/remote terminals working.
|
||||||
|
- Kitty-protocol-only chords (e.g. distinguishing `C-i` from `TAB`)
|
||||||
|
degrade gracefully where noted above — check the frontend's terminal
|
||||||
|
capability negotiation if a chord seems to not fire.
|
||||||
|
|
||||||
|
## 5. Changing bindings
|
||||||
|
|
||||||
|
`pmacs.keymap.bind` / `pmacs.keymap.unbind` are ordinary Lua API,
|
||||||
|
callable from `init.lua`:
|
||||||
|
|
||||||
|
```lua
|
||||||
|
pmacs.keymap.bind { scope = "global", sequence = "C-c g", command = "cursor.goto-line" }
|
||||||
|
pmacs.keymap.unbind { scope = "global", sequence = "M-z" }
|
||||||
|
```
|
||||||
|
|
||||||
|
`scope = "buffer"` additionally takes `buffer = <id>`; buffer-local
|
||||||
|
bindings are pruned automatically when that buffer is removed. This
|
||||||
|
covers §1 and §2 only — §3's Rust-hardcoded modal keys have no Lua
|
||||||
|
surface (see §3's intro).
|
||||||
|
|
||||||
|
## 6. Keeping this file honest
|
||||||
|
|
||||||
|
Update this file in the same PR whenever a binding is added, removed,
|
||||||
|
or moved — same discipline as `docs/agent-handoff.md`. To re-derive it
|
||||||
|
from scratch instead of trusting the table: grep `builtin/` for
|
||||||
|
`pmacs.keymap.bind`/`.bind(` and `pmacs.listview.open`, and grep
|
||||||
|
`src/editor.rs` / `src/minibuffer.rs` for `from_chord`.
|
||||||
|
|
@ -4,6 +4,10 @@ Date: 2026-07-07. Produced from a five-way codebase/docs/memory sweep
|
||||||
(core editing + persistence, LSP surface, GPU parity, extensibility +
|
(core editing + persistence, LSP surface, GPU parity, extensibility +
|
||||||
terminal, deferred-work inventory across all framing docs).
|
terminal, deferred-work inventory across all framing docs).
|
||||||
|
|
||||||
|
> **Historical planning snapshot.** Several arcs below have since
|
||||||
|
> landed. Use `docs/agent-handoff.md` for durable current state and
|
||||||
|
> `docs/active-work.md` for open branches and recovery instructions.
|
||||||
|
|
||||||
**Decision (2026-07-07): push Arc 1 (LSP utility surface), with Arc 2
|
**Decision (2026-07-07): push Arc 1 (LSP utility surface), with Arc 2
|
||||||
(editing table stakes) items interleaved between sub-arcs.**
|
(editing table stakes) items interleaved between sub-arcs.**
|
||||||
|
|
||||||
|
|
@ -50,64 +54,46 @@ modes; DAP debugging; GPU splits / multi-buffer / auto-reconnect.
|
||||||
|
|
||||||
## Arcs, ranked by value-per-effort
|
## Arcs, ranked by value-per-effort
|
||||||
|
|
||||||
### Arc 1 — LSP utility surface: "light up the dark matter" ← ACTIVE
|
### Arc 1 — LSP utility surface: "light up the dark matter" — COMPLETE
|
||||||
|
|
||||||
Data layer is done; only UI is missing.
|
The completion popup and LSP utility panels shipped across #92–#96,
|
||||||
|
followed by hardening in #102, #105, and #106. Completion, hover,
|
||||||
|
references, document symbols, and the supporting semantic/signature
|
||||||
|
paths are now wired through both frontend contracts.
|
||||||
|
|
||||||
- **1a. In-buffer completion popup** (first). Trigger on typing,
|
### Arc 2 — Editing table stakes — COMPLETE
|
||||||
TAB/RET accept, both frontends. GPU needs a wire message — mirror the
|
|
||||||
minibuffer-dropdown pattern (protocol v12). Framework + popup view
|
|
||||||
already exist.
|
|
||||||
- **1b. Panels**: hover popup, code-action picker, references list,
|
|
||||||
document-symbol outline. Generalize the buffer-list UI pattern
|
|
||||||
(`*buffer-list*` buffer-local bindings) into a reusable list-buffer
|
|
||||||
idiom.
|
|
||||||
- **1c. Semantic-token auto-pull fix** (small): pull on attach + on
|
|
||||||
edit-flush, like inlay hints already do.
|
|
||||||
- **1d. Signature-help auto-trigger** on `(`.
|
|
||||||
|
|
||||||
### Arc 2 — Editing table stakes (interleave with Arc 1)
|
Query-replace (#97), the real kill ring and `M-y` (#103/#105/#106),
|
||||||
|
comment toggle (#107), auto-indent (#109), and auto-pairing (#110)
|
||||||
|
landed.
|
||||||
|
|
||||||
Each small, core-only, frontend-agnostic: query-replace (isearch
|
### Arc 3 — Persistence/serialization — COMPLETE
|
||||||
exists; `search.rs` has no replace API), real kill ring + `M-y`,
|
|
||||||
comment/uncomment, auto-indent on newline, auto-pairing.
|
|
||||||
|
|
||||||
### Arc 3 — Persistence/serialization
|
Saveplace/recentf (#98), desktop save/restore (#99),
|
||||||
|
autosave/crash-recovery (#100), and the save-clobber fix (#101) landed.
|
||||||
|
|
||||||
Desktop-save (buffer set + layout + cursors → restore), recentf,
|
### Arc 4 — Themes + extensibility surface — COMPLETE
|
||||||
saveplace, autosave + crash recovery, optional backups. Generalize the
|
|
||||||
`$XDG_STATE_HOME/pmacs/` pattern from minibuffer history. Framing
|
|
||||||
question: what is a "session" in a daemon world; do CRDT snapshots
|
|
||||||
ride along.
|
|
||||||
|
|
||||||
### Arc 4 — Themes + extensibility surface — COMPLETE ON `main`
|
Stages 1–3 landed as #120, #124, and #125: named `ui.*` faces with
|
||||||
|
daemon-resolved `ThemeFacts`; the live global `pmacs.gpu.set_font`
|
||||||
|
preference at protocol v17; and composable per-window
|
||||||
|
`pmacs.statusline` providers transported to semantic/GPU frontends by
|
||||||
|
protocol-v18 `StatuslineSegments`.
|
||||||
|
|
||||||
All three stages landed: #120 added named `ui.*` faces and daemon-resolved
|
### Arc 5 — Terminal, staged — VTERM CORE LANDED
|
||||||
`ThemeFacts`; #124 added the live global `pmacs.gpu.set_font` preference at
|
|
||||||
protocol v17; and #125 added composable `pmacs.statusline` providers,
|
|
||||||
per-window TUI composition, a pure built-in LSP segment, dynamic modeline
|
|
||||||
faces, and semantic/GPU transport through protocol v18.
|
|
||||||
|
|
||||||
### Arc 5 — Terminal, staged — VTERM STAGE 1 ON FEATURE BRANCH
|
- **Compile mode landed in #113**: line-oriented PTY/ANSI output,
|
||||||
|
|
||||||
- **Compile-mode landed** in #113: line-oriented PTY/ANSI output,
|
|
||||||
error-regex navigation, and `M-x compile`.
|
error-regex navigation, and `M-x compile`.
|
||||||
- **Vterm Stage 1 terminal core** is implemented, two review rounds are
|
- **Vterm Stage 1 terminal core landed in #126**: compatibility parser
|
||||||
addressed, and the branch is fully gated on `vterm-core`; PR #126 awaits
|
profiles, bounded VT screen/scrollback/reflow state, input encoders,
|
||||||
merge authorization and is **not merged**. It adds compatibility parser
|
internal `TerminalManager`, read-only identity buffers, process lifecycle,
|
||||||
profiles, bounded VT screen/scrollback/reflow state, IND/NEL/RI, input
|
control-free renderer-boundary cells, and headless real-PTY acceptance.
|
||||||
encoders, internal `TerminalManager`, read-only identity buffers, process
|
- **Vterm Stage 2 TUI is next**: terminal-window composition, input/resize,
|
||||||
lifecycle, renderer-safe control-free cells, and headless real-PTY
|
per-context scroll/selection/copy, and the Lua surface.
|
||||||
acceptance. It intentionally adds no interactive Lua command or frontend
|
- **Vterm Stage 3 protocol/GPU follows Stage 2**: additive protocol v19
|
||||||
rendering.
|
complete frames, authenticated daemon routing, and native GPU terminal
|
||||||
- **Vterm Stage 2 TUI** starts only after Stage 1 merges: terminal-window
|
rendering. Its framing must resolve the 16 MiB transport cap's incompatibility
|
||||||
composition, input/resize, per-context scroll/selection/copy, and the Lua
|
with the legal worst complete terminal frame; never silently chunk.
|
||||||
surface.
|
|
||||||
- **Vterm Stage 3 protocol/GPU** starts only after Stage 2 merges: additive
|
|
||||||
protocol v19 complete frames, authenticated daemon routing, and native GPU
|
|
||||||
cell rendering. Its framing must resolve the current 16 MiB transport cap's
|
|
||||||
incompatibility with the legal worst complete terminal frame; never silently
|
|
||||||
chunk.
|
|
||||||
|
|
||||||
### Arc 6 — Folding (keystone gutter rider)
|
### Arc 6 — Folding (keystone gutter rider)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,11 @@ The direct continuation of the #114–#118 grammar/detection stack.
|
||||||
(`<script>`/`<style>`, template literals, doc-comment code).
|
(`<script>`/`<style>`, template literals, doc-comment code).
|
||||||
- **Modeline detection** — a 5th detection layer (`-*- mode: … -*-`,
|
- **Modeline detection** — a 5th detection layer (`-*- mode: … -*-`,
|
||||||
`# vim: ft=…`) after extension → filetype → filename → shebang.
|
`# vim: ft=…`) after extension → filetype → filename → shebang.
|
||||||
- **JSON + YAML** grammars + LSP (`tree-sitter-json` +
|
- **JSON + YAML — PR #123 open.** Grammars and LSP configs exist on the
|
||||||
vscode-json-languageserver; yaml-language-server). JSON is also the
|
feature line; review fixes are preserved on
|
||||||
prerequisite for the notebook path.
|
`json-yaml-handoff-2026-07-20`. A real YAML-through-pmacs smoke,
|
||||||
|
rebase, and full gates remain before review resumes. JSON is also the
|
||||||
|
prerequisite for the notebook path; see `docs/active-work.md`.
|
||||||
- **More grammars for languages with neither grammar nor LSP** — ruby,
|
- **More grammars for languages with neither grammar nor LSP** — ruby,
|
||||||
php, html, css, sql, etc.
|
php, html, css, sql, etc.
|
||||||
- **Grapheme / combining-mark awareness** in the text view
|
- **Grapheme / combining-mark awareness** in the text view
|
||||||
|
|
@ -112,10 +114,20 @@ The direct continuation of the #114–#118 grammar/detection stack.
|
||||||
|
|
||||||
## Cross-cutting substrate (unblocks whole clusters — high leverage)
|
## Cross-cutting substrate (unblocks whole clusters — high leverage)
|
||||||
|
|
||||||
- **Config/settings registry** (`pmacs.config` / defcustom) — blocks
|
- ~~**Config/settings registry**~~ — **SHIPPED as #127.** `pmacs.config`
|
||||||
language-aware indent, the five hardcoded tab-width sites, per-buffer
|
exists with global + buffer-local scopes. Unblocked: the per-buffer
|
||||||
auto-pair toggle, per-language comment padding, per-project compile
|
auto-pair toggle (shipped in the same PR as `editing.auto-pair`),
|
||||||
commands.
|
language-aware indent, per-language comment padding, and per-project
|
||||||
|
compile commands — the last three are now ordinary work, expressed as
|
||||||
|
a `buffer.after-load` hook calling `set_local`, not blocked work.
|
||||||
|
- **Tab-width rendering parity** — was listed above as a config
|
||||||
|
consequence; it is not. `TAB_WIDTH = 8` appears four times in the
|
||||||
|
daemon (`text_view`, `highlight`, `diag`, `completion`), the GPU
|
||||||
|
minimap's `advance_minimap_col` uses **4**, and the GPU main text path
|
||||||
|
expands tabs *not at all* — raw `\t` reaches glyphon and is shaped by
|
||||||
|
the font. Defining `editor.tab-width` cannot make the GPU honor it;
|
||||||
|
this needs frontend tab expansion plus a wire-or-frontend-local
|
||||||
|
decision. Deferred from #127 on those grounds.
|
||||||
- **Real `read_only` buffer flag** on both edit paths — true immutability
|
- **Real `read_only` buffer flag** on both edit paths — true immutability
|
||||||
for panels / REPL / generated buffers.
|
for panels / REPL / generated buffers.
|
||||||
- **Mode system wiring** — dispatch passes an empty mode list (`&[]`);
|
- **Mode system wiring** — dispatch passes an empty mode list (`&[]`);
|
||||||
|
|
@ -211,8 +223,8 @@ domain excluded — see below), `editor.rs` split (7 k lines),
|
||||||
|
|
||||||
## Excluded — themes / faces main quest (seen, routed elsewhere)
|
## Excluded — themes / faces main quest (seen, routed elsewhere)
|
||||||
|
|
||||||
`pmacs.gpu.set_font` + the statusline-segment API; background/selection
|
The statusline-segment API (Arc 4 stage 3; framing awaiting review);
|
||||||
theming; per-peer stable presence colors; exact quad colors per
|
background/selection theming; per-peer stable presence colors; exact quad colors per
|
||||||
decoration kind; GPU gutter background layer / wash recolor / chrome
|
decoration kind; GPU gutter background layer / wash recolor / chrome
|
||||||
bold-italic-underline; current-line highlight refinements; multi-server
|
bold-italic-underline; current-line highlight refinements; multi-server
|
||||||
semantic-token *style* blending; the compile-mode **severity→color**
|
semantic-token *style* blending; the compile-mode **severity→color**
|
||||||
|
|
@ -225,14 +237,20 @@ guides (visual, not color).
|
||||||
|
|
||||||
## North star (highest-leverage first)
|
## North star (highest-leverage first)
|
||||||
|
|
||||||
**Multi-language injections shipped (#122)** — one of the two original
|
**Both original north-star items have now shipped** — multi-language
|
||||||
north-star items is done, so the highest-leverage board is now:
|
injections (#122) and the config registry (#127) — and JSON + YAML
|
||||||
|
(#123) merged too. The remaining board:
|
||||||
|
|
||||||
1. **Config registry** — frees ~5 editing/indent/comment features at once.
|
1. **Locals-query processing** — restores `.builtin` styling for
|
||||||
2. **JSON (+ YAML) grammar** — the one remaining gate on the Jupyter
|
|
||||||
`.ipynb` path now that injections exist, and a clean highlight one-shot.
|
|
||||||
3. **Locals-query processing** — restores `.builtin` styling for
|
|
||||||
non-shadowed builtins, the last rough edge of the highlight stack.
|
non-shadowed builtins, the last rough edge of the highlight stack.
|
||||||
|
2. **Mode-system wiring** — every editor `KeymapStack::resolve` still
|
||||||
|
passes `&[]`, so mode-scoped keybindings and any mode-scoped setting
|
||||||
|
remain unreachable. Promoted here because #127 made it the largest
|
||||||
|
remaining scoping gap.
|
||||||
|
3. **Tab-width rendering parity** — five constants across two crates
|
||||||
|
with two different values, and no tab expansion at all on the GPU
|
||||||
|
main text path. Explicitly NOT a config-registry task; see the entry
|
||||||
|
under "Cross-cutting substrate".
|
||||||
|
|
||||||
Beyond those, the cleanest one-shots in the highlight family are
|
Beyond those, the cleanest one-shots in the highlight family are
|
||||||
**modeline detection** and the HTML/CSS grammars that light up more
|
**modeline detection** and the HTML/CSS grammars that light up more
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,7 @@
|
||||||
# Vterm — framing (Arc 5 stage 2, three-PR delivery)
|
# Vterm — framing (Arc 5 stage 2, three-PR delivery)
|
||||||
|
|
||||||
**Revision 5 — 2026-07-21. Status: Stage 1 review round 2 fixes implemented
|
**Revision 5 — 2026-07-21. Status: Stage 1 landed on `main` through PR #126
|
||||||
and fully gated on `vterm-core`; awaiting merge authorization, not merged.
|
at merge `643d1e1`. Stages 2 and 3 are not implemented.**
|
||||||
Stages 2 and 3 are not implemented.**
|
|
||||||
|
|
||||||
Revision 5 establishes the renderer-facing cell invariant before Stage 2:
|
Revision 5 establishes the renderer-facing cell invariant before Stage 2:
|
||||||
terminal text discards C0/C1 controls rather than storing host-terminal control
|
terminal text discards C0/C1 controls rather than storing host-terminal control
|
||||||
|
|
@ -37,11 +36,10 @@ later stage starts only after the preceding stage lands on `main`.
|
||||||
|
|
||||||
## 0. Revision 5 — Stage 1 implementation and review record
|
## 0. Revision 5 — Stage 1 implementation and review record
|
||||||
|
|
||||||
The first of the three vterm PRs is implemented, reviewed, and fully gated on
|
The first of the three vterm PRs is implemented, reviewed, fully gated, and
|
||||||
`vterm-core`, awaiting merge authorization. Initial feature commit `bbc1f33`,
|
landed on `main` as merge `643d1e1`. Initial feature commit `bbc1f33`,
|
||||||
first-review fixes through `bf972a7`, and second-review hardening `9797ada` are
|
first-review fixes through `bf972a7`, and second-review hardening `9797ada`
|
||||||
published as open, non-draft PR #126,
|
shipped through PR #126, <https://github.com/levineuwirth/pmacs/pull/126>.
|
||||||
<https://github.com/levineuwirth/pmacs/pull/126>, targeting `main`.
|
|
||||||
It is deliberately headless: there is no `pmacs.terminal` Lua module,
|
It is deliberately headless: there is no `pmacs.terminal` Lua module,
|
||||||
interactive terminal command, TUI paint branch, or GPU/protocol surface yet.
|
interactive terminal command, TUI paint branch, or GPU/protocol surface yet.
|
||||||
|
|
||||||
|
|
@ -837,16 +835,13 @@ Per-stage utilization:
|
||||||
|
|
||||||
## 8. Branch and PR plan
|
## 8. Branch and PR plan
|
||||||
|
|
||||||
Stage 1 is implemented and fully gated on `vterm-core`; review fixes through
|
Stage 1 landed on `main` through PR #126 at merge `643d1e1`. Continue the
|
||||||
`bf972a7` are published in open, non-draft PR #126
|
approved sequential plan:
|
||||||
(<https://github.com/levineuwirth/pmacs/pull/126>), targeting `main` and not
|
|
||||||
merged. Continue the approved plan only after the preceding PR lands:
|
|
||||||
|
|
||||||
1. review PR #126; merge only when the user says;
|
1. create `pmacs-vterm-tui`, branch `vterm-tui`, from post-#126 `main`;
|
||||||
2. after stage 1 merges, create `pmacs-vterm-tui`, branch `vterm-tui`, from the
|
implement, gate, and open the second PR;
|
||||||
new `main`; implement/gate/open a second PR;
|
2. after Stage 2 merges, create `pmacs-vterm-gpu`, branch `vterm-gpu`, from
|
||||||
3. after stage 2 merges, create `pmacs-vterm-gpu`, branch `vterm-gpu`, from the
|
the new `main`; implement, gate, and open the third PR.
|
||||||
new `main`; implement/gate/open the third PR.
|
|
||||||
|
|
||||||
The framing branch is `vterm-framing` in worktree `pmacs-vterm-framing`.
|
The framing branch is `vterm-framing` in worktree `pmacs-vterm-framing`.
|
||||||
Implementation branches are not stacked across an unmerged parent. This avoids
|
Implementation branches are not stacked across an unmerged parent. This avoids
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue