-
-
Save BoltsJ/5942ecac7f0b0e9811749ef6e19d2176 to your computer and use it in GitHub Desktop.
if exists('g:loaded_qfsign') | |
finish | |
endif | |
let g:loaded_qfsign=1 | |
sign define QFErr texthl=QFErrMarker text=E | |
sign define QFWarn texthl=QFWarnMarker text=W | |
sign define QFInfo texthl=QFInfoMarker text=I | |
augroup qfsign | |
autocmd! | |
autocmd QuickFixCmdPre [^l]* call s:clear_signs() | |
autocmd QuickFixCmdPost [^l]* call s:place_signs() | |
augroup END | |
nnoremap <Plug>(QfSignPlace) :silent call <SID>place_signs()<CR> | |
nnoremap <Plug>(QfSignClear) :silent call <SID>clear_signs()<CR> | |
let s:sign_count = 0 | |
function! s:place_signs() abort | |
let l:errors = getqflist() | |
for l:error in l:errors | |
if l:error.bufnr < 0 | |
continue | |
endif | |
let s:sign_count = s:sign_count + 1 | |
if l:error.type ==# 'E' | |
let l:err_sign = 'sign place ' . s:sign_count | |
\ . ' line=' . l:error.lnum | |
\ . ' name=QFErr' | |
\ . ' buffer=' . l:error.bufnr | |
elseif l:error.type ==# 'W' | |
let l:err_sign = 'sign place ' . s:sign_count | |
\ . ' line=' . l:error.lnum | |
\ . ' name=QFWarn' | |
\ . ' buffer=' . l:error.bufnr | |
else | |
let l:err_sign = 'sign place ' . s:sign_count | |
\ . ' line=' . l:error.lnum | |
\ . ' name=QFInfo' | |
\ . ' buffer=' . l:error.bufnr | |
endif | |
silent! execute l:err_sign | |
endfor | |
endfunction | |
function! s:clear_signs() abort | |
while s:sign_count > 0 | |
execute 'sign unplace ' . s:sign_count | |
let s:sign_count = s:sign_count - 1 | |
endwhile | |
redraw! | |
endfunction |
This is really neat! Thank you for sharing.
A few improvements I've made that I would like to share:
Filter out text unmatched by errorformat
In cases where linter/compiler output doesn't match errorformat
exactly, getqflist()
still reports it, causing the unmatched output to be calculated/reported in the final else
control flow.
For example, consider the following tflint.vim
compiler configuration:
CompilerSet makeprg=tflint\ --format=compact\ $*
CompilerSet errorformat=%f:%l:%c:\ %trror\ -\ %m,%f:%l:%c:\ %tarning\ -\ %m,%f:%l:%c:\ -\ %totice\ %m
And the following terraform file:
data "" "" {}
Running :make %
produces:
2 issue(s) found:
data.tf:1:1: Warning - Missing version constraint for provider "" in "required_providers" (terraform_required_providers)
data.tf:1:1: Warning - data "" "" is declared but not used (terraform_unused_declarations)
Running :echo getqflist()
in vim produces (note the first 2 elements in the array):
[
{
"lnum": 0,
"bufnr": 0,
"end_lnum": 0,
"pattern": "",
"valid": 0,
"vcol": 0,
"nr": -1,
"module": "",
"type": "",
"end_col": 0,
"col": 0,
"text": "2 issue(s) found:"
},
{
"lnum": 0,
"bufnr": 0,
"end_lnum": 0,
"pattern": "",
"valid": 0,
"vcol": 0,
"nr": -1,
"module": "",
"type": "",
"end_col": 0,
"col": 0,
"text": ""
},
{
"lnum": 1,
"bufnr": 1,
"end_lnum": 0,
"pattern": "",
"valid": 1,
"vcol": 0,
"nr": -1,
"module": "",
"type": "W",
"end_col": 0,
"col": 1,
"text": "Missing version constraint for provider \"\" in \"required_providers\" (terraform_required_providers)"
},
{
"lnum": 1,
"bufnr": 1,
"end_lnum": 0,
"pattern": "",
"valid": 1,
"vcol": 0,
"nr": -1,
"module": "",
"type": "W",
"end_col": 0,
"col": 1,
"text": "data \"\" \"\" is declared but not used (terraform_unused_declarations)"
}
]
A simple fix is to check if bufnr <= 0
(is buffer number of 0
even possible?), like:
if l:error.bufnr <= 0
continue
endif
Alternative, you can check if lnum
is 0
(because why/how would you show signs for something with no line number?), like:
if l:error.bufnr < 0 || l:error.lnum == 0
continue
endif
Sign Priority
Signs were not showing up in my signcolumn
. This was because Vim assigns a default priority of 10
for signs (:h sign-place
). Priority helps Vim determine which sign to show when multiple signs are placed on the same line. This scenario happes easily & frequently with plugins like vim-signify
or vim-gitgutter
.
I fixed this by assigning a priority of 99
to errors, 98
to warnings, and 97
to info, because I much rather see issues with code than git status indicators in my signcolumn
, like this:
if l:error.type ==# 'E'
let l:qf_sign = 'sign place ' . s:sign_count
\ . ' priority=99'
" ...
elseif l:error.type ==# 'W'
let l:qf_sign = 'sign place ' . s:sign_count
\ . ' priority=98'
" ...
else
let l:qf_sign = 'sign place ' . s:sign_count
\ . ' priority=97'
" ...
Note: priority
must be specified/assigned before file=
or buffer=
.
" this works
:sign place priority=99 line=1 name=QFErr buffer=1
" this does not ¯\_(ツ)_/¯
:sign place line=1 name=QFErr buffer=1 priority=99
Support location lists
" ...
autocmd QuickFixCmdPre * call s:clear_signs()
autocmd QuickFixCmdPost [^l]* call s:place_signs('qf')
autocmd QuickFixCmdPost l* call s:place_signs('ll')
" ...
function! s:place_signs(list_type) abort
if a:list_type == 'qf'
let l:errors = getqflist()
elseif a:list_type == 'll'
let l:errors = getloclist(winnr())
endif
" ...
Surface counts to vimrc
I placed this script in my ~/.vim/plugin/
directory and wanted to surface the count of errors/warnings/infos to show in my statusline.
" ...
let s:sign_count = 0
" Surface counts to .vimrc
let g:qfsigns_error = 0
let g:qfsigns_warn = 0
let g:qfsigns_info = 0
" ...
function! s:place_signs(list_type) abort
let l:qfsigns_error = 0
let l:qfsigns_warn = 0
let l:qfsigns_info = 0
for l:error in l:errors
" ...
if l:error.type ==# 'E'
let l:qfsigns_error = l:qfsigns_error + 1
" ...
elseif l:error.type ==# 'W'
let l:qfsigns_warn = l:qfsigns_warn + 1
" ...
else
let l:qfsigns_info = l:qfsigns_info + 1
" ...
endfor
let g:qfsigns_error = l:qfsigns_error
let g:qfsigns_warn = l:qfsigns_warn
let g:qfsigns_info = l:qfsigns_info
endfunction
Then in my vimrc
:
set statusline+=%#QFErrMarker#%{(g:qfsigns_error>0)?'[E:'.g:qfsigns_error.']':''}%*
set statusline+=%#QFWarnMarker#%{(g:qfsigns_warn>0)?'[W:'.g:qfsigns_warn.']':''}%*
set statusline+=%#QFInfoMarker#%{(g:qfsigns_info>0)?'[I:'.g:qfsigns_info.']':''}%*
Everything put together: https://gist.github.com/pbnj/dc19a2d0f579933daef7f0fd9604dc0c
I added some
<Plug>
mappings that can be mapped, but this gist is mostly just a minimal example of working with signs.