How to use the NeoVim text editor as your Ocaml IDE
Why OCaml?
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.
If you want to know more about OCaml, read the short article Why OCaml?.
In this article, I will show you how to integrate OCaml with NeoVim.
What is NeoVim?
NeoVim is a fork of the terminal-based Vim text editor.
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 by Jeff Kreeftmeijer.
Install a plugin loader for convenience, for example, 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
Merlin is the de-facto code helper for OCaml. If you'd like to use Merlin with Neovim, you need Python and 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. Be aware that those might not 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>
.
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, as it works with every language server I've tried.
Install with your Vim plugin manager, e.g.:
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):
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. ocaml-lsp is a community project written in OCaml that's in active development.
See the installation instructions for further details.
I use opam:
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, you have to change the setup for LanguageClient-neovim 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
Install esy with npm
, the Node.js package manager:
npm install -g esy
Now, you'll need a Vim plugin: vim-reasonml. Install it with your plugin manager. For example, with minpac:
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 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):
call minpac#add('dense-analysis/ale')
Example setup 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 can use ocamlformat for parsing our code.
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 by Marty Stumpf
Top comments (7)
Good guide. Btw for the esy setup you don't need opam and
opam install merlin
because as you mentioned if you start the editor with esy it will automatically set up the merlin that's in your project'sdevDeps
.Thanks. That's good to know. I will update the guide.
Can you enumerate the features it does have? Last time I tried to use merlin with native ocaml it was hard to jump to source, for example to
core
, it was all red with errors and it only worked when the source was compilable, as soon as the source file didn't compile, there were no type inspections, no go to source etc. I'm eager to see experience of others in this areaJump to definition/source works for me. Also auto-completion.
But I had problems with Reason code where Merlin tends to interfere.
I've switched to the reason language server for now. But for ocaml it doesn't work that great.
Esy is an alternative.
Going in I had no idea what any of the words in your title meant, now...I am enlightened ☺
🔥!
Thanks Rohan.
Good point. I didn't mention that NeoVim is an editor (a Vim spin-off). I'll add it.
help vim-bootstrap project :D
vim-bootstrap.com/
github.com/editor-bootstrap/vim-bo...