I recently managed to set up my Emacs environment to work with
haskell-language-server. I took me a while to get this working and come up with a workflow. Since, it was not that straight forward to get this working, I have decided to share some of the nuances I learned during this process in this post.
Ideally, it would be nice if this kind of set up worked out of the box. But, we are not there yet. Therefore, it helps to understand some of the plumbing work going on below so that we can make sense of why something is not working and find our solution.
In this post, I discuss setting up
haskell-language-server or also referred to as
hls. For readers, who have been following recent development in this area, might be aware that this is an effort to bring together various
IDE related tools (
hie) into one integrated experience. In short, this blog talks about
haskell-language-server setup and not
lsp-haskell is the Emacs mode that supports talking to three different tools currently, that currently emulate a language-server. Currently,
haskell-lsp supports three of these tools:
ghcide. We will focus on making this tool working with
haskell-language-server. (Note: The latest version oflsp-haskell
, seems to have replaced this with a custom variablelsp-haskell-server-path
) and the default value is set tohaskell-language-server-wrapper`).
I will assume you are on version of
Linux and using
stack for the rest of the blog post. The concepts described below applies to
cabal setup as well.
As we go through this steps, I try to explain why we perform these steps and what is happening in the background. Knowing this allows us to debug the problems we face in the diverse environments these setup steps are designed to support.
Setting up haskell-language server
You should pretty much follow the instructions for this from (
README has lot more information for different scenarios. Let’s distill this to what we need.
git clone https://github.com/haskell/haskell-language-server --recurse-submodules cd haskell-language-server
Then, comes the build step.
stack ./install.hs hls # to install for latest available GHC version
But, this step is usually not sufficient. The
ghcide under the hood. If you tried to use the version of
haskell-language-server which does not match the version of
GHC your current project is using, then the
haskell-language-server would either not load successfully or partially load leading to all kinds of confusion.
Therefore, I suggest, based on your projects
GHC version dependencies, you can choose to build multiple versions of
haskell-language-server using the supported
GHC versions. You can do this as follows:
stack ./install.hs hls-8.8.3
Tip: Usually, I have multiple version of
haskell-language-server as I have projects using different GHC versions at any point in time.
# Here are some version that are supported, as of this writing stack ./install.hs --help ... - hls-8.10.1 - hls-8.10.2 - hls-8.6.4 - hls-8.6.5 - hls-8.8.2 - hls-8.8.3 - hls-8.8.4 - latest
This step installs, different versions of the
haskell-language-server into your
.local/bin folder (on Linux version I am on). Here is what I have on my machine:
/home/foouser/.local/bin/haskell-language-server /home/foouser/.local/bin/haskell-language-server-8.10 /home/foouser/.local/bin/haskell-language-server-8.10.1 /home/foouser/.local/bin/haskell-language-server-8.10.2 /home/foouser/.local/bin/haskell-language-server-8.6 /home/foouser/.local/bin/haskell-language-server-8.6.5 /home/foouser/.local/bin/haskell-language-server-8.8 /home/foouser/.local/bin/haskell-language-server-8.8.3 /home/foouser/.local/bin/haskell-language-server-wrapper
This completes our set up of
haskell-language-server. What we want to take away from this step, is the need for different
haskell-language-server versions to successfully work with the
lsp client. Note, that later versions of language server report an error to the
lsp-client when the
ghcide versions don’t match. But, the earlier version may not report this which could lead to IDE environment not working as expected.
haskell-language-server with the
GHC API using
This is an important step that I had glossed over initially and paid a dear 2-hours figuring out what was broken.
haskell-language-server uses this information to set up a GHC API session. Its sufficient to understand this we need to generate some file-discovery information that
hls can use while interacting with
GHC API. This is required, since
hls uses the
GHC API for all its parsing needs. This is a way to ensure
hls can keep up with changes to
GHC rather than having its own implementation.
As of this writing, sometime the errors you get running
hls with this step are not intuitive. You may notice the
IDE integration works but does not work fully. If you notices any such behavior one of the potential issues, is that you have not performed this step.
So, here is what you need to do at the very least. You can
git clone https://github.com/Avi-D-coder/implicit-hie.git cd implicit-hie stack build
Identify the executable, this step creates and add it to your local path if required. Now, from the project folder you are on, run
cd `to-your-project` gen-hie > hie.yaml
This step will create an
hie.yaml file which automatically discovers the metadata information that is required by the
hls to load your project successfully. The
hie.yaml file is used by
For advanced configuration of
hie-bios files, you can check out instructions at hie-bios.
Once, you have a
hie.yaml file set up in your project folder, we can move on to the next step.
Also, note that there are efforts to make this step automatic.
Time to check if
hie.yaml loads the project
Before, we use our Emacs, it is helpful to run the
hls server from the project folder and make sure the server loads without any errors. Therefore, let’s do the following
# cd into the project folder. # make sure you have the generated `hie.yaml` file in that folder. # run hls haskell-language-server-wrapper
If we have performed all the steps so far and if it has worked, then you will see a bunch of information rendered by this command. You will also notice, that the
haskell-language-server-wrapper was also able to determine the versions of
GHC and then also the corresponding
ghcide appropriately. Here is a small section you will observe:
Tool versions found on the $PATH cabal: 18.104.22.168 stack: 2.3.1 ghc: 8.8.3 Consulting the cradle to get project GHC version... Project GHC version: 8.8.3 haskell-language-server exe candidates: ["haskell-language-server-8.8.3","haskell-language-server-8.8","haskell-language-server"]
Now, say, if you or the
lsp-client were to run a different version of
hls, then you are likely to see this message as part of the printed output.
Severity: DsError Message: ghcide compiled against GHC 8.10.2 but currently using 8.8.3 This is unsupported, ghcide must be compiled with the same GHC version as the project.
It is good to keep this in mind, if you plan on updating the
lsp-haskell-server-path variable in the next step.
Setting up Emacs
As of this writing, I am using
lsp-haskell 20200924.1508. You can view this using the
list-packages interactive command in Emacs. I think the
lsp-haskell authors have stream-lined a lot of issues dealing with
haskell-language-server version incompatibility issues. I really appreciate the work which has gone into this since the last release. I recommend you upgrade your
lsp-haskell to at least this version. This version now supports
haskell-language-server-wrapper out of the box. (The earlier versions had the default command set to
hie, with options to switch to
hie). As, I suggested earlier, since there is a coordinated effort to bring all of this together, this switch makes perfect sense.
Once you have this step completed, ie. lsp-haskell package installed. Start your Emacs from the project folder and you have a working version of fully integrated IDE.
You might want to read up some some nice
lsp commands you might want to use from
lsp-ui. You might also want to read a little about how Emacs
lsp-mode add/organizes lsp projects.
Here are some demonstrations on capabilities I am currently able to use with my
- Munihac 2019: Making a Haskell IDE