{ pkgs }: let inherit (pkgs) lib; treeSitterLibraryExtension = if pkgs.stdenv.hostPlatform.isDarwin then "dylib" else "so"; treeSitterGrammars = { swift = pkgs.tree-sitter-grammars.tree-sitter-swift; python = pkgs.tree-sitter-grammars.tree-sitter-python; typescript = pkgs.tree-sitter-grammars.tree-sitter-typescript; tsx = pkgs.tree-sitter-grammars.tree-sitter-tsx; javascript = pkgs.tree-sitter-grammars.tree-sitter-javascript; json = pkgs.tree-sitter-grammars.tree-sitter-json; }; treeSitterBundle = pkgs.runCommand "tobmacs-tree-sitter-grammars" { } ( '' mkdir -p "$out/lib" '' + lib.concatStringsSep "\n" (lib.mapAttrsToList (language: grammar: '' ln -s "${grammar}/parser" \ "$out/lib/libtree-sitter-${language}.${treeSitterLibraryExtension}" '') treeSitterGrammars) ); epkgs = (pkgs.emacsPackagesFor pkgs.emacs).overrideScope ( import ./pkgs { inherit pkgs; } ); emacsPackages = with epkgs; [ agent-shell apheleia avy cape catppuccin-theme cider clojure-mode consult consult-denote copilot corfu denote diff-hl dockerfile-mode ef-themes eglot envrc exec-path-from-shell flycheck flycheck-clj-kondo flycheck-swiftlint flymake-collection fontaine git-timemachine graphql-mode helpful htmlize jinx js2-mode json-mode ledger-mode ligature magit magit-todos marginalia markdown-mode minions modus-themes multiple-cursors nerd-icons nerd-icons-completion nerd-icons-corfu nerd-icons-dired nerd-icons-ibuffer nix-mode ob-restclient ob-swift ob-typescript olivetti orderless org org-modern ox-gfm ox-reveal pulsar rainbow-delimiters rainbow-mode restclient smartparens super-save swift-mode swift-ts-mode terraform-mode treesit-auto vertico vterm web-mode winpulse yaml-mode yasnippet ]; configDirectory = pkgs.runCommand "tobmacs-config" { } '' mkdir -p "$out" cp ${./config/early-init.el} "$out/early-init.el" cat > "$out/init.el" <<'EOF' ;; -*- lexical-binding: t; -*- ;; --init-directory points at the immutable Nix store. Reset this after ;; startup has found init.el so mutable state remains in the user's home. (setq user-emacs-directory (expand-file-name "~/.emacs.d/")) (make-directory user-emacs-directory t) (setq yas-snippet-dirs '("${./config/snippets}")) ${builtins.readFile ./config/init.el} ${builtins.readFile ./config/lisp/utils.el} EOF ''; emacsWithPackages = epkgs.emacsWithPackages ( _: emacsPackages ++ [ treeSitterBundle ] ); # Make this package own its init directory, including a real early-init.el. # The inner emacsWithPackages wrapper still supplies all package load paths. emacs = pkgs.symlinkJoin { name = "tobmacs-emacs"; paths = [ emacsWithPackages ]; nativeBuildInputs = [ pkgs.makeBinaryWrapper ]; postBuild = '' wrapProgramBinary "$out/bin/emacs" \ --add-flags "--init-directory=${configDirectory}" if [ -x "$out/Applications/Emacs.app/Contents/MacOS/Emacs" ]; then wrapProgramBinary "$out/Applications/Emacs.app/Contents/MacOS/Emacs" \ --add-flags "--init-directory=${configDirectory}" fi ''; meta = emacsWithPackages.meta; }; in pkgs.buildEnv { name = "tobmacs"; paths = [ emacs pkgs.enchant pkgs.hunspell pkgs.hunspellDicts.de_DE pkgs.hunspellDicts.en_US ]; meta = emacs.meta // { description = "Emacs with Tobias Ostner's configuration and packages"; mainProgram = "emacs"; }; }