How to properly configure the editor for someone using both ReasonML installed from the reason-cli (which is the recommended way to install reason) and ocaml from opam? This question arries 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/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 globaly. 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.
Once we have installed reason-cli globally using yarn, we will have
the 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 globaly for the current
shell. This can be done with export
. But it is even easier to use a
small utilitary like direnv
.
direnv
behavior is pretty simple. It will look for a .envrc
file
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
environment.
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
removed.
For a project using reason-cli (targeting javascript by using bucklescript), the configuration is not much longer:
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
the .envrc
in the directory of our project and it is
legitimate. This is only needed when we create a new .envrc
.
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 actualy the merlin binary we want to use.
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
emacs
, code
, vim
, atom
or any other command corresponding to
wer favorite editor. That’s all.
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
of reason-mode
more robust. For someone using use-package
, it
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
disavantage. As soon as we will jump to a file which is not in a
subdirectory of the .envrc
file, merlin will not be in the path. If
we use merlin-locate
on a function or value comming 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
subdirectories or .envrc
.