I have the file tmp.git.log
in my $HOME:
commit 07f5bbb6a3a112f7b601d6cd1f72ec3f20112920
Author: me
Date: 2024
some
comit
messages
diff --git path/file.end
index e969a7691b..0bf58bcb94 100644
--- removed
+++ added
@@ -1,7 +1,7 @@
- removed
+ added
:key="index"
label
:color="color"
@@ -10,11 +10,13 @@
:class="fontWeight"
etc.
I wanted to fill my quickfix list with the beginnings and endings of the changes, marked here by @@
at the line start. I tried to execute
:vimgrep /^@@ ~/tmp.git.log
And Nvim answered with
E682 invalid search pattern or separator
What is wrong with the above vimgrep command?
Also: Can I :vimgrep
in buffers which aren't written, e.g. non-files?
I want to :read !git show <git-hash>
into nvim to see changes done in my repo by certain commits, I don't need the files persisted. I know, there are plugins, but I want to understand what vanilla nvim can do here.
I want to read the result of git show <git-gash>
within Neovim, and populate the quickfix list with vimgrep.
Vimgrep fails to accept my search
:vimgrep /^@@ ~/tmp.git.log
with E682
.
How do I accomplish to fill my quickfix list with the added/removed file chunk positions for faster navigation?
Ideally without saving the git show result to a temporary file first.
:vimgrep /^@@ ~/tmp.git.log
What is wrong with the above vimgrep command?
You must enclose your pattern with /
and /
(or a pair of another character), as per :help :vimgrep
:
:vimgrep /^@@/ ~/tmp.git.log
^ you forgot this slash
Also: Can I
:vimgrep
in buffers which aren't written, e.g. non-files?
No. Both :grep
and :vimgrep
can only be used to search through files.
The mechanisms for searching in a file and searching in a buffer can't really be identical because the two tasks sit at different abstraction levels. You have very old and simple commands like :vimgrep
for the former but, sadly, only low-level stuff for the latter.
I came up with this monstrous one-liner years ago because I wanted to use the quickfix for buffer-level search but it only works for named buffers. I guess one could use the same basic logic (filter the lines from current buffer) but use :help setqflist()
/:help setloclist()
instead.
--- EDIT ---
FWIW, I just wrote this more versatile version of the linked snippet that makes use of the location list:
function! Global(pat, bang) range
" reverse operation if we have a bang
let operator = a:bang == '!' ? '!~' : '=~'
" padding is needed if range starts after line 1
let padding_top = a:firstline > 1 ? a:firstline - 1 : 0
" one call is better than many
let bufnr = bufnr()
" build the range for a clean quickfix title
let qf_title_range = a:firstline == 1 && a:lastline == line('$') ? '%' : a:firstline .. ',' .. a:lastline
" 1. get all lines in range into list
" 2. transform each item into syntactically valid qf item
" 3. keep items that match/don't match given pattern
let qf_list = getline(a:firstline, a:lastline)
\ ->map({ idx, val -> { 'bufnr': bufnr, 'lnum': idx + 1 + padding_top, 'text': val, 'valid': 1 } })
\ ->filter({ idx, val -> eval("val.text " .. operator .. "'.*' . a:pat . '.*'") })
" if possible…
" 1. set location list with list of items and proper title
" 2. open location list
" 3. jump to first location
if qf_list->empty() == 0
call setloclist(win_getid(), [], ' ', { 'title': ':' .. qf_title_range .. 'Global ' .. a:pat, 'items': qf_list })
lwindow
lfirst
endif
endfunction
" custom Ex command :Global calls custom function Global()
command! -bang -nargs=1 -range=% Global <line1>,<line2>call Global(<q-args>, expand('<bang>'))
which seems to work fine with everything I threw at it, named buffer or not.
Usage, populate location list with matching lines in given range:
:Global @@
:%Global foo
:3,21Global bar
Usage, populate location list with non-matching lines in given range:
:Global! @@
:%Global! foo
:3,21Global! bar