[!NOTE]
- 开箱即用。无需调用
setup!- 不依赖
lspconfig。- 设计上采用惰性初始化。
:link: 快速链接
- :pencil: 先决条件
- :inbox_tray: 安装
- :zap: 快速配置
- :star2: 功能特性
- :gear: 高级配置
- :stethoscope: 故障排除
- :link: 推荐配置
- :green_heart: 贡献指南
:grey_question: 我需要 haskell-tools.nvim 吗
如果你刚开始学习 Haskell,nvim-lspconfig.hls
可能已经足够满足你的需求。
它提供了 LSP 支持的最小公分母。
此插件适用于那些希望拥有 额外功能
且特定于 Haskell 工具链的用户。
:pencil: 先决条件
必需
neovim >= 0.12
[!NOTE]
对于兼容旧版 Neovim 的版本, 请参阅 changelog 中的先前主要版本更新。
可选
haskell-language-server(推荐)。telescope.nvim。- 本地
hoogle安装(推荐,以获得更好的 hoogle 搜索性能)。 fast-tags(作为 [vim.lsp.tagfunc](https://neovim.io/doc/user/lsp.html#vim.lsp.tagfunc() 的后备方案,用于自动生成标签)。haskell-debug-adapter和nvim-dap。
:inbox_tray: 安装
使用 Neovim 内置的插件管理器:
vim.pack.add {{
src = 'https://github.com/mrcjkb/haskell-tools.nvim',
-- To avoid being surprised by breaking changes,
-- I recommend you set a version range
version = vim.version.range('^10')
}}
此插件可在 LuaRocks 上获取,并可使用 rocks.nvim 进行安装:
:Rocks install haskell-tools.nvim
使用 lazy.nvim 的示例:
{
'mrcjkb/haskell-tools.nvim',
-- To avoid being surprised by breaking changes,
-- I recommend you set a version range
version = '^10',
-- This plugin implements proper lazy-loading (see :h lua-plugin-lazy).
-- No need for lazy.nvim to lazy-load it.
lazy = false,
}
[!TIP]
如果您希望避免破坏性变更,建议固定使用带标签的版本。
要手动生成文档,请使用 :helptags ALL。
[!NOTE]
对于启用了 flakes 的 NixOS 用户,本项目以包和 overlay 的形式提供输出;您可以按需在 NixOS 或 home-manager 配置中使用它。 它也可以在
nixpkgs中获取。
:zap: 快速设置
此插件会自动配置 haskell-language-server 内置 LSP
客户端,并与其他 haskell 工具集成。
有关更多信息,请参阅 Features 部分。
[!WARNING]
请勿调用
nvim-lspconfig.hls设置或手动为haskell-language-server设置 lsp 客户端, 因为这样做可能会导致冲突。
这是一个开箱即用的文件类型插件,
因此无需调用 setup 函数或配置任何内容
即可使此插件正常工作。
您很可能希望添加一些键位映射。
大多数键位映射仅在 haskell 和/或 cabal 文件中有用,
因此建议您在 ~/.config/nvim/after/ftplugin/haskell.lua1
和/或 ~/.config/nvim/after/ftplugin/cabal.lua1 中定义它们。
一些建议:
-- ~/.config/nvim/after/ftplugin/haskell.lua
local ht = require('haskell-tools')
local bufnr = vim.api.nvim_get_current_buf()
local opts = { noremap = true, silent = true, buffer = bufnr, }
-- haskell-language-server relies heavily on codeLenses,
-- so auto-refresh (see advanced configuration) is enabled by default
vim.keymap.set('n', '<space>cl', vim.lsp.codelens.run, opts)
-- Hoogle search for the type signature of the definition under the cursor
vim.keymap.set('n', '<space>hs', ht.hoogle.hoogle_signature, opts)
-- Evaluate all code snippets
vim.keymap.set('n', '<space>ea', ht.lsp.buf_eval_all, opts)
-- Toggle a GHCi repl for the current package
vim.keymap.set('n', '<leader>rr', ht.repl.toggle, opts)
-- Toggle a GHCi repl for the current buffer
vim.keymap.set('n', '<leader>rf', function()
ht.repl.toggle(vim.api.nvim_buf_get_name(0))
end, opts)
vim.keymap.set('n', '<leader>rq', ht.repl.quit, opts)
[!TIP]
- 有关更多 LSP 相关键位映射,请参阅
nvim-lspconfig建议。- 如果使用本地
hoogle安装,请按照这些说明 生成数据库。- 请参阅 高级配置 部分 以获取更多配置选项。
[!IMPORTANT]
- 请勿在
after/ftplugin/haskell.lua中设置vim.g.haskell_tools,因为该文件在插件 初始化之后才被加载。
:star2: 功能
- 设置
haskell-language-server客户端。 - 从 JSON 文件中按项目动态加载
haskell-language-server设置。 - 退出时干净地关闭语言服务器,以防止文件损坏 (参见 ghc #14533)。
- 如果已加载,自动为以下插件添加功能:
- cmp-nvim-lsp (为 nvim-cmp 提供补全源)。
- nvim-lsp-selection-range (添加 扩展选择 支持)。
- nvim-ufo。 (添加 折叠范围 支持)。
- 默认情况下自动刷新代码透镜,
haskell-language-server严重依赖此功能。可以禁用。

- 一次性评估所有代码片段
haskell-language-server 可以使用代码透镜评估代码片段。
haskell-tools.nvim 提供了一个 :Haskell hls evalAll
(require('haskell-tools').lsp.buf_eval_all())
快捷键,用于一次性评估所有代码片段。

-
Hoogle 搜索签名
-
搜索光标下的类型签名。
-
如果无法确定类型签名,则回退到光标下的单词。
-
Telescope 键位映射:
<CR>将选中的条目(<name> :: <signature>)复制到剪贴板。<C-b>在浏览器中打开所选条目的 Hackage URL。<C-r>用所选条目替换光标下的单词。
require('haskell-tools').hoogle.hoogle_signature()

- 由 Hoogle 驱动的 Hole-driven development
使用 <C-r> 键位映射,
Hoogle 搜索 telescope 集成可用于填充 holes。

- GHCi repl
为当前项目 / 缓冲区启动一个 GHCi repl。
- 自动检测适用于您项目的命令(
cabal repl、stack ghci或ghci)。 - 在内置处理器和
toggleterm.nvim之间进行选择。 - 为
iron.nvim动态创建 repl 命令 (参见 高级配置)。 - 使用 lua API 在 Haskell 文件内与 repl 进行交互。

- 打开当前缓冲区的项目/包文件

- 悬停操作
此插件通过 :Haskell hover 命令添加以下悬停操作(如果可用):
- Hoogle 搜索
(没有专门的
<Plug>映射, 因为可能存在多个要搜索的签名)。 - 在浏览器中打开文档(
<Plug>HaskellHoverActionDocs)。 - 在浏览器中打开源代码(
<Plug>HaskellHoverActionSource)。 - 跳转到定义(
<Plug>HaskellHoverActionDefinition)。 - 跳转到类型定义(
<Plug>HaskellHoverActionTypeDefinition)。 - 查找引用(
<Plug>HaskellHoverActionReferences)。
您可以通过切换到悬停窗口并在相应行输入 <CR> 来调用它们,
或者使用相应 <Plug> 映射的键位映射。
如果您不希望为每个 <Plug> 映射创建单独的键位映射,
您也可以为 <Plug>HaskellHoverAction 创建一个键位映射,该映射接受
一个 <count> 前缀,作为要调用的悬停操作的(从 1 开始的)索引。
例如,如果您设置以下键位映射:
vim.keymap.set('n', '<space>a', '<Plug>HaskellHoverAction')
你可以使用 3<space>a 调用第三个悬停操作。
此外,默认的美化 Markdown 行为已被禁用。 并且悬停缓冲区的文件类型被设置为 markdown, 以便 nvim-treesitter 用户能够从代码片段的语法高亮中受益。
若要使用 haskell-tools 的悬停操作覆盖 Neovim 内置的悬停键位映射,
你可以在例如 ftplugin/haskell.lua 中添加以下内容:
vim.keymap.set('n', 'K', function() vim.cmd.Haskell { 'hover' } end)

- 自动生成标签
在附加时,Neovim 的 LSP 客户端将设置 tagfunc
以查询语言服务器中可跳转的位置。
如果未找到位置,它将回退到 tags 文件。
如果安装了 fast-tags,
此插件将设置 autocmds 以自动生成标签:
- 对于整个项目,在启动会话时。
- 对于当前(子)包,在写入文件时。
此功能可以在 高级配置 中进行调整或禁用。
- 自动发现
haskell-debug-adapter配置
如果安装了 nvim-dap 插件,
haskell-tools.nvim 将自动发现 haskell-debug-adapter
配置。

[!NOTE]
haskell-debug-adapter是 Haskell 调试适配器的一种实验性设计与实现。
- Planned
对于计划中的功能,请参阅 issues。
:gear: 高级配置
要修改默认配置,请设置 vim.g.haskell_tools。
- 有关所有可用配置选项的详细
文档,请参阅
:help haskell-tools.config。 如果文档尚未安装,您可能需要运行:helptags ALL。 - 默认配置 可在此处找到(参见
HTDefaultConfig)。 - 要查看所有可用的
haskell-language-server设置 (包括未由本插件设置的设置),请运行haskell-language-server generate-default-config。- 有关配置的详细描述,
请参阅
haskell-language-server文档。
- 有关配置的详细描述,
请参阅
vim.g.haskell_tools = {
---@type haskell-tools.tools.Opts
tools = {
-- ...
},
---@type haskell-tools.lsp.ClientOpts
---You can also configure these via `:h vim.lsp.config`,
--- with the "haskell-tools" key.
hls = {
---@param client number The LSP client ID.
---@param bufnr number The buffer number
---@param ht HaskellTools = require('haskell-tools')
on_attach = function(client, bufnr, ht)
-- Set keybindings, etc. here.
end,
-- ...
},
---@type haskell-tools.dap.Opts
dap = {
-- ...
},
}
[!TIP]
vim.g.haskell_tools也可以是一个返回 表的函数。
如何按项目动态加载不同的 haskell-language-server 配置
默认情况下,此插件会在项目根目录中查找 hls.json2 文件,
并尝试加载它。
如果该文件不存在,或无法解码,
则使用 hls.default_settings。
您可以通过 hls.settings 配置更改此行为:
vim.g.haskell_tools = {
-- ...
hls = {
---@param project_root string Path to the project root
settings = function(project_root)
local ht = require('haskell-tools')
return ht.lsp.load_hls_settings(project_root, {
settings_file_pattern = 'hls.json'
})
end,
},
}
如何禁用单个代码透镜
某些代码透镜可能比其他代码透镜更有用。
例如,如果你倾向于导入所有内容或使用自定义预lude,importLens 可能会令人烦恼。
可以通过在相应的插件配置中禁用它们来关闭单个代码透镜:
hls = {
settings = {
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,
},
},
},
},
},
[!NOTE]
或者,您可以 按项目动态启用/禁用不同的代码透镜。
在 Cabal 文件上启动 haskell-language-server
自版本 1.9.0.0 起,haskell-language-server 可以在 Cabal 文件上启动,
但它并不支持其在 Haskell 文件上拥有的所有功能。
您可以在 ~/.config/nvim/after/ftplugin/cabal.lua 中添加 cabal 特定的键位映射等。
配置 iron.nvim 以使用 haskell-tools.nvim
依赖于 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,
},
},
},
}
创建 haskell-debug-adapter 启动配置
此插件有两种方式检测 haskell-debug-adapter 启动配置:
- 自动检测,通过解析 Cabal 或 Stack 项目文件。
- 通过加载项目根目录下的
launch.json文件。
可用函数和命令
要查看完整概览,请在 Neovim 中输入 :help haskell-tools。
LSP
| 命令 | 描述 |
|---|---|
:Haskell hls evalAll | 评估注释中的所有代码片段 |
:Haskell hls start | 为当前缓冲区启动 HLS 客户端 |
:Haskell hls stop | 停止当前缓冲区的 HLS 客户端 |
:Haskell hls restart | 重启当前缓冲区的 HLS 客户端 |
local ht = require('haskell-tools')
--- Start or attach the LSP client.
ht.lsp.start()
--- Callback for dynamically loading haskell-language-server settings
--- Falls back to the `hls.default_settings` if no file is found
--- or one is found, but it cannot be read or decoded.
--- @param project_root string? The project root
ht.lsp.load_hls_settings(project_root)
--- Evaluate all code snippets in comments
ht.lsp.buf_eval_all()
Hoogle
local ht = require('haskell-tools')
--- Run a hoogle signature search for the value under the cursor
ht.hoogle.hoogle_signature()
Repl
| 命令 | 描述 | 参数 |
|---|---|---|
:Haskell repl toggle {file?} | 切换 GHCi repl | filepath (可选) |
:Haskell repl quit | 退出当前 repl | |
:Haskell repl load {file?} | 将文件加载到当前 repl | filepath (可选) |
:Haskell repl reload | 重新加载当前 repl | |
:Haskell repl paste_type {register?} | 查询 repl 中 {register} 的类型 | |
:Haskell repl cword_type | 查询 repl 中光标下单词的类型 | |
:Haskell repl paste_info {register?} | 查询 repl 中 {register} 的信息 | |
:Haskell repl cword_info {register?} | 查询 repl 中光标下单词的信息 |
local ht = require('haskell-tools')
--- Toggle a GHCi repl for the current project
ht.repl.toggle()
--- Toggle a GHCi repl for `file`
--- @param file string Path to a Haskell file
ht.repl.toggle(file)
--- Quit the repl
ht.repl.quit()
--- Paste a command to the repl from register `reg`.
--- @param reg string? Register to paste from (:h registers), defaults to '"'.
ht.repl.paste(reg)
--- Query the repl for the type of register `reg`, and paste it to the repl.
--- @param reg string? Register to paste from (:h registers), defaults to '"'.
ht.repl.paste_type(reg)
--- Query the repl for the type of word under the cursor
ht.repl.cword_type()
--- Query the repl for info on register `reg`.
--- @param reg string? Register to paste from (:h registers), defaults to '"'.
ht.repl.paste_info(reg)
--- Query the repl for info on the word under the cursor
ht.repl.cword_info()
--- Load a file into the repl
--- @param file string The absolute file path
ht.repl.load_file(file)
--- Reload the repl
ht.repl.reload()
项目
| 命令 | 描述 |
|---|---|
:Haskell projectFile | 打开当前缓冲区的项目文件(cabal.project 或 stack.yaml) |
:Haskell packageYaml | 打开当前缓冲区的 package.yaml 文件 |
:Haskell packageCabal | 打开当前缓冲区的 *.cabal 文件 |
local ht = require('haskell-tools')
--- Open the project file for the current buffer (cabal.project or stack.yaml)
ht.project.open_project_file()
--- Open the package.yaml file for the current buffer
ht.project.open_package_yaml()
--- Open the *.cabal file for the current buffer
ht.project.open_package_cabal()
--- Search for files within the current (sub)package
--- @param opts table Optional telescope.nvim `find_files` options
ht.project.telescope_package_files(opts)
--- Live grep within the current (sub)package
--- @param opts table Optional telescope.nvim `live_grep` options
ht.project.telescope_package_grep(opts)
使用 Hoogle 回退的跳转至定义
| 命令 | 描述 |
|---|---|
:Haskell definition | vim.lsp.buf.definition(),但如果未找到定义,则回退到 Hoogle 搜索 |
标签
以下函数依赖于 fast-tags。
local ht = require('haskell-tools')
-- Generate tags for the whole project
---@param path string? An optional file path, defaults to the current buffer
---@param opts table Optional options:
--- opts.refresh boolean
--- - Whether to refresh tags if they have already been generated for a project
ht.tags.generate_project_tags(path, opts)
-- Generate tags for the whole project
---@param path string? An optional file path, defaults to the current buffer
ht.tags.generate_package_tags(path)
[!NOTE]
默认情况下,如果检测到
fast-tags,haskell-tools将自动生成项目和包 标签。
DAP
local ht = require('haskell-tools')
---@param bufnr integer The buffer number
---@param opts table? Optional
---@param opts.autodetect: (boolean)
--- Whether to auto-detect launch configurations
---@param opts.settings_file_pattern: (string)
--- File name or pattern to search for. Defaults to 'launch.json'
ht.dap.discover_configurations(bufnr, opts)
[!NOTE]
haskell-tools.nvim将自动发现 DAP 启动配置, 前提是nivm-dap已安装且调试适配器服务器可执行。 通常无需手动调用此函数。
Telescope 扩展
如果 telescope.nvim 已安装,
haskell-tools.nvim 将注册 ht 扩展,
并提供以下命令:
| 命令 | 描述 |
|---|---|
:Telescope ht package_files | 在当前(子)包中搜索文件 |
:Telescope ht package_hsfiles | 在当前(子)包中搜索 Haskell 文件 |
:Telescope ht package_grep | 在当前(子)包中进行实时 grep |
:Telescope ht package_hsgrep | 在当前(子)包中对 Haskell 文件进行实时 grep |
:Telescope ht hoogle_signature | 对光标下的类型签名执行 Hoogle 搜索 |
要加载该扩展,请调用
require('telescope').load_extension('ht')
[!IMPORTANT]
如果你延迟加载此插件, 请确保它在注册 Telescope 扩展之前加载。
:staphoscope: 故障排除
要进行健康检查,请运行 :checkhealth haskell-tools
LSP 功能不工作
如果 hls 无法显示诊断信息,或在文件顶部显示错误诊断,
你应该首先检查能否使用 cabal 或 stack 编译你的项目。
如果有编译错误,打开无法编译的文件,
hls 应该能够显示这些文件的错误诊断信息。
检查你正在使用的 hls 和 GHC 的版本
(例如,通过调用 haskell-language-server-wrapper --probe-tools
或 haskell-language-server --probe-tools)。
有时,某些功能需要一些时间才能在最新的 GHC 版本中实现。
你可以在 hls 文档 中查看特定 GHC 版本的支持情况。
最小配置
要在临时目录中使用最小配置来排查此插件的问题, 你可以尝试 minimal.lua。
nvim -u minimal.lua
[!NOTE]
如果你使用 Nix,你可以运行
nix run "github:mrcjkb/haskell-tools.nvim#nvim-minimal-stable"。 或者nix run "github:mrcjkb/haskell-tools.nvim#nvim-minimal-nightly"。
如果你无法使用最小配置复现你的问题,
它可能是由另一个插件引起的。
在这种情况下,将额外的插件及其配置添加到 minimal.lua,
直到你可以复现它。
[!NOTE]
此插件仅在 Linux 上进行了测试。 它应该在 MacOS 上正常工作,基本功能在 Windows 上也应该可以工作 (自版本
1.9.5起),但我无法自行测试这一点。 依赖外部工具的功能,例如hoogle,fast-tags或ghci可能在非 Unix 类操作系统上失效。
日志
要启用调试日志,请将日志级别设置为 DEBUG3:
vim.g.haskell_tools = {
tools = { -- haskell-tools options
log = {
level = vim.log.levels.DEBUG,
},
},
}
你还可以通过调用以下命令临时设置日志级别
| 命令 | 参数 |
|---|---|
:Haskell log setLevel | debug error warn info trace off 之一 |
或
:lua require('haskell-tools').log.set_level(vim.log.levels.DEBUG)
您可以通过调用以下命令找到日志文件
-- haskell-tools.nvim log
:lua =require('haskell-tools').log.get_logfile()
-- haskell-language-server logs
:lua =require('haskell-tools').log.get_hls_logfile()
或调用
:lua require('haskell-tools').log.nvim_open_logfile() -- or :Haskell log openLog
:lua require('haskell-tools').log.nvim_open_hls_logfile() -- or :Haskell log openHlsLog
:link: 推荐
以下是我推荐用于 neovim 中 Haskell(以及 nix)开发的其他插件:
- neotest-haskell: 在 neovim 中与测试进行交互。
- ghcid-error-file.nvim 解析由 ghcid 或 ghciwatch 生成的错误文件, 并将其添加到 quickfix 列表中。 当 haskell-language-server 速度过慢时非常有用。
- haskell-snippets.nvim 用于 LuaSnip 的 Haskell 代码片段集合。
- telescope_hoogle: 实时 Hoogle 搜索。
- telescope-manix: Nix 搜索。
- nvim-lint: 作为 haskell-language-server 出现问题时的后备方案 (例如在大型 mono repo 中)。
- nvim-treesitter: 用于语法高亮,以及更多功能。
- nvim-treesitter-textobjects: 用于基于 TreeSitter 的 textobjects。
Footnotes
-
haskell-language-server可以使用generate-default-configCLI 参数 生成 此类文件。 ↩ -
See
:help vim.log.levels: ↩