On Jan 9, 2024, at 8:03 AM, Eli Zaretskii <eliz@gnu.org> wrote:
From: JD Smith <jdtsmith@gmail.com>
Date: Mon, 8 Jan 2024 16:49:21 -0500
Cc: emacs-devel@gnu.org
Why cannot this be done by modifying faces or overlays in the affected
region?
It can, but the region where such face modification is needed could be arbitrarily large (including the
entire buffer), which makes this more like a font-lock-problem in a PCH or via a timer (with the bonus
that it could occur either on modification or just point change). So at worst it would be like
re-font-locking the entire buffer (many thousands of locations, most just 1 character wide) on every
key press (or rapidly with a timer).
Not sure changing faces will be significantly slower than what you had
in mind. Maybe simpler (for a Lisp program), but not necessarily
slower. After all, that's what JIT font-lock does all the time, and
we accept the performance it provides.
As long as the changes happen at the right places (ala font-lock) and not all over the buffer, you may be right. And it’s reasonable to consider this since font-lock is already the one applying those faces in the first place. The trick is how to actually implement it.
What I’m struggling with is how to do something “like font lock” — i.e. refontify some potentially
substantial fraction of all the faces in a buffer, not (just) on modifications in an after-change-hook, but
also on point-dependent “region of interest” changes, with a priority given to the displayed region in
the window. IMO, tree-sitter will make this kind of idea more common.
Did you try to use jit-lock-register machinery?
Maybe Stefan (CC'ed) will have better ideas.
I haven’t. In my (limited) understanding and experience, all the font-lock apparatus is highly tuned to after-change modifications. Here we need to (in addition) mark regions (old and new) as outdated, even when the buffer hasn’t changed at all, just based on the movement of the (selected window’s) point. I can know how to expand modified regions with `font-lock-extend-region-functions’, but I don’t know how to inform font-lock that “even though you didn’t see changes, trust me, these regions are in need of updating”. I would do this in a PCH, rate-limited by a timer.
Maybe it’s as simple as calling `font-lock-flush’ on those regions? If so, can you call font-lock-flush multiple times in a row on overlapping regions without creating issues? Does font-lock-flush do the right (JIT) thing, even when you mark a very large region?
And (this is the big one) how do you handle two chef’s both with their spoons in the font-locking pot at the same time?
- Normal font-lock responding to changes in the buffer to refontify regions.
- PCH+timer marks regions for refontification when the buffer-modified-tick or treesitter region-of-interest changes (the former because you can no longer trust that equal regions are in fact equivalent).
Seem like there’d be a bunch of unnecessarily repetitive refontification in such a scheme. And marking large regions with `font-lock-flush’ every 75ms as you type also seems problematic.
For disclosure, I’ve never used jit-lock-register, and personally find the font-lock documentation light on details for implementing non-standard fontification scenarios such as this.
For the highlighting/unhighlighting operation, I think I also mentioned that the faces of interest can and
will live in display properties, so you’d need to check all ‘display strings within the (un-)highlit region
too, and rebuild those with the updated faces.
I hope the number of faces you use in those display strings is small,
in which case the feature should still work reasonably well, since you
presumably are familiar with those faces and are managing them in your
code.
Yes, here again it's just a handful of faces that may appear inside ‘display strings. But there may be many such strings (e.g. one for every empty/tab-indented line). In the pure font-lock approach we are discussing, these display strings would just be regenerated as text is altered.
But suppose the change of region was precipitated by the removal or addition of text in the buffer,
not just point movement? Now your old region (old-roi, above) is outdated, likely wrong, and possibly
had holes put in it by the edits.
That's exactly what JIT font-lock solves, right? Editing a buffer
changes the text, and thus the faces that need to be put on the text
change also. For example, something that was a comment can become
non-comment, or vice versa. Isn't what you describe similar (read:
identical), except that the faces themselves are different, i.e. not
font-lock faces?
I guess so. The difficulty seems perhaps the other way around: updating font-lock JIT for both buffer modifications and (potentially) point movement (simultaneously, without stepping on each other!).
It could be a buffer-local variable, which defines the size of the
region around point where the faces should change their appearance
This was related to my idea of asking the display engine to handle
faces specially in the vicinity of point. Since the display engine
examines each change in the face property, it could apply special
handling for faces of buffer positions around point. For a simple
example, suppose that the buffer-local variable I mention above has
the value
(BEFORE AFTER :background "red")
where BEFORE and AFTER are numbers of positions before and after point
to give the specified background color to all the faces in that
region. Then the display engine could automatically merge each face
in the region with an anonymous face '(:background "red")', and use
that for the characters in the region.
This could indeed be useful, but in my situation, it’s a bunch of smaller text regions between BEFORE and AFTER that need altering individually. Hence the interest in a top-level “alternative class” type specifier, ala CSS.