Skip to content
Ulysse edited this page Jan 7, 2022 · 29 revisions

This document is a work in progress, any feedback or addition is welcomed.

Minimal vim setup

To successfully use merlin from vim, you need to have an already configured vim setup. The bare vim installation from your system won't do (a bunch of stuff need to be activated: syntax on, filetype plugin on, etc.).

If it's your first time with vim, you should probably document yourself somewhere else before reading the rest of this page. If however you wish to continue in a hurry, you can still start by installing Tim Pope's vim-sensible.

Adding merlin to the mix

Note that we encourage everyone to install merlin through opam, for two reasons:

  1. it's a lot easier
  2. you should then have a version of merlin that is known to work

That being said, it might make sense to install it from git:

  • you want to help improve it (you're very welcome!)
  • you noticed a bug in the opam version which is fixed on git
  • you're cool and no one tells you what to do

With that in mind, feel free to choose the option you prefer.

With opam and OCaml 4.0.6

For OCaml 4.0.6/opam 2.0.5 and if you don't have a .vimrc, just follow the instructions printed by opam after installing merlin, i.e. run

opam user-setup install

That enabled completions in vim. If there's already a .vimrc, user-setup doesn't set up everything merlin needs in it.

With opam

Just run (should work on 4.00.1 and 4.01.0):

opam install merlin

And add the following to your .vimrc:

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).

Note that you need to run :

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

manually to update the documentation.

Thanks to Gabriel Kerneis for this contribution.

From git

Once you have cloned you must run the following commands:

# You can run the following with -help to have more informations
./configure
make
make install

The configure step will have printed you where the vim mode is installed. If it is in /some/path/merlin/vim then you need to add:

set rtp+=/some/path/merlin/vim

To your .vimrc.
You can then generate the documentation using:

:helptags /some/path/merlin/vim/doc

Note: although you can use pathogen here, we strongly recommend not using Vundle. The reason for that being that you need to keep your vim plugin synchronized with the ocamlmerlin executable (they need to understand the same protocol), which probably won't be the case if you fetch the plugin from github.

Discovering the shiny features

Follows a non-ordered list of the features merlin provides, and an explanation on how to access them from vim.

Error reporting

Merlin knows about the syntax and type errors of your code. And it can tell vim about it.

You can get this list in manually by running :MerlinErrorCheck command.

Otherwise, we recommend using Syntastic for displaying these errors. Add this to your .vimrc to integrate it with merlin:

let g:syntastic_ocaml_checkers = ['merlin']

Once you have it, syntastic should ask merlin for errors in your buffer every time you save your file. So little signs should appear on the border of your window when you save and your code contains errors and disappear once you've fixed them.

Completion

Merlin offers completion as you would find in other IDEs: given a prefix, it will suggest all the elements of the current environment matching the given prefix and display their type.

completion in vim

Completion should be available out of the box using the omnicompletion facility of vim, you need only type <C-x><C-o> and a popup like the one in the previous screenshot should appear.

Several plugins exists out there to make completion nicer to use in vim, they should all work with merlin; at the cost of a small configuration.

More information about such plugins is available in the documentation shipped with merlin's vim plugin (so: :h merlin.txt or directly from github).

Extracting type informations

While coding, you can query Merlin for the type of expressions. You just need to place your cursor on the expression you want type, and call :MerlinTypeOf (which is bound by default en <Localleader>t, see :h localleader) and vim should then display the type in the command line, and highlight the considered expression.

For example:

:MerlinTypeOf

Notice here that the cursor is on map, if it were on Option then the signature of the Option module would have been printed.

If instead, you have a No type annotations (.annot) file found error, that is your Vim/NeoVim's default OCaml settings messing up the <LocalLeader>t mapping. Either map something else to trigger :MerlinTypeOf<return> or add let no_ocaml_maps=1 to your .vimrc/init.vim to stop it and a bunch of other (old/outdated) mappings for OCaml from being loaded. More details are in the file /usr/share/[VIM/NVIM]/runtime/ftplugin/ocaml.vim.

Note also that you can grow (or shrink!) the expression you want to type using :MerlinGrowEnclosing (<Localleader>n) or :MerlinShrinkEnclosing (<Localleader>p), for example on my previous code:

:MerlinGrowEnclosing

And you can do that several times (until there's nowhere left to grow).

The TypeOf command also works in visual mode:

visual selection

visual :MerlinTypeOf

And accepts arguments! So :MerlinTypeOf 23 + 42 would have given the same result as on the last screenshot.

Source browsing

There are mainly two ways Merlin can help you browse the source of you project. One, which one can find in nearly every IDE on the planet, is a jump to definition facility, accessible through the :MerlinLocate command. That command is to be used in the same way as :MerlinTypeOf: either you give it an argument (an arbitrary identifier, but no expressions, for obvious reasons) or you place it on a word of your buffer and call it without arguments.

Note that it might happen that merlin fails to find the definition location of the identifier you gave him, several things might explain that:

  1. you are asking him for the definition of something defined in an other file and:
    • either that file is not in the load path of merlin (you need to setup a .merlin!)
    • or you didn't compile with -bin-annot.
  2. you are asking him for the definition of something local to your file but the place where that thing is defined doesn't parse or doesn't typecheck, and the identifier is not present in the environment

If it doesn't work, but none of the above explains why, open an issue!

The other way of browsing your project is through the commands :ML and :MLI which expect a module name as an argument (completion is provided!) and open the corresponding file.

Case analysis (destruct)

Destruct is a powerful feature of Merlin that allows one to generate and manipulate pattern matching expressions.

The main command, :MerlinDestruct, behaves differently depending on the cursor's context.

When called on:

  • an expression it replaces it by a pattern matching over it's constructors

  • a wildcard pattern in a matching it will refine it if possible

  • a pattern of a non-exhaustive matching it will make the pattern matching exhaustive by adding missing cases

Expression construction

Merlin provides commands to browse and fill typed holes (_). Such holes sometimes appear in the result of other commands like destruct and can also also be inserted manually in the source code to get access to code generation.

  • :MerlinConstruct
    Provides valid type-based constructions when the cursor is on a typed hole (_) that could fill this hole. Can be used in alternance with destruct.

  • :MerlinNextHole and :MerlinPreviousHole
    Navigates to the next or previous typed hole (_) in the buffer.

Configuring Merlin

See project configuration for a full description of .merlin files.

Directives used by Vim

S in .merlin files is used for the :ML and :MLI commands.

B is used for :MerlinLocate.

PKG is used for findlib/opam packages. You can also declare packages on a per-session basis using the :MerlinUse command followed by the name of the package (completion on packages name is provided, but you can always call ocamlfind list from a shell to have the list of installed packages).

EXT is used for syntax extensions. You can also declare extensions on a per-session basis with :MerlinExtEnable and :MerlinExtDisable again followed by a name (completion is once more provided).

Troubleshooting

If merlin doesn't seem to work for you, here are a few things you can do to understand what's going wrong :

Ensure merlin (the executable, named "ocamlmerlin") is indeed running, and if it's not make sure that the merlin plugin is loaded. You can use :scriptnames to have a list of the loaded script files.
If it's not, then check that it indeed is in vim's path (:set rtp?)

Check that vim is compiled with python support (vim --version should have +python).

Check that filetype plugins are loaded: :filetype should have plugin:ON in its output. If they aren't, add filetype plugin on to your .vimrc.

That should be enough to ensure that merlin is indeed running (or to identify the problem, if it wasn't).