5

Is it possible to wrap an arbitrary series of operations such that when a single undo command is issued, all of the wrapped operations are undone at once, rather than requiring multiple undos to undo them?

For example, let's say I delete a line, move up two lines, delete another line, move up two more lines, and delete one more line. I'd like to be able to wrap those all those operations somehow so that when a single undo command is issued, all three deleted lines are restored to their proper locations.

Of course, this is just an example, and I'd like to be able to do that for any sequence of operations.

Update: I found an answer here involving with-undo-collapse which worked.

Update 2: The answer linked to above seems to be buggy, and while it does allow me to undo multiple operations in one undo, it sometimes undoes even more than what was wrapped in with-undo-collapse. I'm going to have to try to debug that code to figure out what's going wrong.

Update 3: The buggy code for with-undo-collapse and associated functions from the answer above is impenetrable to me, and I've failed at debugging it. As such, I'm forced to abandon it, as I'd rather have too-granular an undo rather than not-granular-enough. I'll now see if I can further explore Emacs' undo system on my own, using the links and tips provided elsewhere in this thread and maybe come up with my own, working solution.

Update 4: It looks like the simplest and the most bulletproof answer is to just wrap the block in evil-with-single-undo.

izkon
  • 1,798
  • 10
  • 23
  • You probably want to look into adjusting the amalgamation business which is hard-coded to **20** inside `simple.el` -- see `undo-auto-amalgamate`. I actually like one undo per keystroke, so I disabled the amalgamation business. It is interest that you describe the current behavior as requiring multiple undos, since it is the exact opposite by default -- i.e., several keystrokes are undone in one fell swoop by default. – lawlist Sep 12 '17 at 11:06
  • In addition, there are certain commands that are taken into consideration when using the amalgamation business. You may wish to look into adding new commands that will be considered in that regard. To learn more, type `M-x describe-function RET undo-auto-amalgamate RET` to read the doc-string. – lawlist Sep 12 '17 at 11:35
  • @lawlist - It's not keystrokes that I wish to undo at once but elisp function calls. I have a function of my own that does a number of things, and after I call this function and try to undo it, only part of the things it does are undone. To undo everything it does I have to issue multiple undo commands. I'd like to have a single undo undo everything that function does. I tried putting (undo-auto-amalgamate) before everything my function does internally, but it didn't help. – izkon Sep 12 '17 at 13:15

2 Answers2

1

You can use function undo-boundary to set your own Undo boundaries. But that just adds another boundary; it does not remove any existing boundaries.

You can use buffer-disable-undo to disable recording of undo information, and then reenable it again using buffer-enable-undo. But disabling drops all recorded undo info for the buffer.

You can also try modifying the undo information directly. It is in variable buffer-undo-list.

See the Elisp manual, nodes Undo and Maintaining Undo.

See also this related StackOverflow Q&A.

Actually, I think this question is a duplicate of this one. I've voted to close this one.

Drew
  • 75,699
  • 9
  • 109
  • 225
  • Thank you, but `buffer-disable-undo` followed by `buffer-enable-undo` did not work for me. – izkon Sep 12 '17 at 16:38
  • Did you check the message that (I think) this is a duplicate of? – Drew Sep 12 '17 at 17:09
  • I did, and it contained the solution, as I mentioned in the update to my original question above. Thank you. – izkon Sep 12 '17 at 18:16
  • Actually, the solution I refer to seems to be buggy (see my 2nd update to the original question above). – izkon Sep 12 '17 at 18:30
  • I can find nothing either. `buffer-disable-undo` eliminates the entire past undo list as well as preventing recording new entries. The question asks to be able to unwind to a previous checkpointed state, essentially amalgamating all undo entries into one. `buffer-disable-undo` does not do this. – jennykwan Oct 15 '18 at 15:40
1

You may be looking for C-hf atomic-change-group

atomic-change-group is a Lisp macro in ‘subr.el’.

(atomic-change-group &rest BODY)

Perform BODY as an atomic change group.
This means that if BODY exits abnormally,
all of its changes to the current buffer are undone.
This works regardless of whether undo is enabled in the buffer.

This mechanism is transparent to ordinary use of undo;
if undo is enabled in the buffer and BODY succeeds, the
user can undo the change normally.
phils
  • 48,657
  • 3
  • 76
  • 115
  • Thank you, but `atomic-change-group` did not work for me. – izkon Sep 12 '17 at 16:39
  • Explaining how it did not work would be helpful for those coming after you. `atomic-change-group` does however many undo's necessary to return to the buffer state upon entering the block, in the case of an abend. That's not what the question is asking. If the block succeeds, `atomic-change-group` does not amalgamate the undo entries generated by the block so that a single undo entry undoes them all at once. – jennykwan Oct 15 '18 at 15:47