Setup OCaml With NeoVim
Tags: ocaml, vim, neovim
Published: 2019-11-10 15:47:47

---
title: 'Setup OCaml With NeoVim'
tags: ocaml, vim, neovim
cover_image: https://images.unsplash.com/photo-1568602561429-659a9c5f4ebd?crop=entropy&cs=tinysrgb&fit=crop&fm=jpg&h=420&ixid=eyJhcHBfaWQiOjF9&ixlib=rb-1.2.1&q=80&w=1000
series: 'How to Learn OCaml'
canonical_url: 'https://www.rockyourcode.com/setup-ocaml-with-neovim/'
published: true
---


> How to use the NeoVim text editor as your Ocaml IDE

## Why OCaml?

I've always been interested in learning an [ML language](https://en.wikipedia.org/wiki/ML_%28programming_language%29). But Haskell, the poster child of functional programming, has a high learning curve.  
OCaml and ReasonML (an alternative syntax for OCaml) are much more beginner-friendly.

I started a free MOOC on [functional programming with OCaml](https://www.fun-mooc.fr/cours/#search?query=ocaml) a few days ago. Thus, it's the perfect time to set up my editor for OCaml development.

If you want to know more about OCaml, read the short article [Why OCaml?](https://www2.lib.uchicago.edu/keith/ocaml-class/why.html).

**In this article, I will show you how to integrate OCaml with NeoVim.**

## What is NeoVim?

[NeoVim](https://neovim.io/) is a fork of the terminal-based [Vim text editor](https://www.vim.org/).  
It's fully compatible with Vim, but adds more features with a slimmer code base.

### Plugin Manager and Basic Setup

NeoVim comes with very sensible defaults. See `:h nvim-defaults` for more information.  
If you'd like some suggestions for further customization, you can check out [neovim-sensible](https://github.com/jeffkreeftmeijer/neovim-sensible) by Jeff Kreeftmeijer.

Install a plugin loader for convenience, for example, **[minpac][minpac]**.

There are two alternatives for code support in NeoVim: **Merlin (standalone)** or **esy**.

You'll install Merlin with `opam`, the package manager for OCaml. `esy` is the package manager for Reason and OCaml and works with the `npm` ecosystem (Node.js/JavaScript).

You can use Merlin and its Vim integration, or you can use esy's Vim integration.  
(esy only works if you compile your programs to native binaries.)

### Merlin

Let's start with Merlin. _Skip this section section if you want to try out esy._

#### Basic Requirements

- [OCaml][ocamlinstall]
- [OPAM][opam]
- [NeoVim][neovim]
- Python (needed for Merlin, see below)

**[Merlin][merlin]** is the de-facto code helper for OCaml. If you'd like to use Merlin with Neovim, you need **Python** and **[pynvim][pynvim]**.

```bash
pip3 install --user pynvim
```

Make sure that your [Python integration for NeoVim works correctly](https://neovim.io/doc/user/provider.html).  
If you encounter problems, use `:checkhealth provider` for trouble-shooting.

For Merlin, you can follow the [vim from scratch guide][merlinguide]:

```bash
opam install merlin
```

Then add this line to your `init.vim`:

```bash
let g:opamshare = substitute(system('opam config var share'),'\n$','','''')
execute "set rtp+=" . g:opamshare . "/merlin/vim"
```

> That not only adds merlin to your runtime path, but will always pick the version corresponding to your opam switch without you needing to modify your config (assuming you install merlin on each of your switches).

In NeoVim, run the following command:

```bash
:execute "helptags " . substitute(system('opam config var share'),'\n$','','''') .  "/merlin/vim/doc"
```

Merlin comes with default key mappings. [Be aware that those might not work](https://github.com/ocaml/merlin/issues/634).  
You might want to set your own key bindings. I find `:MerlinTypeOf` in normal mode especially useful.  
See `:h merlin` for more info.

#### (Tab) Completion

Merlin supports several completion plugins out of the box, for example, [Deoplete](https://github.com/Shougo/deoplete.nvim) or [SuperTab](https://github.com/ervandew/supertab).

I prefer **[VimCompletesMe][vimcompletesme]**, a minimal plugin that does everything I need:

```bash
call minpac#add('ajh17/VimCompletesMe')
```

Merlin will now suggest completions when you press `<Tab>`.

#### Language Server

Merlin offers the most important features, like showing type signatures.  
If you desire more functionality, for example, hover information about different modules, you can use a language server.

You'll need a Language Server client. I recommed **[LanguageClient-neovim][languageclient]**, as it works with every language server I've tried.

Install with your Vim plugin manager, e.g.:

```bash
call minpac#add('autozimu/LanguageClient-neovim', {'branch': 'next', 'do': 'bash install.sh'})
```

You also have to configure the plugin. Modify `init.vim` (or `~/.vimrc` or similar):

```vim
set hidden

 let g:LanguageClient_serverCommands = {
 \   'ocaml':           ['ocamllsp'],
 \}

nnoremap <F5> :call LanguageClient_contextMenu()<CR>
" Or map each action separately
nnoremap <silent> K :call LanguageClient#textDocument_hover()<CR>
nnoremap <silent> gd :call LanguageClient#textDocument_definition()<CR>
nnoremap <silent> <F2> :call LanguageClient#textDocument_rename()<CR>
```

Now install the language server. There are [several alternatives](https://github.com/ocaml/ocaml-lsp#comparison-to-other-lsp-servers-for-ocaml). **[ocaml-lsp][ocamllsp]** is a community project written in OCaml that's in active development.

See the [installation instructions](https://github.com/ocaml/ocaml-lsp#installation) for further details.

I use opam:

```bash
opam pin add ocaml-lsp-server https://github.com/ocaml/ocaml-lsp.git
```

Open an OCaml file. Press `K` while the cursor is on a definition. The language server will give you information about the function or file.

If you use a different language server than [ocaml-lsp][ocamllsp], you have to change the setup for [LanguageClient-neovim][languageclient] accordingly.

### esy

If you're willing to work with the Node ecosystem, esy is a good choice that works both with OCaml and ReasonML.

#### Basic Requirements

- [OCaml][ocamlinstall]
- [NeoVim][neovim]
- [esy][esy]

Install **[esy][esy]** with `npm`, the Node.js package manager:

```bash
npm install -g esy
```

Now, you'll need a Vim plugin: **[vim-reasonml][vimreasonml]**. Install it with your plugin manager. For example, with minpac:

```sh
call minpac#add('jordwalke/vim-reasonml')
```

The plugin supports Merlin out of the box. For example, if you have a `.merlin` file in your project, `vim-reasonml` will pull merlin from your dev dependencies.  
_That means that you don't have to install Merlin separately._

#### (Tab) Completion

See above.

### ALE And ocamlformat

**[ALE][ale]** is a linting and fixing tool for Vim. I heavily rely on it for avoiding syntax errors and tidying up my code.

ALE adds some bloat to your NeoVim setup. Still, I find the trade-off worthwhile.

Install it with your package manager (or find alternative instructions on [GitHub](https://github.com/dense-analysis/ale#3-installation)):

```bash
call minpac#add('dense-analysis/ale')
```

Example setup in your `init.vim` (or similar):

```bash
let g:ale_sign_error                  = '✘'
let g:ale_sign_warning                = '⚠'
highlight ALEErrorSign ctermbg        =NONE ctermfg=red
highlight ALEWarningSign ctermbg      =NONE ctermfg=yellow
let g:ale_linters_explicit            = 1
let g:ale_lint_on_text_changed        = 'never'
let g:ale_lint_on_enter               = 0
let g:ale_lint_on_save                = 1
let g:ale_fix_on_save                 = 1

let g:ale_linters = {
\   'ocaml':      ['merlin'],
\}

let g:ale_fixers = {
\   'ocaml':      ['ocamlformat'],
\   '*':          ['remove_trailing_lines', 'trim_whitespace'],
\}
```

Improve ALE's performance by setting the linters you need and don't lint every time you type in something.  
Fix a file when you save or call `:ALEFix` manually.  
See `:h ale` for more information.

We can use **[ocamlformat][ocamlformat]** for parsing our code.

```bash
opam install ocamlformat
```

## Thoughts

It took me some time to properly setup OCaml and NeoVim. I hope my article helped you to get there faster.

After following this guide, you should have a fully-fledged OCaml IDE with syntax highlighting, hover information, auto-completion and more.

## Links

- [Setting Up Merlin in Neovim](https://thealmarty.com/2018/08/28/setting-up-merlin-in-neovim/) by Marty Stumpf

[minpac]: https://github.com/k-takata/minpac
[opam]: https://opam.ocaml.org/doc/Install.html
[ocamlinstall]: https://ocaml.org/docs/install.html
[neovim]: https://neovim.io/
[merlin]: https://github.com/ocaml/merlin
[pynvim]: https://github.com/neovim/pynvim
[merlinguide]: https://github.com/ocaml/merlin/wiki/vim-from-scratch
[vimcompletesme]: https://github.com/ajh17/VimCompletesMe
[ale]: https://github.com/dense-analysis/ale
[ocamlformat]: https://github.com/ocaml-ppx/ocamlformat
[esy]: https://esy.sh/
[vimreasonml]: https://github.com/jordwalke/vim-reasonml
[vimtyping]: https://georgebrock.github.io/talks/vim-completion/
[languageclient]: https://github.com/autozimu/LanguageClient-neovim
[ocamllsp]: https://github.com/ocaml/ocaml-lsp