Making the Haskell Language Server extension for Visual Studio Code work under Flatpak

1 March 2020

The Haskell Language Server Client is a project, slowly chugging along since 2016, which aims to add Haskell IDE-like support to Visual Studio Code (VSCode), Microsoft's open source text-editor-cum-IDE.

By the way, if you haven't tried VSCode yet, you should – you can use it as a simple text editor with project support (via the File -> Open Folder command), so there's very little barrier to entry. There's also very little barrier to entry in terms of developing extensions for it, as it's written in JavaScript (it's a wrapped-browser style application based on electron, which in turn is based on Chromium), and many extensions are available for it.

Anyway, back to the Haskell Language Server Client. It relies upon the Haskell IDE Engine (HIE) – which is in the process of merging with the GHCIDE project to form the Haskell Language Server. HIE/HLS looks like it might become the new standard backend for next-generation Haskell IDE implementations. HIE is effectively a user service/daemon which examines Haskell projects and exposes a dynamic view of them via a cross-language, industry open standard called Language Server Protocol (LSP) for IDE and text editor plugins to use, and is already used by a bunch of IDE and text editor plugins in addition to the VSCode one. This should be good news for Haskell IDE efforts – of which there have been many over the years – as at least the backend work for those that choose to use HIE should be centralised in one project (HIE), avoiding needless duplication of effort.

Running HIE under Flatpak

I wanted to try it out on Linux, but VSCode is not available in the official Fedora Linux package repositories, so I chose to install it using flatpak instead, as suggested on the Fedora wiki:

flatpak install --user flathub com.visualstudio.code.oss

Unfortunately, my Linux VM ran out of disk space on its home partition when trying to install it. And even after re-running the install command above again, the disk full problem had seemingly caused flatpak to corrupt the install of VSCode or one of its dependencies, so I then had to force-reinstall it:

flatpak install --user --reinstall flathub com.visualstudio.code.oss

Running it (on Qubes, a highly-secure hypervisor/meta-OS for my desktop Fedora VMs which doesn't provide a desktop for each VM but rather a unified desktop for all of them) was a bit tricky, but this turned out to be the necessary incantation:

flatpak run --user --branch=stable com.visualstudio.code.oss

However, after installing the extension, initially, the extension didn't work, as it wasn't able to start HIE, even though I had followed the instructions and successfully installed HIE first.

The reason why soon became apparent. Flatpaks are run in a containerised environment – a bit like Docker for Linux desktop applications – and although the default permissions for the VSCode flatpak are quite permissive, it's still not quite like running VSCode directly on the host system, i.e. in a non-containerised way.

In particular, launching a terminal from VSCode doesn't work as you might expect, because by default none of the software installed on the host system is on the PATH, only VSCode itself and whatever basic command-line tools the flatpak maintainer has made available within the container. However, launching a terminal from within VSCode was a good way to see more clearly what was going on within the container.

The solution turned out to be relatively straightforward. I used a combination of the two techniques from this StackExchange answer. The extension already had a way to set a custom wrapper script for starting HIE in its preferences, so I created such a wrapper script which invoked the flatpak-spawn command to escape from the flatpak container to run the copy of hie that stack had installed at ~/.local/bin/hie:

#! /bin/bash
exec flatpak-spawn --forward-fd=1 --host --watch-bus --env=BASH_ENV=~/.bashrc bash -c "~/.local/bin/hie $*"

and saved it (as root) as /usr/local/bin/hie-wrapper and marked it as executable (sudo chmod a+x /usr/local/bin/hie-wrapper). I could have equally well saved it under my home directory, I just chose to do it this way.

The --env=BASH_ENV=~/.bashrc part forces bash to reload .bashrc, so that the PATH variable will be set correctly and therefore the correct (newer) version of stack will be found on the PATH. I actually have two versions of stack installed – one in /usr/bin which was installed from a system package, which is too old to build some projects, and one in ~/.local/bin, which was installed manually. You might not need that part of the script, but it's unlikely to hurt.

Then, in the preferences for the extension (which are not be found on the extension's page, as it's done in Chromium, but in the VSCode settings, as it's done in most other applications), I set the wrapper script path to the path that this file is visible from:

Setting the wrapper script preference to /var/run/host/usr/local/bin/hie-wrapper

Fortunately the necessary filesystem mapping (a bind mount or something like that) is already set up by default for the VSCode flatpak, so I didn't have to set that up.

Comments