How to properly configure the editor for someone using both ReasonML installed from the reason-cli and OCaml from opam? This question arises when we are doing both native and bucklescript development.
First thing to remember is that all the editor plugins that really need configuration are based on merlin (as far as I know). More precisely on the ocamlmerlin binary. And they will either take the first ocamlmerlin binary they will find in the path, or the binary from the currently selected opam switch.
The configuration presented bellow allows us to properly configure our environment prior to launch Emacs (with tuareg or reason-mode), vscode, vim, … Once this is done, we can launch our editor from the shell with the expected environment and it will pickup the good version of merlin. In theory it is also possible to launch our editor from our OS launcher rather than from the editor, but then we have to be sure to load the good environment globally. And it doesn't correspond to the usual workflow when mixing native and bucklescript development. So I will not try to get to this point.
I am using yarn rather than npm because I am more familiar with it and the global installation system seems easier to manage. But I guess it is possible to reach the same state with npm.
Also I am using opam2 because it provides nice new features over opam1 and is already very stable.
Putting merlin in the environment
Once we have installed reason-cli globally using yarn, we will have
ocamlmerlin binary in a path like
$HOME/.yarn/bin. We can get
this path from the command
yarn global bin.
For opam, the binary will be in
$HOME/.opam/SWITCH/bin. And this
can be added to our path by calling
eval $(opam env --sw=SWITCH).
So what we want to do is just to add one of those two paths to the environment and then launch our editor.
By hand in bash, it would be something like this for yarn:
PATH="$(yarn global bin):$PATH" emacs
And like this for opam:
PATH="$(opam env --switch=SWITCH):$PATH" emacs
But it is troublesome to do this every time we want to launch our editor. It is actually very troublesome because we have to prefix all the commands that are using binaries from yarn or opam.
So we want to update our environment globally for the current
shell. This can be done with
export. But it is even easier to use a
small utility like direnv.
Automatic environment setup using direnv
direnv behavior is pretty simple. It will look for a
in the current directory or any of its parents. This
.envrc file is
actually a bash script that is sourced by direnv once found. Every
time we change directory, it will look for a
.envrc and update our
The configuration of direnv for each shell is a bit different. I will not detail it here. It is better to check the direnv documentation.
The important parts are what to put in the
.envrc file and where to
put this file.
The answer to where is at the root of our project.
And the content of the file is actually very short.
For a project using opam (so targeting native or bytecode compilation):
eval $(opam env --shell=bash --inplace-path --set-switch --readonly --switch=SWITCH)
If we are using a local switch, the
--switch=SWITCH part can be
PATH_add $(yarn global bin)
PATH_add is a function provided by direnv.
Then we need to execute
direnv allow to tell direnv we know about
.envrc in the directory of our project and it is
legitimate. This is only needed when we create a new
Once all this is done, our
.envrc should be automatically used by
direnv. We can check if it works by executing
which ocamlmerlin and
making sure this is actually the merlin binary we want to use.
Launching the editor
We are now ready to launch the editor. To do so, we need to be in the
project directory so that direnv does it jobs. And then execute
atom or any other command corresponding to
our favorite editor. That's all.
Using direnv allows us to use reason-cli or opam for a project. Or even both. But it also has other benefits. One of them very valuable is to reuse the same switch for multiple directories. This is very convenient while using git worktree.
Note concerning emacs
I am not sure how all the editors plugins are working. But for Emacs
by default the merlin package will try to look for the ocamlmerlin
binary in opam. Even if the environment has not been populated by
opam env. This behavior is not the one expected here. It would take
merlin from opam even if we added the reason-cli's merlin to our
path. It is not hard to change this behavior though. Just add this
line to Emacs configuration:
(setq merlin-command "ocamlmerlin")
The whole Emacs configuration I am using for reason and OCaml is
available in this GitHub repository. It also makes the configuration
reason-mode more robust. For someone using
should automatically install everything that it required.
There is a direnv package for Emacs that could allow us to skip
configuration of the environment prior to launch Emacs. But using this
plugin rather than setting up the environment first has a big
disadvantage. As soon as we will jump to a file which is not in a
sub-directory of the
.envrc file, merlin will not be in the path. If
merlin-locate on a function or value coming from a library
installed by opam it will work. But it is not possible to use merlin
in the file we landed in.
Note that this problem doesn't exist while using yarn or local
switches with opam because all the installed libraries will be in
- Install direnv
- Go to the root of the project
If using reason-cli, execute
echo 'PATH_add $(yarn global bin)' | tee .envrc
If using opam, execute
echo 'eval $(opam env --shell=bash --inplace-path --set-switch --readonly --switch=SWITCH)' | tee .envrc
- Launch editor