quickfix.vim.pro · :h quickfix-pro
A drop-in enhancement of Neovim's native quickfix experience — and a client API so plugins can decorate and expand quickfix entries in place.
The native quickfix list stays the data store. quickfix-pro never replaces
it with a separate view, so everything that fills a quickfix list — :grep,
:make, :vimgrep, LSP references/diagnostics, telescope, :cexpr — lands
in the improved window with nothing to configure. Uninstall it and you are
back to stock quickfix on the identical lists.
-- vim.pack (Neovim 0.12+), no plugin manager required:
vim.pack.add({ "https://github.com/vim-pro/quickfix-pro.nvim" })
-- or lazy.nvim:
{ "vim-pro/quickfix-pro.nvim", opts = {} }Zero config. require("quickfix-pro").setup{} is optional and only tweaks
defaults.
-
A cleaner, aligned display line —
path:lnumpadded to a common width so entry text lines up, with a type flag column when the list carries one. Installed only if you don't already set'quickfixtextfunc'; per-list formatters (other plugins') still win for their own lists. -
Every part colorized separately — the stock
qfsyntax only understands Vim'sfile|lnum col|textshape, so with any custom format it ends up painting the whole row one color. quickfix-pro highlights each segment itself, in a deliberate hierarchy — the text is the content you scan, the path is navigation, the line number is metadata, the:is punctuation:group part default QfProFilefile path DirectoryQfProSeparatorthe :CommentQfProLineNrline number first distinct of Number,DiagnosticWarn,String,CommentQfProTextentry text first distinct of String,Identifier,Operator,NormalQfProTypeE/W/I/N/Htype flag, by severity Diagnostic*QfProCurrentthe current entry's band CursorLineThe defaults adapt to your colorscheme rather than linking blindly. Neovim's stock scheme defines
Number,Constant,Delimiter,TypeandStatementas exactlyNormal's foreground, so a fixed link there would render the line number the same white as the text. quickfix-pro walks each candidate list and takes the first color that is genuinely its own: not near-identical to plain text, and not within 30° of hue of a segment already claimed (a cream text beside an amber line number is ~1° apart and reads as a single color). It re-resolves onColorScheme, and:checkhealth quickfix-proreports what it picked.All are
defaultlinks, so a colorscheme or your config overrides any of them:vim.api.nvim_set_hl(0, "QfProLineNr", { fg = "#7aa2f7" })
The current entry gets the same segment colors as every other row. That needs one trick:
QuickFixLineis a window-level line highlight and outranks buffer extmarks at any priority, so it would repaint that row a single flat color. quickfix-pro remaps it per-window to the background-onlyQfProCurrent, keeping the "selected" band while the segment colors show through. An existingQuickFixLinemapping of your own is left untouched, andhighlight = falsedisables the whole mechanism. -
ddto drop an entry (3ddfor three, visualdfor a range) — removes it from the list in place, preserving the list's id, title, and every entry's data. Works in location lists too.urestores the last deleted batch at its original position, repeatable back through the delete history — pruning is never a one-way door. -
:QfAddto hand-curate a list — append arbitrary locations as you read code, for when you are the search. Vim can build lists from greps and compilers; this covers everything else.:QfAdd " the current line :QfAdd needs a docstring " ...with your own note as the entry text :5,9QfAdd " lines 5-9 as ONE entry spanning the region :'<,'>QfAdd! " one entry per line in the selection
A ranged
:QfAddrecordsend_lnum, so the entry is a region, not a point — consumers that read it (see the client API below) can act on the whole span. Appends to the current list, keeping its id and existing entries; refuses exact duplicates. No global keymap is claimed by default — setkeymaps.addto opt in. -
<Tab>(orza) to expand an entry inline — shows the entry's line plus a few lines of surrounding file context as virtual lines, right under the row. Toggle again to collapse;zR/zMexpand or collapse everything. Useful on any grep/make list. -
:Cfilteralways available — the bundled filter plugin is loaded for you.
All keymaps are buffer-local to the quickfix window and configurable (set any
to false to skip it).
Plugins register a client that decorates and expands the entries it owns
(recognized by the entry's user_data). quickfix-pro consults every client
per entry and renders the result as extmarks in the native window — no writes
to the list, so changedtick stays stable and other tools watching the list
are undisturbed.
local qfpro = require("quickfix-pro")
local dispose = qfpro.register("myplugin", {
-- Per-entry status: sign icon, highlight, whole-line hl, end-of-line text.
status = function(entry)
if not (entry.user_data and entry.user_data.myplugin) then return nil end
return { icon = "●", hl = "DiagnosticOk", eol = "done" }
end,
-- Inline expansion content (overrides the builtin file-context expander).
expand = function(entry)
if not (entry.user_data and entry.user_data.myplugin) then return nil end
return { "custom", "virtual", "lines" }
end,
-- Consulted before a delete; return false to veto.
on_delete = function(entries) return true end,
})
-- Client state changed (no list mutation): repaint decorations, debounced.
qfpro.refresh() -- or qfpro.refresh({ id = list_id }) to scope itstatus/expand are resolved first-non-nil in registration order; each
client returns nil for entries it doesn't own. Callbacks are error-isolated
— a throwing client degrades to stock rendering (with one warning) and never
breaks the window.
The first real client is conjurer.nvim, which
drives multi-site LLM edits through a quickfix list: each entry shows its
per-site status live, expands to a before/after diff, and cancels its in-flight
edit when you dd it.
require("quickfix-pro").setup({
format = "auto", -- "auto" (set only if unset) | true | false | function
highlight = true, -- colorize file / : / lnum / type / text separately
context_lines = 2, -- lines of file context shown on expand
debounce_ms = 30, -- coalesce repaints within this window
max_entries = 2000, -- leave larger lists undecorated (a performance fuse)
keymaps = {
delete = "dd", -- buffer-local to the quickfix window; takes a count
delete_visual = "d",
undo = "u", -- restore the last deleted batch (repeatable)
toggle = "<Tab>",
toggle_alias = "za",
expand_all = "zR",
collapse_all = "zM",
add = false, -- a GLOBAL key for :QfAdd; opt in, e.g. "<leader>q"
},
})Set vim.g.quickfix_pro_disable = 1 before startup to skip the plugin
entirely.
./scripts/test # headless spec suiteRequires Neovim 0.10+ (0.12+ for the vim.pack install snippet).
- The list is never replaced, only decorated — so
:cnext,:cdo,:colder,:cfilter, and every other quickfix command keep working unchanged. - Location lists get the same treatment as quickfix lists.