Vim Undo command (the one with the with capital U) - vim

I don't understand how the undo line command works.
The documentation says Undo all latest changes on one line.
What is the definition of all latest changes here?
One behaviour I have noticed is that sometimes U undoes all writes after the very first write on a line. But this is not consistent. For example, when I open a new file and edit the first line, writing multiple times, the U command undoes every single change.
I haven't been able to find anything concrete from my google searches either.

In my understanding all latest changes means going back the change history until a change in a different line is encountered, and stop there. So if you start with an empty buffer and edit only one line (repeatedly), all additions will be wiped by U.
Vim generally merges near changes occurring in the same line; these appear as a single entry in :changes.

U: return the last line which was modified to its original state (reverse all changes in last modified line)
http://vim.wikia.com/wiki/Undo_and_Redo

Related

Why does Vim's U command work differently when lines are changed vs deleted?

I'm trying to understand the U command in Vim. My understanding is that it undoes all changes that affect only the most recently changed line. However, if I start with the following file:
abc
def
ghi
jxx navigates down and deletes two characters:
abc
f
ghi
kgU2j sets the entire document to uppercase:
ABC
F
GHI
Now, U gives this result:
ABC
def
GHI
If I had used kd2j to delete the entire document instead of changing its case, U would have no effect. I'm wondering why d is treated as a command that affects multiple lines, but gU is seemingly ignored.
Is there an intuitive way to understand this behavior, or is this just an odd edge case of Vim?
To understand this behaviour I decided to dig into the source code and the things I found:
After change that touches single line we store that line number in variable. It's not affected when we touch more than line at once.
In your case, you removed one letter from second line. Current line (and the state before the change) was updated. When you deleted next character, you did it in the already stored line, so current line nor state were updated. Next change touched 3 lines - no updates. U restored current line to saved state. Notice that gUG on the last line would touch only it, so that change updates current line and state under the hood.
After changes to document structure (adding/removing lines), that variable is cleared
This can move current line up (deleting) or down (adding). It's much easier to clear it instead of handling logic of moving it and that's my guess why it's done such way.
Notice that deleting part of line (e.g. with d$) will update current line and state as expected. Opening line with o and type some text (current line is that line, state is empty line) works too. However when you press enter during typing (so affect more than one line), current line is cleared.

Vim - sort the contents of a register before/after pasting it?

As part of a project of mine I'm trying to move certain lines from a file to the top, sorted in a certain fashion. I'm not sure how to do the sort once those lines are up there - I don't want to disturb the other lines in the file.
I'm moving them by yanking them and putting them back down, like so:
g:/pattern/yank A
g:/pattern/d
0put A
This moves all the lines I specify up to the top of the file like I need, but now I need to sort them according to a pattern, like so:
[range]sort r /pattern2/
Is there a way to sort the contents of a register before pasting it? Or a way to sort only lines which match /pattern/? (because all the yanked lines will, of course).
I'm stymied and help would be appreciated.
edit - a possible workaround might be to count the number of lines before they're yanked, and then use that to select and sort those lines once they're placed again. I'm not sure how to count those lines - I can print the number of lines that match a pattern with the command :%s/pattern//n but I can't do anything with that number, or use that in a function.
The whole point of :g/pattern/cmd is to execute cmd on every line matching pattern. cmd can, of course, be :sort.
In the same way you did:
:g/pattern/yank A
to append every line matching pattern to register a and:
:g/pattern/d
to cut every line matching pattern, you can do:
:g/pattern/sort r /pattern2/
to sort every line matching pattern on pattern2.
Your example is wasteful anyway. Instead of abusing registers with three commands you could simply do:
:g/pattern/m0
to move every line matching pattern to the top of the buffer before sorting them with:
:g//sort r /pattern2/
See :help :global, :help :sort, :help :move.
I know this is old, and may not be of any use to you anymore, but I just figured this one out today. It relies on the system's sort command (not vim's). Assuming you're saving to register A:
qaq
:g/pattern/yank A
<C-O>
:put=system('sort --stable --key=2,3',#A)
qaq: clears register A of anything
:g/pattern/yank A: searches current buffer for pattern and copies it to register A
<C-O>: pressing Ctrl+O in normal mode returns you to the last place your cursor was
:put=system('sort --stable --key=2,3',#A): sends the contents of register A to the sort command's STDIN and pastes the output to the current position of the cursor.
I mapped this whole thing to <F8>:
noremap <F8> qaq:g/pattern/yank A<CR><C-O>:put=system('sort --stable --key=2,3',#A)<CR>
I don't know how janky this is considered, cuz I'm a complete noob to vim. I spent hours today trying to figure this out. It works for me and I'm happy with it, hopefully it'll help someone else too.

How to go back to previous when doing find replace confirm in vi?

Frequently when I am doing a find and replace in vi I will do it like this:
:%s/find/replace/gc
This gives you the option to skip by pressing n, or replace by pressing y. But, sometimes I will accidentally skip over one in a large file by pressing n when I meant to press y.
How do I go backwards to the previous one and give me a second change?
Essentially, how to I find (search) the other direction temporarily? thanks.
I'm not sure if you would like to interrupt current find-replace operation and resume it again. But if that is acceptable, here is my suggestion:
Start your find-replace the way you mentioned:
:%s/find/replace/gc
After you accidentally skip over a substitution by pressing n, interrupt the search by pressing <ctrl-C>
Press <shift-N> to go back to the previous occurrence of your find term
Run find-replace a little differently while you are at this word: :.,$s/find/replace/gc
Continue the operation
All this functionality works with vim native capabilities without having to install any addon.
Note: The .,$ range specifier indicates to perform :s (substitute) operation over a range of lines that start with current line (indicated by .) and until last line (indicated by $).
Note2: It might be known to you, but reiterating for anyone else who stumbles upon this post searching for something similar - The % range specifier indicates to perform :s (substitute) operation over all lines of currently active buffer.
This is not answer to the question, but a very good alternative. I recently discovered the CtrlSF plugin and it improves the search /replace process dramatically.
Basically, you have the search results in a buffer and you can do all the replacements in this single buffer.
In your scenario, you first do :CtrlSF find, get a buffer with all the matches in all files and then you do /find and move with n over your targets and change them (of course, you can actually change only the first one and then repeat the replacement with .).
If you miss some target, you just hit N to go back to the previous result and replace it.
Seems like you can't back to previous match using this pattern. Appeared bar propose following commands y/n/a/q/l/^E/^Y but no one of them will return backward to previous match.
But you can use little different pattern described bellow:
Type this /pattern replacing pattern with interested word;
Your cursor is navigated to first occurrence, if you don't need to change it press n it will navigates you to the next occurrence;
Since you understand you need to replace a word, do this by typing cw, this command cuts the forward word and turns you to insertion mode;
Type in desired text on the released place and press ESC to switch back to command mode;
Now again press n until desired occurrence;
Since you realize that you need to change an occurrence, just press on . it will repeat previously mentioned actions;
If you want to go back just use N.

How to undo up to last write in vim?

Let me pose the question this way. I open a new file in vim, (version 1)
#include<stdio.h>
main()
{
...blah
}
and then use <Esc>:w<Enter> to write the file. Then made changes (version 2)
#include<stdio.h>
main()
{
...blah
... edit1
... edit2 //and large number of changes here and there in code
}
then I save changes using <Esc>:w<Enter>.
Is there a way to undo changes to version 1 directly (Since it was a last save) i.e., without constantly pressing u for undoing
From Vim's help:
:earlier {N}f Go to older text state {N} file writes before.
When changes were made since the last write
":earlier 1f" will revert the text to the state when
it was written. Otherwise it will go to the write
before that.
When at the state of the first file write, or when
the file was not written, ":earlier 1f" will go to
before the first change.
So, if you didn't make changes after the second save, you can do what you want with:
:earlier 1f
On the other hand, if you did unsaved changes after the second save, then:
:earlier 2f
will solve your problem.
See :help :earlier, :help :undolist.
You can get all the way back to when you first opened the file pretty easily. Just type a number before u.
10000u, will undo 10000 times. If that's not enough try 1000000u :)
If you want to undo bit by bit, you can do it in any increment, try 5u.
If you just want to reload the file from disk use :e.
Put this into your .vimrc.
In normal mode shift u will undo till previous save. Pressing again it will undo till the save before that. You can still go back to original point with a lot of <C-R> if you dint made any changes.
nnoremap U :ea 1f<CR>

Undo all changes since opening buffer in vim

How can I undo all changes since opening a buffer? I imagine there may be some form of :earlier that does this.
UPDATE: Many are suggesting solutions for traversing to earlier file writes. This isn't what I asked for. I want to return to the original state the file was in when I originally loaded it into a buffer, no matter how many writes were made since then.
To revert the current buffer to the original state prior to the very
first change recorded in its undo list (see :help undo-tree), one
can use the following two consecutive invocations of the :undo
command:
:u1|u
The first command (:undo 1) reverts to the state of the buffer just
after the very first registered change, while the second command
(:undo) reverts that first change itself.
Starting with version 8.1 (see :helpg Patch 8.0.1441), Vim accepts
the change number 0 as a valid argument to the :undo command,
finally providing a way to refer to the state prior to any registered
changes. This makes it possible to achieve the same effect in
a single-command invocation:
:u0
You can use the
:edit!
command to get into the earliest saved state. See :help edit! for more information.
You can also check something like gundo.vim (can be found here), which displays the whole undo tree graphically, and you can easily jump between points. Then there is the histwin plugin which I did not used yet, but offers similar functionality.
In vim 8.1+ as well as in neovim, you can just use :u0
From the documentation
:u[ndo] {N} Jump to after change number {N}. See |undo-branches|
for the meaning of {N}. {not in Vi}
If you type
:u 1
it appears to go to after the first change; pressing u or typing :u will then go back to the change.
Otherwise, you can use a very large count to :earlier or g-
e.g.
:earlier 100000000 or 100000000g-
If you put this into a mapping/command, it could do any of these without too much trouble.
e.g.
:nnoremap <C-F12> :earlier 100000000<CR>
To access previously saved file status, I think the following work :
:earlier 1f
From the documentation :
:earlier {N}f Go to older text state {N} file writes before.
When changes were made since the last write
":earlier 1f" will revert the text to the state when
it was written. Otherwise it will go to the write
before that.
When at the state of the first file write, or when
the file was not written, ":earlier 1f" will go to
before the first change.
:earlier {N}m Go to older text state about {N} minutes before.
That should help... And even you have {N}h which is about {N} hours before.
A graphic solution:
The Gundo plugin allows visual comparison of changes in the undo history.
Open Gundo's "undo history pane", type G go to the last line, then we can back to the original file.

Resources