vim fw, Fw, tw, Tw, and ; in command mode - vim

I've been using shortcutfoo to tighten up my vim muscle memory and I ran across some commands I can't understand even with their animations and explanations.
These are the explanations for the commands:
fw | Move to next 'w' on line
Fw | Move to previous 'w' on line
tw | Move before next 'w' on line
Tw | Move before previous 'w' on line
; | Repeat last f, F, t, or T
I've tried using these commands in my vim but they don't seem to do anything noticeable.

Go to link:
https://www.shortcutfoo.com/app/dojos/vim/cheatsheet
Search keyword: Intermediate Text Navigation I.
Hover in icon camera preview. You can see an example to imagine easy.
Example:
fw | Move to next 'w' on line:
Fw | Move to previous 'w' on line
tw | Move before next 'w' on line
Tw | Move before previous 'w' on line
; | Repeat last f, F, t, or T
Similar with other key shortcut.

Related

VIM - Reformat text to one line paragraphs

I have a text file like the following:
--------
FOX&DOGS
The quick brown. Fox
jumped.
Over the lazy dogs.
-------------------
I want to change it as follow:
--------
FOX&DOGS
The quick brown. Fox jumped.
Over the lazy dogs.
-------------------
So in general:
preserve empty line/lines
have new-lines just after any period_newline ".\n" (end of paragraph... In the above example I don’t want to cut line after "brown." for instance: there is just a period but not followed by newline, so it isn’t an end of a paragraph, so it has to stay on the same line)
My solution:
%s/\n\n/#\r#\r/ | %s/\.\n/\.#\r/ | %j | s/# /\r/g | $$d
The idea is a bit rude:
mark all ends of paragraph and empty lines (I have chosen "#" as marker)
join all lines in a long single one
substitute the marker "# " (there is a space after #) with carriage return "\r" (newline)
delete last empty line created during this procedure
It seemed to work so I also created an alias in vimrc:
command Par %s/\n\n/#\r#\r/ | %s/\.\n/\.#\r/ | %j | s/# /\r/g | $$d
The problem:
If there aren’t any empty lines it returns error "pattern not found", and it doesn’t change anything. Seems a sort of conditional instruction is needed (if you find pattern substitute it with... else don't stop, continue with the other commands).
Any idea to solve in a simple way?
Maybe I found a solution:
add a blank line after the last one, so that the pattern “\n\n” is always found even if it isn’t present in the original file, and the error can’t block next commands.
in the end we will have to remove 2 blank lines at the bottom created by the substitution “s/# /\r/g”
So the command I tried is:
$ | put _ | %s/\n\n/#\r#\r/ | %s/\.\n/\.#\r/ | %j | s/# /\r/g | $$d | $$d
$ go to the last line
append a blank line
mark newlines involving blank lines (also last blank line added) with # character
mark newlines involving period (the last line can’t end with period due to the marker # added at the previous step)
join all lines in a long one
replace markers “# ” with a newline (here we creates two more blank lines at the bottom, have to be removed)
remove the two last blank lines added
Limitations:
if a paragraph ends with a punctuation mark other than “period”, it doesn’t work at all.
Any idea to improve my raw oneliner is welcome!

How g makes loop in Vim ex command script

Consider the following Vim ex command,
:let i=1 | '<,'>g/^/ s/^\ *-/\=i/ | let i+=1
It replaces the heading dash with ordered number in selected lines.
I don't understand why this command works as a loop from the first line to the last line of the selected lines. That is, how g can repeat let i+=1 over and over again.
The pattern of a global command is:
:range g[lobal][!]/pattern/cmd
The global commands work by first scanning through the [range] of of the lines and marking each line where a match occurs. In a second scan the [cmd] is executed for each marked line with its line number prepended. If a line is changed or deleted its mark disappears. The default for the [range] is the whole file. (see http://vimregex.com/#global for more details)
Now let's analyse
:let i=1 | '<,'>g/^/ s/^\ *-/\=i/ | let i+=1
step by step.
let i=1 is a single command executed setting the basic number for the loop. We can just execute it alone at the very beginning. Then '<,'>g/^/ s/^\ *-/\=i/ | let i+=1 looks a little more like a global command.
'<,'>g defines the range. '< represents the first line and '> represents the last line of the selected area. (:help '< for more details)
^ of course matches every line in range.
s/^\ *-/\=i/ | let i+=1 is the [cmd], the number of times it will be executed equals to the number of lines in the selected area, and this is the most important reason why the loop took place.
The part before | is a typical substitute command :range s[ubstitute]/pattern/string/ (see http://vimregex.com/#substitute for more details)
^\ *- matches 0 or more whitespace followed by a dash at the beginning of a line. We substitute \=i for this pattern. (:help :s\= for more details)
After s/^\ *-/\=i/, let i+=1 is executed. Then the next line, ... , till the last line of selected area.
For better understanding that s/^\ *-/\=i/ | let i+=1 is a [cmd] as a whole, we can change the order of the two [sub-cmd], obtaining let i+=1 | s/^\ *-/\=i/. But for the same effect, let i=0 at the very beginning is essential.
This is the general pattern of a :global command:
:g/foo/command
Because everything after the second separator is considered as one command, the counter is incremented each time the command is executed: one time for each matching line.

In Vim, how to remove all lines that are duplicate somewhere

I have a file that contains lines as follows:
one one
one one
two two two
one one
three three
one one
three three
four
I want to remove all occurrences of the duplicate lines from the file and leave only the non-duplicate lines. So, in the example above, the result should be:
two two two
four
I saw this answer to a similar looking question. I tried to modify the ex one-liner as given below:
:syn clear Repeat | g/^\(.*\)\n\ze\%(.*\n\)*\1$/exe 'syn match Repeat "^' . escape(getline ('.'), '".\^$*[]') . '$"' | d
But it does not remove all occurrences of the duplicate lines, it removes only some occurrences.
How can I do this in vim? or specifically How can I do this with ex in vim?
To clarify, I am not looking for sort u.
If you have access to UNIX-style commands, you could do:
:%!sort | uniq -u
The -u option to the uniq command performs the task you require. From the uniq command's help text:
-u, --unique
only print unique lines
I should note however that this answer assumes that you don't mind that the output doesn't match any sort order that your input file might have already.
if you are on linux box with awk available, this line works for your needs:
:%!awk '{a[$0]++}END{for(x in a)if(a[x]==1)print x}'
Assuming you are on an UNIX derivative, the command below should do what you want:
:sort | %!uniq -u
uniq only works on sorted lines so we must sort them first with Vim's buit-in :sort command to save some typing (it works on the whole buffer by default so we don't need to pass it a range and it's a built-in command so we don't need the !).
Then we filter the whole buffer through uniq -u.
My PatternsOnText plugin version 1.30 now has a
:DeleteAllDuplicateLinesIgnoring
command. Without any arguments, it'll work as outlined in your question.
It does not preserve the order of the remaining lines, but this seems to work:
:sort|%s/^\(.*\)\n\%(\1\n\)\+//
(This version is #Peter Rincker's idea, with a little correction from me.) On vim 7.3, the following even shorter version works:
:sort | %s/^\(.*\n\)\1\+//
Unfortunately, due to differences between the regular-expression engines, this no longer works in vim 7.4 (including patches 1-52).
Taking the code from here and modifying it to delete the lines instead of highlighting them, you'll get this:
function! DeleteDuplicateLines() range
let lineCounts = {}
let lineNum = a:firstline
while lineNum <= a:lastline
let lineText = getline(lineNum)
if lineText != ""
if has_key(lineCounts, lineText)
execute lineNum . 'delete _'
if lineCounts[lineText] > 0
execute lineCounts[lineText] . 'delete _'
let lineCounts[lineText] = 0
let lineNum -= 1
endif
else
let lineCounts[lineText] = lineNum
let lineNum += 1
endif
else
let lineNum += 1
endif
endwhile
endfunction
command! -range=% DeleteDuplicateLines <line1>,<line2>call DeleteDuplicateLines()
This is not any simpler than #Ingo Karkat's answer, but it is a little more flexible. Like that answer, this leaves the remaining lines in the original order.
function! RepeatedLines(...)
let first = a:0 ? a:1 : 1
let last = (a:0 > 1) ? a:2 : line('$')
let lines = []
for line in range(first, last - 1)
if index(lines, line) != -1
continue
endif
let newlines = []
let text = escape(getline(line), '\')
execute 'silent' (line + 1) ',' last
\ 'g/\V' . text . '/call add(newlines, line("."))'
if !empty(newlines)
call add(lines, line)
call extend(lines, newlines)
endif
endfor
return sort(lines)
endfun
:for x in reverse(RepeatedLines()) | execute x 'd' | endfor
A few notes:
My function accepts arguments instead of handling a range. It defaults to the entire buffer.
This illustrates some of the functions for manipulating lists. :help list-functions
I use /\V (very no magic) so the only character I need to escape in a search pattern is the backslash itself. :help /\V
Add line number so that you can restore the order before sort
:%s/^/=printf("%d ", line("."))/g
sort
:sort /^\d+/
Remove duplicate lines
:%s/^(\d+ )(.*)\n(\d+ \2\n)+//g
Restore order
:sort
Remove line number added in #1
:%s/^\d+ //g
please use perl ,perl can do it easily !
use strict;use warnings;use diagnostics;
#read input file
open(File1,'<input.txt') or die "can not open file:$!\n";my #data1=<File1>;close(File1);
#save row and count number of row in hash
my %rownum;
foreach my $line1 (#data1)
{
if (exists($rownum{$line1}))
{
$rownum{$line1}++;
}
else
{
$rownum{$line1}=1;
}
}
#if number of row in hash =1 print it
open(File2,'>output.txt') or die "can not open file:$!\n";
foreach my $line1 (#data1)
{
if($rownum{$line1}==1)
{
print File2 $line1;
}
}
close(File2);

VIM How to insert output of a shell command at a different place in the file

I have come up with a perl script that outputs a template for documenting functions and structures given the definition of a function/struct from my C code.
To use it , i visually select the definition of the struct, yank and paste it right above the original definition, and invoke the script on this pasted struct. It replaces it with document for that struct.
Now is there a way that will avoid that yank paste? I am looking for a way to invoke a shell command but the output from that should be pasted somewhere else in the file, not necessarily on top of it.
IOW
:'a,'b!perl ~/bin/document.pl
replaces text between mark a and markb, I want to add the output of document.pl above mark a.
One possible solution would be to modify the perl script in a way that it also outputs its input at the end. Then you would end up with the desired result.
If you have zsh as your shell, you can use co-process:
'a,'b!coproc perl ~/bin/document.pl ; tee >&p | cat <&p
To get output before your text (this command puts it after), you should use a slightly more complex command:
'a,'b!coproc perl ~/bin/document.pl ; tee >&p | cat <(<&p) -
System-independent solution, using vim and temporary buffer:
'a,'byank a | new | 0put a | $d | execute "%!perl ~/bin/document.pl" | %d a | bw! | 'a-1put a
Try with something like this:
function! MyFunc() range
" Preserve the register.
let old_reg = #a
exec a:firstline.','.a:lastline.'yank a'
" Change to do what you need with register a.
" Insert output before a:firstline
exec (a:firstline - 1).'read !your magic with '.#a
" Restore the register
let #a = old_reg
endfunction
" :2,5MyOwn will process lines from 2 to 5 and insert the output before line 2
command! -bar -range -nargs=? MyOwn <line1>,<line2>call MyFunc()

longest line in vim?

Is there a command to determine length of a longest line in vim? And to append that length at the beginning of the file?
Gnu's wc command has a -L --max-line-length option which prints out the max line length of the file. See the gnu man wc. The freebsd wc also has -L, but not --max-line-length, see freebsd man wc.
How to use these from vim? The command:
:%!wc -L
Will filter the open file through wc -L and make the file's contents the maximum line length.
To retain the file contents and put the maximum line length on the first line do:
:%yank
:%!wc -L
:put
Instead of using wc, Find length of longest line - awk bash describes how to use awk to find the length of the longest line.
Ok, now for a pure Vim solution. I'm somewhat new to scripting, but here goes. What follows is based on the FilterLongestLineLength function from textfilter.
function! PrependLongestLineLength ( )
let maxlength = 0
let linenumber = 1
while linenumber <= line("$")
exe ":".linenumber
let linelength = virtcol("$")
if maxlength < linelength
let maxlength = linelength
endif
let linenumber = linenumber+1
endwhile
exe ':0'
exe 'normal O'
exe 'normal 0C'.maxlength
endfunction
command PrependLongestLineLength call PrependLongestLineLength()
Put this code in a .vim file (or your .vimrc) and :source the file. Then use the new command:
:PrependLongestLineLength
Thanks, figuring this out was fun.
If you work with tabulations expanded, a simple
:0put=max(map(getline(1,'$'), 'len(v:val)'))
is enough.
Otherwise, I guess we will need the following (that you could find as the last example in :h virtcol(), minus the -1):
0put=max(map(range(1, line('$')), "virtcol([v:val, '$'])-1"))
:!wc -L %
rather than
:%!wc -L
To append that length at the beginning of the file:
:0r !wc -L % | cut -d' ' -f1
Here is a simple, hence easily-remembered approach:
select all text: ggVG
substitute each character (.) with "a": :'<,'>s/./a/g
sort, unique: :'<,'>sort u
count the characters in the longest line (if too many characters to easily count, just look at the column position in the Vim status bar)
I applied this to examine Enzyme Commission (EC) numbers, prior to making a PostgreSQL table:
I copied the ec_numbers data to Calc, then took each column in Neovim, replaced each character with "a",
:'<,'>s/./a/g
and then sorted for unique lines
:'<,'>sort u
aaaaaaa
aaaaaaaa
aaaaaaaaa
aaaaaaaaaa
aaaaaaaaaaa
... so the longest EC number entry [x.x.x.x] is 11 char, VARCHAR(11).
Similarly applied to the Accepted Names, we get
aaaaa
aaaaaa
...
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
i.e. the longest name is 147 char: VARCHAR(200) should cover it!
For neovim users:
local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, false)
local width = #(lines[1])
for _, line in ipairs(lines) do
if #line > width then
width = #line
end
end

Resources