evil
evil copied to clipboard
evil-join amalgamates multiple joins
Issue type
- Bug report
Environment
Emacs version: GNU Emacs 30.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.34, cairo version 1.16.0)
Operating System: NixOS 22.11 (Raccoon)
Evil version: 1.15.0
Evil installation type: nixpkgs (2d10e73416ec1449ef74aeac7faf2cf8c556ff5a) + nix community emacs overlay (d17b8b8fc033debb3e75cfc44aaaf8f47d5e04fd)
Graphical/Terminal: Wayland
Tested in a make emacs session (see CONTRIBUTING.md): No
Prerequisite
evil-mode with undo-redo or undo-fu evil undo system
Reproduction steps
- Insert several lines of text
- Go to the first line by pressing
gg - Join lines by frequently pressing
Jwithout moving cursor or doing any other actions - Undo joining by pressing
useveral times
Expected behavior
Each line join can be undone one-by-one.
Actual behavior
All line joins are undone simultaneously.
Further notes
If undo-tree is used then undo works as expected. Joins are not amalgamated if there is a delay between them.
I did not find appropriate configuration option (evil-want-fine-undo does not change behavior). I was able to fix that by monkey patching evil-join
(defun evil--add-undo-boundaries (orig &rest r)
"Add undo boundaries around command."
(evil-start-undo-step)
(apply orig r)
(evil-end-undo-step))
(advice-add #'evil-join :around #'evil--add-undo-boundaries)
I am not sure if this is a good fix as I am not familiar with Lisp.
Caused by delete-char, which is used transitively, unconditionally calling undo-auto-amalgamate. Wow, that is a headache!
Your workaround is fine but
(advice-add #'evil-join :after (lambda (&rest _) (undo-boundary)))
does the same without any unnecessary steps, i.e. it adds an explicit undo boundary, instead of one added implicitly after each command, and that will be removed by undo-auto-amalgamate. Could also just (cl-letf (((symbol-function #'undo-auto-amalgamate) #'ignore)) ...).