Can't make HIE work on VS Code with Nix

Hello, I am a beginner in Haskell and Nix, and I am trying desperately to setup a development environment.
My editor of choice is VS Code for multiple reasons. I am trying to use HIE.
My idea is to make a nix-shell with my Haskell development environment and launch VS Code from nix-shell in order to keep my multiple projects separated.
I came up with this shell.nix :

{ pkgs ? import <nixpkgs> { } }:

let
  haskell-env = pkgs.haskellPackages.ghcWithHoogle (hp: with hp; [
    gloss # example
  ]);
 
 all-hies = import (fetchTarball "https://github.com/infinisil/all-hies/tarball/master") {};
 hie = (all-hies.selection { selector = p: { inherit (p) ghc865; }; });
 
in
  pkgs.mkShell {
    buildInputs = [
      haskell-env
      hie
    ];
  }

But when I launch VS Code with nix-shell --run "code .", the HIE plugin gives me two errors:

  • my modules are not found (here import Graphics.Gloss is underlined like an error)
  • “No hoogle db found. Check the README for instructions to generate one”

I’ve been on many blogposts / github issues / …, but I can’t solve it. Has someone ever gotten this problem ?

1 Like

consider using Nix Environment Selector - Visual Studio Marketplace instead

Thx for the suggestion. I get the same errors with nix-env-selector.
I think there is an issue in my shell.nix config…

I’m very much an ignorant Nix user, but I’ve organised my Nix use differently from you, I think.

In each project folder I have both a default.nix and a shell.nix. The default.nix is only for building the project’s various outputs (in my case usually a Haskell binary and a Docker image). The shell.nix is then rather simple to setup, usually something like

with (import <nixpkgs> {});

let
  def = import ./default.nix;

  shell-pkgs = with haskellPackages;
    [cabal-install
     ghcid
     hlint
    ];

in
def.pkg.overrideAttrs (attrs: {
  src = null;
  buildInputs = shell-pkgs ++ attrs.buildInputs;
})

AFAIU that uses mkShell under the bonnet and I can rather easily add in any tools I want to use, manually from shell prompt or from within my editor of choice, to the list of buildInputs.

As I said, I’m new to Nix, but this approach seems to work, and I find it fairly straight forward. Maybe it could simplify things in your setup. I’m always eager to learn, so if I’m completely off about this, please tell me why.

Hi, thanks for sharing! Unfortunately my issue is that HIE does not find the hoogle db generated by Nix.

I investigated, the folder in which hoogle db seems to be generated is stored in $NIX_GHC_DOCDIR.

This would have been helpful to set HIE_HOOGLE_DATABASE, as per https://github.com/haskell/haskell-ide-engine/blob/master/README.md#docs-on-hovercompletion.

However the variable is not set correctly for Hoogle: ${ghcEnv}/share/doc/ghc/html instead of ${ghcEnv}/share/doc/hoogle/html

I’ve opened a issue regarding this. NIX_GHC_DOCDIR not set properly · Issue #76837 · NixOS/nixpkgs · GitHub

In the meantime, if anyone has a workaround I’d be glad: I don’t know how to overwrite things that have been set with let variables…

EDIT: I have set HIE_HOOGLE_DATABASE manually to the right default.hoo in the nix store, HIE finds the hoogle db ; IT WORKS !

My current workaround to make HIE find the hoogle db:

{ pkgs ? import <nixpkgs> { } }:

let
  all-hies = import (fetchTarball "https://github.com/infinisil/all-hies/tarball/master") {};
  hie = all-hies.selection { selector = p: { inherit (p) ghc865; }; };

in
  (pkgs.haskellPackages.shellFor {
    buildInputs = with pkgs; [
      hie
      cabal-install
      stack
    ];

    ...

    withHoogle = true;
  }).overrideAttrs (oldAttrs: rec {
    NIX_GHC_DOCDIR =
      let
        # helper functions
        reverse = builtins.sort (e1: e2: true);
        all-but-last = l: reverse (builtins.tail (reverse l));
        # first element is empty string after split, last element is "html" to remove
        path = all-but-last (builtins.tail (pkgs.lib.strings.splitString "/" oldAttrs.NIX_GHC_DOCDIR)); 
        correctedPath = builtins.map (s: if s == "ghc" then "hoogle" else s) path;
      in
        pkgs.lib.strings.concatMapStrings (s: "/${s}") correctedPath;

    HIE_HOOGLE_DATABASE = NIX_GHC_DOCDIR + "/default.hoo";
  })

You did mention you had two issues:

Did the first of these get solved at the same time?

You are right, my bad! Sorry. I was so focused on the hoogle issue that I forgot the former, haha

I’ve completely changed how I do my setup, because I wanted as little repetition as possible (keep everything DRY).

I now use callCabal2nix in order to load the project from my cabal file, so that I don’t need to specify the packages again in my nix file.

It does not work yet, VS Code HIE extension has an issue with cabal-helper-wrapper, but I think I’ve reached something interesting :slight_smile:

My ./from-cabal.nix:

{ haskellPackages ? (import <nixpkgs> { }).haskellPackages }:

haskellPackages.callCabal2nix "my-project" ./.

Then ./default.nix for classic build:

let
  pkgs = import <nixpkgs> { };
in
  pkgs.haskellPackages.callPackage (import ./from-cabal.nix) {}

And now the big boy (yet to fix), ./shell.nix:

let
  pkgs = import <nixpkgs> { };

  all-hies = import (fetchTarball "https://github.com/infinisil/all-hies/tarball/master") {};
  hie = all-hies.selection { selector = p: { inherit (p) ghc865; }; };

  haskellPackages-withHoogle = (pkgs.haskellPackages.override {
    overrides = (self: super:
      {
        ghc = super.ghc // { withPackages = super.ghc.withHoogle; };
        ghcWithPackages = self.ghc.withPackages;
      }
    );
  });

  project-from-cabal = import ./from-cabal.nix { haskellPackages = haskellPackages-withHoogle; };
in
  (haskellPackages-withHoogle.callPackage project-from-cabal {}).env.overrideAttrs (oldAttrs: rec {
    buildInputs = oldAttrs.buildInputs ++ ([
      # IDE packages
      hie
      pkgs.cabal-install
    ]);

    NIX_GHC_DOCDIR =
      let
        # helper functions
        reverse = builtins.sort (e1: e2: true);
        all-but-last = l: reverse (builtins.tail (reverse l));
        # first element is empty string after split, last element is "html" to remove
        path = all-but-last (builtins.tail (pkgs.lib.strings.splitString "/" oldAttrs.NIX_GHC_DOCDIR)); 
        correctedPath = builtins.map (s: if s == "ghc" then "hoogle" else s) path;
      in
        pkgs.lib.strings.concatMapStrings (s: "/${s}") correctedPath;

    HIE_HOOGLE_DATABASE = NIX_GHC_DOCDIR + "/default.hoo";
  })

I am really close to it working… but I get an error from VS Code HIE.
I ran this command in nix-shell to have more information about the error:

[nix-shell:~/projects/deep_space_fights]$ /nix/store/7xir5b0fvblbrpyyh9zwgl055jsn72g5-cabal-helper-0.9.0.0/bin/cabal-helper-wrapper "--with-ghc=ghc" "--with-ghc-pkg=ghc-pkg" "--with-cabal=cabal" "v1-style" "/home/arsleust/projects/deep_space_fights" "/home/arsleust/projects/deep_space_fights/dist-newstyle/build/x86_64-linux/ghc-8.6.5/deep-space-fights-0.1.0.0" "package-db-stack" "flags" "compiler-version" "ghc-merged-pkg-options" "config-flags" "non-default-config-flags" "ghc-src-options" "ghc-pkg-options" "ghc-lang-options" "ghc-options" "source-dirs" "entrypoints" "needs-build-output"
cabal-helper-wrapper: /home/arsleust/projects/deep_space_fights/dist-newstyle/build/x86_64-linux/ghc-8.6.5/deep-space-fights-0.1.0.0/setup-config: openFile: does not exist (No such file or directory)

Please don’t laugh at the project name, it’s just random haha

Seems that it’s linked to Not working with cabal-2.4.1.0 · Issue #1015 · haskell/haskell-ide-engine · GitHub

So the Nix part is fine, but it’s on the HIE side that something fails.

I’ll dig that later.

I’d recommend setting up GitHub - hercules-ci/ghcide-nix: Nix installation for ghcide as it has much faster feedback loop (for small-mid project size, IDE feedback is immediate).

1 Like

Thanks for the suggestion.

From my reading of the readme, ghcide doesn’t seem to use hoogle documentation on hover like HIE.

Despite this, it is likely a good alternative in my case, especially since I have only small projects…

Got HIE to work a while ago, though it’s been far too long to specifically remember the steps clearly but for your problem it could be resolved through using a custom wrapper relative to each project root and then setting it through the setting for the wrapper file location.

Some files I found off an old project

#! /usr/bin/env nix-shell
#! nix-shell --pure -i bash

hie-wrapper --lsp "$@"
{ nixpkgs ? import ./nix/nixpkgs.nix { }, compiler ? "ghc864" }:

with nixpkgs.pkgs;
with haskell.packages."${compiler}";

let
  hie = (import (builtins.fetchTarball {
    url = "https://github.com/infinisil/all-hies/tarball/master";
  }) { }).versions."${compiler}";

  nix-tools = (import (builtins.fetchTarball {
    url = "https://github.com/input-output-hk/haskell.nix/tarball/master";
  }) { }).nix-tools;

  pkg = (import ./default.nix { }).pkg;
in shellFor {
  withHoogle = true;
  packages = ps: [ hie pkg ];
  nativeBuildInputs = [ hie nix-tools cabal-install ];

  shellHook = ''
    # Ensure HIE can find the hoogle database
    export HIE_HOOGLE_DATABASE="$NIX_GHC_LIBDIR/../../share/doc/hoogle/default.hoo";
  '';
}
1 Like

That will change very soon: Better docs for completions by serras · Pull Request #288 · haskell/ghcide · GitHub

1 Like