emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: disabling undo boundaries


From: Phillip Lord
Subject: Re: disabling undo boundaries
Date: Mon, 11 May 2015 21:42:08 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.5 (gnu/linux)

Stefan Monnier <address@hidden> writes:
>> This fails for me, unfortunately, the user might want to undo in the
>> other buffer. A priori, though, it seems to be a nice idea. I tried this
>> code for instance:
>> #+BEGIN_SRC emacs-lisp
>>   (defvar-local fix-test-on nil)
>>   (defun fix-test-after-change-function (&rest _)
>>     (when fix-test-on
>>       (let (
>>             (undo-inhibit-record-point t)
>>             )
>>         (with-current-buffer
>>             (get-buffer "*scratch*")
>>           (insert "a")))))
>>   (add-hook 'after-change-functions 'fix-test-after-change-function)
>> #+END_SRC
>> And that doesn't have the problem because the inhibit stops record_point
>> from ever setting the undo_boundary.
>
> So, you're saying that the above let binding fixes your problem?
> Then, I guess it's a valid option as well.  It sounds like an obscure
> work-around, so it probably deserves a clear comment.

Sort of. In this particular instance above, it does because
inhibit-record-point stops both the recording of point AND the insertion
of an undo boundary.



>>> - delay the modification of the other buffer (e.g. record in a-c-f the
>>> boundaries of the affected text, and process them later from
>>> post-command-hook).  Not sure if this would really help.
>> Unfortunately that wont work since there there can be many changes on
>> a-c-f for a single p-c-h call. So, I'd have to amalgamate all the
>> changes.
>
> Amalgamate is exactly what I meant by "record the boundaries".
> It's a fairly common approach, typically you only track the beg/end
> enclosing boundaries.

It's difficult in my case. The problem is I have two buffers which I am
keeping in sync. So I need to be able to convert a point location in one
with a point location in another. And I have to do this *before* the
change happens because otherwise the two buffers will hold content which
is not in sync (one has changed, the other hasn't been updated yet).

Unfortunately, the scope of the changes reported by the b-c-f and a-c-f
are not necessarily in agreement with each other -- subst-char-in-region
often gets the before-change region wrong. I have some logic for
detecting this, but it can be fouled by changes that both add and remove
from the buffer at the same time; in these cases, I have to copy the
entire buffer. Amalgamating many changes makes this much more likely to
happen.


>
> E.g. I use it in diff-mode to avoid modifying the buffer right in the
> middle of a-c-f, which tends to break all kinds of assumptions.
>
>>> - change your a-c-f so it records the buffer-undo-list at the beginning,
>>> and it removes the added boundary (if any) at the end.
>> The a-c-f doesn't work, unfortunately because the nil boundary is not
>> present till sometime after the a-c-f has been called (when? not sure).
>
> That is weird.  The code you quoted from record_point should be run
> "right away".  Are you sure you didn't check in the wrong buffer by
> any chance?
>
> I'd expect something like
>
>    (let ((old-ul buffer-undo-list))
>      (with-current-buffer "*scratch*"
>        <doyourthing>)
>      (and (consp buffer-undo-list)
>           (eq (cdr buffer-undo-list) old-ul)
>           (null (car buffer-undo-list))
>           (setq buffer-undo-list (cdr buffer-undo-list))))
>
> to do the trick.  Or, what am I missing?


It's rather more sinister than that. The code is this:

  if ((current_buffer != last_undo_buffer)
         && (MODIFF > SAVE_MODIFF))
     Fundo_boundary ();

  last_undo_buffer = current_buffer;

So, when the after-change-function runs the buffer-undo-list has
*already* been modified. But if the a-c-f function makes an undoable
change, then last_undo_buffer gets reset to the other buffer (*scratch*
in my case).

Now, the *next* change happens, and current_buffer != last_undo_buffer
is true, so we add a boundary BEFORE, we add the next undo. So, the
buffer change in a-c-f adds a boundary before the next change not after
the one that has just been added.


>> I did think of doing this with the pre/post-command-hook -- so, take the
>> car of the b-u-l on the pre-c-h, then on the post-c-h, scan till I get
>> to the old car and delete all nils placed there.
>
> But at that point you won't know which undo-boundaries were added
> because of the buffer-switch and which were added for other reasons.

Yep, that's a problem.

>
>> I can see a few problems with this. First, it will also remove any
>> undo-boundaries added by explict calls to undo-boundary; perhaps not a
>> huge problem as there do not appear to be that many of them in the lisp
>> code base.
>
> Since `doyourthing' shouldn't touch the source buffer, the source
> buffer's undo-list has no reason to have a hand-added undo-boundary.

In between the start and end of a command? That can happen. It's the
point of making undo-boundary a lisp callable function.

I had an evil thought on the way home. Rather than working backwards and
trying to remove the undo-boundary that had been added as a result of
the undo action in another buffer, why not just remove that logic from
undo.c? So, I've tried commenting out the four calls to Fundo_boundary
there and now everything works.

I haven't used this for my daily emacs yet, so perhaps there are demons
lurking, but my question would be, why should a undoable change in
*that* buffer cause a marker boundary in *this*? What is the point of
these undo-boundaries that are causing me all this grief?

Phil









reply via email to

[Prev in Thread] Current Thread [Next in Thread]