Capture Line numbers of lines matching a search - search

Example:
search:
/lastword\s*\n\zs\(\s*\n\)\{6}\ze\s*startword
This search searches 6 empty lines between a line ending with "lastword" and a line starting with "startword"
I would like to catch the linenumbers of the empty lines matching this search.
Is there a way to do this in vim?

You can use :g and :number (or :# for short) to print out the lines with line numbers.
:g/lastword\s*\n\zs\(\s*\n\)\{6}\ze\s*startword/#
To capture this content you have to use :redir to redirect the output to somewhere else. In this case below we redirect it to the unamed register (#")
:redir #"|execute 'g/pattern/#'|redir END
Note: Must use :execute with :g otherwise redir END will be executed on each matching line with the :g command.
Now as it is this is going to print via :# the starting line which is not what we want (we want the empty lines between foo and bar). We can use a range with the :# command to accomplish this.
:redir #"|execute 'g/foo\_.*bar/+,/bar/-#'|redir END
The range is now +,/bar/- which translate to start the next line (+) and the search for bar (/bar/) then subtract one line (-). We can probably simplify this some as we know the number of empty lines should be 6.
:redir #"|execute 'g/foo\_.*bar/+#6'|redir END
We can take this further by putting the content into a new buffer and remove the extra lines.
:redir #"|exe 'g/pattern/+#6'|redir END|new|pu|%s/\d\+\zs.*//|%le|g/^$/d
There is a lot going on here, but the idea is we capture the output, open a new buffer, paste the content, and then clean up the output.
Alternative
Alternatively awk might make this a bit easier. Run the following on the current Vim buffer:
:%!awk '/lastword\s*$/,/^\s*startword/ { if($0 ~ /^\s*$/) { print NR} }'
For more help see:
:h :g
:h :redir
:h :exe
:h :range
:h :new
:h :pu
:h /\zs
:h :le
:h :d

Related

How to move to end of a file upon opening it via a command in .vimrc using vim/MacVim?

I'm trying to open a file using a command I set in my .vimrc file. The relevant line in my .vimrc is similar to the following:
command Of so /Users/Dude/Working/open_file.txt
With open_file.txt containing the following:
tabnew /Users/Dude/Working/Project/config.txt
What I'd like to do when executing the 'Of' command is navigate to the end of config.txt. I've tried adding a large line number which is unlikely to exceed the number of lines in the file like so:
tabnew /Users/Dude/Working/Project/config.txt
250000
This takes me to the end of the file but doesn't seem like the right way to do it. Ideally, I'd also like to add a new line after the last line and navigate there too.
A few things:
I would suggest you use full names instead of short names. e.g. so -> source.
source is probably the wrong choice here as you can do everything with the right-hand-side of command
May want to use ! with command so you can resource your vimrc file. e.g. command! Of ...
$ represents the last line of the file. No need to choose a magic number
Create a new line can be done with :normal o or :put _
So with some tweaks we get the following command:
command! Of tabedit /Users/Dude/Working/Project/config.txt | $put_
For more help see:
:h :command
:h :put
:h :range
:h :bar
Have a look at :h :normal in your case just write :norm Go instead of your number there.
:tabnew, like most variants of :edit (and the command-line arguments when launching Vim), takes arbitrary Ex commands via the [+cmd] argument. The $ command will move to the end of the file:
tabnew +$ /Users/Dude/Working/Project/config.txt

How to search at vim 'scriptnames' command

The vim 'scriptnames' command output all scripts loaded. The problem is that I can't find any practical way to filter/search/find on it. I want to look for some script without having to do this by "eye brute force".
There is no such thing as a script command, there are only scriptencoding and scriptnames (which can be abbreviated as scr, according to :h :scr). I presume you're looking for scriptnames.
With Vim 8 you can filter the results of most commands with :filter:
:filter /pattern/ scriptnames
(cf. :h :filter).
With older versions of Vim you can redirect all messages to a file before running :scriptnames, then cancel the redirection:
:redir >file
:scriptnames
:redir END
(cf. :h :redir). Alternatively, you can redirect messages to a register, then paste the contents of the register to a buffer:
:redir #a
:scriptnames
:redir END
:new
"aP
Finally, Tim Pope's plugin scriptease adds a command :Scriptnames that runs :scriptnames and loads the results into a quickfix list.
Instead of :redir, we have execute() with recent versions of vim.
Thus, you can play with :echo filter(split(execute('scritnames'), "\n"), 'v:val =~ "somepattern") or :new+put=execute(':scriptnames')+search in the buffer as you would have explored a log life.

VIM check for search pattern match in vim script

I just started with vim script and try make translation of opencart language files easier. I want to have a function that looks for a given search pattern and selects it. If there is no match left in the file, it shall open the next file for editing. What I have so far:
function! Nextmatch()
normal /\(= *'\)\#<=.\{-\}\('\)\#=/
normal v//e
endfunction
function! Nextfile()
if !exists("s:filecounter")
execute "!find -iname *.php > files.txt"
normal <CR>
let s:filecounter=0
endif
let s:lines= system("wc -l < files.txt")
if s:filecounter<s:lines
w
let s:filecounter += 1
let s:sedcommand="sed '".s:filecounter."!d' files.txt"
let s:selectedfile=system(s:sedcommand)
execute 'edit' s:selectedfile
else
wq
endif
endfunction
How can I achieve that Nextfile() is called in Nextmatch() if the search pattern is not found between the cursor and the end of the current file? And is there something that you consider to be bad style in my snippet?
Quickfix commands are powerful and well integrated with some external plugins, but if you really need to use your own script, and if you need to check a match in an if statement, just do:
if search("=\\s*'\\zs[^']*\\ze", 'W') == 0
echo 'No match until the end of the buffer'
endif
See :h search(), and please note :
the double backslashes, due to the double quotes
the 'W' flag which forbids wrapping around the end of file
I simplified the pattern you gave
You could simply use the :vim command to get rid of all your script.
I think the following should do quite what you're expecting:
:noremap <f8> <esc>:cn<cr>gn
/\(= *'\)\#<=.\{-\}\('\)\#=
:vim //g *.php
Then, to go to the next pattern in all files while selecting it,
you just have to press the F8 key.
In the noremap line, gn let you select the next actual search.
You may need to do:
:set nohidden
to let you navigate threw modified buffers (but don't forget to save
them with :wa, or list them with :ls)
About your script:
It's a good habit in scripts to always use :normal! instead of :normal (unless you deliberately need it) : thus, your personnal mappings won't interfer in your scripts.

Vi - Use search command with yank

I'm trying to use the yank command like this:
:0,/string,yy
To yank starting from the beginning of the current line, until the first instance of the word "string".
Obviously the above command does not work, as it tries to look for "string,yy". Can someone help me with this?
Thank you.
You can use a range and the :yank ex command to do so
:.,/string/yank
This can be shortened more to :,/string/y as the current line . can be assumed and :y is short for :yank.
Ex commands work line-wise. If you are trying to do this in normal mode and wish to yank characterwise then you are after the following:
y/string<cr>
For more help see:
:h :y
:h :range

Vim - get output of EX mode into a variable

I'm writing a vim script where I need to get the first line of the current buffer. In Ex mode I can simply type 1 and it shows me the content I want.
How can I put the output of the ex command into a variable in vim?
Chris's answer is the right approach.
Note however, that you can use the :redir command to capture the output of an Ex command into a variable:
:let myvar = ""
:redir => myvar
:command
:redir END
See :h :redir for more information.
The expression you want is getline(1). Thus, let x = getline(1).

Resources