7

Currently, when I view the quickfix list with :cw, the columns/pipes are not aligned, as in this example:

enter image description here

I wonder if there is an option in vim or a plugin to rearrange the pipes so that they align in columns. csv.vim does something similar for csv files (see gif in the README).

2 Answers 2

5

Some more searching yielded the qf-layout plugin, which is working great in my initial tests!

In order to show only the basename instead of the entire filepath, I modified line 84 from

let l.filename = bufname(l.bufnr)

to

let l.filename = fnamemodify(bufname(l.bufnr), ":t")

It looks neat!

enter image description here

In case someone else finds this useful, I recommend adding something like the following to your .vimrc, and put the formatting you prefer first in the list. I changed from 'NOP' to 'vim_addon_qf_layout#DefaultFormatter':

let g:vim_addon_qf_layout = {}
let g:vim_addon_qf_layout.quickfix_formatters = [
    \'vim_addon_qf_layout#DefaultFormatter',
    \'vim_addon_qf_layout#FormatterNoFilename',
    \'vim_addon_qf_layout#Reset',
    \'NOP' ]

PS This plugin works great together with the SpellCheck plugin which populates the quickfix list with misspelled words!

5

If your Vim includes the patch 8.2.0959, you can set the 'quickfixtextfunc' option to the name of a Vimscript function whose task is to format each entry displayed in the quickfix window.

To get the desired alignment, use a few printf() invocations appropriately.

As an example:

set qftf=QuickFixTextFunc nowrap | syn on
fu QuickFixTextFunc(info) abort
    if a:info.quickfix
        let qfl = getqflist(#{id: a:info.id, items: 0}).items
    else
        let qfl = getloclist(a:info.winid, #{id: a:info.id, items: 0}).items
    endif
    let l = []
    let lnum_width = range(a:info.start_idx - 1, a:info.end_idx - 1)
        \ ->map({_, v -> qfl[v].lnum})
        \ ->max()
        \ ->len()
    let col_width = range(a:info.start_idx - 1, a:info.end_idx - 1)
        \ ->map({_, v -> qfl[v].col})
        \ ->max()
        \ ->len()
    let fname_width = range(a:info.start_idx - 1, a:info.end_idx - 1)
        \ ->map({_, v -> qfl[v].bufnr->bufname()->fnamemodify(':t')->strchars(1)})
        \ ->max()
    let type_width = range(a:info.start_idx - 1, a:info.end_idx - 1)
        \ ->map({_, v -> get(s:EFM_TYPE, qfl[v].type, '')->strlen()})
        \ ->max()
    let errnum_width = range(a:info.start_idx - 1, a:info.end_idx - 1)
        \ ->map({_, v -> qfl[v].nr})
        \ ->max()
        \ ->len()
    for idx in range(a:info.start_idx - 1, a:info.end_idx - 1)
        let e = qfl[idx]
        if !e.valid
            call add(l, '|| ' .. e.text)
        else
            if e.lnum == 0 && e.col == 0
                call add(l, bufname(e.bufnr))
            else
                let fname = printf('%-*S', fname_width, bufname(e.bufnr)->fnamemodify(':t'))
                let lnum = printf('%*d', lnum_width, e.lnum)
                let col = printf('%*d', col_width, e.col)
                let type = printf('%-*S', type_width, get(s:EFM_TYPE, e.type, ''))
                let errnum = ''
                if e.nr
                    let errnum = printf('%*d', errnum_width + 1, e.nr)
                endif
                call add(l, printf('%s|%s col %s %s%s| %s', fname, lnum, col, type, errnum, e.text))
            endif
        endif
    endfor
    return l
endfu
const s:EFM_TYPE = #{e: 'error', w: 'warning', i: 'info', n: 'note'}
helpg foobar
copen

If you care about speed, and you often deal with long quickfix lists, try to rewrite the code in Vim9 script:

vim9script
set qftf=QuickFixTextFunc nowrap | syn on
def QuickFixTextFunc(info: dict<number>): list<string>
    var qfl: list<any>
    if info.quickfix
        qfl = getqflist({id: info.id, items: 0}).items
    else
        qfl = getloclist(info.winid, {id: info.id, items: 0}).items
    endif
    var l: list<string> = []
    var lnum_width: number = range(info.start_idx - 1, info.end_idx - 1)
        ->map((_, v: number): number => qfl[v].lnum)
        ->max()
        ->len()
    var col_width: number = range(info.start_idx - 1, info.end_idx - 1)
        ->map((_, v: number): number => qfl[v].col)
        ->max()
        ->len()
    var fname_width: number = range(info.start_idx - 1, info.end_idx - 1)
        ->map((_, v: number): number => qfl[v].bufnr->bufname()->fnamemodify(':t')->strchars(true))
        ->max()
    var type_width: number = range(info.start_idx - 1, info.end_idx - 1)
        ->map((_, v: number): number => get(EFM_TYPE, qfl[v].type, '')->strlen())
        ->max()
    var errnum_width: number = range(info.start_idx - 1, info.end_idx - 1)
        ->map((_, v: number): number => qfl[v].nr)
        ->max()
        ->len()
    for idx in range(info.start_idx - 1, info.end_idx - 1)
        var e: dict<any> = qfl[idx]
        if !e.valid
            add(l, '|| ' .. e.text)
        else
            if e.lnum == 0 && e.col == 0
                add(l, bufname(e.bufnr))
            else
                var fname: string = printf('%-*S', fname_width, bufname(e.bufnr)->fnamemodify(':t'))
                var lnum: string = printf('%*d', lnum_width, e.lnum)
                var col: string = printf('%*d', col_width, e.col)
                var type: string = printf('%-*S', type_width, get(EFM_TYPE, e.type, ''))
                var errnum = ''
                if e.nr
                    errnum = printf('%*d', errnum_width + 1, e.nr)
                endif
                add(l, printf('%s|%s col %s %s%s| %s', fname, lnum, col, type, errnum, e.text))
            endif
        endif
    endfor
    return l
enddef
const EFM_TYPE = {e: 'error', w: 'warning', i: 'info', n: 'note'}
helpg foobar
copen

On my machine, it seems to be around 4 times faster.

This requires a recent Vim version. It works on 8.2.2332.


For more info, see:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.