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

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.

Related

vim: Run multiple commands based off of one :global command

Apologies if this has been posted already, for I cannot find an answer, even on the vim wiki.
Is there a way I can run multiple commands in vim command-line mode off of a single :g search?
For example,
:%g/foo/ s/bar/\=#a/g | exe "norm /cat\<enter>\"ayiw"
Which (for what I intend it to do) should, on every line matching foo, replace bar with the contents of register a, and then find the next iteration of cat (even if it is many lines ahead), and put the surrounding word into register a.
Instead, this specific syntax completes the subsitution command using the current contents of the initial a register, and then executes the normal mode command on a single line (after the substitution has been completed).
This example is not my specific use-case but shows one instance where this functionality is useful. I realize I could put it all into a single exe, i.e., %g/foo/exe "norm :s/bar/\\=#a/g\<enter>/cat\<enter>\"ayiw", but I would like to do it the first way, as I feel it is more flexible.
I would prefer to do this using vanilla vim, but if a plugin exists for this, that is an okay alternative. Does anybody know if there is syntax to do such a thing?
Okay a "little bit" dirty, but does this work for you?
:let list = split(execute('g/cat/p'), '\n') | g/foo/ s/bar/\=matchstr(remove(list, 0), '\s\d\+\s\zs.*')/g
It first reads all occurences of cat save them in a list.
Then replace the first bar with the first cat... and so on.
The dirty part ist the matchstr command. the g//p also returns a number for the result so the list looks like this:
1 cat
2 cat
3 cat
...
that's why we have to remove a bit from the front. I would love to hear if someone knows a clean solution for that (I am also interested in a clean vimscript solution, does not have to be a oneliner).
You can do this (at least for multiple :s commands applied to a single :g). Example:
" SHORT STORY TITLES to single word of CapitalizedWords within <h3>s
.,$g/^\L\+$/s/[^A-Z0-9 ]\+//ge|s/\u\+/\L&/ge|s/\<\l\+\>/\u&/ge|s/ \+//ge|s/.*/<h3>&<\/h3>/

Vim - Find pattern on currently line ONLY

I'm wondering if there is a way to find a pattern, but restrict it to the current line. Basically, the equivalent of /PATTERN but restricted to the current line, rather than the entire document.
I've tried :s/PATTERN, but that deletes the pattern from the line and places my cursor at the beginning of the line, which is not at all what I need. I was hoping you could search without replacing...
I'm hoping to use this for a macro in order to place my cursor at the start of that pattern, as would happen when you do /PATTERN on the entire file, so anything that is macro-friendly is even better.
Any vim users out there that might have an idea?
EDIT: 0/PATTERN in a macro would work for my current need, but I'm hoping there's a more specific way to restrict the search.
ANSWER: There's a few ways posted in here so far, but the one I like best right now is using Shift+V to select the current line visually, followed by /\%V to search only in the visual selection. Then Shift+V again will turn off the visual mode.
My knowledge about macro is limited, but interactively, you can select current line with Shift + V, and then do /\%Vsearch (see http://vimdoc.sourceforge.net/htmldoc/pattern.html#/\%V).
try to Find first character of the Pattern by typing
f <letter>
It's not exactly what you need but can help to solve the problem.
/\%9lsearch
Where \%9 means line number 9.
Typing in the line number is still a bit lame. You can ctrl+r= followed by a vim expression and enter to evaluate the vim expression and insert its output. line('.') will return the line of the cursor.
In one complete step
/\%<c-r>=line('.')<cr>lsearch
For more help see:
:h /\%l
:h i_CTRL-R
Place the cursor on the line you want to search in
Select it with shift+v
Type / to begin searching
Prefix your term with \%V, e.g. \%Vabc to search for abc in only the visually selected blocks (in our case the single line)
You can search without replacing by using
:s/PATTERN//gc
Then press n to skip the replacement. If the pattern is not found, you won't even be asked.
You could also just highlight the current line or the range of lines.

vim Jump to the next non-identical line

I am looking at files that may have several consecutive identical lines.
Is there a easy way of jumping to the next non-identical line?
Alternatively I would like to be able to fold all the lines that are equal to the initial one showing just the number of linees that are folded.
You could define your own fold-expr:
first set fdm:
:set fdm=expr
then
:set foldexpr=getline(v:lnum)==#getline(v:lnum-1)?1:0
now you can test by typing zM, to close all fold, if you are lucky ^_^ all duplicated lines are folded.
you could type zR to open all folds.
if it works and you open those kind of file very often, you could put the above lines in your .vimrc.(au with ft) if only one time job, you can write mode line into that file.
Try this:
:nmap <F2> "1y$<CR>/^\(<C-R>1$\)\#!<CR>
It maps F2 to:
copy the current line into register 1
search for (and move to) the first line that does not match the contents of register 1
This seems to work well, unless the text of your copied line has escaped characters that will confuse the search regexp. This is because register 1 is just dropped into the search expression without escaping. This would be tricky to fix reliably, but for normal log files, it shouldn't be much of a problem.
Also: if you're not married to vim and just need to read the non-consecutively-duplicated lines of a file, the canonical UNIX way is:
uniq filename
If you want to be in vim but won't need to make changes to the file, try:
:%!uniq
(If you try the latter, be sure to exit without saving)

Vim: Yank distant line without moving

Suppose I have the following (* = cursor):
...
*
Kittens
Puppies
Humans
...
How do I yank the "Humans" (cursor relative 3rd line) while leaving the cursor in place?
Preferably in one motion or one (generic) command.
Try this:
:+3y
It uses the range +3 , that it is the point where it will begin to yank. and by default it does one line.
UPDATE: If you wanted to copy both the second and third line without moving cursor, you would use same command but with a range of two points, like:
:+2,+3y
It would copy both Puppies and Humans.
Now, for those who are wondering, based on Birei's answer, you can also do :ny, where n is the line number you want to yank. This is actually what I was looking for when I came to this page, and is helpful if you've got set number on and don't want to count relative lines.
Now, I wonder, how to perform this yank to a particular register! :D
My LineJuggler plugin combines yanks and puts into various short mappings; e.g. with 3[f you can fetch a line 3 lines below the current line and put it below the current line.

How to repeat the same search and replace command over disjunct line ranges in Vim?

I had a situation where I wanted to replace FOO with BAR through out a file. However, I only want to do it in certain places, say, between lines 68–104, 500–537, and 1044–1195. In practice, I dropped markers at the lines of interest (via ma, mb, mc, etc.) and ran the following:
:'a,'b s/FOO/BAR/g | 'c,'d s/FOO/BAR/g | 'e,'f s/FOO/BAR/g
I had to repeat this dozens of times with different search and replace terms s/CAT/DOG, etc., and it became a pain to have to rewrite the command line each time. I was lucky in that I had only three places that I needed to confine my search to (imagine how messy the command line would get if there were 30 or 40).
Short of writing a function, is there any neater way of doing this?
On a related note. I copied FOO to the s (search) register, and BAR to the r (replace) and tried running
:'a,'b s/\=#s/\=#r/ | 'c,'d s/\=#s/\=#r/ | 'e,'f s/\=#s/\=#r/
This would have saved me having to rewrite the command line each time, but, alas, it didn’t work. The replace bit \=#r was fine, but the \=#s bit in the search pattern gave me an error.
Any tips would be appreciated.
If you need to perform a set of line-wise operations (like substitutions) on a bunch of different ranges of lines, one trick you can use is to make those lines look different by first adding a prefix (that isn't shared by any of the other lines).
The way I usually do this is to indent the entire file with something like >G performed on the first line, and then use either :s/^ /X/ commands or block-visual to replace the leading spaces with X on the lines I want.
Then use :g in conjunction with :s. eg:
:%g/^X/s/FOO/BAR/g
:%g/^X/s/BAZ/QUUX/g
Finally, remove the temporary prefixes.
In order to get rid of the necessity to retype the same search
pattern, substitution string and flags, one can simply use the
:& command with the & flag:
:'a,'bs/pat/str/g | 'c,'d&& | 'e,'f&&
(See :help :& for details.)
Instead of using marker use this one :
:68,104s/FOO/BAR/g << substitue from line 68 to 104
This should make your job a little bit easier and clearer.
inspired by #Vdt's answer:
I am not sure but you could write all the substitutions down in a file and source that file i think.
substitutions.vim:
68,104s/FOO/BAR/g
168,204s/FOO/BAR/g
618,644s/FOO/BAR/g
681,1014s/FOO/BAR/g
.
.
.
68,104s/BAZ/BOOO/g
168,204s/BAZ/BOOO/g
and then :so substitutions.vim maybe you can also use this for multiple files of same structure. you can add an e to add an ignore error message, if it is not clear that the substitutions are found on the corresponding line blocks.
With q:, you can recall previous command lines and edit them as a normal Vim buffer, so you can quickly replace FOO and BAR with something else, then re-execute the line with Enter.
The s/\=#s/\=#r/ doesn't work; as you said, this only works in the replacement part. But for the pattern, you can use Ctrl + R Ctrl + R s to insert the contents of register s, instead of \=#s. Preferably use the default register, then it's a simple s//, but you probably know that already.
When performed over a closed fold, substitutions are limited to that fold.
fold each region
put the cursor on one closed fold
perform the substitution: :s/foo/bar<CR>
move to the next closed fold with zj or zk
use the command-line history: :<C-p><CR> or :<Up><CR> to perform the same substitution
repeat…
You can also add the c flag at the end of your substitution so that Vim asks you for a confirmation before actually performing it. This can be tedious if you have lot of matches.
Here's the simplest way to do it
:5,10s/old/new/g
5,10 : startlinenum,endlinenum

Resources