test(fold): address PR #142 review round 2 — pin the round-1 wiring
Round 2 correctly found the Finding-2/3 fixes were unpinned (reverting them left the suite green). Both are now bite-verified: - **Kill-path purge (Finding 2).** Replaced the direct `forget_buffer(id)` unit test with `killing_a_buffer_through_the_real_path_purges_its_fold_store`, which drives `pmacs.buffer.remove` — the production route through `after_buffer_removed` — and asserts the store is gone via the dead id (BufferIds never recycle). Mirrors config_registry's real-kill-path test. Bite-verified: reverting the `after_buffer_removed` fold branch turns it red. - **close-all point move (Finding 3).** Added `close_all_command_moves_point_to_enclosing_head`, which invokes the `fold.close-all` command with the point inside the second of two top-level fns and asserts the cursor landed on that fn's head-line content end (and both folds exist). Bite-verified: reverting close_all's `maybe_move_point` loop turns it red. - Ledger: `docs/active-work.md` folding lane now records PR #142 OPEN + the two landed review rounds (was "opens once the gate suite is green"). Correction to the round-1 gate report: the acceptance suite is **21** tests (round 1 was 20, not 24 — a tally slip), green under default and `--features crdt`. Full gate suite otherwise green (fmt, clippy --workspace --all-targets, git diff --check). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q5BkezMppbpCgGAYk2ftxV
This commit is contained in:
parent
9691dd4e9f
commit
036a994639
|
|
@ -61,18 +61,21 @@ If it does not, stop and repair the remote/fetch configuration.
|
||||||
- Framing head: revision 5 of `docs/folding-framing.md` (rev 1 → … → rev 4
|
- Framing head: revision 5 of `docs/folding-framing.md` (rev 1 → … → rev 4
|
||||||
absorbed three review rounds; rev 5 records approval + the Q#FD4 binding
|
absorbed three review rounds; rev 5 records approval + the Q#FD4 binding
|
||||||
decision).
|
decision).
|
||||||
- State: **APPROVED; Stage 1 (fold engine, headless) implementing on this
|
- State: **Stage 1 (fold engine, headless) implemented; PR #142 OPEN**,
|
||||||
branch.** Bindings decided (Q#FD4 → Emacs hideshow `C-c @` set); Bet B1
|
two review rounds landed on the branch. Bindings decided (Q#FD4 → Emacs
|
||||||
accepted as framed.
|
hideshow `C-c @` set); Bet B1 accepted as framed.
|
||||||
Load-bearing decision (Q#FD1): the bundled grammars ship no fold query and
|
Load-bearing decision (Q#FD1): the bundled grammars ship no fold query and
|
||||||
no `folds.scm`, so the roadmap's "tree-sitter fold ranges" is not free; v1
|
no `folds.scm`, so the roadmap's "tree-sitter fold ranges" is not free; v1
|
||||||
is structural node folding (block-like node ≥2 source lines, derived head
|
is structural node folding (block-like node ≥2 source lines, derived head
|
||||||
line, closer-aware tail), with indentation fallback and curated queries
|
line, closer-aware tail), with indentation fallback and curated queries
|
||||||
deferred. `FoldState` already exists in the protocol, declared-but-unproduced
|
deferred. `FoldState` already exists in the protocol; Stage 1 starts
|
||||||
(a test pins it is never emitted); no frontend consumes it yet; gutter
|
*producing* it (authoritative-empty), no protocol bump; gutter markers are
|
||||||
markers are frontend-derived like the diagnostic sign bars, so no new wire
|
frontend-derived like the diagnostic sign bars, so no new wire type. Staged
|
||||||
type. Staged like vterm: Stage 1 engine (headless), Stage 2 TUI, Stage 3 GPU.
|
like vterm: Stage 1 engine (headless), Stage 2 TUI, Stage 3 GPU.
|
||||||
- PR: Stage 1 opens as the first folding PR once the gate suite is green.
|
- PR: **#142** (`Arc 6 folding — Stage 1: instance fold engine`), open
|
||||||
|
against `main`. Round 1 (tail-boundary delete bug + buffer-kill cleanup +
|
||||||
|
close-all point-move) and round 2 (pin the kill-path + close-all through
|
||||||
|
the real command surface) are landed as fix commits on the branch.
|
||||||
- Next: land Stage 1; Stages 2/3 are separate branches/PRs, each re-framed
|
- Next: land Stage 1; Stages 2/3 are separate branches/PRs, each re-framed
|
||||||
in detail after the prior stage lands.
|
in detail after the prior stage lands.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -493,24 +493,60 @@ fn forget_drops_store_and_detaches_view() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn forget_buffer_drops_store_on_kill() {
|
fn killing_a_buffer_through_the_real_path_purges_its_fold_store() {
|
||||||
// The id-keyed reset the pmacs.buffer.kill path uses: the buffer (and
|
// Drive the production `pmacs.buffer.remove` route (not `forget_buffer`
|
||||||
// its attached view) is gone, so only the map entry is cleared.
|
// directly), so the app-data stash + the `after_buffer_removed` branch
|
||||||
|
// are what get exercised — deleting either would leave this red. The
|
||||||
|
// assertion reads through the DEAD id on purpose: BufferIds are never
|
||||||
|
// reused, so a stale id cannot alias a later buffer. Mirrors
|
||||||
|
// config_registry's `killing_a_buffer_through_the_real_path_...`.
|
||||||
|
let s = EditorState::new();
|
||||||
|
exec(
|
||||||
|
&s,
|
||||||
|
"b = pmacs.buffer.from_bytes('kill.rs', 'aaa\\nbbb\\nccc\\nddd\\n')",
|
||||||
|
);
|
||||||
|
let id = {
|
||||||
|
let core = s.core.borrow();
|
||||||
|
let reg = core.registry.borrow();
|
||||||
|
reg.find_by_name("kill.rs").expect("buffer")
|
||||||
|
};
|
||||||
|
// Fold via the data API (no parse tree needed) so the store exists.
|
||||||
|
let folded: bool = eval(&s, "return pmacs.fold.fold(b, { start = 3, ['end'] = 11 })");
|
||||||
|
assert!(folded);
|
||||||
|
assert!(
|
||||||
|
s.fold_registry.store(id).is_some(),
|
||||||
|
"store exists before kill"
|
||||||
|
);
|
||||||
|
|
||||||
|
exec(&s, "pmacs.buffer.remove(b)");
|
||||||
|
assert!(
|
||||||
|
s.fold_registry.store(id).is_none(),
|
||||||
|
"the real kill path purges the fold store"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn close_all_command_moves_point_to_enclosing_head() {
|
||||||
|
// Q#FD3 through the command surface: `fold.close-all` is interactive,
|
||||||
|
// so when it collapses a top-level fold around the invoking point, the
|
||||||
|
// point moves to that fold's head line (Finding 3, round 1).
|
||||||
let s = EditorState::new();
|
let s = EditorState::new();
|
||||||
let id = active_id(&s);
|
let id = active_id(&s);
|
||||||
insert_into(&s, id, "line0\nline1\nline2\n");
|
let src = "fn first() {\n a();\n b();\n}\nfn second() {\n c();\n d();\n}\n";
|
||||||
{
|
insert_into(&s, id, src);
|
||||||
let core = s.core.borrow();
|
install_rust_parse(&s, id);
|
||||||
let mut reg = core.registry.borrow_mut();
|
// Point inside the SECOND function's body.
|
||||||
s.fold_registry
|
s.core.borrow_mut().set_cursor_byte(byte_of(src, "c()"));
|
||||||
.store_or_attach(reg.get_mut(id).unwrap())
|
|
||||||
.lock()
|
exec(&s, "pmacs.command.invoke('fold.close-all')");
|
||||||
.unwrap()
|
|
||||||
.insert(ByteRange { start: 5, end: 11 });
|
let n: i64 = eval(&s, "return #pmacs.fold.folds(pmacs.window.buffer())");
|
||||||
}
|
assert_eq!(n, 2, "both top-level functions are folded");
|
||||||
assert!(s.fold_registry.store(id).is_some());
|
assert_eq!(
|
||||||
s.fold_registry.forget_buffer(id);
|
s.core.borrow().active_window().cursor,
|
||||||
assert!(s.fold_registry.store(id).is_none());
|
line_content_end_of(src, "fn second() {"),
|
||||||
|
"the invoking point moved to the enclosing fold's head line"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue