{ config, pkgs, lib, ... }: let cfg = config.hive.doom; doom-pkgs = with pkgs; [ cmake emacs-all-the-icons-fonts fira fira-code-symbols fontconfig gcc nerd-fonts.fira-code ]; default-core-pkgs = with pkgs; [ (ripgrep.override {withPCRE2 = true;}) (aspellWithDicts (d: [d.en d.de d.en-computers d.en-science])) binutils editorconfig-core-c fd git gnumake gnutls ispell libtool vscode-langservers-extracted ]; default-shell-pkgs = with pkgs; [ emmet-ls ]; default-nix-pkgs = with pkgs; [ alejandra nixd ]; default-latex-pkgs = with pkgs; [ pandoc poppler texlab texlive.combined.scheme-medium ]; default-cxx-pkgs = with pkgs; [ clang clang-tools cmake cppcheck doxygen gdb ninja ]; doom-path-pkgs = lib.optionals cfg.withLatexPkgs (cfg.overrideLatexPkgs default-latex-pkgs) ++ lib.optionals cfg.withShellPkgs (cfg.overrideShellPkgs default-shell-pkgs) ++ lib.optionals cfg.withNixPkgs (cfg.overrideNixPkgs default-nix-pkgs) ++ lib.optionals cfg.withCXXPkgs (cfg.overrideCXXPkgs default-cxx-pkgs) ++ lib.optional cfg.enableCopilot pkgs.unstable.copilot-language-server ++ default-core-pkgs; doom-socket-name = "main"; wrapped-emacs = pkgs.symlinkJoin { name = "wrapped-emacs"; paths = [pkgs.emacs30]; nativeBuildInputs = [pkgs.makeBinaryWrapper]; postBuild = '' wrapProgram $out/bin/emacs \ --prefix PATH : ${lib.makeBinPath doom-path-pkgs} \ --add-flags "--init-directory=${config.xdg.configHome}/doom-emacs" \ --set DOOMDIR "${config.home.sessionVariables.DOOMDIR}" \ --set DOOMLOCALDIR "${config.home.sessionVariables.DOOMLOCALDIR}" wrapProgram $out/bin/emacsclient \ --prefix PATH : ${lib.makeBinPath doom-path-pkgs} \ --set DOOMDIR "${config.home.sessionVariables.DOOMDIR}" \ --set DOOMLOCALDIR "${config.home.sessionVariables.DOOMLOCALDIR}" ''; }; doom-setup = pkgs.writeShellScript "doom-setup" '' export PATH="${lib.makeBinPath doom-path-pkgs}:$PATH" export EMACS="${wrapped-emacs}/bin/emacs" export DOOMDIR="${config.home.sessionVariables.DOOMDIR}" export DOOMLOCALDIR="${config.home.sessionVariables.DOOMLOCALDIR}" if [ ! -d "$DOOMLOCALDIR" ]; then ${config.xdg.configHome}/doom-emacs/bin/doom install --force --no-env else ${config.xdg.configHome}/doom-emacs/bin/doom "$@" fi ''; doom-open = pkgs.writeShellScriptBin "doom-open" '' if [ -t 0 ]; then exec ${wrapped-emacs}/bin/emacsclient -t "$@" else exec ${wrapped-emacs}/bin/emacsclient -c "$@" fi ''; in { options.hive.doom = { enable = lib.mkEnableOption "Enable Doom Emacs"; asDefaultEditor = lib.mkOption { type = lib.types.bool; default = false; description = "set the EDITOR variable to use the current emacs server (graphical/non-graphical)"; }; enableCopilot = lib.mkEnableOption "Enable Copilot in Doom Emacs"; enableTidal = lib.mkEnableOption "Enable TidalCycles"; withLatexPkgs = lib.mkEnableOption "Enable LaTeX packages in doom path"; withShellPkgs = lib.mkEnableOption "Enable shell packages in doom path"; withNixPkgs = lib.mkEnableOption "Enable LaTeX packages in doom path"; withCXXPkgs = lib.mkEnableOption "Enable CXX packages in doom path"; overrideLatexPkgs = lib.mkOption { type = lib.types.functionTo (lib.types.listOf lib.types.package); default = pkgs: pkgs; example = '' prev: with pkgs; [ texlive.combined.scheme-full ] ''; description = "Override the default LaTeX packages in the doom path."; }; overrideShellPkgs = lib.mkOption { type = lib.types.functionTo (lib.types.listOf lib.types.package); default = pkgs: pkgs; example = '' prev: with pkgs; [ shellcheck ] ''; description = "Override the default shell packages in the doom path."; }; overrideNixPkgs = lib.mkOption { type = lib.types.functionTo (lib.types.listOf lib.types.package); default = pkgs: pkgs; example = '' prev: with pkgs; [ nixpkgs-fmt ] ''; description = "Override the default Nix packages in the doom path."; }; overrideCXXPkgs = lib.mkOption { type = lib.types.functionTo (lib.types.listOf lib.types.package); default = pkgs: pkgs; example = '' prev: with pkgs; [ openmp ] ''; description = "Override the default C++ packages in the doom path."; }; }; config = lib.mkIf cfg.enable { fonts.fontconfig.enable = true; programs.emacs = { enable = true; package = wrapped-emacs; }; home = { sessionPath = ["${config.xdg.configHome}/doom-emacs/bin"]; sessionVariables = { DOOMDIR = "${config.xdg.configHome}/doom-config"; DOOMLOCALDIR = "${config.xdg.configHome}/doom-local"; } // lib.optionalAttrs cfg.asDefaultEditor { EDITOR = "${doom-open}"; }; packages = doom-pkgs ++ lib.optional cfg.asDefaultEditor doom-open; }; systemd.user.services.doom-emacs-server = { Unit = { Description = "Doom Emacs Server"; }; Service = { ExecStart = "${wrapped-emacs}/bin/emacs --fg-daemon=${doom-socket-name}"; SuccessExitStatus = 15; }; Install = { WantedBy = []; # Lazy start by socket }; }; systemd.user.sockets.doom-emacs-server = { Socket = { ListenStream = "/run/user/%U/emacs/${doom-socket-name}"; DirectoryMode = "0700"; }; Install = { WantedBy = ["sockets.target"]; }; }; xdg = { enable = true; configFile = { "doom-config/splash.png" = { source = ./static/splash.png; }; "doom-config/config.el" = { source = ./static/config.el; }; "doom-config/config.d/copilot.el" = { enable = cfg.enableCopilot; source = ./static/config.d/copilot.el; }; "doom-config/packages/treesit-docgen.el" = { source = ./static/packages/treesit-docgen.el; }; "doom-config/init.el" = { source = ./static/init.el; onChange = "${doom-setup} sync --force -e"; }; "doom-config/packages.el" = { source = ./static/packages.el; onChange = "${doom-setup} sync --force -u -e"; }; "doom-config/packages.d/copilot.el" = { enable = cfg.enableCopilot; source = ./static/packages.d/copilot.el; onChange = "${doom-setup} sync --force -u -e"; }; "doom-config/packages.d/tidal.el" = { enable = cfg.enableTidal; source = ./static/packages.d/tidal.el; onChange = "${doom-setup} sync --force -u -e"; }; "doom-emacs" = { source = builtins.fetchGit { url = "https://github.com/doomemacs/doomemacs"; rev = "c27621a777c11354a4913c7eb455db3766984709"; }; onChange = "${doom-setup} --force sync -u -e"; }; }; desktopEntries = { emacs = { name = "Doom Emacs"; genericName = "Text Editor"; icon = ./static/icon.png; exec = "${wrapped-emacs}/bin/emacs %F"; terminal = false; categories = ["Application" "Development" "TextEditor"]; mimeType = ["text/*"]; settings = { StartupWMClass = "Doom Emacs"; }; }; emacsclient = { name = "Doom Emacs (Client)"; genericName = "Text Editor"; icon = ./static/icon.png; exec = '' sh -c "if [ -n \\"\\$*\\" ]; then exec ${wrapped-emacs}/bin/emacsclient --alternate-editor= --display=\\"\\$DISPLAY\\" \\"\\$@\\"; else exec emacsclient --alternate-editor= --create-frame; fi" sh %F ''; terminal = false; categories = ["Application" "Development" "TextEditor"]; mimeType = ["text/*"]; settings = { StartupWMClass = "Doom Emacs"; }; }; }; }; }; }