Subject: Re: bug#76108: Call `modify_text' only on the text being replaced in `subst-char-in-region'

From: Thuna <[email-masked]>
Date: Fri, 07 Feb 2025 18:17:14 +0100

>> Personally I find it quite unintuitive to have `modification-hooks' be
>> "Hooks that are called when some characters in region _might_ be
>> modified".
>
> You have to understand the distinction between the "before" and the
> "after" calls (which correspond to the `before-change-functions` and the
> `after-change-functions` hooks): the "before" call is there to announce
> that some change*s* *may* happen in the specified region.

It still reads to me as though it's called before some changes _will_
happen - that is, when changing text, you call before-change-functions
(and co.) on the text, change it, and call after-change-functions (and
co.) afterwards - but I understand that it's up to interpretation and
mine just happens to not match with the implementation.

> In the vast majority of cases you should care only about the "after" call.
> [ Sadly, for the `modification-hooks` placed on text properties (as
>   opposed to overlays), only the "before" call is available.  ]

Now, I am not entirely sure how I've managed to miss this, but
`subst-char-in-region' actually _also_ messes up the after calls.  I
have no idea how or why this is happening, but a buffer whose contents
are

  "foo bar\nquux baz ban"

with an overlay from 14 to 17 (that is, around the word "baz") with the
modification hook

  (lambda (&rest args) (print args))

reports being called with the arguments

  (#<overlay from 17 to 17 in temp> nil 8 21)
  (#<overlay from 14 to 17 in temp> t 8 9 1)

when I do

  (subst-char-in-region (point-min) (point-max) ?\n ?\s)

which means that it's being called even though it doesn't overlap at all
with the range of characters reported as being modified.

I thought it might have been because `subst-char-in-region' calls
`signal_after_change' from the first to the last change (and that is
likely not unrelated) but even if that were the case the first and last
change in the above situation is the sole newline at the middle, so it
shouldn't have reached the overlay regardless.  Maybe
`report_overlay_modification' calls everything called in the before call
as well, but I honestly can't tell.