Subject: bug#78221: `inhibit-recording-text-property-changes`

From: Daniel Colascione <[email-masked]>
Date: Mon, 12 May 2025 21:12:29 -0700

On May 12, 2025 8:48:16 PM PDT, "Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors" <[email-masked]> wrote:
>>> - Don't notify changes to text-properties.
>>>   Currently, things like `put-text-property` run the *-change-functions
>>>   like any other buffer modification.  In practice this is of dubious value:
>>>   - It's already the case that most calls to `put-text-property` and
>>>     friends don't cause any notification because they are wrapped within
>>>     a `with-silent-modifications` or otherwise take place from code
>>>     which always runs with `inhibit-modification-hooks` set
>>>     (e.g. because it's run from a *-change-function).
>>>     So *-change-functions can't reliably track changes to text-properties.
>>>   - Most *-change-functions are *not* interested in text-property
>>>     changes anyway.
>>>   The above two points suggest maybe we could just refrain from
>>>   running *-change-functions when changing the text-properties (just
>>>   like we do for changes to overlay properties).
>>>   Then again, this would be a backward-incompatible change, so there's
>>>   a chance some packages out there would be negatively affected.
>>>   Another option would be to allow packages to choose whether to receive
>>>   notifications like now or only for changes to the text.
>>>   E.g. just like we have `buffer-modified-tick` and
>>>   `buffer-chars-modified-tick`, we could have `*-change-functions`
>>>   and `*-chars-change-functions` (with some questions remaining about
>>>   `first-change-hook` and the `modification-hooks` property).
>>>   Or we could rely on a (symbol) property being set on the functions
>>>   added to `*-change-functions` to tell whether they want to know about
>>>   changes to text-properties or not.
>>>   Or maybe a more crude way would be a buffer-local variable controlling
>>>   whether `*-change-functions` are called for text-property changes.
>>
>> I think it was a mistake in the beginning to have text property
>> modifications trigger before/after-c-f.
>
>+1

The idea, I think, was for users to directly manipulate text properties to attach semantic information. That's why 

In practice, text properties ended up being volatile implementation details.

I think XEmacs got this more right by directly exposing intervals (which we have anyway) as extents. Water under the bridge now. 

>
>> But seeing it's been that way for so long, I don't think it would be
>> a good idea to change it now.
>
>The more I look at it, the more I see it as a serious mistake, indeed.
>I still have not found a single *-change-function that needs to know
>about text-property changes.
>
>On the contrary, I see that most code that changes text-properties
>wraps those changes in `with-silent-modifications` to avoid the various
>undesirable side-effects of treating text-properties as being part of
>the text.  Actually, the whole purpose of `with-silent-modifications` is
>to work around that historical mistake.
>
>Of all the code that touches text-properties I see that the majority is
>wrapped (directly or indirectly) in `with-silent-modifications`, and of
>the remaining ones, the majority would actually benefit from being
>wrapped as well (the authors just haven't bumped (yet) into the
>downsides).
>
>The only case I have found so far where we do want to record
>text-property changes (not to run *-change-functions, but to mark the
>buffer as modified and to record the changes in the undo-list) is the
>`enriched-mode`.

Can we just delete enriched mode? Good idea, but the world ended up going in a different direction.

>For that single use case the rest of ELisp code has had to pay the
>complexity cost of `with-silent-modifications` (which is delicate to use
>since its effect is nasty if you do modify the buffer's text) plus the
>occasional performance cost of uselessly running the *-change-functions
>and wasting memory by recording those changes on the undo-list (not to
>mention the occasional breakage of promises of how *-change-functions
>are run).
>
>AFAICT, in the long term, ELisp code would benefit from fixing that
>historical mistake.
>
>I suggest we introduce a new boolean variable
>`inhibit-recording-text-property-changes`: if nil we get the current
>behavior, but if non-nil then all text-property modifications are
>implicitly wrapped in a `with-silent-modifications`: they don't change
>the modified-p flag, they don't change the undo list, they don't run any
>modification hooks.
>
>I'd expect it to default to nil (for obvious backward compatibility
>reasons), and major modes can set it buffer-locally to t.  Eventually,
>I'd hope even `enriched-mode` will set it to t, and will instead record
>the text-property changes "by hand".

Minor modes might need it too. I think it's fine to add a new hook variable, but I don't think it's safe or even especially convenient (relative to the new variable) to have something that changes the behavior of existing code at a distance. 

And speaking of hindsight: if we're going to add a new hook variable, how about making it a function compatible with add-function and such instead of a list of functions?