How to use the NeoVim text editor as your Ocaml IDE

I’ve always been interested in learning an ML language. 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 a few days ago. Thus, it’s the perfect time to set up my editor for OCaml development.

What does it take to develop OCaml in NeoVim?

Basic Requirements

  • OCaml
  • OPAM
  • NeoVim (a text editor based on Vim)
  • Python (needed for Merlin, see below)

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 by Jeff Kreeftmeijer.

Install a plugin loader for convenience, for example, minpac.

Merlin

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

pip2 install --user pynvim
pip3 install --user pynvim

Make sure that your Python integration for NeoVim works correctly.
If you encounter problems, use :checkhealth provider for trouble-shooting.

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

opam install merlin

Then add this line to your init.vim:

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:

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

Merlin comes with default key mappings. Those don’t work.
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 or SuperTab.

I prefer VimCompletesMe, a minimal plugin that does everything I need:

call minpac#add('ajh17/VimCompletesMe')

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

ALE And ocamlformat

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

Install it with your package manager (or find alternative instructions on GitHub):

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

In your init.vim (or similar):

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 use ocamlformat for parsing our code.

opam install ocamlformat