Contents

Neovim Setup for OCaml

Info

Updates:

  • 2025-09-07: Make necessary changes because I use the Neovim v0.11 native LSP feature instead of relying on nvim-lspconfig.
  • 2025-03-22: Replace the null-ls/none-ls plugin with conform.nvim for simplicity :)

In previous post, I elaborated on how to set up Neovim from scratch. However, I didn’t answer the following questions:

  1. What if an LSP does not support formatting? How do you configure Neovim to support a third-party code formatting tool?
  2. How do you change configuration files when adding a new programming language?

Recently, I started learning OCaml, and I thought it would be a good opportunity to answer these questions.

Info
I assume you have already read my previous post, so I will omit some details here.
Info
If you are not using a Mac, you may refer to the official instructions for more details.

It is quite handy to install OCaml on a Mac with Homebrew.

$ brew install opam

According to the output of Homebrew, we need to perform additional configurations and modify ~/.zshrc(for Zsh users) or ~/.bashrc(for Bash users).

$ opam init
# press `y` to confirm

To make the modifications work, run the following command in your terminal

$ source ~/.zshrc
# $ source ~/.bashrc # if you use bash

Finally, we can use this command to check if everything goes right.

$ opam switch

The output will be like

#   switch   compiler                   description
->  5.0.0    ocaml-base-compiler.5.0.0  5.0.0
    default  ocaml.4.14.0               default

Similar to IPython for Python, OCaml offers a REPL tool called utop. We can install this to practice programming in OCaml.

$ opam install utop dune

Just try the utop REPL yourself :)

$ utop

The output would be like

─────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────┬──────────────────────────────────────────────────────────
                                                          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_instrumentAlias_analysisAllocated_constAnnotApplicativeArchArgArg_helperArrayArrayLabelsAsmgenAsmlibrarianAsmlinkAsmpackagerAssert_failureAst_helperAst_inva
└──────────────┴──────────────┴───────────────┴─────┴───────────┴────┴───┴──────────┴─────┴───────────┴──────┴────────────┴───────┴───────────┴──────────────┴──────────┴────────┘
Info

If you follow my previous post, then the file hierarchy of your setup will be like

.
├── init.lua
├── lazy-lock.json
├── lsp
│   └── ty.lua
└── lua
    ├── colorscheme.lua
    ├── keymaps.lua
    ├── lsp.lua
    ├── options.lua
    └── plugins.lua

Previously, we leveraged mason.nvim to set up LSPs. To add LSP for OCaml:

  1. Open Neovim and type :Mason, find ocaml-lsp in the (2) LSP section, and press i when the cursor stays on the line.
  2. Open the configurations file for LSP(lsp.lua in my configurations), add the following Lua code
return {
	cmd = { "ocamllsp" },
	filetypes = { "ocaml", "menhir", "ocamlinterface", "ocamllex", "reason", "dune" },
	root_markers = { "*.opam", "esy.json", "package.json", ".git", "dune-project", "dune-workspace" },
}
  1. Open the lsp.lua and add ocamllsp to vim.lsp.enable
... -- rest of the configurations
vim.lsp.enable({ "ty", "ocamllsp" })
Warning

You may have your configuration files and do not follow my previous post. Thus, only adding the ocamllsp.lua under the lsp folder may not work for you. However, you can check the full configuration here to understand how it works and adapt to your configurations.

We can restart Neovim to make the configurations take effect.

Now you can create a new dune project to see if LSP works. If you type :checkhealth vim.lsp when you’re editing a .ml file, you should see similar output like this.

vim.lsp: Active Clients ~
- ocamllsp (id: 1)
  - Version: 1.23.0
  - Root directory: /private/tmp/dummy
  - Command: { "ocamllsp" }
  - Settings: {}
  - Attached buffers: 1

The ocaml-lsp LSP does not support formatting OCaml code. If you try to format OCaml code, you will get an error message:

[LSP] Format request failed, no matching language servers.

Now, I will talk about how to set up a formatter for OCaml.

You may have noticed that the Mason tool also offers linters and formatters. The formatter for OCaml is ocamlformat, so we can install it just like we install an LSP.

However, ocamlformat is a CLI tool which can not be integrated into Neovim. Luckily, we have conform.nvim plugin which can do us this favor.

Open the plugins.lua file and add the Lua code. After saving the changes and restarting Neovim, you should see that lazy.nvim is installing the plugin.

... -- rest of the configurations, omit for simplicity
require("lazy").setup({
    {
        "stevearc/conform.nvim",
        opts = {
            formatters_by_ft = {
                ocaml = { "ocamlformat" },
            },

            formatters = {
                ocamlformat = {
                    prepend_args = {
                        "--if-then-else",
                        "vertical",
                        "--break-cases",
                        "fit-or-vertical",
                        "--type-decl",
                        "sparse",
                    },
                },
            },
        },
    }
    ... -- rest of the configurations, omit for simplicity
})

The code above seems to be complicated at first glance, but I am sure that you will find the patterns

  • formatters_by_ft - add an entry for formatter
  • formatters - add more configurations if needed
Tip

FAQs

  1. How do I find which formatter should be used for a programming language? Type :help conform-formatters, search for the programming language, and you should see some options. Pick one and install it with :Mason.
  2. How do I add a formatter to conform.nvim? Add a new entry in formatters_by_ft, as shown in the example.
  3. How do I customize a formatter? Check the official manual of the formatter.

Finally, we also need to modify the lsp.lua file. The original lines related to code formmating look like this:

... -- rest of the configurations, omit for simplicity
vim.api.nvim_create_autocmd("LspAttach", {
    callback = function (args)
        ... -- rest of the configurations, omit for simplicity
        keymap.set("n", "<space>f", function()
            vim.lsp.buf.format({ async = true })
        end, bufopts)
    end
})
... -- rest of the configurations, omit for simplicity

Now we are using conform.nvim to do formatting. Thus, we need to use the format function in conform.nvim.

... -- rest of the configurations, omit for simplicity
vim.api.nvim_create_autocmd("LspAttach", {
    callback = function (args)
        ... -- rest of the configurations, omit for simplicity
        keymap.set("n", "<space>f", function()
            require("conform").format({ async = true, lsp_fallback = true })
        end, bufopts)
    end
})
... -- rest of the configurations, omit for simplicity

Save the changes and restart Neovim to see if it works.

Now, let’s summarize how to set up Neovim for a new programming language. If you also use mason.nvim and conform.nvim, you can follow the instructions to set up:

  1. Read the official pages and install the programming language’s environment on your machine.
  2. Open Neovim, type :Mason, and install the corresponding LSP. Refer to the official LSP documentation and check whether it includes a built-in formatter. If it does not, use :Mason to install one.
  3. Create a new file for this lsp under the lsp folder. If you have trouble writing this file, you can always refer to nvim-lspconfig. Note that you also need to modify the lsp.lua file - put the new LSP in vim.lsp.enable.
  4. If you install the formatter with :Mason by yourself, you also need to change the configurations for conform.nvim.

For anything not clearly stated in this post, you can check my dotfiles configurations repo for more details.