obelisk icon indicating copy to clipboard operation
obelisk copied to clipboard

haskell-language-server support

Open dimsmol opened this issue 2 years ago • 11 comments

I'm trying to get haskell-language-server that would work with the old GHC used in GHCJS and transitively by Obelisk. Couldn't find any ready to use option in NixOS - the versions that have GHC 8.6.5 don't have haskell-language-server package, those that have haskell-language-server don't support GHC 8.6.5, though the latest haskell-language-server itself still supports it.

I guess I can get haskell-language-server with shellToolOverrides similarly to how it's done for ghcide. But I'm not sure how the dependency tree was obtained there - was it done manually or with using some tooling?

I'm trying to do something similar manually, but hope there is some simpler path.

dimsmol avatar Sep 13 '22 06:09 dimsmol

We have a fork of obelisk on ghc 8.6.5 with HLS support. It requires forking reflex-platform to build HLS against the GHC it uses and pinning obelisk to use that reflex-platform.

We're using this commit: https://github.com/BeFunctional/obelisk/commit/e6db3beea4f8fd4195257ca472d22a23077aae89

It would be great to see this upstreamed. HLS is a major productivity boost, especially for those who do not have much experience with obelisk / reflex-dom

o1lo01ol1o avatar Sep 13 '22 07:09 o1lo01ol1o

I agree, this should be more prioritised in my opinion.

fendor avatar Sep 13 '22 07:09 fendor

This is how I do it in my default.nix:

let
  pkgs20_09 = import (pkgs.fetchFromGitHub {
    owner = "NixOS";
    repo = "nixpkgs";
    rev = "20.09";
    sha256 = "1wg61h4gndm3vcprdcg7rc4s1v3jkm5xd7lw8r2f67w502y94gcy";
  }) {};
  # ...
in
  obelisk.project ./. ({ ... }: {
    shellToolOverrides = self: super: {
      inherit (pkgs20_09.haskell.packages.ghc865) haskell-language-server;
    };

This isn't without hick-ups, though. I get

> nix-shell -A shells.ghc
>  haskell-language-server-wrapper --version
haskell-language-server version: 0.4.0.0 (GHC: 8.6.5) (PATH: /nix/store/ql52ly8zyi6nmyms9zzc0mgasbfkl94k-haskell-language-server-0.4.0.0/bin/haskell-language-server-wrapper)

where the current version of haskell language server is 1.7.0.0

rubenmoor avatar Sep 13 '22 14:09 rubenmoor

Ridiculous, there needs to be first class support, recent HLS versions have more than a dozen more features, bug-fixes, and performance improvements!

fendor avatar Sep 13 '22 15:09 fendor

As we improve our ability to keep up with upstream nixpkgs infrastructure, we will be able to put work into sustainably and durably supporting HLS. We are at the last stages of validating cross-platform support for our next version bump (to 21.05, admittedly still very far behind, but subsequent updates will be much faster). Then we can revisit official HLS support.

madeline-os avatar Sep 13 '22 16:09 madeline-os

I am now running my obelisk project with the latest version of haskell language server (1.9.1.0) by building it myself.

I thought it would be trouble, but it turned out to be as easy as

$ nix-shell -A shells.ghc
$ cd ../haskell-language-server
$ cabal install

... and then I configured lsp-haskell (emacs) to use ~/.cabal/bin/haskell-language-server as path. The wrapper doesn't work, don't know why.

In my default.nix I only have

    shellToolOverrides = self: super: {
      inherit (pkgs.haskellPackages) cabal-plan cabal-install;
    };

And the only ghc on my path is the one from obelisk's nix-shell.

I switched to obelisk's development branch and activated GHC8.10.7 via useGHC810 = true;.

rubenmoor avatar Feb 24 '23 15:02 rubenmoor

@rubenmoor thanks for posting your solution, would you be able to give some more detail? I want to try out reflex/obelisk but I have no experience with Nix so I'm finding it quite difficult to get started. I'm attempting to use obelisk + HLS + VS Code.

  • How can I install the development version of obelisk?
  • Where do I put the line useGHC810 = true;?
  • Where do I put the line shellToolOverrides = ...? Is this something specific to obelisk or is it a Nix thing?

Thanks!

benrbray avatar Mar 12 '23 09:03 benrbray

How can I install the development version of obelisk?

You have started an obelisk project with ob init? In there you find the fold .obelisk/impl with the file github.json. This is my file:

{
    "owner": "rubenmoor",
    "repo": "obelisk",
    "branch": "development",
    "private": false,
    "rev": "e45b37817c74a199e618804a70480d241666e5e0",
    "sha256": "1xkdzxlc75s5655v2wx2ga8nmhqi9afh1jjwddmjm2k457dcvh4m"
}

"Owner" is rubenmoor because I forked (and did a pull request) the official repo. The rev field is something you select on github, i.e. the commit hash of the obelisk version that you are going to use. I use the tool nix-prefetch-git like this to get the correct entry for the "sha256":

$ nix-prefetch-git [email protected]:obsidiansystems/obelisk

Any call to ob will use the obelisk version selected in the file .obelisk/impl/github.json.

Where do I put the line useGHC810 = true;?

This belongs into default.nix in your obelisk project. Compare my default.nix file:

https://github.com/rubenmoor/learn-palantype/blob/main/default.nix

... right where obelisk is defined

obelisk = (import ./.obelisk/impl {
    inherit system;
    iosSdkVersion = "13.2";
    terms.security.acme.acceptTerms = true;
    useGHC810 = true;
  });

Where do I put the line shellToolOverrides = ...? Is this something specific to obelisk or is it a Nix thing?

Those shouldn't be necessary in your case.

Nix has a notion of pure shells. From within a pure shell, you don't have access to any program just because it's on your path. A pure shell is an isolated environment. So if I want to run cabal or ghc from within a shell, I have to make it available (ghc is made available by obelisk, though).

Even with impure Nix shells, I prefer not to rely on the cabal version that I have installed for my user (and thus available everywhere), but rather I want to be able to specify versions for cabal, ghc and other build tools on a project-by-project basis.

I am not sure, but shellToolOverrides seems obelisk-specific. From nix I know buildTools and nativeBuildInputs and, sorry, I can't tell what there differences are.

So shellToolOverrides seems the obelisk way of explicitly making additional programs available from with in the obelisk shell, which I start with

  • nix-shell -A shells.ghc
  • or ob shell

... but if you have cabal-install installed, you shouldn't need to bother with shellToolOverrides.

Maybe someone else can correct me whereever the information I provide here is inaccurate.

rubenmoor avatar Mar 14 '23 21:03 rubenmoor

@rubenmoor Thanks for your help, unfortunately I'm still not able to resolve the issue.

Starting from a fresh ob init, I changed my ~.obelisk/impl/github.json` to the following:

{
  "owner": "obsidiansystems",
  "repo": "obelisk",
  "branch": "develop",
  "private": false,
  "rev": "1c4b07ec67639356316b610142d3be8d302c07cc",
  "sha256": "04bpzji7y3nz573ib3g6icb56s5zbj4zxpakhqaql33v2v77hi9g"
}

and simplified my default.nix to the following, based on your example:

{ system ? builtins.currentSystem }:
let
  obelisk = (import ./.obelisk/impl {
    inherit system;
    useGHC810 = true;
    terms.security.acme.acceptTerms = false;
  });
in
  with obelisk;
  project ./. ({ ... }: {
    # empty
  })

However, running ob shell fails with an error:

$ ob shell
./.obelisk/impl: command not cached, building ...
DONE Built on ./.obelisk/impl [command]
error: anonymous function at /nix/store/f2i0mx8z05i8ic5x49g1kr54hfnik38q-source/default.nix:1:1 called with unexpected argument 'useGHC810', at /home/benjamin/projects/haskell/learn-obelisk/default.nix:9:14
Process exited with code 1; /nix/store/4z4336r8yhyznz3fiscvz53wgdj43kjb-nix-2.3.11/bin/nix-shell -E $'{root, pkgs, shell}: ((import root {}).passthru.__unstable__.self.extend (_: _: {shellPackages = builtins.fromJSON pkgs;})).project.shells.${shell}' --arg root ./. --argstr pkgs $'{"backend":"/home/benjamin/projects/haskell/learn-obelisk/backend","common":"/home/benjamin/projects/haskell/learn-obelisk/common","frontend":"/home/benjamin/projects/haskell/learn-obelisk/frontend"}' --argstr shell ghc

Since useGHC810 is defined in default.nix of the develop branch of Obelisk, this error makes me think that either:

  1. I am not correctly passing the argument
  2. my github.json file is being ignored, and a different version of obelisk is in use

Any ideas?

benrbray avatar Mar 31 '23 06:03 benrbray

In an attempt to pin the obelisk version, I tried to make a flake.nix. However it doesn't work either.

{
  description = "Minimal Obelisk + HLS Setup";
  
  inputs.obelisk = {
    url = github:obsidiansystems/obelisk/develop;
    flake = false;
  };

  inputs.nixpkgs.url = github:NixOS/nixpkgs/release-22.11;

  outputs = { self, nixpkgs, obelisk }: {

    packages.x86_64-linux.hello = nixpkgs.legacyPackages.x86_64-linux.hello;

    # packages.x86_64-linux.default = self.packages.x86_64-linux.hello;
    packages.x86_64-linux.default =
      obelisk.project ./. ({ ... }: {
        # empty
      });
  };
}
$ nix shell
warning: Git tree '/home/benjamin/projects/haskell/learn-obelisk' is dirty
error: attribute 'project' missing

       at /nix/store/h5m6lsb90x28ddkpqmcmvy2xylgzslq1-source/flake.nix:17:7:

           16|     packages.x86_64-linux.default =
           17|       obelisk.project ./. ({ ... }: {
             |       ^
           18|         # empty

After some Googling though I found that for some reason Nix flakes can't take parameters, so perhaps this approach is doomed anyway because I would need to pass in useGHC10 = true.

benrbray avatar Mar 31 '23 07:03 benrbray

I repeated the steps exactly like in your last try and for me it works.

mkdir obelisk-ghc810-test
cd obelisk-ghc810-test
ob init

My file "obelisk-ghc810-test/.obelisk/impl/github.json":

{
  "owner": "obsidiansystems",
  "repo": "obelisk",
  "branch": "develop",
  "private": false,
  "rev": "1c4b07ec67639356316b610142d3be8d302c07cc",
  "sha256": "1j8zjdwv912s7gxzfi3bfy1kh840j2bvhqycxgi7v4pc0mi54fav"
}

my file "obelisk-ghc810-test/default.nix":

{ system ? builtins.currentSystem
, obelisk ? import ./.obelisk/impl {
    inherit system;
    iosSdkVersion = "13.2";
    useGHC810 = true;

    # You must accept the Android Software Development Kit License Agreement at
    # https://developer.android.com/studio/terms in order to build Android apps.
    # Uncomment and set this to `true` to indicate your acceptance:
    # config.android_sdk.accept_license = false;

    # In order to use Let's Encrypt for HTTPS deployments you must accept
    # their terms of service at https://letsencrypt.org/repository/.
    # Uncomment and set this to `true` to indicate your acceptance:
    # terms.security.acme.acceptTerms = false;
  }
}:
with obelisk;
project ./. ({ ... }: {
  android.applicationId = "systems.obsidian.obelisk.examples.minimal";
  android.displayName = "Obelisk Minimal Example";
  ios.bundleIdentifier = "systems.obsidian.obelisk.examples.minimal";
  ios.bundleName = "Obelisk Minimal Example";
})

and then

ob shell
ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.10.7

rubenmoor avatar Mar 31 '23 14:03 rubenmoor