Supercharge your Haskell experience in neovim!
- Installation
- Quick Setup
- Features
- Advanced configuration
- Troubleshooting
- Recommendations
- Contributing
neovim >= 0.8
nvim-lspconfig
plenary.nvim
haskell-language-server
telescope.nvim
- A local
hoogle
installation (recommended for better hoogle search performance)
Example using packer.nvim
:
use {
'MrcJkb/haskell-tools.nvim',
requires = {
'neovim/nvim-lspconfig',
'nvim-lua/plenary.nvim',
'nvim-telescope/telescope.nvim', -- optional
},
-- tag = 'x.y.z' -- [^1]
}
[^1] It is suggested to use the latest release tag and to update it manually, if you would like to avoid breaking changes.
This plugin automatically configures the haskell-language-server neovim client.
lspconfig.hls
setup or set up the lsp manually, as doing so may cause conflicts.
To get started quickly with the default setup, add the following to your neovim config:
local ht = require('haskell-tools')
local def_opts = { noremap = true, silent = true, }
ht.setup {
hls = {
-- See nvim-lspconfig's suggested configuration for keymaps, etc.
on_attach = function(client, bufnr)
local opts = vim.tbl_extend('keep', def_opts, { buffer = bufnr, })
-- haskell-language-server relies heavily on codeLenses,
-- so auto-refresh (see advanced configuration) is enabled by default
vim.keymap.set('n', '<space>ca', vim.lsp.codelens.run, opts)
vim.keymap.set('n', '<space>hs', ht.hoogle.hoogle_signature, opts)
-- default_on_attach(client, bufnr) -- if defined, see nvim-lspconfig
end,
},
}
-- Suggested keymaps that do not depend on haskell-language-server
-- Toggle a GHCi repl for the current package
vim.keymap.set('n', '<leader>rr', ht.repl.toggle, def_opts)
-- Toggle a GHCi repl for the current buffer
vim.keymap.set('n', '<leader>rf', funciton()
ht.repl.toggle(vim.api.nvim_buf_get_name(0))
end, def_opts)
vim.keymap.set('n', '<leader>rq', ht.repl.quit, def_opts)
If using a local hoogle
installation, follow these instructions
to generate a database.
- Basic haskell-language-server functionality on par with
nvim-lspconfig.hls
- Clean shutdown of language server on exit to prevent corrupted files (see ghc #14533).
- Automatically adds capabilities for the following plugins, if loaded:
- cmp-nvim-lsp (provides completion sources for nvim-cmp).
- nvim-lsp-selection-range (Adds haskell-specific expand selection support).
- Automatically refreshes code lenses by default, which haskell-language-server heavily relies on. Can be disabled.
- The following code lenses are currently supported:
The below features are not implemented by haskell-language-server.
- Search for the type signature under the cursor.
- Falls back to the word under the cursor if the type signature cannot be determined.
- Telescope keymaps:
<CR>
to copy the selected entry ( :: ) to the clipboard.<C-b>
to open the selected entry's Hackage URL in a browser.<C-r>
to replace the word under the cursor with the selected entry.
require('haskell-tools').hoogle.hoogle_signature()
With the <C-r>
keymap, the Hoogle search telescope integration can be used to fill holes.
Start a GHCi repl for the current project / buffer.
- Automagically detects the appropriate command (
cabal new-repl
,stack ghci
orghci
) for your project. - Choose between a builtin handler or
toggleterm.nvim
. - Dynamically create a repl command for
iron.nvim
(see advanced configuration). - Interact with the repl from any buffer using a lua API.
For planned features, refer to the issues.
To modify the default configs, call
-- defaults
require('haskell-tools').setup {
tools = { -- haskell-tools options
codeLens = {
-- Whether to automatically display/refresh codeLenses
autoRefresh = true,
},
hoogle = {
-- 'auto': Choose a mode automatically, based on what is available.
-- 'telescope-local': Force use of a local installation.
-- 'telescope-web': The online version (depends on curl).
-- 'browser': Open hoogle search in the default browser.
mode = 'auto',
},
repl = {
-- 'builtin': Use the simple builtin repl
-- 'toggleterm': Use akinsho/toggleterm.nvim
handler = 'builtin',
builtin = {
create_repl_window = function(view)
-- create_repl_split | create_repl_vsplit | create_repl_tabnew | create_repl_cur_win
return view.create_repl_split { size = vim.o.lines / 3 }
end
},
},
},
hls = { -- LSP client options
-- ...
haskell = { -- haskell-language-server options
formattingProvider = 'ormolu',
checkProject = true, -- Setting this to true could have a performance impact on large mono repos.
-- ...
}
}
}
- The full list of defaults can be found here.
- To view all available language server settings (including those not set by this plugin), run
haskell-language-server generate-default-config
. - For detailed descriptions of the configs, look at the haskell-language-server documentation.
Some code lenses might be more interesting than others.
For example, the importLens
could be annoying if you prefer to import everything or use a custom prelude.
Individual code lenses can be turned off by disabling them in the respective plugin configurations:
hls = {
haskell = {
plugin = {
class = { -- missing class methods
codeLensOn = false,
},
importLens = { -- make import lists fully explicit
codeLensOn = false,
},
refineImports = { -- refine imports
codeLensOn = false,
},
tactics = { -- wingman
codeLensOn = false,
},
moduleName = { -- fix module names
globalOn = false,
},
eval = { -- evaluate code snippets
globalOn = false,
},
['ghcide-type-lenses'] = { -- show/add missing type signatures
globalOn = false,
},
},
},
},
Set up iron.nvim
to use haskell-tools.nvim
Depends on iron.nvim/#300.
local iron = require("iron.core")
iron.setup {
config = {
repl_definition = {
haskell = {
command = function(meta)
local file = vim.api.nvim_buf_get_name(meta.current_bufnr)
-- call `require` in case iron is set up before haskell-tools
return require('haskell-tools').repl.mk_repl_cmd(file)
end,
},
},
},
}
local ht = require('haskell-tools')
-- Run a hoogle signature for the value under the cursor
ht.hoogle.hoogle_signature()
-- Toggle a GHCi repl
ht.repl.toggle()
-- Toggle a GHCi repl for `file`
ht.repl.toggle(file)
-- Quit the repl
ht.repl.quit()
-- Paste a command to the repl from register `reg`. (`reg` defaults to '"')
ht.repl.paste(reg)
-- Query the repl for the type of register `reg`. (`reg` defaults to '"')
ht.repl.paste_type(reg)
-- Query the repl for the type of word under the cursor
ht.repl.cword_type()
" Open the project file for the current buffer (cabal.project or stack.yaml)
:HsProjectFile
" Open the package.yaml file for the current buffer
:HsPackageYaml
" Open the *.cabal file for the current buffer
:HsPackageCabal
Check which version of GHC you are using (:LspInfo
).
Sometimes, certain features take some time to be implemented for the latest GHC versions.
You can see how well a specific GHC version is supported here.
Here are some other plugins I recommend for Haskell (and nix) development in neovim:
- MrcJkb/neotest-haskell: Interact with tests in neovim.
- luc-tielen/telescope_hoogle: Live Hoogle search.
- MrcJkb/telescope-manix: Nix search.
- mfussenegger/nvim-lint: As a fallback in case there are problems with haskell-language-server (e.g. in large mono repos).
- aloussase/scout: CLI for searching Hackage with telescope.nvim integration.