OCaml 的 Neovim 配置方案
更新说明:
- 2025-03-22: 将
null-ls/none-ls
插件更换为conform.nvim
插件,因为配置更简单 :)
前言
在上一篇博客中,我详细介绍了如何从零开始配置 Neovim,让它更接近 IDE。但我没有讲到的是:
- 有的 LSP 本身不支持代码格式化,要如何配置 Neovim 让它可以用第三方的代码格式化工具?
- 新增一门编程语言支持的时候,要怎么修改配置文件?
最近在学习函数式编程需要在我的 Mac 上配置 OCaml,正好以它为例,回答以上两个问题
本文假定你已经阅读过我之前的文章,因此对于一些细节问题,我就不再赘述
安装 OCaml
其他系统的安装方法可以看这里
在 Mac 上安装 OCaml 用 Homebrew 比较方便
$ brew install opam
按照 Homebrew 的输出,我们还需要进行额外的配置,需要修改 ~/.zshrc
(如果你用的是 Zsh)或者是 ~/.bashrc
(如果你用的是 Bash)
$ opam init
# 按照提示按下 y 确认即可
接下来让我们上一步的配置生效
$ source ~/.zshrc
# $ source ~/.bashrc # if you use bash
最后,我们可以通过如下的命令检查是否安装成功
$ opam switch
输出应该跟下面的差不多
# switch compiler description
-> 5.0.0 ocaml-base-compiler.5.0.0 5.0.0
default ocaml.4.14.0 default
接下来需要安装一些包,因为网络原因访问可能会比较慢。加速可以通过换源实现,这里用的是上海交通大学的源,配置命令为
$ opam repo \
set-url default \
https://mirrors.sjtug.sjtu.edu.cn/git/opam-repository.git \
--all --set-default
等待该命令执行完就完成了换源
类似 Python 有 REPL,如 IPython。在 OCaml 里也有类似的工具,叫做 utop
$ opam install utop
接下来尝试使用 utop 看是否安装成功
$ utop
如果一切正常,你应该可以看到如下的输出
─────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────
│ Welcome to utop version 2.13.1 (using OCaml version 5.0.0)! │
└─────────────────────────────────────────────────────────────┘
Type #utop_help for help about using utop.
─( 10:33:26 )─< command 0 >────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────{ counter: 0 }─
utop #
┌──────────────┬──────────────┬───────────────┬─────┬───────────┬────┬───┬──────────┬─────┬───────────┬──────┬────────────┬───────┬───────────┬──────────────┬──────────┬────────┐
│Afl_instrument│Alias_analysis│Allocated_const│Annot│Applicative│Arch│Arg│Arg_helper│Array│ArrayLabels│Asmgen│Asmlibrarian│Asmlink│Asmpackager│Assert_failure│Ast_helper│Ast_inva│
└──────────────┴──────────────┴───────────────┴─────┴───────────┴────┴───┴──────────┴─────┴───────────┴──────┴────────────┴───────┴───────────┴──────────────┴──────────┴────────┘
配置 OCaml LSP
如果你跟着我上一篇的教程进行配置,那么你的目录结构应该长下面这样
.
├── init.lua
└── lua
├── colorscheme.lua
├── keymaps.lua
├── lsp.lua
├── options.lua
└── plugins.lua
之前的文章里面我们配置 LSP 的时候借助了 mason.nvim。要增加新的编程语言的 LSP 同样可以用这个工具,步骤如下
- 打开 Neovim,输入
:Mason
,在(2) LSP
里面找到ocaml-lsp
,将光标放在该行然后按下i
就可以安装了,稍候片刻☕️ - 打开 LSP 配置文件(我放在了
lsp.lua
文件里面),添加如下的内容
... -- rest of the configurations
lspconfig.ocamllsp.setup({
on_attach = on_attach,
})
修改完 lsp.lua
文件之后,重启 Neovim 之后就生效了
和 rust-analyzer 类似,ocaml-lsp
的 LSP 也只支持在项目里启用,无法在单文件下生效
接下来,可以用 dune 新建项目,尝试写一下 OCaml 的代码,检查 LSP 是否正常工作
配置 OCaml Formatter
ocaml-lsp
本身并不支持 OCaml 代码的格式化,在完成如上的配置之后,如果你尝试格式化,会发现 Neovim 报错:
[LSP] Format request failed, no matching language servers.
因此,接下来要讲解的就是如何配置 OCaml 的代码格式化功能。
之前你可能已经注意到,在 Neovim 里面输入 :Mason
的时候可以看到,它不仅支持安装 LSP,还可以安装 Linter、Formatter 等。其中 Formatter 就是用于代码的格式化。OCaml 的 Formmater 是 ocamlformat,首先我们在 Mason 的窗口 (5) Formatter
里找到它,将光标停留在该选项,然后按下 i
安装。等待片刻之后就可以看到安装成功了☕️
ocamlformat 本身只是一个 CLI 工具,没法方便地集成到 Neovim 里面。这里我们可以用 conform.nvim 插件,该插件会对接 ocamlformat
等各种 Formatter
打开 plugins.lua
新增如下 Lua 代码,保存并重启后 lazy.nvim
就会自动安装新插件,稍候片刻☕️
... -- rest of the configurations, omit for simplicity
require("lazy").setup({
{
"stevearc/conform.nvim",
opts = {
formatters_by_ft = {
ocaml = { "ocamlformat" },
},
formatters = {
ocamlformat = {
command = "ocamlformat",
args = {
"--if-then-else",
"vertical",
"--break-cases",
"fit-or-vertical",
"--type-decl",
"sparse",
-- $FILENAME - absolute path to the file
"$FILENAME",
},
},
},
},
}
... -- rest of the configurations, omit for simplicity
})
上面的配置看起来可能有点复杂,但其实意思很直白
formatters_by_ft
用来指定对应编程语言的 Formatter,这里是ocaml
- 如果想要进一步定制化 Formatter 的功能,可以在
formatters
进行进一步定制
FAQs:
- 如何确定编程语言用什么 Formatter?用
:help conform-formatters
就可以看到所有conform.nvim
支持的 Formatter,在:Mason
的(5) Formatter
里面找到并安装 - 如何往
conform.nvim
里添加 Formatter?在formatters_by_ft
里面新增 Entry,格式可以参考上面ocaml
的设置 - 如何确定 Formatter 都有什么具体的配置项来定制化?查看各个工具的官方文档
最后,我们还需要修改 lsp.lua
文件,原本跟代码格式化有关的行长这个样子
... -- rest of the configurations, omit for simplicity
vim.keymap.set("n", "<space>f", function()
vim.lsp.buf.format({ async = true })
end, bufopts)
... -- rest of the configurations, omit for simplicity
现在因为我们使用 conform.nvim
,相当于所有的格式化都交给它来做,因此用的是 conform.nvim
的 format
函数
... -- rest of the configurations, omit for simplicity
vim.keymap.set("n", "<space>f", function()
require("conform").format({ async = true, lsp_fallback = true })
end, bufopts)
... -- rest of the configurations, omit for simplicity
这里 lsp_fallback
的意思是如果没有对应的 Formatter 就尝试用 LSP 自带的 Formatter(若有)
完成文件编辑之后,重启 Neovim 就生效了
总结
现在对前文的内容进行总结,如果你跟我一样也使用 mason.nvim, mason-lspconfig, conform.nvim 这些插件。那么在添加一门新的编程语言的时候,你可以这样配置:
- 首先在你的机器上配置好该编程语言的开发环境,查询官方文档即可
- 打开 Neovim,输入
:Mason
安装对应的 LSP,先检查官方文档看有没有自带 Formatter,如果没有就用:Mason
安装对应的 Formatter - 打开
lsp.lua
文件配置 LSP - 如果是自己单独下载的 Formatter,还需要在
conform.nvim
的配置项里面进行设置
对于本文没有说清楚的地方,你可以查看我的 dotfiles 配置 来了解更多的信息~~