1{ 2 lib, 3 stdenv, 4 haskellPackages, 5 haskell, 6 7 # Which GHC versions this hls can support. 8 # These are looked up in nixpkgs as `pkgs.haskell.packages."ghc${version}`. 9 # Run 10 # $ nix-instantiate --eval -E 'with import <nixpkgs> {}; builtins.attrNames pkgs.haskell.packages' 11 # to list for your nixpkgs version. 12 supportedGhcVersions ? [ 13 (lib.strings.replaceStrings [ "." ] [ "" ] (lib.versions.majorMinor haskellPackages.ghc.version)) 14 ], 15 16 # Whether to build hls with the dynamic run-time system. 17 # See https://haskell-language-server.readthedocs.io/en/latest/troubleshooting.html#static-binaries for more information. 18 dynamic ? true, 19 20 # Which formatters are supported. Pass `[]` to remove all formatters. 21 # 22 # Maintainers: if a new formatter is added, add it here and down in knownFormatters 23 supportedFormatters ? [ 24 "ormolu" 25 "fourmolu" 26 "floskell" 27 "stylish-haskell" 28 ], 29}: 30 31# make sure the user only sets GHC versions that actually exist 32assert supportedGhcVersions != [ ]; 33assert lib.asserts.assertEachOneOf "supportedGhcVersions" supportedGhcVersions ( 34 lib.pipe haskell.packages [ 35 lib.attrNames 36 (lib.filter (lib.hasPrefix "ghc")) 37 (map (lib.removePrefix "ghc")) 38 ] 39); 40 41let 42 # A mapping from formatter name to 43 # - cabal flag to disable 44 # - formatter-specific packages that can be stripped from the build of hls if it is disabled 45 knownFormatters = { 46 ormolu = { 47 cabalFlag = "ormolu"; 48 packages = [ 49 "hls-ormolu-plugin" 50 ]; 51 }; 52 fourmolu = { 53 cabalFlag = "fourmolu"; 54 packages = [ 55 "hls-fourmolu-plugin" 56 ]; 57 }; 58 floskell = { 59 cabalFlag = "floskell"; 60 packages = [ 61 "hls-floskell-plugin" 62 ]; 63 }; 64 stylish-haskell = { 65 cabalFlag = "stylishhaskell"; 66 packages = [ 67 "hls-stylish-haskell-plugin" 68 ]; 69 }; 70 }; 71 72in 73 74# make sure any formatter that is set is actually supported by us 75assert lib.asserts.assertEachOneOf "supportedFormatters" supportedFormatters ( 76 lib.attrNames knownFormatters 77); 78 79# 80# The recommended way to override this package is 81# 82# pkgs.haskell-language-server.override { supportedGhcVersions = [ "90" "92"]; } 83# 84# for example. Read more about this in the haskell-language-server section of the nixpkgs manual. 85# 86let 87 inherit (haskell.lib.compose) 88 justStaticExecutables 89 overrideCabal 90 enableCabalFlag 91 disableCabalFlag 92 ; 93 94 getPackages = version: haskell.packages."ghc${version}"; 95 96 # Given the list of `supportedFormatters`, remove every formatter that we know of (knownFormatters) 97 # by disabling the cabal flag and also removing the formatter libraries. 98 removeUnnecessaryFormatters = 99 let 100 # only formatters that were not requested 101 unwanted = lib.pipe knownFormatters [ 102 (lib.filterAttrs (fmt: _: !(lib.elem fmt supportedFormatters))) 103 lib.attrsToList 104 ]; 105 # all flags to disable 106 flags = map (fmt: fmt.value.cabalFlag) unwanted; 107 # all dependencies to remove from hls 108 deps = lib.concatMap (fmt: fmt.value.packages) unwanted; 109 110 # remove nulls from a list 111 stripNulls = lib.filter (x: x != null); 112 113 # remove all unwanted dependencies of formatters we don’t want 114 stripDeps = overrideCabal (drv: { 115 libraryHaskellDepends = lib.pipe (drv.libraryHaskellDepends or [ ]) [ 116 # the existing list may contain nulls, so let’s strip them first 117 stripNulls 118 (lib.filter (dep: !(lib.elem dep.pname deps))) 119 ]; 120 }); 121 122 in 123 drv: lib.pipe drv ([ stripDeps ] ++ map disableCabalFlag flags); 124 125 tunedHls = 126 hsPkgs: 127 lib.pipe hsPkgs.haskell-language-server ( 128 [ 129 (haskell.lib.compose.overrideCabal (old: { 130 enableSharedExecutables = dynamic; 131 ${if !dynamic then "postInstall" else null} = '' 132 ${old.postInstall or ""} 133 134 remove-references-to -t ${hsPkgs.ghc} $out/bin/haskell-language-server 135 ''; 136 })) 137 ((if dynamic then enableCabalFlag else disableCabalFlag) "dynamic") 138 removeUnnecessaryFormatters 139 ] 140 ++ lib.optionals (!dynamic) [ 141 justStaticExecutables 142 ] 143 ); 144 145 targets = 146 version: 147 let 148 packages = getPackages version; 149 in 150 [ "haskell-language-server-${packages.ghc.version}" ]; 151 152 makeSymlinks = 153 version: 154 lib.concatMapStringsSep "\n" ( 155 x: "ln -s ${tunedHls (getPackages version)}/bin/haskell-language-server $out/bin/${x}" 156 ) (targets version); 157 158in 159stdenv.mkDerivation { 160 pname = "haskell-language-server"; 161 version = haskellPackages.haskell-language-server.version; 162 163 buildCommand = '' 164 mkdir -p $out/bin 165 ln -s ${tunedHls (getPackages (builtins.head supportedGhcVersions))}/bin/haskell-language-server-wrapper $out/bin/haskell-language-server-wrapper 166 ${lib.concatMapStringsSep "\n" makeSymlinks supportedGhcVersions} 167 ''; 168 169 meta = haskellPackages.haskell-language-server.meta // { 170 maintainers = [ lib.maintainers.maralorn ]; 171 longDescription = '' 172 This package provides the executables ${ 173 lib.concatMapStringsSep ", " (x: lib.concatStringsSep ", " (targets x)) supportedGhcVersions 174 } and haskell-language-server-wrapper. 175 You can choose for which ghc versions to install hls with pkgs.haskell-language-server.override { supportedGhcVersions = [ "90" "92" ]; }. 176 ''; 177 }; 178}