Counting occurrences in Vim without marking the buffer changed - vim

In order to know how many times a pattern exists in current buffer, I do:
:%s/pattern-here/pattern-here/g
It gives the number of occurrences of the pattern, but is obviously cumbersome and also has the side-effect of setting the 'changed' status.
Is there a more elegant way to count?

To avoid the substitution, leave the second pattern empty, and add the ā€œnā€ flag:
:%s/pattern-here//gn
This is described as an official tip.

:help count-items
In VIM 6.3, here's how you do it.
:set report=0
:%s/your_word/&/g # returns the count without substitution
In VIM 7.2, here's how you'd do it:
:%s/your_word/&/gn # returns the count, n flag avoids substitution

:!cat %| grep -c "pattern"
It's not exactly vim command, but it will give you what you need from vim.
You can map it to the command if you need to use it frequently.

The vimscript IndexedSearch enhances the Vim search commands to display "At match #N out of M matches".

Put the cursor on the word you want to count and execute the following.
:%s/<c-r><c-w>//gn
See :h c_ctrl-r_ctrl-w

vimgrep is your friend here:
vimgrep pattern %
Shows:
(1 of 37)

Related

the difference between : and / in s/vi/VIM/g and s:^vi$:VIM:

I read vim regex example
s/vi/VIM/g
and
s:^vi$:VIM:
What' the difference between / and :,
Search but found few helpful materials.
Vim lets you change the character you use to start and end the search pattern arbitrarily. This is useful if you're going to have to escape the slash a lot in a particular expression.
For example, these two commands are equivalent:
s/\/\//ss/g
s://:ss:g
but the second one is much easier to type and read.
The two :s commands are different.
s/vi/VIM/g replace all vi by VIM no matter where vi is and how many times it occurred.
However, s:^vi$:VIM: replace lines containing only vi two characters by line: VIM
Regarding the / and : they are just separators of :s command. They make no difference in your command. If you want to read this part explanation, do :h E146 in your vim.

Vim keyboard shortcut general word count

How do I count the words in a document in vim? According to vim's definition of a word.
Seems easy to answer however I couldn't find an answer on here as they all addressed word matches, or having it in a function. I just want the word count.
You may also find it useful to know that you could find the answer yourself using helpgrep.
e.g.
:helpgrep count words
You can then use :cnext to flip through the search result. Result #2 in this case is what you need.
The keyboard shortcut is g Ctrl-g.
Thanks - Assaf Lavie
For further information about the usage of this see :help g
Thanks - Munen
For those who have access to the GNU coreutils (E.G Linux) you can use the program wc see man wc for more information
:!wc -w %<tab>
The %tab expands to the current file in the buffer
I like using standard shell commands for such tasks. Therefore I would pipe the text to wc. Highlight the lines you want to count the words in, then:
:!wc -w
This will replace the text with the actual wordcount. Of course you can bring back your text by undoing the last action with u.

VIM: Finding and replacing first N occurrences of a word

I am editing a file and i want to change only a specific word with another word, but only for first N occurrences. I tried multiple commands
N :s/word/newword/
:%s/word/newword/count
And other commands that i could find on google. But none of them works.
EDIT:: Vim commands is preferred. But Vim script can also be used. I don't have any prior experience in vim scripting.
Using a disposable recording allows you to control exactly how many changes you do:
qq " start recording in register q
/foo<CR> " search for next foo
cgnbar<Esc> " change it to bar
q " end recording
11#q " play recording 11 times
See :help recording and :help gn.
Another way, using :normal:
:norm! /foo<C-v><CR>cgnbar<C-v><Esc> <-- should look like this: :norm! /foo^Mcgnbar^[
11#:
See :help :normal and :help #:.
Or simply:
:/foo/|s//bar<CR>
11#:
Although a bit longer, you can do:
:call feedkeys("yyyq") | %s/word/newword/gc
to replace the first 3 occurrences and then stop.
You can change the amount of y's for more or less replacements. (Can also use n to skip some)
Explanation: this is feeding y keystrokes into the /c confirm option of the substitution command.
I'm not sure about specifying the first N occurrences, but I often use this command:
:%s/word/newword/gc
Vim then asks for confirmation of each occurrence of word so you can selectively change some but not others.
My PatternsOnText plugin provides (among many others) a command that takes answers in the form of either yyynyn or 1-5:
:%SubstituteSelected/word/newword/g 1-5

Vim: How to delete the same block of text over the whole file

I'm reviewing some logs with Java exception spam. The spam is getting is making it hard to see the other errors.
Is is possible in vim to select a block of text, using visual mode. Delete that block every place it occurs in the file.
If vim can't do it, I know silly question, vim can do everything. What other Unix tools might do it?
Sounds like you are looking for the :global command
:g/pattern/d
The :global command takes the form :g/{pat}/{cmd}. Read it as: run command, {cmd}, on every line matching pattern, {pat}.
You can even supply a range to the :delete (:d for short) command. examples:
:,+3d
:,/end_pattern/d
Put this togehter with the :global command and you can accomplish a bunch. e.g. :g/pat/,/end_pat/d
For more help see:
:h :g
:h :d
:h :range
Vim
To delete all matching lines:
:g/regex/d
To only delete the matches themselves:
:%s/regex//g
In either case, you can copy the visual selection to the command line by yanking it and then inserting it with <C-r>". For example, if your cursor (|) is positioned as follows:
hello wo|rld
Then you can select world with viw, yank the selection with y, and then :g/<C-r>"/d.
sed
To delete all matching lines:
$ sed '/regex/d' file
To only delete the matches themselves:
$ sed 's/regex//g' file
grep
To delete all matching lines:
$ grep -v 'regex' file
grep only operates line-wise, so it's not possible to only delete matches within lines.
you can try this in vim
:g/yourText/ d
Based on our discussion in the comments, I guess a "block" means several complete lines. If the first and last lines are distinctive, then the method you gave in the comments should work. (By "distinctive" I mean that there is no danger that these lines occur anywhere else in your log file.)
For simplifications, I would use "ay$ to yank the first line into register a and "by$ to yank the last line into register b instead of using Visual mode. (I was going to suggest "ayy and "byy, but that wold capture the newlines)
To be on the safe side, I would anchor the patterns: /^{text}$/ just in case the log file contains a line like "Note that {text} marks the start of the Java exception." On the command line, I would use <C-R>a and <C-R>b to paste in the contents of the two registers, as you suggested.
:g/^<C-R>a$/,/^<C-R>b$/d
What if the yanked text includes characters with special meaning for search patterns? To be on the really safe side, I would use the \V (very non-magic) modifier and escape any slashes and backslashes:
:g/\V\^<C-R>=escape(#a, '/\')<CR>\$/,/\V\^<C-R>=escape(#b, '/\')<CR>\$/d
Note that <C-R>= puts you on a fresh command line, and you return to the main one with <CR>.
It is too bad that \V was not available when matchit was written. It has to deal with text from the buffer in a search pattern, much like this.

More detailed advice on advanced mapping

I've found substantial use for commands that will find differences in two lists of words, and the best (and really only) solution to this I've found is this command:
g/^/kl |if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW') |'ld
I want to make a mapping in my _vimrc file that will execute this line whenever I hit F2 for example, but I haven't been able to make it work.
If someone could explain character by character what this line actually does, I think it would make all the difference in the world. I've seen a dozen or so articles on vim mapping and none of them explain what things like / or ^ or \.*{}^$/' do in contexts like this one.
When are : needed in mappings? I've seen some examples with and most without.
When is <CR> needed?
Thanks in advance
This sounds like a job for Awk
:%!awk '!a[$0]{a[$0]=1;print}'
However you asking a two questions:
What does :g/^/kl |if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW') |'ld do?
How can I make a mapping to this?
The Mapping
Let's start with "How can I make a mapping to this?":
:nnoremap <f2> :g/^/kl<bar>if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW')<bar>'ld<cr>
The trick is to use <bar> instead of | and to actually execute the command with <cr>. See :h keycodes.
What does this do?
It goes over every line of the buffer with :g/^/ and deletes lines that are the same as a line from above, if search('^'.escape(getline('.'),'\.*[]^$/').'$','bW') and d. The confusing parts to me are the following:
Using marks needlessly i.e. :k and range with the :d command.
A complicated building of the regex for the search() function. By using the \V (very nomagic) we can reduce the line noise in the regex: '\V\^'.escape(getline('.'),'\').'\$'
Why are you doing an O(N^2) operation when you can do an O(N)?
Simplify the command
g/^/if search('\V\^'.escape(getline('.'),'\').'\$','bWn') | d | endif
We remove the needless marks and simplify the escaping. I also added the endif to show the end of the if statement (this can be optionally left off because it will be assumed).
:g/{pat}/{cmd} The :global command runs {cmd} on ever line matching {pat}
:g/^/ is a common idiom to run a command on every line, since all lins have a beginning, ^.
if {expr} | {cmds} | endif. Execute {cmds} if the expression, {expr}, evaluates to true
search({pat}, {flags}) search for {pat} in the buffer. {flag} alter the search behavior
search() returns the line number of a match or 0 for no match found
b flag for search() means search backwards
W flag means do not wrap around the buffer when searching
n do not move the cursor
escape({str}, {chars}) escape {chars} with \
\V pattern uses very no magic meaning all regex meta characters need to be escaped
\^ and \$ are escaped for start and end of line because of \V option
:delete or :d for short delete the current line
I suggest you use the awk solution at the start of this answer.
:nnoremap <f2> :%!awk '!a[$0]{a[$0]=1;print}'<cr>
For more help see the following:
:h :range!
:h :g
:h :d
:h :l
:h :if
:h search(
:h escape(
:h /\V

Resources