Neotest adapter for Haskell (cabal or stack) with support for Sydtest, Hspec and Tasty
Lua
69
445 commits
updated Sep 22, 2026
--match filters for the cursor's position using tree-sitter.--pattern filters for the cursor's position using tree-sitter.neovim >= 0.12:Rocks install neotest-haskell
rocks.nvim will install all dependencies if not already installed (including tree-sitter-haskell).
See also: neotest installation instructions.
The following example uses lazy.nvim:
{
'nvim-neotest/neotest',
dependencies = {
-- ...,
'mrcjkb/neotest-haskell',
'nvim-lua/plenary.nvim',
}
}
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' }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
$XDG_CONFIG_HOME/nvim/after/queries/haskell/tasty-positions.scm.
[!NOTE]
:h runtimepath- See examples in
queries/haskell/.
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.
See issues.
To run a health check, run :checkhealth neotest-haskell in Neovim.
sydtest tests of type 'file', sydtest >= 0.13.0.4 is required,
if the file has more than one top-level namespace (describe, context, ..).Here are some other plugins I recommend for Haskell development:
Thanks goes to these wonderful people (emoji key):
Perigord π» | Sebastian Witte π» π π | Andy Bell π» | Tom Sydney Kerckhove π§βπ« | Nadeem Bitar π | Mango The Fourth π | HΓ©cate Moonlight π |
Amaan Qureshi π» | Brad Sherman π» | Agustin Jimenez π |
This project follows the all-contributors specification. Contributions of any kind welcome!
Lua
88.0%
Nix
7.4%
Tree-sitter Query
4.5%
Neotest adapter for Haskell (cabal or stack) with support for Sydtest, Hspec and Tasty
Lua
69
445 commits
updated Sep 22, 2026
--match filters for the cursor's position using tree-sitter.--pattern filters for the cursor's position using tree-sitter.neovim >= 0.12:Rocks install neotest-haskell
rocks.nvim will install all dependencies if not already installed (including tree-sitter-haskell).
See also: neotest installation instructions.
The following example uses lazy.nvim:
{
'nvim-neotest/neotest',
dependencies = {
-- ...,
'mrcjkb/neotest-haskell',
'nvim-lua/plenary.nvim',
}
}
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' }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
$XDG_CONFIG_HOME/nvim/after/queries/haskell/tasty-positions.scm.
[!NOTE]
:h runtimepath- See examples in
queries/haskell/.
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.
See issues.
To run a health check, run :checkhealth neotest-haskell in Neovim.
sydtest tests of type 'file', sydtest >= 0.13.0.4 is required,
if the file has more than one top-level namespace (describe, context, ..).Here are some other plugins I recommend for Haskell development:
Thanks goes to these wonderful people (emoji key):
Perigord π» | Sebastian Witte π» π π | Andy Bell π» | Tom Sydney Kerckhove π§βπ« | Nadeem Bitar π | Mango The Fourth π | HΓ©cate Moonlight π |
Amaan Qureshi π» | Brad Sherman π» | Agustin Jimenez π |
This project follows the all-contributors specification. Contributions of any kind welcome!
Lua
88.0%
Nix
7.4%
Tree-sitter Query
4.5%