The Haskell tooling dream is near, we need your help!
How to contact the haskell ide team
Join our IRC channel at
Follow the Haskell IDE team twitter account for updates and help.
Join the #haskell-tooling channel in the Functional Programming discord server. You can join the server via this invitation.
Join the haskell-tooling channel in matrix.
Visit the project GitHub repo to view the source code, or open issues or pull requests.
Clone the repository:
$ git clone https://github.com/haskell/haskell-language-server
The project can then be built with both
cabal build and
# If you have not run `cabal update` in a while $ cabal update # Then $ cabal build
$ stack build
The instructions below show how to set up a Cachix binary cache and open a nix shell for local development.
$ cachix use haskell-language-server $ nix-shell $ cabal update $ cabal build
If you are using nix 2.4 style command (enabled by
experimental-features = nix-command),
you can use
nix develop instead of
nix-shell to enter the development shell. To enter the shell with specific GHC versions:
nix develop .#haskell-language-server-dev- default GHC version
nix develop .#haskell-language-server-901-dev- GHC 9.0.1 (substitute GHC version as appropriate)
If you are looking for a Nix expression to create haskell-language-server binaries, see https://github.com/haskell/haskell-language-server/issues/122
To create binaries:
nix build .#haskell-language-server- default GHC version
nix build .#haskell-language-server-901- GHC 9.0.1 (substitute GHC version as appropriate)
The tests make use of the Tasty test framework.
There are two test suites in the main haskell-language-server package, functional tests, and wrapper tests.
Some of the wrapper tests expect
stack to be present on the system, or else they fail.
Other project packages, like the core library or plugins, can have their own test suite.
Testing with Cabal
Running all the tests
$ cabal test
Running just the functional tests
$ cabal test func-test
Running just the wrapper tests
$ cabal test wrapper-test
Running a subset of tests
Tasty supports providing Patterns as command line arguments, to select the specific tests to run.
$ cabal test func-test --test-option "-p hlint"
The above recompiles everything every time you use a different test option though.
An alternative, which only recompiles when tests (or dependencies) change:
$ cabal run haskell-language-server:func-test -- -p "hlint enables"
Using HLS on HLS code
Project source code should load without
In other cases:
hie.yml) files left from previous configurations.
If the main project needs special configuration, note that other internal subprojects probably also would need configuration.
To create an explicit configuration for all projects - use implicit-hie generator directly:
gen-hie > hie.yaml # into the main HLS directory
that configuration should help.
Inspect & tune configuration explicitly.
Configuring project build applies to HLS project source code loading just as to any other.
Note: HLS may implicitly detect codebase as a Stack project (see hie-bios implicit configuration documentation). To use Cabal, try creating an
Manually testing your hacked HLS
If you want to test HLS while hacking on it, follow the steps below.
To do once:
Open some codebase on which you want to test your hacked HLS in your favorite editor (it can also be HLS codebase itself: see previous section for configuration)
Configure this editor to use your custom HLS executable
On Unix systems:
cabal exec which haskell-language-server
cabal exec where haskell-language-server
$(stack path --dist-dir)/build/haskell-language-server/haskell-language-server
To do every time you change HLS code and want to test it:
cabal build exe:haskell-language-server
stack build haskell-language-server:exe:haskell-language-server
With VS Code:
Haskell: Restart Haskell LSP Server
The project includes a
.editorconfig file with the editor basic settings used by the project.
However, most editors will need some action to honour those settings automatically.
For example vscode needs to have installed a specific extension.
Please, try to follow those basic settings to keep the codebase as uniform as possible.
Formatter pre-commit hook
We are using pre-commit to configure git pre-commit hook for formatting. Although it is possible to run formatting manually, we recommend you to use it to set pre-commit hook as our CI checks pre-commit hook is applied or not.
If you are using Nix or Gitpod, pre-commit hook is automatically installed. Otherwise, follow instructions on
https://pre-commit.com/ to install the
pre-commit tool, then run the following command:
Why some components are excluded from automatic formatting?
test/dataare there as we want to test formatting plugins.
hie-compatis there as we want to keep its code as close to GHC as possible.
hls-tactics-pluginis there as the main contributor of the plugin (@isovector) does not want auto-formatting.
See the tutorial on writing a plugin in HLS.
Measuring, benchmarking and tracing
When ghcide is built with the
ekg flag, HLS opens a metrics server on port 8999 exposing GC and ghcide metrics. The ghcide metrics currently exposed are:
ghcide.values_count- count of build results in the store
ghcide.database_count- count of build keys in the store (these two would be the same in the absence of GC)
ghcide.build_count- build count. A key is GC’ed if it is dirty and older than 100 builds
ghcide.dirty_keys_count- non transitive count of dirty build keys
ghcide.indexing_pending_count- count of items in the indexing queue
ghcide.exports_map_count- count of identifiers in the exports map.
If you are touching performance sensitive code, take the time to run a differential benchmark between HEAD and master using the benchHist script. This assumes that “master” points to the upstream master.
Run the benchmarks with
It should take around 25 minutes and the results will be stored in the
bench-results folder. To interpret the results, see the comments in the
More details in bench/README
HLS records opentelemetry eventlog traces via opentelemetry. To generate the traces, build with
-eventlog and run with
+RTS -l. To visualize the traces, install Tracy and use eventlog-to-tracy to open the generated eventlog.
Adding support for a new editor
Adding support for new editors is fairly easy if the editor already has good support for generic LSP-based extensions.
In that case, there will likely be an editor-specific support system for this (like
lsp-mode for Emacs).
This will typically provide instructions for how to support new languages.
In some cases you may need to write a small bit of additional client support, or expose a way for the user to set the server’s configuration options and for them to configure how the server is started.
Building the docs
The docs are built with Sphinx and ReadTheDocs, the documentation for both is helpful.
To build the docs you need to install some Python prerequisites. You can either
pip install -r docs/requirements.txt, or simply enter a
Then to build and preview the docs:
cd docs make html firefox _build/html/index.html
Alternatively, you can build the entire thing as a Nix derivation from the flake with
nix build .#docs.
The docs are also built and previewed on every PR, so you can check them from the PR status.
Working on code actions
To make HLS easier to maintain, please follow these design guidelines when adding or modifying code actions:
ghc-exactprintto manual text parsing.
ghc-exactprintto manual code generation.
Code generating actions should not try to format the generated code. Assume that the user is also leveraging HLS for automated code formatting.
Put new code actions in their own plugin unless they are very closely aligned with an existing ghcide code action.
If you want to contribute financially you can do so via open-collective. In the past the funding has been used to sponsor summer student projects.