From eb4560115c14358a87748ed5514250f003e96d47 Mon Sep 17 00:00:00 2001 From: willemml Date: Fri, 17 Mar 2023 13:32:04 -0700 Subject: [PATCH] get rid of NUR --- flake-parts/apps.nix | 4 +- flake-parts/home-manager/modules.nix | 10 + flake-parts/home-manager/users.nix | 32 +- flake-parts/overlays.nix | 1 + flake.lock | 57 +- flake.nix | 8 +- .../user/willem/{default.nix => base.nix} | 8 - .../profiles/user/willem/darwin/default.nix | 6 - .../profiles/user/willem/programs/default.nix | 5 - .../profiles/user/willem/programs/emacs.nix | 4 +- .../profiles/user/willem/programs/firefox.nix | 2 +- home-manager/modules/programs/emacs-init.nix | 556 ++++++++++++++++++ 12 files changed, 632 insertions(+), 61 deletions(-) rename home-manager/modules/profiles/user/willem/{default.nix => base.nix} (91%) create mode 100644 home-manager/modules/programs/emacs-init.nix diff --git a/flake-parts/apps.nix b/flake-parts/apps.nix index 2ae8a27..2691d48 100644 --- a/flake-parts/apps.nix +++ b/flake-parts/apps.nix @@ -1,10 +1,10 @@ -{ lib, self, inputs, withSystem, ... }: { +{ lib, self, inputs, withSystem, config, ... }: { flake = let buildProgram = system: definition: (withSystem system ({ pkgs, self', ... }: definition self.lib pkgs)); defineProgram = system: name: definition: { ${system}.${name} = buildProgram system definition; }; appsDir = self.lib.importDirToAttrs ../apps; - builtPrograms = lib.mapAttrsToList (name: value: (lib.forEach value.systems (system: defineProgram system name value.definition))) appsDir; + builtPrograms = lib.mapAttrsToList (name: value: (lib.forEach (builtins.filter (system: builtins.elem system config.systems) value.systems) (system: defineProgram system name value.definition))) appsDir; flattenedPrograms = lib.flatten builtPrograms; assembledPrograms = builtins.foldl' (a: b: lib.recursiveUpdate a b) { } flattenedPrograms; assembledApps = lib.mapAttrsRecursiveCond (as: !(as ? "type" && as.type == "derivation")) (path: value: { type = "app"; program = lib.getExe value; }) assembledPrograms; diff --git a/flake-parts/home-manager/modules.nix b/flake-parts/home-manager/modules.nix index 756c00a..d76dc6e 100644 --- a/flake-parts/home-manager/modules.nix +++ b/flake-parts/home-manager/modules.nix @@ -15,6 +15,16 @@ modules = self.lib.importDirToAttrs ../../home-manager/modules; in { + default = { + imports = builtins.attrValues modules; + }; + + nixpkgs-Config = { + nixpkgs.config.allowUnfreePredicate = lib.const true; + nixpkgs.config.allowUnsupportedSystem = true; + nixpkgs.overlays = builtins.attrValues self.overlays; + }; + nixpkgs-useFlakeNixpkgs = { home.sessionVariables.NIX_PATH = "nixpkgs=${inputs.nixpkgs}"; systemd.user.sessionVariables.NIX_PATH = lib.mkForce "nixpkgs=${inputs.nixpkgs}"; diff --git a/flake-parts/home-manager/users.nix b/flake-parts/home-manager/users.nix index 7e74c8e..7228fc8 100644 --- a/flake-parts/home-manager/users.nix +++ b/flake-parts/home-manager/users.nix @@ -1,31 +1,19 @@ { inputs, self, lib, ... }: { - flake = { - homeManagerModules.nixpkgsConfig = { - nixpkgs.config.allowUnfreePredicate = lib.const true; - nixpkgs.config.packageOverrides = pkgs: { - nur = import inputs.nur { inherit pkgs; nurpkgs = pkgs; }; - }; - nixpkgs.config.allowUnsupportedSystem = true; - nixpkgs.overlays = builtins.attrValues self.overlays; - }; - testOutput = builtins.attrValues self.overlays; - }; perSystem = { pkgs, self', ... }: rec { homeConfigurations.willem = inputs.home-manager.lib.homeManagerConfiguration { inherit pkgs; - modules = [ - self.homeManagerModules.nixpkgs-useFlakeNixpkgs - self.homeManagerModules.nixpkgsConfig - self.homeManagerModules.profiles-user-willem - ]; - extraSpecialArgs = { - nurNoPkgs = import inputs.nur { - nurpkgs = pkgs; - pkgs = throw "nixpkgs eval"; - }; - }; + modules = + let + nurNoPkgs = (import inputs.nur { pkgs = null; nurpkgs = pkgs; }); + in + [ + self.homeManagerModules.programs-emacsInit + self.homeManagerModules.nixpkgs-useFlakeNixpkgs + self.homeManagerModules.nixpkgs-Config + self.homeManagerModules.default + ]; }; packages = let activationPackages = builtins.mapAttrs (_: lib.getAttr "activationPackage") homeConfigurations; diff --git a/flake-parts/overlays.nix b/flake-parts/overlays.nix index feb39e3..d19ca9e 100644 --- a/flake-parts/overlays.nix +++ b/flake-parts/overlays.nix @@ -9,5 +9,6 @@ lib.mapAttrs (name: value: value.definition self.lib prev) appsDir ); default = import ../packages; + rycee-firefox-addons = final: prev: { rycee-firefox-addons = inputs.rycee-firefox-addons.outputs.packages.${prev.system}; }; }; } diff --git a/flake.lock b/flake.lock index b2cc4c3..622fdda 100644 --- a/flake.lock +++ b/flake.lock @@ -84,6 +84,21 @@ "type": "github" } }, + "flake-utils_3": { + "locked": { + "lastModified": 1629284811, + "narHash": "sha256-JHgasjPR0/J1J3DRm4KxM4zTyAj4IOJY8vIl75v/kPI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "c5d161cc0af116a2e17f54316f0bf43f0819785c", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, "gitignore": { "inputs": { "nixpkgs": [ @@ -213,18 +228,19 @@ "type": "github" } }, - "nur": { + "nixpkgs_3": { "locked": { - "lastModified": 1678117608, - "narHash": "sha256-hgqRYieVMKmdW6/JIQxUxBvz1o0jea+VZPtvefeof+I=", - "owner": "nix-community", - "repo": "NUR", - "rev": "9613bf1f8846150d3453d6ea69f538264148c68a", + "lastModified": 1627814220, + "narHash": "sha256-P+MDgdZw2CBk9X1ZZaUgHgN+32pTfLFf3XVIBOXirI4=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "ab5b6828af26215bf2646c31961da5d3749591ef", "type": "github" }, "original": { - "owner": "nix-community", - "repo": "NUR", + "owner": "nixos", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", "type": "github" } }, @@ -258,8 +274,8 @@ "home-manager": "home-manager", "nixos-apple-silicon": "nixos-apple-silicon", "nixpkgs": "nixpkgs", - "nur": "nur", - "pre-commit-hooks-nix": "pre-commit-hooks-nix" + "pre-commit-hooks-nix": "pre-commit-hooks-nix", + "rycee-firefox-addons": "rycee-firefox-addons" } }, "rust-overlay": { @@ -278,6 +294,27 @@ "type": "github" } }, + "rycee-firefox-addons": { + "inputs": { + "flake-utils": "flake-utils_3", + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "dir": "pkgs/firefox-addons", + "lastModified": 1676584149, + "narHash": "sha256-3gWGqaVDx4N0Xkb5xFotVoKsqDTo4Jes3b6VSxP/UDw=", + "ref": "refs/heads/master", + "rev": "db2f2ff538c8c755e6b062c9be1c514752c6ee1a", + "revCount": 2706, + "type": "git", + "url": "https://git.sr.ht/~rycee/nur-expressions?dir=pkgs%2ffirefox-addons" + }, + "original": { + "dir": "pkgs/firefox-addons", + "type": "git", + "url": "https://git.sr.ht/~rycee/nur-expressions?dir=pkgs%2ffirefox-addons" + } + }, "utils": { "locked": { "lastModified": 1676283394, diff --git a/flake.nix b/flake.nix index 74bed51..e7bcbae 100644 --- a/flake.nix +++ b/flake.nix @@ -11,7 +11,7 @@ home-manager.inputs.nixpkgs.follows = "nixpkgs"; nixos-apple-silicon.url = "github:tpwrules/nixos-apple-silicon"; nixos-apple-silicon.inputs.nixpkgs.follows = "nixpkgs"; - nur.url = "github:nix-community/NUR"; + rycee-firefox-addons.url = "git+https://git.sr.ht/~rycee/nur-expressions?dir=pkgs/firefox-addons"; pre-commit-hooks-nix.url = "github:cachix/pre-commit-hooks.nix"; }; @@ -22,10 +22,10 @@ ]; systems = [ - "x86_64-linux" - "x86_64-darwin" +# "x86_64-linux" +# "x86_64-darwin" "aarch64-darwin" - "aarch64-linux" +# "aarch64-linux" ]; }; } diff --git a/home-manager/modules/profiles/user/willem/default.nix b/home-manager/modules/profiles/user/willem/base.nix similarity index 91% rename from home-manager/modules/profiles/user/willem/default.nix rename to home-manager/modules/profiles/user/willem/base.nix index d2b0b6f..7916f1f 100644 --- a/home-manager/modules/profiles/user/willem/default.nix +++ b/home-manager/modules/profiles/user/willem/base.nix @@ -43,12 +43,4 @@ rec { stateVersion = "22.11"; username = "willem"; }; - - imports = [ - ./accounts.nix - ./darwin - ./feeds.nix - ./packages.nix - ./programs - ]; } diff --git a/home-manager/modules/profiles/user/willem/darwin/default.nix b/home-manager/modules/profiles/user/willem/darwin/default.nix index a176418..5ed81d7 100644 --- a/home-manager/modules/profiles/user/willem/darwin/default.nix +++ b/home-manager/modules/profiles/user/willem/darwin/default.nix @@ -15,12 +15,6 @@ let }; in { - imports = [ - ./finder.nix - ./iterm2.nix - ./launchd.nix - ]; - home.file.".gnupg/gpg-agent.conf" = mkIf stdenv.isDarwin { text = '' pinentry-program "${pkgs.pinentryTouchid}/bin/pinentry-touchid" diff --git a/home-manager/modules/profiles/user/willem/programs/default.nix b/home-manager/modules/profiles/user/willem/programs/default.nix index 1668aef..e595915 100644 --- a/home-manager/modules/profiles/user/willem/programs/default.nix +++ b/home-manager/modules/profiles/user/willem/programs/default.nix @@ -1,9 +1,4 @@ { lib, config, pkgs, ... }: { - imports = [ - ./emacs.nix - ./firefox.nix - ]; - programs = { bash.enableCompletion = true; diff --git a/home-manager/modules/profiles/user/willem/programs/emacs.nix b/home-manager/modules/profiles/user/willem/programs/emacs.nix index be7a32a..5fb19f0 100644 --- a/home-manager/modules/profiles/user/willem/programs/emacs.nix +++ b/home-manager/modules/profiles/user/willem/programs/emacs.nix @@ -1,11 +1,9 @@ -{ config, pkgs, nurNoPkgs, ... }: +{ config, pkgs, ... }: let pcfg = config.programs.emacs.init.usePackage; in { - imports = [ nurNoPkgs.repos.rycee.hmModules.emacs-init ]; - programs.emacs.enable = true; services.emacs = pkgs.lib.mkIf pkgs.stdenv.isLinux { diff --git a/home-manager/modules/profiles/user/willem/programs/firefox.nix b/home-manager/modules/profiles/user/willem/programs/firefox.nix index 6714aba..bc36f0a 100644 --- a/home-manager/modules/profiles/user/willem/programs/firefox.nix +++ b/home-manager/modules/profiles/user/willem/programs/firefox.nix @@ -68,7 +68,7 @@ "privacy.donottrackheader.enabled" = true; "signon.rememberSignons" = false; }; - extensions = with pkgs.nur.repos.rycee.firefox-addons; [ + extensions = with pkgs.rycee-firefox-addons; [ browserpass #bypass-paywalls-clean clearurls diff --git a/home-manager/modules/programs/emacs-init.nix b/home-manager/modules/programs/emacs-init.nix new file mode 100644 index 0000000..2f50097 --- /dev/null +++ b/home-manager/modules/programs/emacs-init.nix @@ -0,0 +1,556 @@ +# MIT License + +# Copyright (c) 2019 Robert Helgesson + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +{ config, lib, pkgs, ... }: + +with lib; + +let + + cfg = config.programs.emacs.init; + + packageFunctionType = mkOptionType { + name = "packageFunction"; + description = "function from epkgs to package"; + check = isFunction; + merge = mergeOneOption; + }; + + usePackageType = types.submodule ({ name, config, ... }: { + options = { + enable = mkEnableOption "Emacs package ${name}"; + + package = mkOption { + type = types.either (types.str // { description = "name of package"; }) + packageFunctionType; + default = name; + description = '' + The package to use for this module. Either the package name + within the Emacs package set or a function taking the Emacs + package set and returning a package. + ''; + }; + + defer = mkOption { + type = types.either types.bool types.ints.positive; + default = false; + description = '' + The setting. + ''; + }; + + defines = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + The entries to use for . + ''; + }; + + demand = mkOption { + type = types.bool; + default = false; + description = '' + The setting. + ''; + }; + + diminish = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + The entries to use for . + ''; + }; + + chords = mkOption { + type = types.attrsOf types.str; + default = { }; + example = { + "jj" = "ace-jump-char-mode"; + "jk" = "ace-jump-word-mode"; + }; + description = '' + The entries to use for . + ''; + }; + + functions = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + The entries to use for . + ''; + }; + + mode = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + The entries to use for . + ''; + }; + + after = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + The entries to use for . + ''; + }; + + bind = mkOption { + type = types.attrsOf types.str; + default = { }; + example = { + "M-" = "drag-stuff-up"; + "M-" = "drag-stuff-down"; + }; + description = '' + The entries to use for . + ''; + }; + + bindLocal = mkOption { + type = types.attrsOf (types.attrsOf types.str); + default = { }; + example = { + helm-command-map = { "C-c h" = "helm-execute-persistent-action"; }; + }; + description = '' + The entries to use for local keymaps in . + ''; + }; + + bindKeyMap = mkOption { + type = types.attrsOf types.str; + default = { }; + example = { "C-c p" = "projectile-command-map"; }; + description = '' + The entries to use for . + ''; + }; + + command = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + The entries to use for . + ''; + }; + + config = mkOption { + type = types.lines; + default = ""; + description = '' + Code to place in the section. + ''; + }; + + extraConfig = mkOption { + type = types.lines; + default = ""; + description = '' + Additional lines to place in the use-package configuration. + ''; + }; + + hook = mkOption { + type = types.listOf types.str; + default = [ ]; + description = '' + The entries to use for . + ''; + }; + + earlyInit = mkOption { + type = types.lines; + default = ""; + description = '' + Lines to add to when + this package is enabled. + + Note, the package is not automatically loaded so you will have to + require the necessary features yourself. + ''; + }; + + init = mkOption { + type = types.lines; + default = ""; + description = '' + The entries to use for . + ''; + }; + + extraPackages = mkOption { + type = types.listOf types.package; + default = [ ]; + description = '' + Extra packages to add to . + ''; + }; + + assembly = mkOption { + type = types.lines; + readOnly = true; + internal = true; + description = "The final use-package code."; + }; + }; + + config = mkIf config.enable { + assembly = let + quoted = v: ''"${escape [ ''"'' ] v}"''; + mkBindHelper = cmd: prefix: bs: + optionals (bs != { }) ([ ":${cmd} (${prefix}" ] + ++ mapAttrsToList (n: v: " (${quoted n} . ${v})") bs ++ [ ")" ]); + + mkAfter = vs: optional (vs != [ ]) ":after (${toString vs})"; + mkCommand = vs: optional (vs != [ ]) ":commands (${toString vs})"; + mkDefines = vs: optional (vs != [ ]) ":defines (${toString vs})"; + mkDiminish = vs: optional (vs != [ ]) ":diminish (${toString vs})"; + mkMode = map (v: ":mode ${v}"); + mkFunctions = vs: optional (vs != [ ]) ":functions (${toString vs})"; + mkBind = mkBindHelper "bind" ""; + mkBindLocal = bs: + let mkMap = n: v: mkBindHelper "bind" ":map ${n}" v; + in flatten (mapAttrsToList mkMap bs); + mkBindKeyMap = mkBindHelper "bind-keymap" ""; + mkChords = mkBindHelper "chords" ""; + mkHook = map (v: ":hook ${v}"); + mkDefer = v: + if isBool v then + optional v ":defer t" + else + [ ":defer ${toString v}" ]; + mkDemand = v: optional v ":demand t"; + in concatStringsSep "\n " ([ "(use-package ${name}" ] + ++ mkAfter config.after ++ mkBind config.bind + ++ mkBindKeyMap config.bindKeyMap ++ mkBindLocal config.bindLocal + ++ mkChords config.chords ++ mkCommand config.command + ++ mkDefer config.defer ++ mkDefines config.defines + ++ mkFunctions config.functions ++ mkDemand config.demand + ++ mkDiminish config.diminish ++ mkHook config.hook + ++ mkMode config.mode + ++ optionals (config.init != "") [ ":init" config.init ] + ++ optionals (config.config != "") [ ":config" config.config ] + ++ optional (config.extraConfig != "") config.extraConfig) + ")"; + }; + }); + + usePackageStr = name: pkgConfStr: '' + (use-package ${name} + ${pkgConfStr}) + ''; + + mkRecommendedOption = type: extraDescription: + mkOption { + type = types.bool; + default = false; + example = true; + description = '' + Whether to enable recommended ${type} settings. + '' + optionalString (extraDescription != "") '' + + ${extraDescription} + ''; + }; + + # Recommended GC settings. + gcSettings = '' + (defun hm/reduce-gc () + "Reduce the frequency of garbage collection." + (setq gc-cons-threshold most-positive-fixnum + gc-cons-percentage 0.6)) + + (defun hm/restore-gc () + "Restore the frequency of garbage collection." + (setq gc-cons-threshold 16777216 + gc-cons-percentage 0.1)) + + ;; Make GC more rare during init, while minibuffer is active, and + ;; when shutting down. In the latter two cases we try doing the + ;; reduction early in the hook. + (hm/reduce-gc) + (add-hook 'minibuffer-setup-hook #'hm/reduce-gc -50) + (add-hook 'kill-emacs-hook #'hm/reduce-gc -50) + + ;; But make it more regular after startup and after closing minibuffer. + (add-hook 'emacs-startup-hook #'hm/restore-gc) + (add-hook 'minibuffer-exit-hook #'hm/restore-gc) + + ;; Avoid unnecessary regexp matching while loading .el files. + (defvar hm/file-name-handler-alist file-name-handler-alist) + (setq file-name-handler-alist nil) + + (defun hm/restore-file-name-handler-alist () + "Restores the file-name-handler-alist variable." + (setq file-name-handler-alist hm/file-name-handler-alist) + (makunbound 'hm/file-name-handler-alist)) + + (add-hook 'emacs-startup-hook #'hm/restore-file-name-handler-alist) + ''; + + # Whether the configuration makes use of `:diminish`. + hasDiminish = any (p: p.diminish != [ ]) (attrValues cfg.usePackage); + + # Whether the configuration makes use of `:bind`. + hasBind = any (p: p.bind != { } || p.bindLocal != { } || p.bindKeyMap != { }) + (attrValues cfg.usePackage); + + # Whether the configuration makes use of `:chords`. + hasChords = any (p: p.chords != { }) (attrValues cfg.usePackage); + + usePackageSetup = '' + (eval-when-compile + (require 'use-package) + ;; To help fixing issues during startup. + (setq use-package-verbose ${ + if cfg.usePackageVerbose then "t" else "nil" + })) + + '' + optionalString hasDiminish '' + ;; For :diminish in (use-package). + (require 'diminish) + '' + optionalString hasBind '' + ;; For :bind in (use-package). + (require 'bind-key) + + ;; Fixes "Symbol’s function definition is void: use-package-autoload-keymap". + (autoload #'use-package-autoload-keymap "use-package-bind-key") + '' + optionalString hasChords '' + ;; For :chords in (use-package). + (use-package use-package-chords + :config (key-chord-mode 1)) + ''; + + earlyInitFile = '' + ;;; hm-early-init.el --- Emacs configuration à la Home Manager -*- lexical-binding: t; -*- + ;; + ;;; Commentary: + ;; + ;; The early init component of the Home Manager Emacs configuration. + ;; + ;;; Code: + + ${cfg.earlyInit} + + (provide 'hm-early-init) + ;; hm-early-init.el ends here + ''; + + initFile = '' + ;;; hm-init.el --- Emacs configuration à la Home Manager -*- lexical-binding: t; -*- + ;; + ;;; Commentary: + ;; + ;; A configuration generated from a Nix based configuration by + ;; Home Manager. + ;; + ;;; Code: + + ${optionalString cfg.startupTimer '' + (defun hm/print-startup-stats () + "Prints some basic startup statistics." + (let ((elapsed (float-time (time-subtract after-init-time + before-init-time)))) + (message "Startup took %.2fs with %d GCs" elapsed gcs-done))) + (add-hook 'emacs-startup-hook #'hm/print-startup-stats) + ''} + + ${cfg.prelude} + + ${usePackageSetup} + '' + concatStringsSep "\n\n" (map (getAttr "assembly") + (filter (getAttr "enable") (attrValues cfg.usePackage))) + '' + + ${cfg.postlude} + + (provide 'hm-init) + ;; hm-init.el ends here + ''; + +in { + options.programs.emacs.init = { + enable = mkEnableOption "Emacs configuration"; + + recommendedGcSettings = mkRecommendedOption "garbage collection" '' + This will reduce garbage collection frequency during startup and + while the minibuffer is active. + ''; + + startupTimer = mkEnableOption "Emacs startup duration timer"; + + earlyInit = mkOption { + type = types.lines; + default = ""; + description = '' + Configuration lines to add in early-init.el. + ''; + }; + + prelude = mkOption { + type = types.lines; + default = ""; + description = '' + Configuration lines to add in the beginning of + init.el. + ''; + }; + + postlude = mkOption { + type = types.lines; + default = ""; + description = '' + Configuration lines to add in the end of + init.el. + ''; + }; + + packageQuickstart = mkOption { + type = types.bool; + default = true; + description = '' + Whether to enable package-quickstart. This will make sure that + package.el is activated and all autoloads are + available. + + If disabled you can save quite a few milliseconds on the startup time, + but you will most likely have to tweak the command + option of various packages. + + As an example, running (emacs-init-time) on an Emacs + configuration with this option enabled reported ~300ms. Disabling the + option dropped the init time to ~200ms. + ''; + }; + + usePackageVerbose = mkEnableOption "verbose use-package mode"; + + usePackage = mkOption { + type = types.attrsOf usePackageType; + default = { }; + example = literalExpression '' + { + dhall-mode = { + mode = [ '''"\\.dhall\\'"''' ]; + }; + } + ''; + description = '' + Attribute set of use-package configurations. + ''; + }; + }; + + config = mkIf (config.programs.emacs.enable && cfg.enable) { + # Collect the extra packages that should be included in the user profile. + # These are typically tools called by Emacs packages. + home.packages = concatMap (v: v.extraPackages) + (filter (getAttr "enable") (builtins.attrValues cfg.usePackage)); + + programs.emacs.init.earlyInit = let + + standardEarlyInit = mkBefore '' + ${optionalString cfg.recommendedGcSettings gcSettings} + + ${if cfg.packageQuickstart then '' + (setq package-quickstart t + package-quickstart-file "hm-package-quickstart.el") + '' else '' + (setq package-enable-at-startup nil) + ''} + + ;; Avoid expensive frame resizing. Inspired by Doom Emacs. + (setq frame-inhibit-implied-resize t) + ''; + + # Collect the early initialization strings for each package. + packageEarlyInits = map (p: p.earlyInit) + (filter (p: p.earlyInit != "") (builtins.attrValues cfg.usePackage)); + + in mkMerge ([ standardEarlyInit ] ++ packageEarlyInits); + + programs.emacs.extraPackages = epkgs: + let + getPkg = v: + if isFunction v then + [ (v epkgs) ] + else + optional (isString v && hasAttr v epkgs) epkgs.${v}; + + packages = concatMap (v: getPkg (v.package)) + (filter (getAttr "enable") (builtins.attrValues cfg.usePackage)); + in [ + (epkgs.trivialBuild { + pname = "hm-early-init"; + src = pkgs.writeText "hm-early-init.el" earlyInitFile; + packageRequires = packages; + preferLocalBuild = true; + allowSubstitutes = false; + }) + + (epkgs.trivialBuild { + pname = "hm-init"; + src = pkgs.writeText "hm-init.el" initFile; + packageRequires = [ epkgs.use-package ] ++ packages + ++ optional hasBind epkgs.bind-key + ++ optional hasDiminish epkgs.diminish + ++ optional hasChords epkgs.use-package-chords; + preferLocalBuild = true; + allowSubstitutes = false; + preBuild = '' + # Do a bit of basic formatting of the generated init file. + emacs -Q --batch \ + --eval '(find-file "hm-init.el")' \ + --eval '(let ((indent-tabs-mode nil) (lisp-indent-offset 2)) (indent-region (point-min) (point-max)))' \ + --eval '(write-file "hm-init.el")' + + ${optionalString cfg.packageQuickstart '' + # Generate a package quickstart file to make autoloads and such + # available. + emacs -Q --batch \ + --eval "(require 'package)" \ + --eval "(setq package-quickstart-file \"hm-package-quickstart.el\")" \ + --eval "(package-quickstart-refresh)" + + # We know what we're doing? + sed -i '/no-byte-compile: t/d' hm-package-quickstart.el + ''} + ''; + }) + ]; + + home.file = { + ".emacs.d/early-init.el".text = '' + (require 'hm-early-init) + (provide 'early-init) + ''; + + ".emacs.d/init.el".text = '' + (require 'hm-init) + (provide 'init) + ''; + }; + }; +}