diff --git a/README.md b/README.md index 990f5b5..dd4a126 100644 --- a/README.md +++ b/README.md @@ -1 +1,38 @@ -# nix-doom-emacs \ No newline at end of file +# nix-doom-emacs + +Nix expression to install and configure +[doom-emacs](https://github.com/hlissner/doom-emacs). + +The expression builds a `doom-emacs` distribution with dependencies +pre-installed based on an existing `~/.doom.d` directory. + +It is not a fully fledged exprerience as some dependenices are not installed and +some may not be fully compatible as the version available in NixOS or +[emacs-overlay](https://github.com/nix-community/emacs-overlay) may not be +compatible with the `doom-emacs` requirements. + +## Getting started + +Using [home-manager](https://github.com/rycee/home-manager): + + ``` nix +{ pkgs, ... }: + +let + doomPrivateDir = ./doom.d; + + doom-emacs = pkgs.callPackage (builtins.fetchTarball { + url = https://github.com/vlaci/nix-doom-emacs/archive/master.tar.gz; + }) { inherit doomPrivateDir; }; +in { + home.packages = [ doom-emacs ]; + home.file.".doom.d".source = doomPrivateDir; + home.file.".emacs.d".source = doom-emacs.emacsd; +} +``` + +## Under the hood + +This expression leverages +[nix-straight.el](https://github.com/vlaci/nix-straight.el) under the hood for +installing depdendencies. The restrictions of that package apply here too. diff --git a/advice.el b/advice.el new file mode 100644 index 0000000..9e85ca8 --- /dev/null +++ b/advice.el @@ -0,0 +1,20 @@ +;;; -*- lexical-binding: t; -*- +(advice-add 'nix-straight-get-used-packages + :before (lambda (&rest r) + (message "Advising doom installer to gather packages to install...") + (advice-add 'doom-reload-autoloads + :override (lambda (&optional file force-p) + (message "Skipping generating autoloads..."))) + (advice-add 'doom--format-print + :around (lambda (orig-print &rest r) + (let ((noninteractive nil)) + (apply orig-print r)))))) + + + +(advice-add 'doom-call-process + :before (lambda (&rest r) + (message "> call-process %s" r))) +(advice-add 'doom-call-process + :after (lambda (&rest r) + (message "< call-process %s" r))) diff --git a/default.nix b/default.nix new file mode 100644 index 0000000..fb07c09 --- /dev/null +++ b/default.nix @@ -0,0 +1,145 @@ +{ # The files would be going to ~/.config/doom (~/.doom.d) + doomPrivateDir + # Package set to install emacs and dependent packages from +, emacsPackages +, lib +, pkgs +, stdenv +, buildEnv +, makeWrapper +, runCommand +, fetchFromGitHub +, substituteAll +, writeScriptBin +, writeTextDir }: + +let + # Packages we need to get the default doom configuration run + overrides = self: super: { + evil-escape = super.evil-escape.overrideAttrs (esuper: { + patches = [ ./evil-escape.patch ]; + }); + org-yt = self.trivialBuild rec { + pname = "org-yt"; + version = "1"; + recipe = null; + ename = pname; + src = fetchFromGitHub { + owner = "TobiasZawada"; + repo = "org-yt"; + rev = "40cc1ac76d741055cbefa13860d9f070a7ade001"; + sha256 = "0jsm3azb7lwikvc53z4p91av8qvda9s15wij153spkgjp83kld3p"; + }; + }; + }; + + # Stage 1: prepare source for byte-compilation + doomSrc = stdenv.mkDerivation { + name = "doom-src"; + src = fetchFromGitHub { + owner = "hlissner"; + repo = "doom-emacs"; + rev = "22ae9cca15f5aa9215cf0ec2c7b0b78d64deddc0"; + sha256 = "0nya6qf2v0snd4zskxxradqdpiylpx3lxfrfi7xs04yb39ma99pn"; + }; + phases = ["unpackPhase" "patchPhase" "installPhase"]; + patches = [ + (substituteAll { + src = ./fix-paths-pre.patch; + private = builtins.toString doomPrivateDir; + }) + ]; + installPhase = '' + mkdir -p $out + cp -r * $out + ''; + }; + + # Stage 2:: install dependencies and byte-compile prepared source + doomLocal = + let + straight-env = pkgs.callPackage (fetchFromGitHub { + owner = "vlaci"; + repo = "nix-straight.el"; + rev = "v1.0"; + sha256 = "038dss49bfvpj15psh5pr9jyavivninl0rzga9cn8qyc4g2cj5i0"; + }) { + emacsPackages = emacsPackages.overrideScope' overrides; + emacsLoadFiles = [ ./advice.el ]; + emacsArgs = [ + "install" + "--no-fonts" + "--no-env" + ]; + + # Need to reference a store path here, as byte-compilation will bake-in + # absolute path to source files. + emacsInitFile = "${doomSrc}/bin/doom"; + }; + + packages = straight-env.packageList (super: { + phases = [ "installPhase" ]; + preInstall = '' + export DOOMDIR=$(mktemp -d) + export DOOMLOCALDIR=$DOOMDIR/local/ + cp ${doomPrivateDir}/* $DOOMDIR + ''; + }); + + # I don't know why but byte-compilation somehow triggers Emacs to look for + # the git executable. It does not seem to be executed though... + git = writeScriptBin "git" '' + >&2 echo Executing git is not allowed; command line: "$@" + ''; + in (straight-env.emacsEnv { + inherit packages; + straightDir = "$DOOMLOCALDIR/straight"; + }).overrideAttrs (super: { + phases = [ "installPhase" ]; + buildInputs = super.buildInputs ++ [ git ]; + preInstall = '' + export DOOMDIR=$(mktemp -d) + export DOOMLOCALDIR=$out/ + mkdir -p $DOOMDIR + cp ${doomPrivateDir}/* $DOOMDIR + ''; + }); + + # Stage 3: do additional fixups to refer compiled files in the store + # and additional files in the users' home + doom-emacs = stdenv.mkDerivation rec { + name = "doom-emacs"; + src = doomSrc; + patches = [ + (substituteAll { + src = ./fix-paths.patch; + local = doomLocal; + }) + ]; + buildPhase = ":"; + installPhase = '' + mkdir -p $out + cp -r * $out + ''; + }; + + # Stage 4: catch-all wrapper capable to run doom-emacs even + # without installing ~/.emacs.d + emacs = (emacsPackages.emacsWithPackages (epkgs: [ + (writeTextDir "share/emacs/site-lisp/default.el" '' + (message "doom-emacs is not placed in `doom-private-dir', + loading from `site-lisp'") + (when (> emacs-major-version 26) + (load "${doom-emacs}/early-init.el")) + (load "${doom-emacs}/init.el") + '') + ])).overrideAttrs (super: { + outputs = [ "out" "emacsd" ]; + buildInputs = [ doom-emacs ]; + installPhase = super.installPhase + '' + echo ln -snf ${doom-emacs} $emacsd + ln -snf ${doom-emacs} $emacsd + ''; + }); + +in emacs diff --git a/evil-escape.patch b/evil-escape.patch new file mode 100644 index 0000000..71a6077 --- /dev/null +++ b/evil-escape.patch @@ -0,0 +1,8 @@ +--- a/evil-escape.el ++++ b/evil-ewcape.el +@@ -1,4 +1,4 @@ +- ;;; evil-escape.el --- Escape from anything with a customizable key sequence ++;;; evil-escape.el --- Escape from anything with a customizable key sequence + + ;; Copyright (C) 2014-2015 syl20bnr + ;; diff --git a/fix-paths-pre.patch b/fix-paths-pre.patch new file mode 100644 index 0000000..36ae118 --- /dev/null +++ b/fix-paths-pre.patch @@ -0,0 +1,13 @@ +diff --git a/core/autoload/config.el b/core/autoload/config.el +index 6bb9ae300..81ab62860 100644 +--- a/core/autoload/config.el ++++ b/core/autoload/config.el +@@ -20,7 +20,7 @@ + (defun doom/find-file-in-private-config () + "Search for a file in `doom-private-dir' inside nixos-config." + (interactive) +- (doom-project-find-file doom-private-dir)) ++ (doom-project-find-file "@private@")) + + ;;;###autoload + (defun doom/reload () diff --git a/fix-paths.patch b/fix-paths.patch new file mode 100644 index 0000000..f6bcd81 --- /dev/null +++ b/fix-paths.patch @@ -0,0 +1,50 @@ +diff --git a/bin/doom b/bin/doom +index bd381e5ab..37cc69569 100755 +--- a/bin/doom ++++ b/bin/doom +@@ -6,7 +6,7 @@ + ":"; DOOMBASE=$(dirname "$0")/.. + ":"; [ "$1" = -d ] || [ "$1" = --debug ] && { shift; export DEBUG=1; } + ":"; [ "$1" = doc ] || [ "$1" = doctor ] && { cd "$DOOMBASE"; shift; exec $EMACS --script bin/doom-doctor "$@"; exit 0; } +-":"; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/doom "$@"; exit 0; } ++":"; [ "$1" = run ] && { cd "$DOOMBASE"; shift; exec $EMACS -q --no-splash -l bin/.doom-wrapped "$@"; exit 0; } + ":"; exec $EMACS --script "$0" -- "$@" + ":"; exit 0 + +diff --git a/core/core.el b/core/core.el +index 9cc1cfc25..fcbc3a3cf 100644 +--- a/core/core.el ++++ b/core/core.el +@@ -54,20 +54,20 @@ decrease this. If you experience stuttering, increase this.") + + (defvar doom-local-dir + (or (getenv "DOOMLOCALDIR") +- (concat doom-emacs-dir ".local/")) ++ "@local@/") + "Root directory for local storage. + + Use this as a storage location for this system's installation of Doom Emacs. + These files should not be shared across systems. By default, it is used by + `doom-etc-dir' and `doom-cache-dir'. Must end with a slash.") + +-(defvar doom-etc-dir (concat doom-local-dir "etc/") ++(defvar doom-etc-dir "~/.local/doom" + "Directory for non-volatile local storage. + + Use this for files that don't change much, like server binaries, external + dependencies or long-term shared data. Must end with a slash.") + +-(defvar doom-cache-dir (concat doom-local-dir "cache/") ++(defvar doom-cache-dir "~/.cache/doom/" + "Directory for volatile local storage. + + Use this for files that change often, like cache files. Must end with a slash.") +@@ -189,7 +189,7 @@ users).") + (setq abbrev-file-name (concat doom-local-dir "abbrev.el") + async-byte-compile-log-file (concat doom-etc-dir "async-bytecomp.log") + bookmark-default-file (concat doom-etc-dir "bookmarks") +- custom-file (concat doom-private-dir "init.el") ++ custom-file (concat doom-etc-dir "init.el") + custom-theme-directory (concat doom-private-dir "themes/") + desktop-dirname (concat doom-etc-dir "desktop") + desktop-base-file-name "autosave"