Haskell 'go to (non-local)* definitions' VS Code extension
This repository contains a VS Code extension and a Haskell server that implements a 'go to definition' command for Haskell code (for both local and non-local (remote libraries, local cabal.project
dependencies)) definitions.
* "non-local" - check this issue. HLS does not support 'go to definition' for non-local symbols (like base
, containers
, etc.). This extension attempts to fill this gap (hopefully, temporarily).
The extension:
- supports only
*.cabal
-based projects,
- supports multi-folder VS Code workspaces, as long as either
*.cabal
or cabal.project
files are present in the folders' roots,
- is likely to work only on Linux (it was not tested on other platforms).
How to install
- Install the VS Code extension (through the
*.vsix
or the marketplace).
- Clone this repository. Execute
cabal install
in its root directory.
git clone https://github.com/kr3v/haskell-gtd-nl
cd haskell-gtd-nl
cabal update
cabal install
- The extension should activate when a Haskell file gets opened.
Notes:
- on Ubuntu22, I had to install
zlib1g-dev
package for cabal install
to succeed;
- on Fedora 34,
zlib-devel
should be installed.
The first attempt to perform a go to definition
action will take time (to fetch all the dependencies, to parse & cache them), yet consequent attempts should take much less time.
When you save a Haskell file, the extension will re-parse it, its module and all the modules that depend on it. In case you save a Cabal file, then all the directory cache is dropped.
The re-parsing process is quite fast, in my experience it takes up to a few seconds.
Overall, the memory usage should be low:
- ~100-150 MiB when serving after initialization,
- ~4 GiB during initialization / after a cache drop, but tunable.
By default, the --dynamic-memory-usage
parameter is enabled for the server.
If there are less than 8/4/2 GiB of RAM available, the parsing process -A
parameters takes values of 64/16/4 M
instead of the default -A128M
. The intention here is to reduce memory usage when there are not much memory available by sacrificing performance (which is heavily dependent on the GC and RAM performance).
HLS
VS Code extension haskell-gtd-nl.extension.disable-local-definitions-when-hls-is-active
setting
In case HLS is enabled and is actually working (i.e. it provides 'go-to definitions' for local symbols), the default extension behavior is to not provide such definitions. This extension can produce local definitions, but they are different from the HLS' ones, i.e. you might see two different definitions for the same symbol. This is a UX-only issue, both definitions are correct, but it might be confusing.
In case you want to see local definitions even when HLS is active, you can set haskell-gtd-nl.extension.disable-local-definitions-when-hls-is-active
to false
in VS Code extension settings.
This is an extension-only setting, the server does not know about it.
The extension server stores its info at ~/.local/share/haskell-gtd-nl
directory. Cabal packages are cloned into repos
directory at the extension root via cabal get
.
repos
directory is added to a working directory root as a .repos
symlink and all the extension accesses to repos
directory happen through this symlink. This allows to avoid having multiple hls
instances when multiple directories are opened with VS Code.
In other words, when you go to definition
on a return
symbol (from Prelude.Monad
), the server would clone a proper base
package into ~/.local/share/haskell-gtd-nl/repos
directory, yet the extension would access it through <project root>/.repos
symlink (opening something like <project root>/.repos/base-<version>/src/GHC/Base.hs
file).
If the extension was to open the repos
directory directly, then hls
would create a separate instance for each opened directory (which is not what we want).
Why not implement this feature in HLS?
The main concern was that I was not sure if an in-HLS implementation I could come up with would be accepted by the HLS team. As far as I know, there are 2 PRs that implement similar functionality, but one was not accepted and the other one is in review (and it was created after I started working on this extension).
I do not want my extension to replace HLS, I want it to be a solution until HLS gets the same functionality.
The other concern is that HLS does not support certain projects (HLS does not work in base
; HLS does not 'see' cabal.project
configuration). My initial desire was the ability to browse the base
package.