Unverified Commit b9534d65 authored by maralorn's avatar maralorn Committed by GitHub
Browse files

Merge pull request #261848 from Profpatsch/haskell-language-server-disable-formatters

haskell-language-server: allow specifying a set of formatters that should be included
parents baa32b9a 77908eaa
Loading
Loading
Loading
Loading
+29 −0
Original line number Diff line number Diff line
@@ -50,4 +50,33 @@ rec {
      lib.generators.toPretty {} xs}, but is: ${
        lib.generators.toPretty {} val}";

  /* Specialized `assertMsg` for checking if every one of `vals` is one of the elements
     of the list `xs`. Useful for checking lists of supported attributes.

     Example:
       let sslLibraries = [ "libressl" "bearssl" ];
       in assertEachOneOf "sslLibraries" sslLibraries [ "openssl" "bearssl" ]
       stderr> error: each element in sslLibraries must be one of [
       stderr>   "openssl"
       stderr>   "bearssl"
       stderr> ], but is: [
       stderr>   "libressl"
       stderr>   "bearssl"
       stderr> ]

     Type:
       assertEachOneOf :: String -> List ComparableVal -> List ComparableVal -> Bool
  */
  assertEachOneOf =
    # The name of the variable the user entered `val` into, for inclusion in the error message
    name:
    # The list of values of what the user provided, to be compared against the values in `xs`
    vals:
    # The list of valid values
    xs:
    assertMsg
    (lib.all (val: lib.elem val xs) vals)
    "each element in ${name} must be one of ${
      lib.generators.toPretty {} xs}, but is: ${
        lib.generators.toPretty {} vals}";
}
+123 −15
Original line number Diff line number Diff line
{ lib
, stdenv
, supportedGhcVersions ? [ "94" ]
, dynamic ? true
, haskellPackages
, haskell

# Which GHC versions this hls can support.
# These are looked up in nixpkgs as `pkgs.haskell.packages."ghc${version}`.
# Run
#  $ nix-instantiate --eval -E 'with import <nixpkgs> {}; builtins.attrNames pkgs.haskell.packages'
# to list for your nixpkgs version.
, supportedGhcVersions ? [ "94" ]

# Whether to build hls with the dynamic run-time system.
# See https://haskell-language-server.readthedocs.io/en/latest/troubleshooting.html#static-binaries for more information.
, dynamic ? true

# Which formatters are supported. Pass `[]` to remove all formatters.
#
# Maintainers: if a new formatter is added, add it here and down in knownFormatters
, supportedFormatters ? [ "ormolu" "fourmolu" "floskell" "stylish-haskell" ]
}:

# make sure the user only sets GHC versions that actually exist
assert supportedGhcVersions != [];
assert
  lib.asserts.assertEachOneOf
    "supportedGhcVersions"
    supportedGhcVersions
    (lib.pipe haskell.packages [
      lib.attrNames
      (lib.filter (lib.hasPrefix "ghc"))
      (map (lib.removePrefix "ghc"))
    ]);

let
  # A mapping from formatter name to
  # - cabal flag to disable
  # - formatter-specific packages that can be stripped from the build of hls if it is disabled
  knownFormatters = {
    ormolu = {
      cabalFlag = "ormolu";
      packages = [
        "hls-ormolu-plugin"
      ];
    };
    fourmolu = {
      cabalFlag = "fourmolu";
      packages = [
        "hls-fourmolu-plugin"
      ];
    };
    floskell = {
      cabalFlag = "floskell";
      packages = [
        "hls-floskell-plugin"
      ];
    };
    stylish-haskell = {
      cabalFlag = "stylishhaskell";
      packages = [
        "hls-stylish-haskell-plugin"
      ];
    };
  };

in

# make sure any formatter that is set is actually supported by us
assert
  lib.asserts.assertEachOneOf
    "supportedFormatters"
    supportedFormatters
    (lib.attrNames knownFormatters);

#
# The recommended way to override this package is
#
@@ -13,9 +80,43 @@
# for example. Read more about this in the haskell-language-server section of the nixpkgs manual.
#
let
  inherit (lib) concatStringsSep concatMapStringsSep take splitString pipe optionals;
  inherit (haskell.lib.compose) justStaticExecutables overrideCabal enableCabalFlag disableCabalFlag;
  inherit (haskell.lib.compose)
    justStaticExecutables
    overrideCabal
    enableCabalFlag
    disableCabalFlag
    ;

  getPackages = version: haskell.packages."ghc${version}";

  # Given the list of `supportedFormatters`, remove every formatter that we know of (knownFormatters)
  # by disabling the cabal flag and also removing the formatter libraries.
  removeUnnecessaryFormatters =
    let
      # only formatters that were not requested
      unwanted = lib.pipe knownFormatters [
        (lib.filterAttrs (fmt: _: ! (lib.elem fmt supportedFormatters)))
        lib.attrsToList
      ];
      # all flags to disable
      flags = map (fmt: fmt.value.cabalFlag) unwanted;
      # all dependencies to remove from hls
      deps = lib.concatMap (fmt: fmt.value.packages) unwanted;

      # remove nulls from a list
      stripNulls = lib.filter (x: x != null);

      # remove all unwanted dependencies of formatters we don’t want
      stripDeps = overrideCabal (drv: {
        libraryHaskellDepends = lib.pipe (drv.libraryHaskellDepends or []) [
          # the existing list may contain nulls, so let’s strip them first
          stripNulls
          (lib.filter (dep: ! (lib.elem dep.pname deps)))
        ];
      });

    in drv: lib.pipe drv ([stripDeps] ++ map disableCabalFlag flags);

  tunedHls = hsPkgs:
    lib.pipe hsPkgs.haskell-language-server ([
      (haskell.lib.compose.overrideCabal (old: {
@@ -27,32 +128,39 @@ let
        '';
      }))
      ((if dynamic then enableCabalFlag else disableCabalFlag) "dynamic")
    ] ++ optionals (!dynamic) [
      removeUnnecessaryFormatters
    ]
    ++ lib.optionals (!dynamic) [
      justStaticExecutables
    ]);

  targets = version:
    let packages = getPackages version;
    in [
      "haskell-language-server-${packages.ghc.version}"
    ];
    in [ "haskell-language-server-${packages.ghc.version}" ];

  makeSymlinks = version:
    concatMapStringsSep "\n" (x:
    lib.concatMapStringsSep "\n"
      (x:
        "ln -s ${
          tunedHls (getPackages version)
      }/bin/haskell-language-server $out/bin/${x}") (targets version);
in assert supportedGhcVersions != []; stdenv.mkDerivation {
        }/bin/haskell-language-server $out/bin/${x}")
      (targets version);

in stdenv.mkDerivation {
  pname = "haskell-language-server";
  version = haskellPackages.haskell-language-server.version;

  buildCommand = ''
    mkdir -p $out/bin
    ln -s ${tunedHls (getPackages (builtins.head supportedGhcVersions))}/bin/haskell-language-server-wrapper $out/bin/haskell-language-server-wrapper
    ${concatMapStringsSep "\n" makeSymlinks supportedGhcVersions}
    ${lib.concatMapStringsSep "\n" makeSymlinks supportedGhcVersions}
  '';

  meta = haskellPackages.haskell-language-server.meta // {
    maintainers = [ lib.maintainers.maralorn ];
    longDescription = ''
      This package provides the executables ${
        concatMapStringsSep ", " (x: concatStringsSep ", " (targets x))
        lib.concatMapStringsSep ", " (x: lib.concatStringsSep ", " (targets x))
        supportedGhcVersions
      } and haskell-language-server-wrapper.
      You can choose for which ghc versions to install hls with pkgs.haskell-language-server.override { supportedGhcVersions = [ "90" "92" ]; }.