Introduction
Quickfix Lists and Location Lists are a built in functionality in vim, that can be really useful in many use cases. It can help you out setting sort of bookmarks within a file for certain search terms.
This ocurrences will be parsed automatically and been shown on a separate buffer (the Quickfix List) in which you can navigate by j down and k up and pressing Enter to access each ocurrence, this will then set your initial buffer into the line of the ocurrence.
Even the file to be parsed can be an entire subdirectory, for instance a project, and this Quick Fix list will help you access each file of the ocurrence in a new buffer.
I can’t help but wonder the amount of use cases I have for my workflow, so we will get into oneuse case for me and see how I adapt it to make the perfect usage of it, learning the insights of the tool on the way of customizing our experience.
My use case , learning notes
I have simplyied the way I study almost everything in this life by using text files. From programing languages , to cooking notes, maths, networking technologies, economies etc…
I have got an entire subdirectory of notes which looks similar to
$ ls $HOME/notes/
(...)
learning.java.not
learning.js.not
learning.ps1.not
learning.tmux.not
learning.vim.not
(...)
cheatsheet.java.not
cheatsheet.js.not
(...)
- The learning files are actual notes with code snippets and notes written by myself after having learned something in particular.
- The cheathseet files are made of pure documentation dumps for each technology
For instance cheatsheet.vim.not
will be all the documents in plain-text of the documentation of vim , been parsed.
Starting from A-Z Arabic.txt Vim Reference Workshop.txt Vim Reference
This ends up me having a document with 123.684 Lines!! The reason of storing this offline , is to make my own notes, tracking the progression of functionalities I have already seen, mostly a way to quickly search for method Signatures that I have already used for a certain API.
Mainly I write labels to separate parts of the documents such as
goto:index
arabic.txt help.txt.vim-tiny os_dos.txt quotes.txt usr_02.txt usr_40.txt(X)
autocmd.txt howto.txt os_haiku.txt README.Debian usr_02.txt usr_41.txt(X)
(...)
goto:events
Name triggered by ~
Reading
|BufNewFile| starting to edit a file that doesn't exist (X)
|BufReadPre| starting to edit a new buffer, before reading the file (X)
|BufRead| starting to edit a new buffer, after reading the file (X)
(...)
goto:builtinfunctions
(...)
USAGE RESULT DESCRIPTION ~
abs({expr}) Float or Number absolute value of {expr}
acos({expr}) Float arc cosine of {expr} (X)
As you can see as well I append a (X)
to each line that I considered imporant (such as Method signatures of already used methods) so I can grep the document by grep "(X)" cheatsheet.vim.not
As my lines highlighted have been growing this method of parsing by grep has prooved insuficient , having to start using a pager, then using vim.
Is not the case of study I wanted for this article, I want yout o focus on my labels goto:
What I want is to create a New Tab that parses all the goto: lines and that I can access them quickly.
For that we can use Quickfix Lists
Greping those labels
I am going to explain broadly all the concepts , refering to the documentation for the functionalities that we are going to use.
Please find the Quickfix Reference Manual , and have it alongside you if you want to grasp each of the Vim Commands and functions.
By using :vimgrep
we can create such a Quickfix list and open it in a tab
:set switchbuf+=usetab,newtab
:vimgrep /goto:/ %
:tab copen
And voila !! you just have an interactive new tab
By using j,k you can scroll up , down and by using
1 cheatsheet.vim.not|63 col 1-6| goto:index
1 cheatsheet.vim.not|635 col 1-6| goto:events(X)
2 cheatsheet.vim.not|13261 col 1-6| goto:predefinedvimvariables(X)------------------------------- --------------------
3 cheatsheet.vim.not|13924 col 1-6| goto:builtinfunctions(X)
That was the main functionality that I wanted to add. But I am going to go an extra mile.
I want to have 2 different Tabs:
- One for the entries with the ocurrence goto:
- Other for the ocurrence (X)
Having two simultaneous lists opened at the same time
One would say , it will be easy just do something like
:vimgrep /goto:/ %
:tab copen
:vimgrep /(X)/ %
:tab copen
This wont work , as the Window used for the QuickFix list is the sameone , you can only have one Quickfix List opened at once (though you can have multiple ones in different buffers , but you need to be switching the focus of the Quickfix Window by doing :cnext :cprev
So use the other Quickfix lists format , they can be used alternatively at the same time, called Location Lists , this are intended to use for only within one file ocurrence mainly (see documentation for more)
The Location List are used by almost the same syntax commands but use an l instead of a c on them
:lvimgrep
:lopen
:lnext
(...)
I have finally this on my .vimrc so to identify the file extension I am using and execute this
augroup OpenFileAnd
autocmd!
autocmd BufReadPost *.not call MyFunction()
augroup END
def MyFunction()
lvimgrep /goto:/ %
tab lopen
vimgrep /(X)/ %
tab copen
enddef
set switchbuf+=usetab,newtab
It works perfectly now. Only issue is that my lines now look like
2 cheatsheet.vim.not|670 col 71-74| |BufNewFile| starting to edit a file that doesn't exist (X)
3 cheatsheet.vim.not|671 col 71-74| |BufReadPre| starting to edit a new buffer, before read ing the file (X)
They look too wide to be displayed on a narrow screen , plus all the info relating to fileName , and ocurrence line and column is a bit useless , if only I could trim it?
Adanced usage of Quickfix Lists Functions overview
You can fine create and modify Quickfix Lists in a finer way by using the functions setqflist()
getqflist()
see reference pages.
setqflist Function Reference getqflist Function Reference
In a nutshell I will paste it as plain-text the signatures and the arguments that we are going to use.
Note all of the parameters are either Dictionaries (Objects in vim script) or Lists (Arrays in vim script)
# setqflist({list} [, {action} [, {what}]])
Create or replace or add to the quickfix list. (X)
{action} values: *setqflist-action* *E927* (X)
- 'a' The items from {list} are added to the existing (X)
- 'r' The items from the current quickfix list are replaced (X)
(...)
{what} is optional , but if used , it will deem {list} useless
{what}: (X)
- idx index of the current entry in the quickfix list specified by 'id' or 'nr'. If set to '$', then the last entry in the list is set as the current entry. (X)
- quickfixtextfunc function to get the text to display in the quickfix window. (X)
- items list of quickfix entries. Same as the {list} argument. (X)
- bufnr buffer number; must be the number of a valid buffer (X)
- lnum line number in the file (X)
- text description of the error (X)
# getqflist([{what}])
Returns a |List| with all the current quickfix errors. Each (X)
{what} dictionary (X)
- id get information for the quickfix list with (X)
- items quickfix list entries (X)
Creating a quickfix list
For creating a quickfix list with just one entry pointing to line number 42 of the current opened buffer do
call setqflist([{'bufnr': bufnr(''), 'lnum': 42, 'text': 'entry'}], 'a')
You can put the {list} as a property of the property items
in {what}
argument
call setqflist([] , 'a' , {'title' : 'pepe', 'items': [{'bufnr': bufnr(''), 'lnum': 42, 'text': 'entry'}]} )
Modifying an existing quickfix list
Seeing that the commands can get messy with all of those nested dictionaries and Lists we are going to write a Quickfix List , creating it first , then modifying its content adding two entries afterwards.
" Establishing the current buffer to be referenced
let bufferNoRef = bufnr("")
" Creating a new qflist
call setqflist(
\ [] , 'r',
\ {
\ 'title': 'pepe'
\ }
\)
" Getting the qfId of the newly created qflist
let qfIdRef = getqflist({'id' : 0}).id
" Adding two entries to that mentioned list
call setqflist(
\ [] , 'r',
\ {
\ 'id': qfIdRef,
\ 'items': [ {
\ 'bufnr': bufferNoRef,
\ 'lnum': 42,
\ 'text': 'entry'
\ },{
\ 'bufnr': bufferNoRef,
\ 'lnum': 5,
\ 'text': 'entry'
\ }]
\ }
\)
Breaking down an item for each list
Following the previous code we are going to retrieve information of each item of the list by
let myItemList = getqflist( {'id' : qfIdRef , 'items': 1}).items
echo myItemList[0]
# {'lnum': 42, 'bufnr': 1, 'end_lnum': 0, 'pattern': '', 'valid': 1, 'vcol': 0, 'nr': 0, 'module': '' , 'type': '', 'end_col': 0, 'col': 0, 'text': 'entry'}
echo myItemList[0].lnum
# 42
As you can see you first grab an item , so item:1 and perform the query with getqflist()
this will return you an iterable object (a List) with all the entries for that Quickfix List
Each of this entries have all the properties accesible such as lnum
bufnr
or text
Customizing your quickfix list
Following again the previous code, we can grab an existing List, and modify its property quickfixtextfunc
which value points to a Function that will set its format.
First we define the function. then we modify the list to make an effect on that List.
"Customizing the style of the quickfix text displayed
func QfFormating(info)
let items = getqflist({'id' : a:info.id, 'items' : 1}).items
let l = []
for idx in range(a:info.start_idx - 1, a:info.end_idx - 1)
call add(l, items[idx].text)
endfor
return l
endfunc
" Applying the formating function to the current Quickfix List
call setqflist(
\ [] , 'r',
\ {
\ 'id': qfIdRef,
\ 'quickfixtextfunc': 'QfFormating'}
\)
Applying this to any list will only leave the text of the ocurrence, no more filename or linenumber and other clutter.
Objective accomplished.