Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 8 additions & 14 deletions src/webview/cm/bounded-recompute.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,8 @@
// Small pure leaf helpers shared by the block-widget StateFields that maintain
// their DecorationSet / model list with a changed-range-bounded recompute
// (image/image-field.ts, table/table-field.ts, table/table-skeleton.ts — see
// PERF.md). These were byte-identical copies in each file; they live here as ONE
// implementation so the copies cannot drift.
//
// SCOPE: only the small, self-contained leaf helpers are shared. A full
// `defineBlockWidgetField` FACTORY over `buildAll` was deliberately REJECTED as
// leaky — the table field's global ordinal makes bounding it genuinely different
// from the image field's widget-reuse — and is gated on a future third consumer
// (the list block widget). See the recorded decision in
// .claude/docs/TODO-archive.md. Do NOT grow this module into that factory.
// Small pure leaf helpers shared by the changed-range-bounded StateFields. The
// structural block widgets (table, image) use them through the contract module
// structural-blocks.ts, which owns the bounded update itself; this module stays the
// home of the leaf helpers only, so the field logic lives in exactly one place
// (structural-blocks.ts) and the helpers cannot drift between consumers.
//
// The three fold-gutter fields (fold/index.ts), the callout marker-conceal field
// (decorations/callout-marker-conceal.ts), and fenced-code collapse
Expand Down Expand Up @@ -71,8 +64,9 @@ export function intersects(intervals: readonly Interval[], from: number, to: num
}

/** True iff any range in `selection` overlaps the inclusive `[from, to]` line
* interval. `from`/`to` are line-aligned by the block fields' `buildAll`, so
* the closed interval catches caret-at-boundary cases half-open would miss.
* interval. `from`/`to` are line-aligned by each structural block's `build`
* (structural-blocks.ts `validRange`), so the closed interval catches
* caret-at-boundary cases half-open would miss.
* A `SelectionRange` is structurally `{ from, to }`, so this is just
* `intersects` over the selection's ranges. */
export function lineRangeOverlapsSelection(
Expand Down
13 changes: 7 additions & 6 deletions src/webview/cm/fenced-code/fenced-code-collapse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
// zone is non-atomic, and reachability is the auto-expand's job — not the generic
// blockZoneArrowKeymap's.

import { syntaxTreeAvailable } from "@codemirror/language";
import type { EditorState, Transaction } from "@codemirror/state";
import type { DecorationSet } from "@codemirror/view";
import { type Interval, lineExpandWithNeighbours, mergeIntervals } from "../bounded-recompute.js";
Expand All @@ -29,6 +28,7 @@ import {
} from "../collapse/line-collapse-field.js";
import { leadingFrontmatterEnd } from "../frontmatter/detect.js";
import { linkReferenceRisk } from "../link-reference-risk.js";
import { frontierIncomplete } from "../structural-guard.js";
import { fencedCollapseTarget } from "./fenced-code-collapse-state.js";
import { FencedCollapseToggleWidget } from "./fenced-code-collapse-widget.js";

Expand Down Expand Up @@ -195,11 +195,12 @@ function computeExtendedSpan(tr: Transaction): Interval[] {
}

// The fenced policy, unchanged: GF structural edit or a top-level boundary move → full;
// an incomplete parse → full (G2 frontier); otherwise changed lines ±1 ∪ selection lines.
// an incomplete parse frontier → full; otherwise changed lines ±1 ∪ selection lines.
//
// The frontier gate (an incomplete parse can reveal nodes outside the span) is spelled out
// here, and the structural-reparse fallback is deliberately NOT part of this condition, the
// way it is in the fields that import ../structural-guard.js: `touchesStructural` /
// The frontier gate (an incomplete parse can reveal nodes outside the span, and records
// built over a partial snapshot miss blocks beyond it) is the shared `frontierIncomplete`,
// but the structural-reparse half of `requiresFullBoundedRebuild` is deliberately NOT part
// of this condition, the way it is in the other bounded fields: `touchesStructural` /
// `topLevelBoundaryRisk` above already return "full" before the gate is reached. That guard
// is narrower than the shared predicate on purpose — this field's hot path is editing INSIDE
// a code fence, where a `#` comment or a `___` line must stay bounded; ../structural-guard.ts's
Expand All @@ -221,7 +222,7 @@ const fencedCollapseSpec: LineCollapseSpec<null> = {
leadingFrontmatterEnd(tr.startState) !== leadingFrontmatterEnd(tr.state) ||
touchesStructural(tr) ||
topLevelBoundaryRisk(tr, prevBlocks) ||
!syntaxTreeAvailable(tr.state, tr.state.doc.length)
frontierIncomplete(tr)
? "full"
: computeExtendedSpan(tr),
};
Expand Down
Loading
Loading