Skip to content

Instantly share code, notes, and snippets.

@romainl
Last active November 7, 2021 20:57
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save romainl/3c7ee68125f822ec550c to your computer and use it in GitHub Desktop.
Save romainl/3c7ee68125f822ec550c to your computer and use it in GitHub Desktop.
Show ]I, [I, ]D, [D, :ilist and :dlist results — even spanning multiple files — in the quickfix window.
" This is an updated, more powerful, version of the function discussed here:
" http://www.reddit.com/r/vim/comments/1rzvsm/do_any_of_you_redirect_results_of_i_to_the/
" The purpose of this function is to make the results of ']I', '[I', ']D', '[D', ':ilist' and ':dlist'
" easier to navigate and more persistant by using the quickfix window instead of the default list-like interface.
function! List(command, selection, start_at_cursor, ...)
" Derive the commands used below from the first argument.
let excmd = a:command . "list"
let normcmd = toupper(a:command)
" If we are operating on a visual selection, redirect the output of '[I', ']I', '[D' or ']D'.
" If we don't, redirect the output of ':ilist argument' or ':dlist argument'.
let output = ""
if a:selection
if a:0 > 0 && len(a:1) > 0
let search_pattern = a:1
else
let old_reg = @v
normal! gv"vy
let search_pattern = substitute(escape(@v, '\/.*$^~[]'), '\\n', '\\n', 'g')
let @v = old_reg
endif
redir => output
silent! execute (a:start_at_cursor ? '+,$' : '') . excmd . ' /' . search_pattern
redir END
else
redir => output
silent! execute 'normal! ' . (a:start_at_cursor ? ']' : '[') . normcmd
redir END
endif
" Clean up the output.
let lines = split(output, '\n')
" Bail out on errors.
if lines[0] =~ '^Error detected'
echomsg 'Could not find "' . (a:selection ? search_pattern : expand("<cword>")) . '".'
return
endif
" Our results may span multiple files so we need to build a relatively complex list based on filenames.
let filename = ""
let qf_entries = []
for line in lines
if line !~ '^\s*\d\+:'
let filename = split(line, '\.\./')[-1]
else
let lnum = split(line)[1]
let text = substitute(line, '^\s*.\{-}:\s*\S\{-}\s\s', "", "")
let col = match(text, a:selection ? search_pattern : expand("<cword>")) + 2
call add(qf_entries, {"filename" : filename, "lnum" : lnum, "col" : col, "text" : text})
endif
endfor
" Build the quickfix list from our results.
call setqflist(qf_entries)
" Open the quickfix window if there is something to show.
cwindow
endfunction
" Override the built-in [I and ]I.
nnoremap <silent> [I :call List("i", 0, 0)<CR>
nnoremap <silent> ]I :call List("i", 0, 1)<CR>
" Add [I and ]I for visual mode.
xnoremap <silent> [I :<C-u>call List("i", 1, 0)<CR>
xnoremap <silent> ]I :<C-u>call List("i", 1, 1)<CR>
" Add the :Ilist command.
command! -nargs=1 Ilist call List("i", 1, 0, <f-args>)
" Override the built-in [D and ]D.
nnoremap <silent> [D :call List("d", 0, 0)<CR>
nnoremap <silent> ]D :call List("d", 0, 1)<CR>
" Add [D and ]D for visual mode.
xnoremap <silent> [D :<C-u>call List("d", 1, 0)<CR>
xnoremap <silent> ]D :<C-u>call List("d", 1, 1)<CR>
" Add the :Dlist command.
command! -nargs=1 Dlist call List("d", 1, 0, <f-args>)
@johnmastro
Copy link

This is very useful, thanks!

Should if len(a:1) > 0 be replaced with if a:0 > 0 or if a:0 > 0 && len(a:1) > 0?

I get an error (Undefined variable: a1) in the former case and, if I understand correctly, the intent is to check whether a:1 exists.

@romainl
Copy link
Author

romainl commented Mar 27, 2015

I updated the function as per @johnmastro's comment above and @Kyiv's comment on reddit:

  • better check for the existence of a:1,
  • better check for filename lines

@romainl
Copy link
Author

romainl commented Apr 11, 2015

New version jumps to the actual match not the the line as it did (stupidly) before.

@c0ffeeartc
Copy link

Line 47, for my use case, should be:

let filename=split(line, '\.\./')[-1]

I have includes like this in *.hpp file:

#include "../foo/bar.hpp"

Snippet shows such paths as:

~/path/to/../foo/bar.hpp

And hitting Enter on top of such item opens empty buffer with path like in quickfix. Going to netrw opens correct folder, and hitting Enter opens file in correct form foo/bar.hpp.
[count][<c-i> also works well for such case.

@romainl
Copy link
Author

romainl commented Apr 11, 2015

Fixed the column jumping.

@romainl
Copy link
Author

romainl commented Apr 11, 2015

Modified line 47 as per c0ffeeartc's comment. Didn't notice any undesirable side effect.

@romainl
Copy link
Author

romainl commented Apr 11, 2015

I'm wondering if I should turn this thing into an actual plugin. On one hand it would help tracking issues, on the other hand this function may be too small for that.

@romainl
Copy link
Author

romainl commented Apr 11, 2015

This gist is no longer maintained. See romainl/vim-qlist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment