I’m an Emacs newbie (using Doom Emacs with GNU Emacs 29.1). I came from vim, and battling with undo there was crazy enough, but I won using this:
inoremap u
inoremap u
inoremap u
inoremap u
inoremap u
inoremap u
inoremap u
inoremap , ,u
inoremap . .u
inoremap ( (u
inoremap [ [u
inoremap = =u
inoremap \" \"u
inoremap u
inoremap u
Also, I had autogroup that breaks undo every 4 seconds.
Basically, this configuration breaks undo on almost every possible type command, every Spacebar, Enter, comma, bracket, moving up, down, everything. This is because I hate when undo deletes the whole screen of text.
How do I replicate this in Emacs? I read this, but it doesn’t say what is considered a “recent change”.
For evil-mode, I did a package in the past, it should be on melpa: https://github.com/VanLaser/evil-nl-break-undo
What does “break undo” mean? Do you mean that if you type “x” and then you type “y” and then you use undo, the first undo should remove only the “y” and the second would remove the “x”?
Exactly. I probably should have explained it in more detail.
I think you are not used to ‘modal’ editing in Vi. How does the world use it? With atomic edits, which means living in NORMAL mode and only entering INSERT mode when you need it. Your undo history works like a commit history and you can undo by action: inserted text, changed text inside brackets, deleted 6 lines, etc. If you get used to this, you’ll never go back (even in Emacs, Evil user here hehe).
This way of modal editing is fine when you write source code.
I write texts, articles. Most of the time I spend in Insert mode. And when I press
u
I see a wall of text disappeared.And when I press
u
I see a wall of text disappeared.Maybe Vim let’s things go too far between undos.
I write a lot of text too, but I’ve never seen Emacs’s undo remove a whole wall of text that was typed by hand. If it was inserted from the kill-ring, sure.
Occasionally, it’ll undo a few more keystrokes than I’d prefer, but that’s rare. In that case, you can just undo the undo by pressing “C-g C-/”. And then remove only the text you wanted to get rid of.
Maybe Vim let’s things go too far between undos?
By default Vim makes each foray into insert mode undo in a single step. So you have total control over how much gets undone each time: just exit and renter insert mode if you want more granular undo.
I think /u/bravosierrasierra has your solution with
amalgamating-undo-limit
then. Except the docstring may have an off-by-one error: experimentally it seems as if you need(setq amalgamating-undo-limit 0)
rather than the indicated value of 1.
may be you need tune amalgamating-undo-limit variable?
Emacs is king of undo. Theres a number of ways but you can bsck up the undo chain a step or steps at a time. Ie if you’ve done 20 changes you can just keep going back with a keystroke.
There is also undo-tree package so you can visually see what state your buffer has been in over time and then just go pick the state you want.
Do note that undo-tree not only provides a visual, it is a fundamentally different way of doing undos. The stock emacs way is not a tree, it’s a line. There’s no real “redo” in emacs for instance, you just undo an undo. It’s odd, but undo tree works really well.
the paragraph here gives some useful details: https://www.gnu.org/software/emacs/manual/html_node/elisp/Undo.html#index-undo_002dauto_002damalgamate
The editor command loop automatically calls undo-boundary just before executing each key sequence, so that each undo normally undoes the effects of one command. A few exceptional commands are amalgamating: these commands generally cause small changes to buffers, so with these a boundary is inserted only every 20th command, allowing the changes to be undone as a group. By default, the commands self-insert-command, which produces self-inserting input characters (see User-Level Insertion Commands), and delete-char, which deletes characters (see Deleting Text), are amalgamating.
this means emacs will group together a certain number of inserted characters into an atomic unit; it’s that unit which is undone by the
undo
command. if you want character-by-character undo then you will need to tell emacs to not perform this grouping. one thing to try would be to add advice to theinsert-char
function to set that undo boundary for you upon each keystroke; I’m sure there are other ways to do this as well.The following paragraph from what you quote has the heavy handed approach whereby no changes are amalgamated and undo would work character by character.
The maximum number of changes that can be amalgamated is controlled by the amalgamating-undo-limit variable. If this variable is 1, no changes are amalgamated.
good find!
paste this in chat gpt and go from there?
I used to have these vim bindings in vim how do i write emacs lisp to do this same stuff in evil-mode inoremap u inoremap u inoremap u inoremap u inoremap u inoremap u inoremap u inoremap , ,u inoremap . .u inoremap ( (u inoremap [ [u inoremap = =u inoremap \" \"u inoremap u inoremap u
I got something like this:
Good Lord! Thank your for this advice, but I’d rather suffer with what I have than follow advice from this hellish machine.
Good, because that is very bad advice. orzechod already gave you a link to the manual, but the upshot is that basically every emacs command already breaks the undo into segments. Arrow keys invoke commands to move the cursor around, and backspace and return invoke commands as well, so you don’t need to add anything to your config to get most of what you want.
The only difference is that the default Emacs config groups up inserted characters into segments of 20 characters, instead of splitting them on punctuation and spaces. Getting that last bit is harder, so you might actually prefer to live with it. You should be able to do it with advice on
self-insert-command
, but I would have to do a little research and try it out before I would be confident in my answer. (I am 90% sure that you can set a variable to control whether the current command should be amalgamated or not, but it’s been years since I last looked at how undo is implemented.)I should also point out that brackets might or might not be amalgamated depending on other choices you might have made. For example, if you use a minor mode that automatically inserts the closing bracket when you type the opener, then it won’t be amalgamated because that is invoking a command rather than merely inserting a character.
Advising
self-insert-command
may be a way. But with my zero knowledge of elisp I have absolutely no idea how to do that. All my attempts have failed.So for now this kinda adds to my long list of “Not solved in any reasonable way”.