Subject: bug#34720: 26.1; Reverting a GPG buffer moves all markers to the end of the file

From: Eli Zaretskii <[email-masked]>
Date: Mon, 26 Aug 2019 12:42:03 +0300

> From: Lars Ingebrigtsen <[email-masked]>
> Cc: [email-masked],  [email-masked]
> Date: Mon, 26 Aug 2019 10:14:10 +0200
> 
> Eli Zaretskii <[email-masked]> writes:
> 
> > Markers cannot be preserved in every situation, there's no way around
> > this basic fact.
> 
> No, but commands like `revert-buffer' (via insert-file-contents) try to
> keep most of them around.

"As much as possible".  In some situation, it's impossible, at least
for some of the markers.  That happens when the text where markers
pointed to is entirely replaced.

IOW, there's no guarantee that markers will be preserved across
operations that replace text.

> > replace-buffer-contents is a primitive, so it can legitimately rely on
> > Lisp programs to set up whatever preconditions it needs for it to
> > work.  It MUST have a buffer to work with; if you want to replace with
> > a string, insert that string into a buffer and call the primitive.
> > Why is that a problem?
> 
> Why MUST it have a buffer to get the input data from?

Because no one has written code to support a string, I guess.  Feel
free to work on that if you think it's important enough.

> You get a text from somewhere, and it often ends up in a string (as in
> the epa case).  If you want to safely feed that to this function, you
> have to say something along the lines of
> 
> (let ((buffer (current-buffer)))
>   (with-temp-buffer
>     (insert string)
>     (let ((temp (current-buffer)))
>       (with-current-buffer buffer
> 	(replace-buffer-contents temp)))))
> 
> which is horrible, horrible, and horrible.

I see nothing horrible here.  More importantly, I suspect the string
actually started in some buffer, in which case all of the
complications above could be avoided.  (I generally consider code in
Emacs that manipulates large text strings to be broken by design.)

But I'm not opposed to adding support for string as the source for
replacement.  Just be aware that the code which access such a string
must be highly optimized, because it is executed in the innermost loop
of the code.

> No wonder this function has gotten one single usage after it was
> introduced two years ago.  (Well, one usage to
> replace-region-contents, which then calls the function.)  (Unless
> I'm grepping wrong.)

replace-region-contents is used in json.el.

As for the users of replace-buffer-contents, I could think of several
alternative reasons for its rare usage:

  . the function is relatively new, and was horribly slow before Emacs 27
  . the use cases for it are relatively rare, otherwise we'd have it
    long ago

> >> Would anybody mind if I just write a `with-saved-markers' macro in
> >> subr-x, which would make all these problems to away and make the
> >> solution a two-liner in epa itself?
> >
> > What would that macro do, exactly?
> 
> Gather the markers, execute the body, and then put the markers back
> where they were.

How do you "put the markers back where they were"?  That's the crucial
point, and I don't see how would you pull that out when the text is
significantly modified.