From 1d453785e5ac06e595a27255187c971649276220 Mon Sep 17 00:00:00 2001
From: Christopher Smyth <18294397+RossSmyth@users.noreply.github.com>
Date: Tue, 4 Mar 2025 11:23:28 -0500
Subject: [PATCH] Clean up Nix Flake & make it easier to customize (#12831)

---
 default.nix  |  84 ++++++++++++++++++++--
 flake.lock   |  28 ++------
 flake.nix    | 195 +++++++++++----------------------------------------
 grammars.nix |  32 +++++----
 4 files changed, 142 insertions(+), 197 deletions(-)

diff --git a/default.nix b/default.nix
index d2c51ec3..0efa75bb 100644
--- a/default.nix
+++ b/default.nix
@@ -1,8 +1,78 @@
-# Flake's default package for non-flake-enabled nix instances
-let
-  compat = builtins.fetchTarball {
-    url = "https://github.com/edolstra/flake-compat/archive/b4a34015c698c7793d592d66adbab377907a2be8.tar.gz";
-    sha256 = "sha256:1qc703yg0babixi6wshn5wm2kgl5y1drcswgszh4xxzbrwkk9sv7";
-  };
+{
+  lib,
+  rustPlatform,
+  callPackage,
+  runCommand,
+  installShellFiles,
+  git,
+  ...
+}: let
+  fs = lib.fileset;
+
+  src = fs.difference (fs.gitTracked ./.) (fs.unions [
+    ./.envrc
+    ./rustfmt.toml
+    ./screenshot.png
+    ./book
+    ./docs
+    ./flake.lock
+    (fs.fileFilter (file: lib.strings.hasInfix ".git" file.name) ./.)
+    (fs.fileFilter (file: file.hasExt "svg") ./.)
+    (fs.fileFilter (file: file.hasExt "md") ./.)
+    (fs.fileFilter (file: file.hasExt "nix") ./.)
+  ]);
+
+  # Next we actually need to build the grammars and the runtime directory
+  # that they reside in. It is built by calling the derivation in the
+  # grammars.nix file, then taking the runtime directory in the git repo
+  # and hooking symlinks up to it.
+  grammars = callPackage ./grammars.nix {};
+  runtimeDir = runCommand "helix-runtime" {} ''
+    mkdir -p $out
+    ln -s ${./runtime}/* $out
+    rm -r $out/grammars
+    ln -s ${grammars} $out/grammars
+  '';
 in
-  (import compat {src = ./.;}).defaultNix
+  # Currently rustPlatform.buildRustPackage doesn't have the finalAttrs pattern
+  # hooked up. To get around this while having good customization, mkDerivation is
+  # used instead.
+  rustPlatform.buildRustPackage (self: {
+    cargoLock.lockFile = ./Cargo.lock;
+
+    nativeBuildInputs = [
+      installShellFiles
+      git
+    ];
+
+    buildType = "release";
+
+    name = with builtins; (fromTOML (readFile ./helix-term/Cargo.toml)).package.name;
+    src = fs.toSource {
+      root = ./.;
+      fileset = src;
+    };
+
+    # Helix attempts to reach out to the network and get the grammars. Nix doesn't allow this.
+    HELIX_DISABLE_AUTO_GRAMMAR_BUILD = "1";
+
+    # So Helix knows what rev it is.
+    HELIX_NIX_BUILD_REV = self.rev or self.dirtyRev or null;
+
+    doCheck = false;
+    strictDeps = true;
+
+    # Sets the Helix runtimedir to the grammars
+    env.HELIX_DEFAULT_RUNTIME = "${runtimeDir}";
+
+    # Get all the application stuff in the output directory.
+    postInstall = ''
+      mkdir -p $out/lib
+      installShellCompletion ${./contrib/completion}/hx.{bash,fish,zsh}
+      mkdir -p $out/share/{applications,icons/hicolor/256x256/apps}
+      cp ${./contrib/Helix.desktop} $out/share/applications
+      cp ${./contrib/helix.png} $out/share/icons/hicolor/256x256/apps
+    '';
+
+    meta.mainProgram = "hx";
+  })
diff --git a/flake.lock b/flake.lock
index 62ff3447..7e3d5bd3 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,20 +1,5 @@
 {
   "nodes": {
-    "crane": {
-      "locked": {
-        "lastModified": 1737563566,
-        "narHash": "sha256-GLJvkOG29XCynQm8XWPyykMRqIhxKcBARVu7Ydrz02M=",
-        "owner": "ipetkov",
-        "repo": "crane",
-        "rev": "849376434956794ebc7a6b487d31aace395392ba",
-        "type": "github"
-      },
-      "original": {
-        "owner": "ipetkov",
-        "repo": "crane",
-        "type": "github"
-      }
-    },
     "flake-utils": {
       "inputs": {
         "systems": "systems"
@@ -35,11 +20,11 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1728018373,
-        "narHash": "sha256-NOiTvBbRLIOe5F6RbHaAh6++BNjsb149fGZd1T4+KBg=",
+        "lastModified": 1740560979,
+        "narHash": "sha256-Vr3Qi346M+8CjedtbyUevIGDZW8LcA1fTG0ugPY/Hic=",
         "owner": "nixos",
         "repo": "nixpkgs",
-        "rev": "bc947f541ae55e999ffdb4013441347d83b00feb",
+        "rev": "5135c59491985879812717f4c9fea69604e7f26f",
         "type": "github"
       },
       "original": {
@@ -51,7 +36,6 @@
     },
     "root": {
       "inputs": {
-        "crane": "crane",
         "flake-utils": "flake-utils",
         "nixpkgs": "nixpkgs",
         "rust-overlay": "rust-overlay"
@@ -64,11 +48,11 @@
         ]
       },
       "locked": {
-        "lastModified": 1737599167,
-        "narHash": "sha256-S2rHCrQWCDVp63XxL/AQbGr1g5M8Zx14C7Jooa4oM8o=",
+        "lastModified": 1740623427,
+        "narHash": "sha256-3SdPQrZoa4odlScFDUHd4CUPQ/R1gtH4Mq9u8CBiK8M=",
         "owner": "oxalica",
         "repo": "rust-overlay",
-        "rev": "38374302ae9edf819eac666d1f276d62c712dd06",
+        "rev": "d342e8b5fd88421ff982f383c853f0fc78a847ab",
         "type": "github"
       },
       "original": {
diff --git a/flake.nix b/flake.nix
index 1fda3fa9..7d176cb0 100644
--- a/flake.nix
+++ b/flake.nix
@@ -8,13 +8,11 @@
       url = "github:oxalica/rust-overlay";
       inputs.nixpkgs.follows = "nixpkgs";
     };
-    crane.url = "github:ipetkov/crane";
   };
 
   outputs = {
     self,
     nixpkgs,
-    crane,
     flake-utils,
     rust-overlay,
     ...
@@ -24,167 +22,56 @@
         inherit system;
         overlays = [(import rust-overlay)];
       };
-      mkRootPath = rel:
-        builtins.path {
-          path = "${toString ./.}/${rel}";
-          name = rel;
-        };
-      filteredSource = let
-        pathsToIgnore = [
-          ".envrc"
-          ".ignore"
-          ".github"
-          ".gitignore"
-          "logo_dark.svg"
-          "logo_light.svg"
-          "rust-toolchain.toml"
-          "rustfmt.toml"
-          "runtime"
-          "screenshot.png"
-          "book"
-          "docs"
-          "README.md"
-          "CHANGELOG.md"
-          "shell.nix"
-          "default.nix"
-          "grammars.nix"
-          "flake.nix"
-          "flake.lock"
-        ];
-        ignorePaths = path: type: let
-          inherit (nixpkgs) lib;
-          # split the nix store path into its components
-          components = lib.splitString "/" path;
-          # drop off the `/nix/hash-source` section from the path
-          relPathComponents = lib.drop 4 components;
-          # reassemble the path components
-          relPath = lib.concatStringsSep "/" relPathComponents;
-        in
-          lib.all (p: ! (lib.hasPrefix p relPath)) pathsToIgnore;
-      in
-        builtins.path {
-          name = "helix-source";
-          path = toString ./.;
-          # filter out unnecessary paths
-          filter = ignorePaths;
-        };
-      makeOverridableHelix = old: config: let
-        grammars = pkgs.callPackage ./grammars.nix config;
-        runtimeDir = pkgs.runCommand "helix-runtime" {} ''
-          mkdir -p $out
-          ln -s ${mkRootPath "runtime"}/* $out
-          rm -r $out/grammars
-          ln -s ${grammars} $out/grammars
-        '';
-        helix-wrapped =
-          pkgs.runCommand
-          old.name
-          {
-            inherit (old) pname version;
-            meta = old.meta or {};
-            passthru =
-              (old.passthru or {})
-              // {
-                unwrapped = old;
-              };
-            nativeBuildInputs = [pkgs.makeWrapper];
-            makeWrapperArgs = config.makeWrapperArgs or [];
-          }
-          ''
-            cp -rs --no-preserve=mode,ownership ${old} $out
-            wrapProgram "$out/bin/hx" ''${makeWrapperArgs[@]} --set HELIX_RUNTIME "${runtimeDir}"
-          '';
-      in
-        helix-wrapped
-        // {
-          override = makeOverridableHelix old;
-          passthru =
-            helix-wrapped.passthru
-            // {
-              wrapper = old: makeOverridableHelix old config;
-            };
-        };
-      stdenv =
-        if pkgs.stdenv.isLinux
-        then pkgs.stdenv
-        else pkgs.clangStdenv;
-      rustFlagsEnv = pkgs.lib.optionalString stdenv.isLinux "-C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment --cfg tokio_unstable";
-      rustToolchain = pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
-      craneLibMSRV = (crane.mkLib pkgs).overrideToolchain rustToolchain;
-      craneLibStable = (crane.mkLib pkgs).overrideToolchain pkgs.pkgsBuildHost.rust-bin.stable.latest.default;
-      commonArgs = {
-        inherit stdenv;
-        inherit (craneLibMSRV.crateNameFromCargoToml {cargoToml = ./helix-term/Cargo.toml;}) pname;
-        inherit (craneLibMSRV.crateNameFromCargoToml {cargoToml = ./Cargo.toml;}) version;
-        src = filteredSource;
-        # disable fetching and building of tree-sitter grammars in the helix-term build.rs
-        HELIX_DISABLE_AUTO_GRAMMAR_BUILD = "1";
-        buildInputs = [stdenv.cc.cc.lib];
-        nativeBuildInputs = [pkgs.installShellFiles];
-        # disable tests
-        doCheck = false;
-        meta.mainProgram = "hx";
+
+      # Get Helix's MSRV toolchain to build with by default.
+      msrvToolchain = pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
+      msrvPlatform = pkgs.makeRustPlatform {
+        cargo = msrvToolchain;
+        rustc = msrvToolchain;
       };
-      cargoArtifacts = craneLibMSRV.buildDepsOnly commonArgs;
     in {
-      packages = {
-        helix-unwrapped = craneLibStable.buildPackage (commonArgs
-          // {
-            cargoArtifacts = craneLibStable.buildDepsOnly commonArgs;
-            postInstall = ''
-              mkdir -p $out/share/applications $out/share/icons/hicolor/scalable/apps $out/share/icons/hicolor/256x256/apps
-              cp contrib/Helix.desktop $out/share/applications
-              cp logo.svg $out/share/icons/hicolor/scalable/apps/helix.svg
-              cp contrib/helix.png $out/share/icons/hicolor/256x256/apps
-              installShellCompletion contrib/completion/hx.{bash,fish,zsh}
-            '';
-            # set git revision for nix flake builds, see 'git_hash' in helix-loader/build.rs
-            HELIX_NIX_BUILD_REV = self.rev or self.dirtyRev or null;
-          });
-        helix = makeOverridableHelix self.packages.${system}.helix-unwrapped {};
-        default = self.packages.${system}.helix;
+      packages = rec {
+        helix = pkgs.callPackage ./default.nix {};
+
+        # The default Helix build. Uses the latest stable Rust toolchain, and unstable
+        # nixpkgs.
+        #
+        # This can be overridden though to add Cargo Features, flags, and different toolchains with
+        # packages.${system}.default.override { ... };
+        default = helix;
       };
 
-      checks = {
-        # Build the crate itself
-        inherit (self.packages.${system}) helix;
-
-        clippy = craneLibMSRV.cargoClippy (commonArgs
-          // {
-            inherit cargoArtifacts;
-            cargoClippyExtraArgs = "--all-targets -- --deny warnings";
-          });
-
-        fmt = craneLibMSRV.cargoFmt commonArgs;
-
-        doc = craneLibMSRV.cargoDoc (commonArgs
-          // {
-            inherit cargoArtifacts;
-          });
-
-        test = craneLibMSRV.cargoTest (commonArgs
-          // {
-            inherit cargoArtifacts;
-          });
+      checks.helix = self.outputs.packages.${system}.helix.override {
+        buildType = "debug";
+        rustPlatform = msrvPlatform;
       };
 
-      devShells.default = pkgs.mkShell {
-        inputsFrom = builtins.attrValues self.checks.${system};
-        nativeBuildInputs = with pkgs;
-          [lld_13 cargo-flamegraph rust-analyzer]
-          ++ (lib.optional (stdenv.isx86_64 && stdenv.isLinux) pkgs.cargo-tarpaulin)
-          ++ (lib.optional stdenv.isLinux pkgs.lldb)
-          ++ (lib.optional stdenv.isDarwin pkgs.darwin.apple_sdk.frameworks.CoreFoundation);
-        shellHook = ''
-          export HELIX_RUNTIME="$PWD/runtime"
-          export RUST_BACKTRACE="1"
-          export RUSTFLAGS="''${RUSTFLAGS:-""} ${rustFlagsEnv}"
-        '';
-      };
+      # Devshell behavior is preserved.
+      devShells.default = let
+        rustFlagsEnv = pkgs.lib.optionalString pkgs.stdenv.isLinux "-C link-arg=-fuse-ld=lld -C target-cpu=native -Clink-arg=-Wl,--no-rosegment --cfg tokio_unstable";
+      in
+        pkgs.mkShell
+        {
+          inputsFrom = [self.checks.${system}.helix];
+          nativeBuildInputs = with pkgs;
+            [
+              lld_13
+              cargo-flamegraph
+              rust-bin.nightly.latest.rust-analyzer
+            ]
+            ++ (lib.optional (stdenv.isx86_64 && stdenv.isLinux) cargo-tarpaulin)
+            ++ (lib.optional stdenv.isLinux lldb)
+            ++ (lib.optional stdenv.isDarwin darwin.apple_sdk.frameworks.CoreFoundation);
+          shellHook = ''
+            export HELIX_RUNTIME="$PWD/runtime"
+            export RUST_BACKTRACE="1"
+            export RUSTFLAGS="''${RUSTFLAGS:-""} ${rustFlagsEnv}"
+          '';
+        };
     })
     // {
       overlays.default = final: prev: {
-        inherit (self.packages.${final.system}) helix;
+        helix = final.callPackage ./default.nix {};
       };
     };
 
diff --git a/grammars.nix b/grammars.nix
index 967b1b13..bc99d21d 100644
--- a/grammars.nix
+++ b/grammars.nix
@@ -32,10 +32,10 @@
   # If `use-grammars.except` is set, use all other grammars.
   # Otherwise use all grammars.
   useGrammar = grammar:
-    if languagesConfig?use-grammars.only then
-      builtins.elem grammar.name languagesConfig.use-grammars.only
-    else if languagesConfig?use-grammars.except then
-      !(builtins.elem grammar.name languagesConfig.use-grammars.except)
+    if languagesConfig ? use-grammars.only
+    then builtins.elem grammar.name languagesConfig.use-grammars.only
+    else if languagesConfig ? use-grammars.except
+    then !(builtins.elem grammar.name languagesConfig.use-grammars.except)
     else true;
   grammarsToUse = builtins.filter useGrammar languagesConfig.grammar;
   gitGrammars = builtins.filter isGitGrammar grammarsToUse;
@@ -66,10 +66,10 @@
       version = grammar.source.rev;
 
       src = source;
-      sourceRoot = if builtins.hasAttr "subpath" grammar.source then
-        "source/${grammar.source.subpath}"
-      else
-        "source";
+      sourceRoot =
+        if builtins.hasAttr "subpath" grammar.source
+        then "source/${grammar.source.subpath}"
+        else "source";
 
       dontConfigure = true;
 
@@ -116,15 +116,19 @@
       '';
     };
   grammarsToBuild = builtins.filter includeGrammarIf gitGrammars;
-  builtGrammars = builtins.map (grammar: {
-    inherit (grammar) name;
-    value = buildGrammar grammar;
-  }) grammarsToBuild;
+  builtGrammars =
+    builtins.map (grammar: {
+      inherit (grammar) name;
+      value = buildGrammar grammar;
+    })
+    grammarsToBuild;
   extensibleGrammars =
     lib.makeExtensible (self: builtins.listToAttrs builtGrammars);
-  overlaidGrammars = lib.pipe extensibleGrammars
+  overlaidGrammars =
+    lib.pipe extensibleGrammars
     (builtins.map (overlay: grammar: grammar.extend overlay) grammarOverlays);
-  grammarLinks = lib.mapAttrsToList
+  grammarLinks =
+    lib.mapAttrsToList
     (name: artifact: "ln -s ${artifact}/${name}.so $out/${name}.so")
     (lib.filterAttrs (n: v: lib.isDerivation v) overlaidGrammars);
 in