Skip to content

Leading-whitespace summary fix with always-head invariant#5

Merged
agzam merged 6 commits into
mainfrom
robustness-followups
Apr 18, 2026
Merged

Leading-whitespace summary fix with always-head invariant#5
agzam merged 6 commits into
mainfrom
robustness-followups

Conversation

@agzam

@agzam agzam commented Apr 11, 2026

Copy link
Copy Markdown
Owner

Folding a region that begins with blank lines used to collapse the summary to just the indicator + ellipsis, because occult--visible-end measured the first line from the region start. This PR introduces a three-overlay structure (parent / head / body) where a new head overlay hides any leading whitespace and always hosts the indicator, even when zero-length. The visible summary now starts at the first non-whitespace character, and occult-summary-max-length is measured from there so leading blank lines no longer consume any of the budget. Parent and head are non-evaporating so a text edit cannot drop them before the modification-hook cleans up.

Martin's two commits from #2 are cherry-picked verbatim to establish the head overlay and the occult--leading-whitespace helper. Follow-up commits apply the always-head invariant he proposed in his own review thread (indicator lives uniformly on head regardless of input shape), refactor the helper to use skip-chars-forward, clarify the occult--visible-end docstring around the new semantics, and update SPEC.md and changelog.org to describe the three-overlay structure and the max-length measurement point.

Closes #2

ImFstAsFckBoi and others added 6 commits April 11, 2026 16:05
The previous iteration dispatched the indicator `before-string`
between parent and head depending on whether the region had
leading whitespace.  The head overlay was `evaporate t` and
relied on Emacs dropping it when zero-length.  That broke the
uniform "indicator lives on parent" invariant and triggered
display quirks where parent's `before-string` could be shadowed
by head's `invisible` property when both overlays started at the
same position.

This change makes the head overlay the single, uniform home of
the indicator.  Head is always created (even zero-length) and
does not evaporate, so the indicator has a stable host
regardless of leading whitespace.  Parent is also made
non-evaporating so that an edit cannot drop the parent before
the modification-hook runs cleanup on head and body.

Tests covering the parent-before-string and head-evaporates
cases are rewritten to match the new invariant.
Replace the character-by-character `looking-at-p' + `forward-char'
loop in `occult--leading-whitespace' with a single
`skip-chars-forward' call bounded by END.  This is the idiomatic
form, runs in C, and handles a slightly broader ASCII whitespace
set (form feed and vertical tab in addition to the previous
space/tab/LF/CR).

Update `occult--visible-end' docstring to state explicitly that
BEG is the start of the fold's visible content (first
non-whitespace position), not the fold's outer start, and that
`occult-summary-max-length' is measured from there.  This
documents the semantic introduced by the leading-whitespace fix:
the summary budget is now spent on real content and leading
blank lines do not consume any of it.
Four new tests lock in behavior that was previously only
indirectly covered:

- `occult-summary-max-length' is measured from the first
  non-whitespace position, not from the fold start.  The test
  uses cap=3 on a region with two leading newlines and asserts
  body-split lands at head-split+cap, not beg+cap.

- `occult-edit-region' cleans up the head overlay inside the
  indirect edit buffer alongside parent and body, so the fold
  content is fully visible for editing.

- The base buffer's head overlay survives an edit session
  opening and still carries the indicator `before-string'.

- `revert-buffer' persistence round-trips leading-whitespace
  folds: after save + delete + restore, the head overlay is
  re-created with the correct extent and indicator.
SPEC.md:
- Core Mechanism now describes three overlays (parent, head, body)
  rather than two, and notes that parent and head are
  non-evaporating.
- Summary Line Format states that `occult-summary-max-length' is
  measured from `head-split' (first non-whitespace position), not
  from the region start.
- Overlay Properties gains a Head overlay table, removes
  `before-string' from the Parent table, and flips Parent
  `evaporate' to nil.

changelog.org:
- New 1.2.0 entry covering the leading-whitespace fix, the
  uniform indicator placement, and the max-length semantic
  clarification.  Credits @ImFstAsFckBoi for the initial
  investigation and prototype in PR #2.
@agzam
agzam merged commit 82f766e into main Apr 18, 2026
4 checks passed
@agzam
agzam deleted the robustness-followups branch April 18, 2026 00:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants