mrcjkb
neotest-haskell
Lua

Neotest adapter for Haskell (cabal or stack) with support for Sydtest, Hspec and Tasty

Last updated Jul 9, 2026
69
Stars
3
Forks
14
Issues
0
Stars/day
Attention Score
55
Language breakdown
Lua 88.1%
Nix 7.3%
Tree-sitter Query 4.5%
Haskell 0.0%
Shell 0.0%
β–Έ Files click to expand
README


neotest-haskell

Report Bug

A neotest adapter for Haskell.

πŸ¦₯

[![Neovim][neovim-shield]][neovim-url] [![Lua][lua-shield]][lua-url] [![Haskell][haskell-shield]][haskell-url] [![Nix][nix-shield]][nix-url]

[![GPL2 License][license-shield]][license-url] [![Issues][issues-shield]][issues-url] [![Build Status][ci-shield]][ci-url] [![LuaRocks][luarocks-shield]][luarocks-url] All Contributors

Quick links

Features

  • [x] Supports Cabal (single/multi-package) projects.
  • [x] Supports Stack
(single/multi-package) projects. and Sydtest --match filters for the cursor's position using tree-sitter. --pattern filters for the cursor's position using tree-sitter.
  • [x] Parses test results and displays error messages as diagnostics.
https://user-images.githubusercontent.com/12857160/224197351-8ca64bd5-8d89-4689-8c40-18d1d018896e.mp4

Requirements

Installation

rocks.nvim

:Rocks install neotest-haskell

rocks.nvim will install all dependencies if not already installed (including tree-sitter-haskell).

Other plugin managers

See also: neotest installation instructions.

  • Requires the tree-sitter parser for haskell to be installed.
The following example uses lazy.nvim:
{
  'nvim-neotest/neotest',
  dependencies = {
    -- ...,
    'mrcjkb/neotest-haskell',
    'nvim-lua/plenary.nvim',
  }
}

Configuration

Make sure the Haskell parser for tree-sitter is installed, you can do so via nvim-treesitter like so:

require('nvim-treesitter.configs').setup {
  ensure_installed = {
    'haskell',
    --...,
  },
}

Add neotest-haskell to your neotest adapters:

require('neotest').setup {
  -- ...,
  adapters = {
    -- ...,
    require('neotest-haskell')
  },
}

You can also pass a config to the setup. The following are the defaults:

require('neotest').setup {
  adapters = {
    require('neotest-haskell') {
      -- Default: Use stack if possible and then try cabal
      build_tools = { 'stack', 'cabal' },
      -- Default: Check for tasty first and then try hspec
      frameworks = { 'tasty', 'hspec', 'sydtest' },
    },
  },
}
[!NOTE]
>
If you were to use build_tools = { 'cabal', 'stack' }, then cabal will almost
always be chosen, because almost all stack projects can be built with cabal.

Alternately, you can pair each test framework with a list of modules, used to identify the respective framework in a test file:

require('neotest').setup {
  adapters = {
    require('neotest-haskell') {
      frameworks = {
        { framework = 'tasty', modules = { 'Test.Tasty', 'MyTestModule' }, },
        'hspec',
        'sydtest',
      },
    },
  },
}

This can be useful if you have test files that do not import one of the default modules used for framework identification:

  • tasty: modules = { 'Test.Tasty' }
  • hspec: modules = { 'Test.Hspec' }
  • sydtest: modules = { 'Test.Syd' }

Advanced configuration

This plugin uses tree-sitter queries in files that match <runtimepath>/queries/haskell/<framework>-positions.scm

For example, to add position queries for this plugin for tasty, without having to fork this plugin, you can add them to $XDGCONFIGHOME/nvim/after/queries/haskell/tasty-positions.scm.

[!NOTE]
>
- :h runtimepath
- See examples in queries/haskell/.

Examples

module FixtureSpec ( spec ) where
import Test.Hspec
import Test.Hspec.QuickCheck
import Control.Exception ( evaluate )

spec :: Spec spec = describe "Prelude.head" $ do it "returns the first element of a list" $ head [23 ..] shouldBe (23 :: Int)

prop "returns the first element of an arbitrary list" $ \x xs -> head (x : xs) shouldBe (x :: Int)

describe "Empty list" $ it "throws an exception if used with an empty list" $ evaluate (head []) shouldThrow anyException

In the above listing, calling :lua require('neotest').run.run() with the cursor on the line...

describe "Empty list" $

...will run the tests with the following Cabal command:

# Assuming a Cabal package called "my_package"
cabal test my_package --test-option -m --test-option "/Prelude.head/Empty list/"

...or with the following Stack command:

# Assuming a Stack package called "my_package"
stack test my_package --ta "--match \"/Prelude.head/Empty list/\""

...which will run the "throws an exception if used with an empty list" test.

Calling :lua require('neotest').run.run() with the cursor on the line...

spec = describe "Prelude.head" $ do

...will run the tests with the following Cabal command:

# Assuming a Cabal package called "my_package"
cabal test my_package --test-option -m --test-option "/Prelude.head/"

...or with the following Stack command:

# Assuming a Stack package called "my_package"
stack test my_package --ta "--match \"/Prelude.head/\""

...which will run all tests in the module.

TODO

See issues.

Troubleshooting

To run a health check, run :checkhealth neotest-haskell in Neovim.

Limitations

  • To run sydtest tests of type 'file', sydtest >= 0.13.0.4 is required,
if the file has more than one top-level namespace (describe, context, ..).

Recommendations

Here are some other plugins I recommend for Haskell development:

Toolset to improve the Haskell experience in Neovim. Collection of Haskell snippets for LuaSnip. Hoogle search.

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Perigord
Perigord

πŸ’»
Sebastian Witte
Sebastian Witte

πŸ’» πŸš‡ πŸ“–
Andy Bell
Andy Bell

πŸ’»
Tom Sydney Kerckhove
Tom Sydney Kerckhove

πŸ§‘β€πŸ«
Nadeem Bitar
Nadeem Bitar

πŸ›
Mango The Fourth
Mango The Fourth

πŸ›
HΓ©cate Moonlight
HΓ©cate Moonlight

πŸ›
Amaan Qureshi
Amaan Qureshi

πŸ’»
Brad Sherman
Brad Sherman

πŸ’»
Agustin Jimenez
Agustin Jimenez

πŸ›

This project follows the all-contributors specification. Contributions of any kind welcome!

[neovim-shield]: https://img.shields.io/badge/NeoVim-%2357A143.svg?&style=for-the-badge&logo=neovim&logoColor=white [neovim-url]: https://neovim.io/ [lua-shield]: https://img.shields.io/badge/lua-%232C2D72.svg?style=for-the-badge&logo=lua&logoColor=white [lua-url]: https://www.lua.org/ [nix-shield]: https://img.shields.io/badge/nix-0175C2?style=for-the-badge&logo=NixOS&logoColor=white [nix-url]: https://nixos.org/ [haskell-shield]: https://img.shields.io/badge/Haskell-5e5086?style=for-the-badge&logo=haskell&logoColor=white [haskell-url]: https://www.haskell.org/ [issues-shield]: https://img.shields.io/github/issues/mrcjkb/neotest-haskell.svg?style=for-the-badge [issues-url]: https://github.com/mrcjkb/neotest-haskell/issues [license-shield]: https://img.shields.io/github/license/mrcjkb/neotest-haskell.svg?style=for-the-badge [license-url]: https://github.com/mrcjkb/neotest-haskell/blob/master/LICENSE [ci-shield]: https://img.shields.io/github/actions/workflow/status/mrcjkb/neotest-haskell/nix-build.yml?style=for-the-badge [ci-url]: https://github.com/mrcjkb/neotest-haskell/actions/workflows/nix-build.yml [luarocks-shield]: https://img.shields.io/luarocks/v/MrcJkb/neotest-haskell?logo=lua&color=purple&style=for-the-badge [luarocks-url]: https://luarocks.org/modules/MrcJkb/neotest-haskell

πŸ”— More in this category

Β© 2026 GitRepoTrend Β· mrcjkb/neotest-haskell Β· Updated daily from GitHub