diff --git a/flake.lock b/flake.lock index b01b611..6e419b4 100644 --- a/flake.lock +++ b/flake.lock @@ -66,9 +66,6 @@ }, "emacsConfig": { "inputs": { - "home-manager": [ - "home-manager" - ], "nixpkgs": [ "nixpkgs" ] diff --git a/flake.nix b/flake.nix index 3b4914d..90193ab 100644 --- a/flake.nix +++ b/flake.nix @@ -32,7 +32,6 @@ emacsConfig = { url = "path:./flakes/emacs"; inputs.nixpkgs.follows = "nixpkgs"; - inputs.home-manager.follows = "home-manager"; }; }; diff --git a/flakes/emacs/README.md b/flakes/emacs/README.md new file mode 100644 index 0000000..2c87db6 --- /dev/null +++ b/flakes/emacs/README.md @@ -0,0 +1,54 @@ +# Tobmacs flake + +This flake builds the complete Emacs configuration without Home Manager. It +contains Emacs, all Emacs packages, tree-sitter grammars, snippets, Hunspell, +and dictionaries. + +## Run or install directly + +```console +nix run 'git+https://codeberg.org/oibot/dotfiles?dir=flakes/emacs' +nix profile install 'git+https://codeberg.org/oibot/dotfiles?dir=flakes/emacs' +``` + +From this repository, use `nix run path:./flakes/emacs`. + +## NixOS + +Add the flake as an input and make its nixpkgs follow the system nixpkgs: + +```nix +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + tobs-emacs = { + url = "git+https://codeberg.org/oibot/dotfiles?dir=flakes/emacs"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { nixpkgs, tobs-emacs, ... }: { + nixosConfigurations.server = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + modules = [ + tobs-emacs.nixosModules.default + { + # Installs Tobmacs and starts an Emacs user service. No Home Manager + # module is involved. + services.emacs.enable = true; + services.emacs.startWithGraphical = false; + } + ]; + }; + }; +} +``` + +If no daemon is wanted, either set `services.emacs.install = true` instead of +`enable`, or install the package directly: + +```nix +environment.systemPackages = [ + tobs-emacs.packages.${pkgs.stdenv.hostPlatform.system}.default +]; +``` diff --git a/flakes/emacs/flake.lock b/flakes/emacs/flake.lock new file mode 100644 index 0000000..5b993c3 --- /dev/null +++ b/flakes/emacs/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1784120854, + "narHash": "sha256-KesHgItiZPgGX740axSiQLcIQ8D24MDqNpkKYWIek8k=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "753cc8a3a87467296ddd1fa93f0cc3e81120ee46", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flakes/emacs/flake.nix b/flakes/emacs/flake.nix index a87b52d..4dc5e2c 100644 --- a/flakes/emacs/flake.nix +++ b/flakes/emacs/flake.nix @@ -1,15 +1,55 @@ { - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - home-manager.url = "github:nix-community/home-manager/"; - home-manager.inputs.nixpkgs.follows = "nixpkgs"; - }; + description = "Standalone Tobmacs package and NixOS integration"; - outputs = inputs: + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + + outputs = { self, nixpkgs }: + let + systems = [ + "aarch64-darwin" + "aarch64-linux" + "x86_64-linux" + ]; + forAllSystems = nixpkgs.lib.genAttrs systems; + in { + packages = forAllSystems (system: + let + pkgs = import nixpkgs { + inherit system; + config.allowUnfreePredicate = package: + builtins.elem (nixpkgs.lib.getName package) [ + "copilot-language-server" + ]; + }; + in + { + default = import ./package.nix { inherit pkgs; }; + tobs-emacs = self.packages.${system}.default; + }); + + apps = forAllSystems (system: { + default = { + type = "app"; + program = "${self.packages.${system}.default}/bin/emacs"; + meta.description = "Run Tobmacs"; + }; + }); + + # Importing this module selects Tobmacs as services.emacs.package. The + # caller still decides whether to enable or merely install the service. + nixosModules.default = { pkgs, ... }: { + services.emacs.package = + self.packages.${pkgs.stdenv.hostPlatform.system}.default; + }; + + # Optional compatibility integration for this dotfiles repository. The + # standalone package and NixOS module above do not depend on Home Manager. homeManagerModules = { - aarch64-darwin = import ./home.nix; - x86_64-linux = import ./home.nix; + default = import ./home.nix { inherit self; }; + aarch64-darwin = self.homeManagerModules.default; + aarch64-linux = self.homeManagerModules.default; + x86_64-linux = self.homeManagerModules.default; }; }; } diff --git a/flakes/emacs/home.nix b/flakes/emacs/home.nix index 321e8d7..b09e210 100644 --- a/flakes/emacs/home.nix +++ b/flakes/emacs/home.nix @@ -1,125 +1,17 @@ -{ config, pkgs, lib, ... }: +{ self }: +{ pkgs, lib, ... }: let - 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; - }; + emacs = self.packages.${pkgs.stdenv.hostPlatform.system}.default; in { - home.packages = with pkgs; [ - enchant - hunspell - hunspellDicts.de_DE - hunspellDicts.en_US - ]; + home.packages = [ emacs ]; - home.file = - (lib.mapAttrs' - (language: grammar: - lib.nameValuePair - ".emacs.d/tree-sitter/libtree-sitter-${language}.${treeSitterLibraryExtension}" - { source = "${grammar}/parser"; }) - treeSitterGrammars) - // { - ".emacs.d/early-init.el".source = ./config/early-init.el; - ".emacs.d/snippets".source = ./config/snippets; - }; - - programs.emacs = { - enable = true; - extraConfig = builtins.readFile ./config/init.el + - builtins.readFile ./config/lisp/utils.el; - extraPackages = epkgs: - let - myEpkgs = epkgs.overrideScope (import ./pkgs { inherit pkgs; }); - in - with myEpkgs; [ - 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 - git-timemachine - graphql-mode - helpful - htmlize - jinx - js2-mode - json-mode - ledger-mode - ligature - markdown-mode - minions - multiple-cursors - nerd-icons - nerd-icons-completion - nerd-icons-corfu - nerd-icons-dired - nerd-icons-ibuffer - ob-restclient - ob-swift - olivetti - orderless - org - org-modern - ox-gfm - ox-reveal - pulsar - rainbow-delimiters - rainbow-mode - restclient - smartparens - swift-mode - swift-ts-mode - super-save - terraform-mode - treesit-auto - ob-typescript - yaml-mode - vterm - fontaine - magit - magit-todos - marginalia - modus-themes - nix-mode - vertico - web-mode - winpulse - yasnippet - ]; - }; - - launchd.agents.emacs-server = { + launchd.agents.emacs-server = lib.mkIf pkgs.stdenv.hostPlatform.isDarwin { enable = true; config = { KeepAlive = true; ProgramArguments = [ - "${config.programs.emacs.finalPackage}/bin/emacs" + "${emacs}/bin/emacs" "--fg-daemon" ]; RunAtLoad = true; diff --git a/flakes/emacs/package.nix b/flakes/emacs/package.nix new file mode 100644 index 0000000..7fc23c9 --- /dev/null +++ b/flakes/emacs/package.nix @@ -0,0 +1,158 @@ +{ 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"; + }; +}