Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

19 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

quickfix-pro.nvim

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.

What you get, standalone

  • A cleaner, aligned display linepath:lnum padded 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 qf syntax only understands Vim's file|lnum col|text shape, 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
    QfProFile file path Directory
    QfProSeparator the : Comment
    QfProLineNr line number first distinct of Number, DiagnosticWarn, String, Comment
    QfProText entry text first distinct of String, Identifier, Operator, Normal
    QfProTypeE/W/I/N/H type flag, by severity Diagnostic*
    QfProCurrent the current entry's band CursorLine

    The defaults adapt to your colorscheme rather than linking blindly. Neovim's stock scheme defines Number, Constant, Delimiter, Type and Statement as exactly Normal'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 on ColorScheme, and :checkhealth quickfix-pro reports what it picked.

    All are default links, 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: QuickFixLine is 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-only QfProCurrent, keeping the "selected" band while the segment colors show through. An existing QuickFixLine mapping of your own is left untouched, and highlight = false disables the whole mechanism.

  • dd to drop an entry (3dd for three, visual d for 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. u restores the last deleted batch at its original position, repeatable back through the delete history — pruning is never a one-way door.

  • :QfAdd to 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 :QfAdd records end_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 — set keymaps.add to opt in.

  • <Tab> (or za) 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/zM expand or collapse everything. Useful on any grep/make list.

  • :Cfilter always 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).

The client API

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 it

status/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.

Configuration (defaults shown)

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.

Development

./scripts/test          # headless spec suite

Requires Neovim 0.10+ (0.12+ for the vim.pack install snippet).

Notes

  • 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.

About

Sharpens Neovim's native quickfix window in place: aligned rows, colors resolved against your scheme, dd/u editing, inline expansion, and a client API for other plugins. The list itself is never replaced.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages