On Jan 5, 2024, at 3:19 AM, Eli Zaretskii <eliz@gnu.org> wrote:

From: JD Smith <jdtsmith@gmail.com>
Date: Thu, 4 Jan 2024 19:51:46 -0500
Cc: emacs-devel@gnu.org

On Jan 4, 2024, at 1:58 AM, Eli Zaretskii <eliz@gnu.org> wrote:

It is called where the 'face' property changes.  The display engine
finds the next position where that happens, and arranges for calling
face_at_buffer_position when it gets to process that position.  The
results of face merging at that position are kept in a cache, to avoid
performing it more often than needed.

This makes sense, thanks.  In the ‘face-remap property scenario, the number of such ‘face
property-change positions doesn’t actually change.

I don't think this is true. If the 'face-remap' property changes at
some buffer position, the realized faces need to change there,
although the 'face' property didn't change. So with the 'face-remap'
property, we'd need to arrange for face-merging also where the
'face-remap' property changes, not only where the 'face' property
changes.

I wondered about that.  Since I don’t know emacs internals, I am (blissfully?) unaware of how close at hand the information of (get-text-property (point) 'face-remap) is.  Since a typical buffer has maybe N=hundreds of ‘face changes, this puts an approximate upper limit on the number of sensible ‘face + ‘face-remap changes: 2N.  Much more likely would be just a handful of ‘face-remap changes.  So I doubt the overhead of “additional merging” would be significant.  Of course you have to find these special points, but already you must find ‘face and ‘font-lock-face (and others?).

and use it together with
`face-remapping-alist' to compute the final face merge. I would expect that performance would
depend on whether the face-merge cache could be (largely) preserved, even as 'face-remap
properties are added/removed/moved around (typically in repetitive fashion).

The result of merging faces when 'face-remap' is in effect will be
different faces, even though the named faces didn't change.  Those
different faces will be realized and cached, and will have their own
internal face IDs, but will not be exposed to Lisp as symbols, via
APIs such as face-at-point.

That’s good, since it sounds like even if a ‘face-remap property range is moved, but includes the same underlying face data, the cache for those internal merged faces should remain valid.

How many faces do you have defined on that frame?  I'm guessing not
many, or maybe they are all almost identical (differ in only one or
two simple attributes).

Right now it’s just a couple of faces (remove one face-remap, add another).  If I had access to a
‘face-remap property, I could imagine maybe 10 faces at most being affected as point changes. 
Each face would change by one or two attributes like foreground, stipple, etc.  

What matters is the total number of named faces defined for the frame,
not the number of faces affected by face-remap.

I define the faces on the fly based on max indentation depth, but in practice it’s of order 5 more than the normal contingent of a dozen or so font-lock-faces.  So not a huge number.

I’m sure there could be lots of uses of this type of functionality, but “enhance the focus on one region
of importance” seems like a very common one.  For example, you might imagine dim/gray version of
all the font-lock faces outside the “treesitter region of interest” and bright ones inside.  Whenever that
TS-prescribed region changes, an overlay with a ‘face-remap property gets moved.  

The question I suggest to ponder is whether simpler, less general ways
to implement these features are "good enough".  For example, changing
the appearance of a region of text can be handled by moving an overlay
there with a suitable face definition and high priority;

Indeed I have pondered this for several months and found nothing suitable.  Do you have any specific approaches to consider, which would allow you to selectively affect the appearance of certain small ranges of text within a (much) larger region of text?  The problem is, the region is well known, but changing the appearance everywhere within it is not suitable to the problem at hand.  I could obviously scrub through the entire region on the fly replacing face properties as I go (including inside ‘display properties), but that’s painful and probably too slow for interactive use.

changing the appearance of text around point can be handled by a special variable
which the display engine will consider when working on text around
point; etc.  

I’m afraid I don’t understand your “special variable to consider for text around point” idea.  Can you give an example of how this could work in practice?  How would such a variable be updated and associated with text, if not via text properties?

IOW, it is not always beneficial to generalize a feature
to the most general abstraction level, certainly not when talking
about Emacs, where a gazillion features related to the proposed new
one are already available and expected to work after the proposed
change.  Adding significant enhancements to Emacs that change previous
design assumptions can easily become a proverbial death by thousand
cuts.

Of course, well understood.  It’s eminently sensible to guard against kitchen-sink-itis.  Lacking familiarity with the core face code, I wondered if there was a straightforward extension to the global face-remapping-alist which could be brought to bear on the problem.  But perhaps there are other more straightforward means.

If you have other simpler ideas in mind that would allow turning on and off a latent “plane” of varying face information within a region of text (information which, I should have mentioned, could easily be computed and pre-applied by font-lock), I’d be very glad to hear them.

In the world of CSS, you’d do this quite simply by (say) updating the class of the div which wraps your text of interest, and having special styling for, e.g., "alternate bold" and "alternate italic”, which is only activated when the “alternate” class is applied:

<div class=“normal”>
This is <i>some italic text</i> and <b>some bold</b> text.  This is unrelated text.
</div>

In CSS
.alternate b {...}
.alternate i {…}

(In JS later:)

 myDiv.classList.add('alternate');