ghci-quickfix: GHC plugin that writes errors to a file for use with quickfix

This is a package candidate release! Here you can preview how this package release will appear once published to the main package index (which can be accomplished via the 'maintain' link below). Please note that once a package has been published to the main package index it cannot be undone! Please consult the package uploading documentation for more information.

[maintain] [Publish]

Warnings:

GHC plugin that writes errors to a file for use with vim/nvim's quickfix system


[Skip to Readme]

Properties

Versions 0.1.0.0, 0.1.0.0
Change log CHANGELOG.md
Dependencies async (>=2.2 && <3), base (<4.23), deferred-folds (>=0.9 && <1), directory, foldl (>=1 && <2), ghc (>=9.6 && <9.13), stm, stm-containers (>=1.2 && <1.3), text [details]
License BSD-3-Clause
Author Aaron Allen
Maintainer aaronallen8455@gmail.com
Category Development
Source repo head: git clone https://github.com/aaronallen8455/ghci-quickfix
Uploaded by aaronallen8455 at 2026-01-12T16:09:57Z

Modules

[Index] [Quick Jump]

Downloads

Maintainer's Corner

Package maintainers

For package maintainers and hackage trustees


Readme for ghci-quickfix-0.1.0.0

[back to package description]

ghci-quickfix 📝

This is a GHC plugin that will write diagnostics to a file during compilation, which can then be used with vim/nvim's quickfix feature. By default, the file is errors.err in the project root directory but this can be customized (see plugin options).

NOTE: If you're using this plugin via repl-alliance, you need to explicitly enable it by passing --fplugin-opt ReplAlliance:--quickfix to GHC or by setting the environment variable GHCI_QUICKFIX_ENABLED=true.

Usage

This plugin is intended to be used with GHCi or adjacent utilities such as ghcid and ghciwatch as a development tool, not as a package dependency.

Stack Projects

To use with a stack project (you may need to add ghci-quickfix to your extra-deps first):

stack repl my-project --package ghci-quickfix --ghci-options='-fplugin GhciQuickfix'

Cabal Projects

To use with a cabal project (you may need to run cabal update first):

cabal repl my-project --build-depends ghci-quickfix --repl-options='-fplugin GhciQuickfix'

Vim/Neovim Integration

After starting your REPL with the plugin enabled, you can load errors in Vim:

:cf errors.err

The errors.err argument can be omitted since it is the default file.

Or to update without jumping to the first error:

:cg errors.err

Navigate between error locations using :cn (next) and :cp (previous).

To get highlighting for errors and warnings in the editor, you can turn the quickfix entries into diagnostics. The following snippet can be added to your init.lua file to accomplish this.

-- [[ Quickfix to diagnostics ]]
-- Configure errorformat to distinguish between warning and error type.
vim.o.errorformat = '%f:%l:%c: %tarning: %m,%f:%l:%c: %trror: %m,' .. vim.o.errorformat

-- Create namespace for quickfix diagnostics
local qf_ns = vim.api.nvim_create_namespace('quickfix_diagnostics')

-- Configure diagnostic display for quickfix namespace
vim.diagnostic.config({
  underline = true,
  virtual_text = false,
  signs = true,
  update_in_insert = false,
}, qf_ns)

-- Helper function to create diagnostic entry from quickfix item
local function create_diagnostic_from_qf_item(item)
  local severity = vim.diagnostic.severity.ERROR

  -- Determine severity based on type field
  if item.type == 'W' or item.type == 'w' then
    severity = vim.diagnostic.severity.WARN
  end

  -- Handle column range - if no end_col, highlight to end of line
  local col_start = (item.col or 1) - 1
  local col_end = nil
  local end_lnum = nil

  if item.end_col and item.end_col > 0 and item.end_lnum then
    col_end = item.end_col
    end_lnum = item.end_lnum - 1
  end

  return {
    lnum = item.lnum - 1,
    col = col_start,
    end_lnum = end_lnum,
    end_col = col_end,
    severity = severity,
    message = item.text or '',
    source = 'quickfix',
  }
end

-- Function to convert quickfix entries to diagnostics and apply to all buffers
local function quickfix_to_diagnostics()
  -- Clear all quickfix diagnostics from all buffers
  vim.diagnostic.reset(qf_ns)

  local qf_list = vim.fn.getqflist()
  local diagnostics_by_buf = {}

  for _, item in ipairs(qf_list) do
    if item.bufnr > 0 and item.lnum > 0 then
      if not diagnostics_by_buf[item.bufnr] then
        diagnostics_by_buf[item.bufnr] = {}
      end
      table.insert(diagnostics_by_buf[item.bufnr], create_diagnostic_from_qf_item(item))
    end
  end

  -- Set diagnostics for each buffer
  for bufnr, diagnostics in pairs(diagnostics_by_buf) do
    vim.diagnostic.set(qf_ns, bufnr, diagnostics, {})
  end
end

-- Function to apply diagnostics for a specific buffer
local function apply_quickfix_diagnostics_for_buffer(bufnr)
  local qf_list = vim.fn.getqflist()
  local diagnostics = {}

  for _, item in ipairs(qf_list) do
    if item.bufnr == bufnr and item.lnum > 0 then
      table.insert(diagnostics, create_diagnostic_from_qf_item(item))
    end
  end

  vim.diagnostic.set(qf_ns, bufnr, diagnostics, {})
end

-- Auto-convert quickfix entries to diagnostics when loading from file
vim.api.nvim_create_autocmd('QuickFixCmdPost', {
  pattern = '[cg]file,[cg]getfile',
  callback = function()
    quickfix_to_diagnostics()
  end,
})

-- Reapply diagnostics when a buffer is read (handles newly opened buffers)
vim.api.nvim_create_autocmd('BufReadPost', {
  pattern = '*',
  callback = function(args)
    local qf_list = vim.fn.getqflist()
    if #qf_list == 0 then
      return
    end

    -- Only apply diagnostics for this specific buffer if it has quickfix entries
    local bufnr = args.buf
    for _, item in ipairs(qf_list) do
      if item.bufnr == bufnr then
        apply_quickfix_diagnostics_for_buffer(bufnr)
        return
      end
    end
  end,
})

Plugin Options

Plugin options are passed using the --fplugin-opt flag. For example:

-fplugin GhciQuickfix -fplugin-opt GhciQuickfix:--quickfix-file=my-errors.err

Available Options

Output Format

The plugin generates quickfix entries in GCC-style format:

filename.hs:line:col: severity: message

This format is automatically recognized by Vim's quickfix system.

Compatibility

This plugin aims to support the 4 latest GHC major releases (i.e. 9.6.* through 9.12.*). Check the cabal file for the currently supported versions.