Skip to content

Fix delimited block highlighting under jit-lock - #90

Merged
bbatsov merged 2 commits into
masterfrom
fix-fontlock-blocks
Oct 7, 2026
Merged

bbatsov merged 2 commits into
masterfrom
fix-fontlock-blocks

Conversation

@bbatsov

@bbatsov bbatsov commented Oct 7, 2026

Copy link
Copy Markdown
Owner

Delimited blocks came out as prose whenever jit-lock fontified a chunk that started or ended inside one, which is the normal case while scrolling a long document. The specs only ever used font-lock-ensure, so they never saw it. Block extents are now tracked by a scan that pairs delimiters from the start of the buffer, and the font-lock region never splits a block. On the CIDER and RuboCop docs, fontifying chunk by chunk now gives exactly the same faces as fontifying the whole buffer at once; before, 34 of 242 file/chunk-size runs differed.

Typing inside a very large block refontifies the whole block on every key. That was already the case whenever such a block got matched whole, because font-lock marks multi-line matches font-lock-multiline; for the largest blocks in those docs it's around 10ms.

font-lock abandons a keyword for the rest of the region as soon as its
matcher returns nil. The admonition paragraph, literal paragraph,
first-whites and two-line title matchers searched once and returned nil
when the match failed a check (reserved text, a face, the underline
length), so one rejected match, like a `NOTE:' inside a listing block,
switched the keyword off for everything after it.

adoc-kwf-std already retries; it now takes a PREDICATE for the extra
checks and those matchers go through it.
The font-lock keywords match a delimited block with one regexp, from
its opening to its closing delimiter, so they only see a block when the
region being fontified covers all of it. jit-lock fontifies a chunk at
a time, and a chunk starting inside a block can't tell an opening
delimiter from a closing one, so long blocks came out as prose (or the
prose between two blocks as code). font-lock-ensure, which the specs
use, does the whole buffer at once and never showed it.

A scan that always pairs the delimiters up from the start of the buffer
now records the extent of every top-level block in an
`adoc-delimited-block' property, kept up to date from
before-change-functions, and adoc-font-lock-extend-region widens the
region so it never splits a block. Like Asciidoctor, a block ends at the
first line repeating its opening delimiter exactly; the block regexp
follows suit, which retires the old guess that a block's last line
can't be blank. The extents also let the block keywords skip a
delimiter line inside a listing or literal block, and the title
keywords skip block interiors altogether.

With that, the example/open block keyword no longer needs to paint its
whole block with a nil face, which wiped the highlighting of the lists,
comments and nested blocks inside it (heading navigation relied on that
to reject `==' lines in blocks). Quote and sidebar blocks append their
face instead of replacing the content's.
@bbatsov
bbatsov merged commit 4b5e8db into master Oct 7, 2026
6 of 8 checks passed
@bbatsov
bbatsov deleted the fix-fontlock-blocks branch October 7, 2026 12:03
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.

1 participant