├── .gitignore ├── doomdir ├── config.org ├── packages.el └── init.el ├── .github ├── dependabot.yml ├── workflows │ ├── flake-update.yml │ ├── push-flake-update.yml │ ├── check.yml │ ├── ci.yml │ └── cachix.yml └── actions │ └── cache-downloads │ └── action.yml ├── elisp-packages-early.nix ├── CONTRIBUTING.md ├── flake.lock ├── tests.el ├── elisp-patches ├── apel-library.patch ├── semi-library.patch ├── flim-library.patch └── wanderlust-library.patch ├── init.el ├── fetch-overrides.nix ├── HACKING.md ├── checks.nix ├── flake.nix ├── home-manager.nix ├── LICENSE ├── elisp-packages-late.nix ├── README.md └── default.nix /.gitignore: -------------------------------------------------------------------------------- 1 | /result 2 | -------------------------------------------------------------------------------- /doomdir/config.org: -------------------------------------------------------------------------------- 1 | #+title: Config 2 | 3 | Example literate config, used by ~$nix build .#doom-emacs-tangle~. 4 | 5 | #+begin_src emacs-lisp :tangle init.el 6 | (doom! :lang org) 7 | #+end_src 8 | -------------------------------------------------------------------------------- /doomdir/packages.el: -------------------------------------------------------------------------------- 1 | ;; Copyright 2024 Google LLC 2 | ;; 3 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 4 | ;; you may not use this file except in compliance with the License. 5 | ;; You may obtain a copy of the License at 6 | ;; 7 | ;; http://www.apache.org/licenses/LICENSE-2.0 8 | ;; 9 | ;; Unless required by applicable law or agreed to in writing, software 10 | ;; distributed under the License is distributed on an "AS IS" BASIS, 11 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | ;; See the License for the specific language governing permissions and 13 | ;; limitations under the License. 14 | 15 | ;; -*- no-byte-compile: t; -*- 16 | ;;; $DOOMDIR/packages.el 17 | 18 | (package! evil-escape :disable t) 19 | 20 | (package! dotenv-mode) 21 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | version: 2 16 | updates: 17 | - package-ecosystem: "github-actions" 18 | directory: "/" # Location of package manifests 19 | schedule: 20 | interval: "weekly" 21 | -------------------------------------------------------------------------------- /elisp-packages-early.nix: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Overrides applied before Doom's pins. 16 | # 17 | # Use sparingly: these override the original derivation even if Doom's pins are not applied to it 18 | # (if the package is used as a transitive dependency without Doom's module for it being enabled). 19 | # This means packages defined here must build as-is / must still build without src overridden. 20 | 21 | { 22 | eself, 23 | esuper, 24 | }: 25 | { 26 | # Upstream renamed from opencl-mode to opencl-c-mode. melpa2nix requires single-file-package file 27 | # names match the package name. So rename the package (not the file, just in case someone loads it 28 | # explicitly). 29 | opencl-mode = esuper.opencl-c-mode; 30 | } 31 | -------------------------------------------------------------------------------- /doomdir/init.el: -------------------------------------------------------------------------------- 1 | ;;; init.el -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright 2024 Google LLC 4 | ;; 5 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 6 | ;; you may not use this file except in compliance with the License. 7 | ;; You may obtain a copy of the License at 8 | ;; 9 | ;; http://www.apache.org/licenses/LICENSE-2.0 10 | ;; 11 | ;; Unless required by applicable law or agreed to in writing, software 12 | ;; distributed under the License is distributed on an "AS IS" BASIS, 13 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ;; See the License for the specific language governing permissions and 15 | ;; limitations under the License. 16 | 17 | (doom! :completion 18 | vertico 19 | 20 | :ui 21 | doom 22 | doom-dashboard 23 | modeline 24 | nav-flash 25 | ophints 26 | (popup +defaults) 27 | window-select 28 | 29 | :editor 30 | evil 31 | 32 | :emacs 33 | undo 34 | 35 | :term 36 | eshell 37 | vterm 38 | 39 | :os 40 | (:if (featurep :system 'macos) macos) 41 | (tty +osc) 42 | 43 | :lang 44 | emacs-lisp 45 | (nix +lsp) 46 | 47 | :tools 48 | (lsp +eglot) 49 | 50 | :config 51 | (default +bindings +smartparens)) 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | I love patches! 4 | 5 | Because this is a [side 6 | project](https://opensource.google/documentation/reference/releasing) I released 7 | while working for Google, you will need to sign a CLA before I can accept them. 8 | 9 | ## Before you begin 10 | 11 | ### Sign our Contributor License Agreement 12 | 13 | Contributions to this project must be accompanied by a 14 | [Contributor License Agreement](https://cla.developers.google.com/about) (CLA). 15 | You (or your employer) retain the copyright to your contribution; this simply 16 | gives us permission to use and redistribute your contributions as part of the 17 | project. 18 | 19 | If you or your current employer have already signed the Google CLA (even if it 20 | was for a different project), you probably don't need to do it again. 21 | 22 | Visit to see your current agreements or to 23 | sign a new one. 24 | 25 | ### Review our Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | 30 | ## Contribution process 31 | 32 | ### Code Reviews 33 | 34 | All submissions, including submissions by project members, require review. We 35 | use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests) 36 | for this purpose. 37 | -------------------------------------------------------------------------------- /.github/workflows/flake-update.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: nix flake update 16 | 17 | on: 18 | workflow_dispatch: 19 | workflow_call: 20 | 21 | jobs: 22 | update: 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: "write" 26 | steps: 27 | - name: Check out repository 28 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 29 | with: 30 | ref: main 31 | - name: Install Nix 32 | uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31 33 | with: 34 | github_access_token: '${{ secrets.GITHUB_TOKEN }}' 35 | - name: Update and push flake.lock 36 | run: | 37 | git config user.email "github-actions[bot]@users.noreply.github.com" 38 | git config user.name "github-actions[bot]" 39 | git checkout -b flake-update 40 | nix flake update --commit-lock-file 41 | git push --force origin flake-update 42 | -------------------------------------------------------------------------------- /.github/actions/cache-downloads/action.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: 'Cache downloads' 16 | description: "Cache Nix's git and tarball cache in GitHub's action cache." 17 | runs: 18 | using: "composite" 19 | steps: 20 | # Update the caches daily, flush the cache monthly. 21 | - name: Set cache keys 22 | id: cache-keys 23 | shell: bash 24 | run: | 25 | { 26 | echo "key=$(date +'%Y-%m-%d')" 27 | echo "restore=$(date +'%Y-%m-')" 28 | } >> "$GITHUB_OUTPUT" 29 | - name: Cache git checkouts 30 | uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 31 | with: 32 | path: ~/.cache/nix/gitv3 33 | key: nix-gitv3-cache-${{ steps.cache-keys.outputs.key }} 34 | restore-keys: nix-gitv3-cache-${{ steps.cache-keys.outputs.restore }} 35 | - name: Cache tarballs 36 | uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 37 | with: 38 | path: ~/.cache/nix/tarball-cache 39 | key: nix-tarball-cache-${{ steps.cache-keys.outputs.key }} 40 | restore-keys: nix-tarball-cache-${{ steps.cache-keys.outputs.restore }} 41 | -------------------------------------------------------------------------------- /.github/workflows/push-flake-update.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: Push flake-update 16 | 17 | on: 18 | workflow_dispatch: 19 | workflow_call: 20 | 21 | jobs: 22 | commit: 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: "write" 26 | steps: 27 | - name: Check out repository 28 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 29 | with: 30 | ref: flake-update 31 | # Fetch the parent of the commit we want to push too: 32 | # that should be the current ref of `main`, which we need 33 | # for push to succeed (without forcing). 34 | fetch-depth: 2 35 | - name: Push changes 36 | run: git push origin HEAD:main 37 | # `git push` only works because branch protection is not enabled. 38 | # 39 | # Currently branch protection is not effective anyway, since the only 40 | # contributor (marienz) has admin permissions, and applying branch 41 | # protection to administrators seems to be an "organization" feature. 42 | # 43 | # The supported path seems to be "create a PR and use the API to merge 44 | # it", but that's more work to implement: revisit later if needed. 45 | -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: check 16 | 17 | on: 18 | push: 19 | pull_request: 20 | workflow_dispatch: 21 | workflow_call: 22 | inputs: 23 | ref: 24 | description: 'Ref/branch to check' 25 | required: false 26 | type: string 27 | 28 | jobs: 29 | check: 30 | strategy: 31 | matrix: 32 | os: 33 | - ubuntu-latest # x86_64-linux 34 | - macos-latest # aarch64-darwin 35 | runs-on: ${{ matrix.os }} 36 | steps: 37 | - name: Check out repository 38 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 39 | with: 40 | ref: ${{ inputs.ref }} 41 | - name: Install Nix 42 | uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31 43 | with: 44 | github_access_token: '${{ secrets.GITHUB_TOKEN }}' 45 | extra_nix_config: | 46 | trusted-public-keys = doom-emacs-unstraightened.cachix.org-1:O5oOlRPnmQEvVaFyuMTmthCEooHbrg54WgSLR07tmg4= cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY= 47 | substituters = https://doom-emacs-unstraightened.cachix.org/ https://cache.nixos.org/ 48 | - name: Cache downloads 49 | uses: ./.github/actions/cache-downloads 50 | - name: Check flake.lock 51 | uses: DeterminateSystems/flake-checker-action@3164002371bc90729c68af0e24d5aacf20d7c9f6 # v12 52 | - name: nix flake check 53 | run: nix flake check -L --show-trace 54 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: CI 16 | 17 | on: 18 | workflow_dispatch: 19 | schedule: 20 | - cron: '23 8 * * *' # runs daily at a randomly selected time 21 | 22 | jobs: 23 | flake-update: 24 | if: github.repository == 'marienz/nix-doom-emacs-unstraightened' 25 | permissions: 26 | contents: write 27 | uses: ./.github/workflows/flake-update.yml 28 | check: 29 | uses: ./.github/workflows/check.yml 30 | with: 31 | ref: flake-update 32 | needs: [flake-update] 33 | cachix: 34 | uses: ./.github/workflows/cachix.yml 35 | with: 36 | ref: flake-update 37 | secrets: 38 | CACHIX_AUTH_TOKEN: ${{ secrets.CACHIX_AUTH_TOKEN }} 39 | needs: [flake-update] 40 | push: 41 | permissions: 42 | contents: write 43 | uses: ./.github/workflows/push-flake-update.yml 44 | needs: [check, cachix] 45 | 46 | # TODO: try to improve caching. 47 | # 48 | # We spend a lot of time fetching sources. Caching all of ~/.cache/nix/gitv3 is 49 | # not ideal: it is too large (3GiB) and we don't expire individual checkouts. 50 | # https://github.com/DeterminateSystems/magic-nix-cache/issues/28 may help. 51 | # 52 | # The "magic" nix cache hits usage limits: 53 | # 54 | # 2024-05-18T06:45:19.165515Z ERROR magic_nix_cache::gha: Upload of path '/nix/store/fpq1vaw8vr88a67lc2jspskf2fa7zbvj-emacs-treepy-20230715.2154' failed: GitHub API error: API error (429 Too Many Requests): StructuredApiError { message: "Request was blocked due to exceeding usage of resource 'Count' in namespace ''." } 55 | # 56 | # This might get better as the cache populates, as long as I don't hit size 57 | # limits. 58 | -------------------------------------------------------------------------------- /.github/workflows/cachix.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | name: cachix 16 | 17 | on: 18 | push: 19 | branches: 20 | - 'main' 21 | workflow_dispatch: 22 | workflow_call: 23 | inputs: 24 | ref: 25 | description: 'Ref/branch to check' 26 | required: false 27 | type: string 28 | secrets: 29 | CACHIX_AUTH_TOKEN: 30 | description: 'auth token for writing to cachix (when called from CI)' 31 | required: true 32 | 33 | jobs: 34 | build: 35 | strategy: 36 | matrix: 37 | os: 38 | - ubuntu-latest # x86_64-linux 39 | - macos-latest # aarch64-darwin 40 | emacs: 41 | - emacs30 42 | - emacs30-nox 43 | - emacs30-gtk3 44 | - emacs30-pgtk 45 | - emacs-without-nativecomp 46 | runs-on: ${{ matrix.os }} 47 | steps: 48 | - name: Check out repository 49 | uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 50 | with: 51 | ref: ${{ inputs.ref }} 52 | - name: Install Nix 53 | uses: cachix/install-nix-action@4e002c8ec80594ecd40e759629461e26c8abed15 # v31 54 | with: 55 | github_access_token: '${{ secrets.GITHUB_TOKEN }}' 56 | - name: Enable Cachix 57 | uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16 58 | with: 59 | name: doom-emacs-unstraightened 60 | authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}' 61 | cachixArgs: '-v' 62 | - name: Cache downloads 63 | uses: ./.github/actions/cache-downloads 64 | - name: Build packages for Cachix 65 | run: nix build .#cachix-${{ matrix.emacs }} 66 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "doomemacs": { 4 | "flake": false, 5 | "locked": { 6 | "lastModified": 1765960743, 7 | "narHash": "sha256-zR+EfDMcyAWPSkkXauzaEcbzsKUoDMwNVVtJvsQSXqo=", 8 | "owner": "doomemacs", 9 | "repo": "doomemacs", 10 | "rev": "0dfb874b57690dbbddd8167792de5ea9ff9a7d76", 11 | "type": "github" 12 | }, 13 | "original": { 14 | "owner": "doomemacs", 15 | "repo": "doomemacs", 16 | "type": "github" 17 | } 18 | }, 19 | "emacs-overlay": { 20 | "inputs": { 21 | "nixpkgs": [], 22 | "nixpkgs-stable": [] 23 | }, 24 | "locked": { 25 | "lastModified": 1766023180, 26 | "narHash": "sha256-YpyEjEQWAPZm8/uG0VE4MsjHbdouSCutoH3FTeZLgig=", 27 | "owner": "nix-community", 28 | "repo": "emacs-overlay", 29 | "rev": "3517fcb29c95e4e9a556042754df0f0e66a08d74", 30 | "type": "github" 31 | }, 32 | "original": { 33 | "owner": "nix-community", 34 | "repo": "emacs-overlay", 35 | "type": "github" 36 | } 37 | }, 38 | "nixpkgs": { 39 | "locked": { 40 | "lastModified": 1765934234, 41 | "narHash": "sha256-pJjWUzNnjbIAMIc5gRFUuKCDQ9S1cuh3b2hKgA7Mc4A=", 42 | "owner": "NixOS", 43 | "repo": "nixpkgs", 44 | "rev": "af84f9d270d404c17699522fab95bbf928a2d92f", 45 | "type": "github" 46 | }, 47 | "original": { 48 | "id": "nixpkgs", 49 | "type": "indirect" 50 | } 51 | }, 52 | "root": { 53 | "inputs": { 54 | "doomemacs": "doomemacs", 55 | "emacs-overlay": "emacs-overlay", 56 | "nixpkgs": "nixpkgs", 57 | "systems": "systems" 58 | } 59 | }, 60 | "systems": { 61 | "locked": { 62 | "lastModified": 1681028828, 63 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 64 | "owner": "nix-systems", 65 | "repo": "default", 66 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 67 | "type": "github" 68 | }, 69 | "original": { 70 | "owner": "nix-systems", 71 | "repo": "default", 72 | "type": "github" 73 | } 74 | } 75 | }, 76 | "root": "root", 77 | "version": 7 78 | } 79 | -------------------------------------------------------------------------------- /tests.el: -------------------------------------------------------------------------------- 1 | ;; -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright 2024 Google LLC 4 | ;; 5 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 6 | ;; you may not use this file except in compliance with the License. 7 | ;; You may obtain a copy of the License at 8 | ;; 9 | ;; http://www.apache.org/licenses/LICENSE-2.0 10 | ;; 11 | ;; Unless required by applicable law or agreed to in writing, software 12 | ;; distributed under the License is distributed on an "AS IS" BASIS, 13 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ;; See the License for the specific language governing permissions and 15 | ;; limitations under the License. 16 | 17 | ;; Config file for integration tests. 18 | ;; 19 | ;; This tries to hook into startup as late as possible, write a sign of life 20 | ;; (currently a string written to $out), and then exits. 21 | 22 | (defun test-no-profile () 23 | (when doom-profile 24 | (error "doom-profile should be unset, is %s" doom-profile))) 25 | 26 | (defun test-nix-profile () 27 | (unless (and doom-profile (equal (car doom-profile) "nix")) 28 | (error "non-nix doom-profile %s" doom-profile))) 29 | 30 | (defun test-external-org () 31 | "Test org can be loaded and it's not built-in." 32 | (require 'org) 33 | (let ((path (find-library-name "org"))) 34 | (unless (string-search "/site-lisp/" path) 35 | (error "org-mode probably built-in: %s" path)))) 36 | 37 | (defun test-org-re-reveal () 38 | "Test org-re-reveal can find reveal.js." 39 | (require 'ox) 40 | (require 'org-re-reveal) 41 | (unless (string-search "/site-lisp/revealjs" org-re-reveal-root) 42 | (error "org-re-reveal does not find our revealjs: %s" org-re-reveal-root))) 43 | 44 | (defun test-cmake () 45 | "Test cmake-mode autoloads are loaded." 46 | (unless (functionp 'cmake-mode) 47 | (error "cmake-mode not available"))) 48 | 49 | (defun test-doom () 50 | (let* ((out (getenv "out")) 51 | (test (intern-soft (format "test-%s" (getenv "testName")))) 52 | (result (condition-case err 53 | (funcall test) 54 | (error 55 | (format "%s failed: %s" test err)) 56 | (:success 57 | "Doom functions")))) 58 | (write-region result nil out nil nil nil 'mustbenew)) 59 | (kill-emacs)) 60 | 61 | (defun test-extraPackages () 62 | (require 'vterm)) 63 | 64 | (add-hook 'doom-after-init-hook 'test-doom) 65 | -------------------------------------------------------------------------------- /elisp-patches/apel-library.patch: -------------------------------------------------------------------------------- 1 | From 54204ee25128875d77720746e1f8d47c76fef6eb Mon Sep 17 00:00:00 2001 2 | From: Jonas Bernoulli 3 | Date: Sat, 19 Aug 2017 17:41:00 +0200 4 | Subject: [PATCH] [PATCH] Add library apel.el whose name matches that of the 5 | package apel 6 | 7 | A package should always contain a library with the same name. One 8 | benefit of that is that Melpa can then extract the package description 9 | from the library commentary. 10 | 11 | Remove "apel-pkg.el". The only additional information it contains is a 12 | version string, which has not ever been bumped since it was first added 13 | in 2014. 14 | 15 | [Emacsmirror] This commit is being rebased onto upstream. 16 | --- 17 | apel-pkg.el | 4 ---- 18 | apel.el | 33 +++++++++++++++++++++++++++++++++ 19 | 2 files changed, 33 insertions(+), 4 deletions(-) 20 | delete mode 100644 apel-pkg.el 21 | create mode 100644 apel.el 22 | 23 | diff --git a/apel-pkg.el b/apel-pkg.el 24 | deleted file mode 100644 25 | index 0229b36..0000000 26 | --- a/apel-pkg.el 27 | +++ /dev/null 28 | @@ -1,4 +0,0 @@ 29 | -(define-package "apel" "10.8" 30 | - "A Portable Emacs Library provides support for portable Emacs Lisp programs" 31 | - '((emacs "24.5")) 32 | - :maintainers '(("Kazuhiro Ito" . "kzhr@d1.dion.ne.jp"))) 33 | diff --git a/apel.el b/apel.el 34 | new file mode 100644 35 | index 0000000..f2fbb14 36 | --- /dev/null 37 | +++ b/apel.el 38 | @@ -0,0 +1,33 @@ 39 | +;;; apel.el --- Support for portable Emacs Lisp programs 40 | + 41 | +;; Copyright (C) 1996-2022 Free Software Foundation, Inc. 42 | + 43 | +;; Package-Requires: ((emacs "24.5")) 44 | + 45 | +;; This file is part of APEL (A Portable Emacs Library). 46 | + 47 | +;; This program is free software; you can redistribute it and/or 48 | +;; modify it under the terms of the GNU General Public License as 49 | +;; published by the Free Software Foundation; either version 2, or 50 | +;; (at your option) any later version. 51 | + 52 | +;; This program is distributed in the hope that it will be useful, 53 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of 54 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 55 | +;; GNU General Public License for more details. 56 | + 57 | +;; You should have received a copy of the GNU General Public License 58 | +;; along with GNU Emacs; see the file COPYING. If not, write to the 59 | +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 60 | +;; Boston, MA 02110-1301, USA. 61 | + 62 | +;;; Commentary: 63 | + 64 | +;; APEL (A Portable Emacs Library) provides support for portable Emacs 65 | +;; Lisp programs. 66 | + 67 | +;;; Code: 68 | + 69 | +(provide 'apel) 70 | + 71 | +;;; apel.el ends here 72 | -------------------------------------------------------------------------------- /elisp-patches/semi-library.patch: -------------------------------------------------------------------------------- 1 | From af70ef0df032fd74a28f41bf1eeb2d207f96e9d9 Mon Sep 17 00:00:00 2001 2 | From: Jonas Bernoulli 3 | Date: Sat, 19 Aug 2017 17:50:00 +0200 4 | Subject: [PATCH] [PATCH] Add library semi.el whose name matches that of the 5 | package semi 6 | 7 | A package should always contain a library with the same name. One 8 | benefit of that is that Melpa can then extract the package description 9 | from the library commentary. 10 | 11 | Remove "semi-pkg.el". The only additional information it contains is a 12 | version string, which has not ever been bumped since it was first added 13 | in 2014. 14 | 15 | [Emacsmirror] This commit is being rebased onto upstream. 16 | --- 17 | semi-pkg.el | 6 ------ 18 | semi.el | 34 ++++++++++++++++++++++++++++++++++ 19 | 2 files changed, 34 insertions(+), 6 deletions(-) 20 | delete mode 100644 semi-pkg.el 21 | create mode 100644 semi.el 22 | 23 | diff --git a/semi-pkg.el b/semi-pkg.el 24 | deleted file mode 100644 25 | index f4a6e72e..00000000 26 | --- a/semi-pkg.el 27 | +++ /dev/null 28 | @@ -1,6 +0,0 @@ 29 | -(define-package "semi" "1.14.7" 30 | - "A library to provide MIME features." 31 | - '((emacs "24.5") 32 | - (apel "10.8") 33 | - (flim "1.14.9")) 34 | - :maintainers '(("Kazuhiro Ito" . "kzhr@d1.dion.ne.jp"))) 35 | diff --git a/semi.el b/semi.el 36 | new file mode 100644 37 | index 00000000..4002879d 38 | --- /dev/null 39 | +++ b/semi.el 40 | @@ -0,0 +1,34 @@ 41 | +;;; semi.el --- MIME features 42 | + 43 | +;; Copyright (C) 1996-2024 Free Software Foundation, Inc. 44 | + 45 | +;; Author: MORIOKA Tomohiko 46 | +;; Keywords: MIME, multimedia, mail, news 47 | +;; Package-Requires: ((emacs "24.5") (apel "0") (flim "0")) 48 | + 49 | +;; This file is part of SEMI (Setting for Emacs MIME Interfaces). 50 | + 51 | +;; This program is free software; you can redistribute it and/or 52 | +;; modify it under the terms of the GNU General Public License as 53 | +;; published by the Free Software Foundation; either version 2, or 54 | +;; (at your option) any later version. 55 | + 56 | +;; This program is distributed in the hope that it will be useful, 57 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of 58 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 59 | +;; GNU General Public License for more details. 60 | + 61 | +;; You should have received a copy of the GNU General Public License 62 | +;; along with GNU Emacs; see the file COPYING. If not, write to the 63 | +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 64 | +;; Boston, MA 02110-1301, USA. 65 | + 66 | +;;; Commentary: 67 | + 68 | +;; SEMI (Setting for Emacs MIME Interfaces) provides MIME features. 69 | + 70 | +;;; Code: 71 | + 72 | +(provide 'semi) 73 | + 74 | +;;; semi.el ends here 75 | -------------------------------------------------------------------------------- /elisp-patches/flim-library.patch: -------------------------------------------------------------------------------- 1 | From b1165d7dbc8d001024254d8d26bc740022f4d2a8 Mon Sep 17 00:00:00 2001 2 | From: Jonas Bernoulli 3 | Date: Sat, 19 Aug 2017 17:44:00 +0200 4 | Subject: [PATCH] [PATCH] Add library flim.el whose name matches that of the 5 | package flim 6 | 7 | A package should always contain a library with the same name. One 8 | benefit of that is that Melpa can then extract the package description 9 | from the library commentary. 10 | 11 | Remove "flim-pkg.el". The only additional information it contains is a 12 | version string, which has not ever been bumped since it was first added 13 | in 2014. 14 | 15 | [Emacsmirror] This commit is being rebased onto upstream. 16 | --- 17 | flim-pkg.el | 6 ------ 18 | flim.el | 34 ++++++++++++++++++++++++++++++++++ 19 | 2 files changed, 34 insertions(+), 6 deletions(-) 20 | delete mode 100644 flim-pkg.el 21 | create mode 100644 flim.el 22 | 23 | diff --git a/flim-pkg.el b/flim-pkg.el 24 | deleted file mode 100644 25 | index 0c90ee8..0000000 26 | --- a/flim-pkg.el 27 | +++ /dev/null 28 | @@ -1,6 +0,0 @@ 29 | -(define-package "flim" "1.14.9" 30 | - "A library to provide basic features about message representation or encoding." 31 | - '((emacs "24.5") 32 | - (apel "10.8") 33 | - (oauth2 "0.11")) 34 | - :maintainers '(("Kazuhiro Ito" . "kzhr@d1.dion.ne.jp"))) 35 | diff --git a/flim.el b/flim.el 36 | new file mode 100644 37 | index 0000000..cb64f7a 38 | --- /dev/null 39 | +++ b/flim.el 40 | @@ -0,0 +1,34 @@ 41 | +;;; flim.el --- Basic message representation and encoding features 42 | + 43 | +;; Copyright (C) 1997-2024 Free Software Foundation, Inc. 44 | + 45 | +;; Keywords: MIME, multimedia, mail, news 46 | +;; Package-Requires: ((emacs "24.5") (apel "0") (oauth2 "0.17")) 47 | + 48 | +;; This file is part of FLIM (Faithful Library about Internet Message). 49 | + 50 | +;; This program is free software; you can redistribute it and/or 51 | +;; modify it under the terms of the GNU General Public License as 52 | +;; published by the Free Software Foundation; either version 2, or 53 | +;; (at your option) any later version. 54 | + 55 | +;; This program is distributed in the hope that it will be useful, 56 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of 57 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 58 | +;; GNU General Public License for more details. 59 | + 60 | +;; You should have received a copy of the GNU General Public License 61 | +;; along with GNU Emacs; see the file COPYING. If not, write to the 62 | +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 63 | +;; Boston, MA 02110-1301, USA. 64 | + 65 | +;;; Commentary: 66 | + 67 | +;; FLIM (Faithful Library about Internet Message) provides basic 68 | +;; message representation and encoding features. 69 | + 70 | +;;; Code: 71 | + 72 | +(provide 'flim) 73 | + 74 | +;;; flim.el ends here 75 | -------------------------------------------------------------------------------- /init.el: -------------------------------------------------------------------------------- 1 | ;;; init.el -*- lexical-binding: t; -*- 2 | 3 | ;; Copyright 2024 Google LLC 4 | ;; 5 | ;; Licensed under the Apache License, Version 2.0 (the "License"); 6 | ;; you may not use this file except in compliance with the License. 7 | ;; You may obtain a copy of the License at 8 | ;; 9 | ;; http://www.apache.org/licenses/LICENSE-2.0 10 | ;; 11 | ;; Unless required by applicable law or agreed to in writing, software 12 | ;; distributed under the License is distributed on an "AS IS" BASIS, 13 | ;; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | ;; See the License for the specific language governing permissions and 15 | ;; limitations under the License. 16 | 17 | ;; Extra initialization code for nix-doom-emacs-unstraightened. 18 | ;; 19 | ;; Loaded from the profile init file. 20 | 21 | (defadvice! nix-doom-skip-core-packages (&rest _) 22 | "HACK: don't install straight and core packages. 23 | 24 | `doom-initialize-core-packages' would no-op out if 25 | `straight-recipe-repositories' is set, but we do not want to set 26 | it. Just skip it entirely." 27 | :override #'doom-initialize-core-packages 28 | (doom-log "nix-doom-emacs-unstraightened overriding core package init") 29 | ;; doom-initialize-core-packages normally registers recipes, which loads the 30 | ;; build cache by side effect, which leaves straight--build-cache available 31 | ;; afterwards. Doom assumes this cache is available, so force a load here. 32 | (require 'straight) ;; straight-load-build-cache is not autoloaded. 33 | (straight--load-build-cache)) 34 | 35 | (after! doom-straight 36 | (setq straight-base-dir 37 | (file-name-directory (directory-file-name doom-user-dir)))) 38 | 39 | ;; Doom adds a minor mode that makes flycheck-mode's emacs subprocess initialize 40 | ;; Doom. Extend this to run the profile loader first: what Doom does here is 41 | ;; similar enough to its normal startup it needs the same fixes. 42 | ;; 43 | ;; Note this assumes DOOMPROFILELOADFILE and DOOMPROFILE leak into child 44 | ;; processes (the loader does nothing if DOOMPROFILE is unset). 45 | (setq-hook! +emacs-lisp--flycheck-non-package-mode 46 | flycheck-emacs-lisp-check-form 47 | (prin1-to-string `(progn 48 | (load (getenv "DOOMPROFILELOADFILE") nil 'nomessage) 49 | ,(read flycheck-emacs-lisp-check-form)))) 50 | 51 | ;; The restart-emacs package redefines the restart-emacs function provided by 52 | ;; Emacs (which takes no arguments) with a replacement that takes an "args" 53 | ;; argument. We need to define our advice after the correct function has been 54 | ;; loaded: advice-add would normally handle autoloads but does not handle this. 55 | (after! restart-emacs 56 | (defadvice! nix-doom-restart-emacs (fargs) 57 | "Add --init-directory argument necessary to start Doom." 58 | :filter-args #'restart-emacs 59 | (cl-destructuring-bind (&optional args) fargs 60 | (let* ((init-arg (format "--init-directory=%s" doom-emacs-dir)) 61 | (fixed-args (cons init-arg args))) 62 | (list fixed-args))))) 63 | -------------------------------------------------------------------------------- /elisp-patches/wanderlust-library.patch: -------------------------------------------------------------------------------- 1 | From a04a5baa7f488ef90bd906ebfaba7979d447ea76 Mon Sep 17 00:00:00 2001 2 | From: Jonas Bernoulli 3 | Date: Sat, 19 Aug 2017 17:51:00 +0200 4 | Subject: [PATCH] [PATCH] Add library wanderlust.el whose name matches that of 5 | the package wanderlust 6 | 7 | A package should always contain a library with the same name. One 8 | benefit of that is that Melpa can then extract the package description 9 | from library commentary. 10 | 11 | Remove "wanderlust-pkg.el". The only additional information it contains 12 | is a version string, which has not ever been bumped since it was first 13 | added in 2014. 14 | 15 | [Emacsmirror] This commit is being rebased onto upstream. 16 | --- 17 | wanderlust-pkg.el | 7 ------- 18 | wl/wanderlust.el | 38 ++++++++++++++++++++++++++++++++++++++ 19 | 2 files changed, 38 insertions(+), 7 deletions(-) 20 | delete mode 100644 wanderlust-pkg.el 21 | create mode 100644 wl/wanderlust.el 22 | 23 | diff --git a/wanderlust-pkg.el b/wanderlust-pkg.el 24 | deleted file mode 100644 25 | index 093998a66..000000000 26 | --- a/wanderlust-pkg.el 27 | +++ /dev/null 28 | @@ -1,7 +0,0 @@ 29 | -(define-package "wanderlust" "2.15.9" 30 | - "Yet Another Message Interface on Emacsen" 31 | - '((emacs "24.5") 32 | - (apel "10.8") 33 | - (flim "1.14.9") 34 | - (semi "1.14.7")) 35 | - :maintainers '(("Kazuhiro Ito" . "kzhr@d1.dion.ne.jp"))) 36 | diff --git a/wl/wanderlust.el b/wl/wanderlust.el 37 | new file mode 100644 38 | index 000000000..055498505 39 | --- /dev/null 40 | +++ b/wl/wanderlust.el 41 | @@ -0,0 +1,38 @@ 42 | +;;; wanderlust.el --- Yet Another Message Interface on Emacsen. 43 | + 44 | +;; Copyright (C) 1998-2000 Yuuichi Teranishi 45 | +;; Copyright (C) 1998-2000 Masahiro MURATA 46 | + 47 | +;; Author: Yuuichi Teranishi 48 | +;; Masahiro MURATA 49 | +;; Keywords: mail, net news 50 | +;; Package-Requires: ((emacs "24.5") (apel "0") (flim "0") (semi "0")) 51 | + 52 | +;; This file is part of Wanderlust (Yet Another Message Interface on Emacsen). 53 | + 54 | +;; This program is free software; you can redistribute it and/or modify 55 | +;; it under the terms of the GNU General Public License as published by 56 | +;; the Free Software Foundation; either version 2, or (at your option) 57 | +;; any later version. 58 | +;; 59 | +;; This program is distributed in the hope that it will be useful, 60 | +;; but WITHOUT ANY WARRANTY; without even the implied warranty of 61 | +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 62 | +;; GNU General Public License for more details. 63 | +;; 64 | +;; You should have received a copy of the GNU General Public License 65 | +;; along with GNU Emacs; see the file COPYING. If not, write to the 66 | +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, 67 | +;; Boston, MA 02111-1307, USA. 68 | + 69 | +;;; Commentary: 70 | + 71 | +;; Wanderlust is a mail/news management system with IMAP4rev1 support 72 | +;; for Emacs. It was originally developed by Yuuichi Teranishi. 73 | + 74 | +;;; Code: 75 | + 76 | +(require 'wl) 77 | +(provide 'wanderlust) 78 | + 79 | +;;; wanderlust.el ends here 80 | -------------------------------------------------------------------------------- /fetch-overrides.nix: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Data loaded by default.nix. 16 | { 17 | extraUrls = { 18 | # Straight recipe from el-get 19 | font-lock-ext = "https://github.com/sensorflo/font-lock-ext.git"; 20 | sln-mode = "https://github.com/sensorflo/sln-mode.git"; 21 | # Straight recipe from emacsmirror-mirror 22 | # (emacsmirror-mirror includes emacsattic, emacs-overlay does not...) 23 | nose = "https://github.com/emacsattic/nose.git"; 24 | ob-ammonite = "https://github.com/emacsattic/ob-ammonite.git"; 25 | ammonite-term-repl = "https://github.com/emacsattic/ammonite-term-repl.git"; 26 | ob-clojure-literate = "https://github.com/emacsattic/ob-clojure-literate.git"; 27 | # sourcehut fetcher doesn't work for the same reason codeberg doesn't. 28 | fennel-mode = "https://git.sr.ht/~technomancy/fennel-mode"; 29 | graphql-ts-mode = "https://git.sr.ht/~joram/graphql-ts-mode"; 30 | minitest = "https://git.sr.ht/~shoshin/minitest-emacs"; 31 | # nixpkgs uses a release from nongnu ELPA. 32 | corfu-terminal = "https://codeberg.org/akib/emacs-corfu-terminal"; 33 | haskell-ts-mode = "https://codeberg.org/pranshu/haskell-ts-mode"; 34 | treesit-fold = "https://github.com/emacs-tree-sitter/treesit-fold.git"; 35 | # Dropped from Melpa, see https://github.com/melpa/melpa/pull/8106#issuecomment-2425152728 36 | lean-mode = "https://github.com/leanprover/lean3-mode"; 37 | company-lean = "https://github.com/leanprover/lean3-mode"; 38 | # Dropped from Melpa, see https://github.com/melpa/melpa/issues/9496 and 39 | # https://github.com/melpa/melpa/pull/9520 40 | helm-icons = "https://github.com/yyoncho/helm-icons"; 41 | helm-posframe = "https://github.com/tumashu/helm-posframe"; 42 | # Dropped from Melpa (repository archived but still usable). 43 | bundler = "https://github.com/endofunky/bundler.el"; 44 | }; 45 | 46 | # Pins for packages not pinned by Doom and not in nixpkgs or emacs-overlay. 47 | extraPins = { 48 | # Looks stable enough we can get away with pinning it. 49 | sly-stepper = "da84e3bba8466c2290c2dc7c27d7f4c48c27b39e"; 50 | # In emacsattic, so shouldn't change underneath us. 51 | ammonite-term-repl = "b552fe21977e005c1c460bf6607557e67241a6b6"; 52 | }; 53 | 54 | # :files passed in to melpa2nix (currently only if not already present in recipe). 55 | extraFiles = { 56 | # These build from the same repository. Without this, lean-mode does not build because 57 | # we try to build company-lean without depending on company. 58 | lean-mode = ''("lean-*.el")''; 59 | company-lean = ''("company-lean.el")''; 60 | }; 61 | } 62 | -------------------------------------------------------------------------------- /HACKING.md: -------------------------------------------------------------------------------- 1 | # Internals/design notes 2 | 3 | ## Why [profiles](https://github.com/doomemacs/doomemacs/tree/master/profiles)? 4 | 5 | Because the profile loader runs early enough we can get Doom to load the profile 6 | init file from outside `DOOMLOCALDIR` relatively cleanly. Not using the "global" 7 | profile is a largely unintended side effect, but the changes to other paths made 8 | seem largely reasonable. 9 | 10 | We do not regain control in time after the loader without patching Doom: the 11 | first user code it runs is `init.el` from `doom-user-dir` (which we point into 12 | the store), but it loads that from the profile init file. 13 | 14 | Setting `profileName` to the empty string triggers a special case: we unset 15 | `DOOMPROFILE` from the profile loader. This feels like a hack, but it does get 16 | us the usual `DOOMLOCALDIR`-relative `doom-cache-dir` and friends back. 17 | 18 | ## Why put `doom-user-dir`/`DOOMDIR` in the Nix store? 19 | 20 | Doom forces my hand. I would prefer for just `packages.el` and possibly 21 | `init.el` to live in the store, but splitting out where those are loaded from 22 | looks non-trivial. 23 | 24 | Doom uses `doom-user-dir` as the path to a special module (`:user`). 25 | 26 | At profile generation time, this is how `packages.el` gets loaded. We want our 27 | generated `packages.el` to take effect, so we want the `:user` module's path to 28 | be a store path when generating the profile. 29 | 30 | Paths to all modules are embedded in the generated profile and used to 31 | initialize `doom-modules` at runtime. Among other things, this controls where 32 | the user's `config.el` is loaded from (it's loaded along with module `config.el` 33 | files). So (without further hacks) the path to `packages.el` at build time and 34 | `config.el` at runtime are the same. 35 | 36 | (And even if that wasn't the case, functions like `doom/help-packages` load 37 | `packages.el`, and I currently expect less overall confusion if that loads our 38 | generated `packages.el`, not the original one. So I do think we want the `:user` 39 | module loaded from the store.) 40 | 41 | In several places, Doom assumes (at runtime) that `doom-user-dir` and the path 42 | to the `:user` module are the same. This is mostly in functions like 43 | `doom/help-packages` and `doom-module-from-path` that map paths back to modules. 44 | 45 | Combine all that and I think consistently having `doom-user-dir` and the `:user` 46 | module live in the Nix store is the least bad option. 47 | 48 | This does break things that write to DOOMDIR at runtime. `custom-file` is an 49 | obvious example, but there are probably a few more. 50 | 51 | ## `programs.emacs.package` / nesting emacsWithPackages 52 | 53 | Home Manager's `programs.emacs` wraps its Emacs package with emacsWithPackages. 54 | We don't work as an input to emacsWithPackages. 55 | 56 | First bug: emacsWithPackages writes a site-start that loads 57 | `$emacs/share/site-lisp/site-start` first. That is: it assumes the emacs package 58 | it wraps has its own site-start. That's true if it's an actual emacs but at 59 | first glance might also break if it's another emacsWithPackages, because its 60 | site-start goes in a separate `emacs-packages-deps` derivation (I didn't test 61 | this further). 62 | 63 | We could fix that (by adding a trivial site-start.el of our own), but there's a 64 | second bug: when Doom loads its profile, it overwrites `load-path`. This defeats 65 | the purpose of having that outer emacsWithPackages in the first place. 66 | 67 | During normal interactive startup, the second bug masks the first: site-start 68 | gets loaded from the Doom profile's load path, skipping the outer 69 | emacsWithPackages entirely. So at first glance the Home Manager `programs.emacs` 70 | module will seem to work... 71 | 72 | During non-interactive startup, the first bug surfaces. The easiest way of 73 | triggering this is `doom` cli, which fails with: 74 | 75 | ``` 76 | Unexpected error in Doom’s core: "/nix/store/3hr4amd670vbf5h1w1jw18y3a9hv1689-source/lisp/doom-cli.el", (file-missing "Cannot open load file" "No such file or directory" "/nix/store/7vvp8axf8h4qrx7mj3mh1dsxj80393k2-emacs-pgtk-with-doom-29.3/share/emacs/site-lisp/site-start") 77 | ``` 78 | 79 | Setting DEBUG=1 makes it more obvious where this fails (doom-cli.el loads 80 | site-start). 81 | 82 | Non-interactive use of emacs also seems to trigger this: in particular it breaks 83 | flycheck of elisp code with a similar error message about site-start. 84 | -------------------------------------------------------------------------------- /checks.nix: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | { 16 | callPackages, 17 | emacs, 18 | lib, 19 | linkFarm, 20 | runCommand, 21 | testers, 22 | tmux, 23 | writeText, 24 | writeTextDir, 25 | 26 | doomSource, 27 | makeDoomPackages, 28 | toInit, 29 | }: 30 | let 31 | inherit (lib.generators) toPretty; 32 | doomDirs = callPackages ./build-helpers/doomdirs.nix { inherit doomSource; }; 33 | common = { 34 | doomLocalDir = "~/.local/share/nix-doom-unstraightened"; 35 | experimentalFetchTree = true; 36 | emacs = emacs.override { withNativeCompilation = false; }; 37 | }; 38 | mkDoom = args: (makeDoomPackages (common // args)).doomEmacs; 39 | mkDoomDir = args: writeTextDir "init.el" (toInit args); 40 | minimalDoomDir = mkDoomDir { config.default = true; }; 41 | doomTest = 42 | name: init: doomArgs: 43 | testers.testEqualContents { 44 | assertion = "name = ${name}; modules = ${toPretty { } init}; args = ${toPretty { } doomArgs};"; 45 | expected = writeText "doom-expected" "Doom functions"; 46 | # Runs Doom in tmux, waiting (by polling) until its window disappears. 47 | actual = 48 | runCommand "interactive" 49 | { 50 | # Read by tests.el. 51 | testName = name; 52 | nativeBuildInputs = [ 53 | tmux 54 | (mkDoom ( 55 | doomArgs 56 | // { 57 | doomDir = linkFarm "test-doomdir" { 58 | "config.el" = ./tests.el; 59 | "init.el" = writeText "init.el" (toInit init); 60 | }; 61 | } 62 | )) 63 | ]; 64 | } 65 | '' 66 | tmux new-session -s doom-testing -d 67 | tmux new-window -n doom-window 'doom-emacs -nw' 68 | for ((i = 0; i < 100; i++)); do 69 | tmux list-windows -a | grep -q doom-window || break 70 | sleep .1 71 | done 72 | tmux kill-session -t doom-testing 73 | ''; 74 | }; 75 | doomBuildTest = init: mkDoom { doomDir = mkDoomDir init; }; 76 | in 77 | { 78 | minimal = mkDoom { doomDir = minimalDoomDir; }; 79 | minimalEmacs = 80 | (makeDoomPackages ( 81 | common 82 | // { 83 | doomDir = minimalDoomDir; 84 | } 85 | )).emacsWithDoom; 86 | minimalExtraPackages = mkDoom { 87 | doomDir = minimalDoomDir; 88 | extraPackages = epkgs: [ 89 | epkgs.vterm 90 | epkgs.treesit-grammars.with-all-grammars 91 | ]; 92 | }; 93 | allModules = mkDoom { doomDir = doomDirs.allModules; }; 94 | allModulesAndFlags = mkDoom { doomDir = doomDirs.allModulesAndFlags; }; 95 | allModulesMostFlags = mkDoom { doomDir = doomDirs.allModulesMostFlags; }; 96 | example = mkDoom { doomDir = ./doomdir; }; 97 | example-without-loader = mkDoom { 98 | doomDir = ./doomdir; 99 | profileName = ""; 100 | }; 101 | interactive = doomTest "nix-profile" { config.default = true; } { }; 102 | interactive-unset-profile = doomTest "no-profile" { config.default = true; } { profileName = ""; }; 103 | 104 | cmake = doomTest "cmake" { lang.cc = true; } { }; 105 | 106 | org-re-reveal = doomTest "org-re-reveal" { lang.org = [ "+present" ]; } { }; 107 | 108 | # Various tests of module combinations. 109 | unpinned-org = doomTest "external-org" { app.rss = [ "+org" ]; } { }; 110 | # Dependencies that require a module flag enabled and a different module or flag disabled. 111 | # flycheck-eglot needs flymake disabled. 112 | flycheck-eglot = doomBuildTest { 113 | tools.lsp = [ "+eglot" ]; 114 | checkers.syntax = true; 115 | }; 116 | # multiple-cursors needs :editor evil disabled. 117 | multiple-cursors = doomBuildTest { editor.multiple-cursors = true; }; 118 | # flx needs +prescient disabled. 119 | flx = doomBuildTest { completion.ivy = [ "+fuzzy" ]; }; 120 | # corfu pulls in unpinned orderless if vertico is disabled. 121 | corfu-orderless = doomBuildTest { completion.corfu = [ "+orderless" ]; }; 122 | # flyspell can pull in one of three completion modules. 123 | flyspell-correct-ivy = doomBuildTest { 124 | checkers.spell = [ "+flyspell" ]; 125 | completion.ivy = true; 126 | }; 127 | flyspell-correct-helm = doomBuildTest { 128 | checkers.spell = [ "+flyspell" ]; 129 | completion.helm = true; 130 | }; 131 | flyspell-correct-popup = doomBuildTest { checkers.spell = [ "+flyspell" ]; }; 132 | # To pull in dap-mode at all we need +lsp on some language module, 133 | # but (debugger +lsp) pins it. 134 | # 135 | # TODO: add a check confirming this actually pulled in unpinned dap-mode. 136 | unpinned-dap-mode = doomBuildTest { 137 | lang.java = [ "+lsp" ]; 138 | lang.scala = [ "+lsp" ]; 139 | }; 140 | 141 | extraPackages = doomTest "extraPackages" { config.default = true; } { 142 | extraPackages = epkgs: [ epkgs.vterm ]; 143 | }; 144 | } 145 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | { 16 | inputs = { 17 | # Default to reusing the system's emacs package if it has nixpkgs in the system flake registry. 18 | nixpkgs.url = "nixpkgs"; 19 | systems.url = "github:nix-systems/default"; 20 | doomemacs = { 21 | url = "github:doomemacs/doomemacs"; 22 | flake = false; 23 | }; 24 | emacs-overlay = { 25 | url = "github:nix-community/emacs-overlay"; 26 | inputs = { 27 | # These should be unused, but let's unset them to make that explicit. 28 | nixpkgs-stable.follows = ""; 29 | nixpkgs.follows = ""; 30 | }; 31 | }; 32 | }; 33 | 34 | outputs = 35 | { 36 | self, 37 | systems, 38 | doomemacs, 39 | nixpkgs, 40 | emacs-overlay, 41 | ... 42 | }: 43 | let 44 | perSystemPackages = 45 | let 46 | eachSystem = nixpkgs.lib.genAttrs (import systems); 47 | in 48 | f: eachSystem (system: f nixpkgs.legacyPackages.${system}); 49 | 50 | doomFromPackages = 51 | pkgs: args: 52 | let 53 | # Hack to avoid pkgs.extend having to instantiate an additional nixpkgs. 54 | # 55 | # We need emacsPackagesFor from the overlay, but neither the overlay itself 56 | # (it only uses "super", not "self") nor us actually needs anything overlaid 57 | # on nixpkgs. So we can call the overlay and pass emacsPackagesFor through 58 | # directly instead of having pkgs.callPackage do it. 59 | inherit (emacs-overlay.overlays.package { } pkgs) emacsPackagesFor; 60 | mergedArgs = args // { 61 | inherit emacsPackagesFor toInit; 62 | doomSource = doomemacs; 63 | }; 64 | in 65 | pkgs.callPackages self mergedArgs; 66 | 67 | # Convert a Nix expression to a `doom!` block suitable for init.el. 68 | # 69 | # Input: a nested attribute set. 70 | # The keys of the first level are categories (like `lang`). 71 | # The keys of the second level are module names (like `nix`). 72 | # The values are lists of module flags, or `true` for no flags. 73 | toInit = 74 | lib: 75 | let 76 | inherit (lib) 77 | concatLines 78 | concatStringsSep 79 | isList 80 | mapAttrsToList 81 | toPretty 82 | ; 83 | in 84 | attrs: 85 | concatLines ( 86 | [ "(doom!" ] 87 | ++ (mapAttrsToList ( 88 | cat: modules: 89 | (concatLines ( 90 | [ (":" + cat) ] 91 | ++ 92 | (mapAttrsToList ( 93 | mod: value: 94 | if value == true then 95 | mod 96 | else if isList value then 97 | "(${mod} ${concatStringsSep " " value})" 98 | else 99 | abort "${toPretty value} not supported" 100 | )) 101 | modules 102 | )) 103 | ) attrs) 104 | ++ [ ")" ] 105 | ); 106 | 107 | homeModule = import ./home-manager.nix { 108 | inherit doomFromPackages; 109 | }; 110 | in 111 | { 112 | checks = perSystemPackages ( 113 | pkgs: 114 | pkgs.callPackages ./checks.nix { 115 | toInit = toInit nixpkgs.lib; 116 | doomSource = doomemacs; 117 | makeDoomPackages = doomFromPackages pkgs; 118 | } 119 | ); 120 | formatter = perSystemPackages (pkgs: pkgs.nixfmt-rfc-style); 121 | packages = perSystemPackages ( 122 | pkgs: 123 | let 124 | default = doomFromPackages pkgs { 125 | doomDir = ./doomdir; 126 | doomLocalDir = "~/.local/share/nix-doom-unstraightened"; 127 | }; 128 | in 129 | { 130 | doom-emacs = default.doomEmacs; 131 | emacs-with-doom = default.emacsWithDoom; 132 | doom-emacs-unset-profile = default.doomEmacs.override { profileName = ""; }; 133 | doom-emacs-tangle = default.doomEmacs.override { tangleArgs = "."; }; 134 | } 135 | // ( 136 | let 137 | inherit (nixpkgs) lib; 138 | depBuilds = 139 | emacs: 140 | lib.mapAttrs 141 | ( 142 | name: doomDir: 143 | (doomFromPackages pkgs { 144 | inherit doomDir emacs; 145 | doomLocalDir = "~/.local/share/nix-doom-unstraightened"; 146 | experimentalFetchTree = true; 147 | }).doomEmacs.emacsWithPackages.deps 148 | ) 149 | ( 150 | pkgs.callPackages ./build-helpers/doomdirs.nix { 151 | inherit emacs; 152 | doomSource = doomemacs; 153 | } 154 | ); 155 | emacsen = 156 | # Keep in sync with .github/workflows/cachix.yml 157 | (lib.genAttrs [ 158 | "emacs30" 159 | "emacs30-nox" 160 | "emacs30-gtk3" 161 | "emacs30-pgtk" 162 | ] (name: pkgs.${name})) 163 | // { 164 | emacs-without-nativecomp = pkgs.emacs.override { withNativeCompilation = false; }; 165 | }; 166 | in 167 | lib.mapAttrs' ( 168 | name: emacs: 169 | let 170 | p = pkgs.linkFarm "cachix-${name}" (depBuilds emacs); 171 | in 172 | lib.nameValuePair p.name p 173 | ) emacsen 174 | ) 175 | ); 176 | overlays.default = final: prev: { 177 | doomEmacs = args: (doomFromPackages final args).doomEmacs; 178 | emacsWithDoom = args: (doomFromPackages final args).emacsWithDoom; 179 | }; 180 | inherit homeModule; 181 | # Original name used in our documentation. 182 | # `nix flake check` (as of Nix 2.24) prefers homeModule, so we provide both. 183 | hmModule = homeModule; 184 | }; 185 | } 186 | -------------------------------------------------------------------------------- /home-manager.nix: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | { doomFromPackages }: 16 | { 17 | config, 18 | options, 19 | lib, 20 | pkgs, 21 | ... 22 | }: 23 | 24 | let 25 | cfg = config.programs.doom-emacs; 26 | inherit (lib) 27 | literalExpression 28 | mkEnableOption 29 | mkIf 30 | mkMerge 31 | mkOption 32 | types 33 | hm 34 | ; 35 | in 36 | { 37 | options = { 38 | programs.doom-emacs = { 39 | enable = mkEnableOption "Doom Emacs"; 40 | 41 | # Options passed through to default.nix. 42 | # Keep in the same order as default.nix, and in sync with the inherit below! 43 | doomDir = mkOption { 44 | type = types.pathInStore; 45 | example = literalExpression "./doom"; 46 | description = "The DOOMDIR to build from and bundle."; 47 | }; 48 | 49 | doomLocalDir = mkOption { 50 | type = types.path; 51 | default = "${config.xdg.dataHome}/nix-doom"; 52 | defaultText = literalExpression ''"''${config.xdg.dataHome}/nix-doom"''; 53 | example = literalExpression "~/.local/state/doom"; 54 | description = '' 55 | DOOMLOCALDIR. 56 | 57 | `~` is expanded, but shell variables are not! Use `config.xdg.*`, not 58 | `XDG_DATA_*`.''; 59 | }; 60 | 61 | emacs = mkOption { 62 | type = types.package; 63 | default = pkgs.emacs; 64 | defaultText = literalExpression "pkgs.emacs"; 65 | example = literalExpression "pkgs.emacs29-pgtk"; 66 | description = "The Emacs package to wrap."; 67 | }; 68 | 69 | profileName = mkOption { 70 | type = types.str; 71 | default = "nix"; 72 | example = literalExpression ""; 73 | description = "Doom profile. Set to the empty string to disable."; 74 | }; 75 | 76 | experimentalFetchTree = mkOption { 77 | type = types.bool; 78 | default = false; 79 | example = true; 80 | description = '' 81 | Fetch packages using fetchTree instead of fetchGit. 82 | 83 | This makes use of Nix's "github" fetcher, which is more efficient: 84 | it fetches tarballs generated by GitHub instead of using git. 85 | 86 | It is not enabled by default because that fetcher is still "subject 87 | to change" according to Nix's documentation. 88 | 89 | This should be safe to enable, as long as you remember to disable it 90 | if you encounter fetch issues, especially if they start after an 91 | upgrade of Nix. 92 | ''; 93 | }; 94 | 95 | extraPackages = mkOption { 96 | default = self: [ ]; 97 | type = hm.types.selectorFunction; 98 | defaultText = "epkgs: [ ]"; 99 | example = literalExpression "epkgs: [ epkgs.treesit-grammars.with-all-grammars ]"; 100 | description = '' 101 | Extra Emacs packages from nixpkgs available to Doom Emacs, 102 | unless that packages is handled by Doom Emacs. 103 | 104 | If Doom Emacs specifies a package, 105 | then that specific package and version will be exactly as Doom specifies even if it's 106 | included in 'extraPackages'. 107 | 108 | To use 'extraPackages' to override a specific package otherwise specified by Doom Emacs, 109 | it is required that the Doom Emacs config use the following arguments for the package: 110 | '(package! ... :built-in t)' 111 | This allows nix to be used to apply patches to an Emacs package. 112 | 113 | Some Emacs packages from nixpkgs have additional side-effects specific to nix, 114 | consider the Emacs Package 'treesit-grammars.with-all-grammars'. 115 | It downloads all treesitter grammars defined in nixpkgs at build time and makes them 116 | available on path for Emacs at runtime. 117 | Doom cannot specify that package using the '(package! ...)' syntax. 118 | ''; 119 | }; 120 | 121 | extraBinPackages = mkOption { 122 | default = [ 123 | config.programs.ripgrep.package 124 | config.programs.git.package 125 | config.programs.fd.package 126 | ]; 127 | type = types.listOf types.package; 128 | defaultText = literalExpression "[ programs.ripgrep.package programs.git.package programs.fd.package ]"; 129 | description = "Extra packages to add to Doom's $PATH."; 130 | }; 131 | 132 | tangleArgs = mkOption { 133 | default = null; 134 | example = "."; 135 | type = types.nullOr types.str; 136 | defaultText = literalExpression null; 137 | description = '' 138 | When set, run `doom +org tangle $tangleArgs` in `doomDir`. 139 | ''; 140 | }; 141 | 142 | # Passed to overrideScope (see https://nixos.org/manual/nixpkgs/stable/#sec-emacs-config). 143 | emacsPackageOverrides = mkOption { 144 | default = eself: esuper: { }; 145 | example = literalExpression "eself: esuper: { somePackage = esuper.somePackage.overrideAttrs { ignoreCompilationError = true; }; }"; 146 | type = types.functionTo (types.functionTo (types.lazyAttrsOf types.package)); 147 | description = '' 148 | Function passed to (emacsPackagesFor emacs).overrideScope. 149 | 150 | This is applied after applying Doom's pins (and after generating derivations for packages 151 | where we cannot pin the original nixpkgs derivation). 152 | 153 | See https://nixos.org/manual/nixpkgs/stable/#sec-emacs-config for more information. 154 | ''; 155 | }; 156 | 157 | lspUsePlists = mkOption { 158 | default = true; 159 | example = false; 160 | type = types.bool; 161 | description = '' 162 | Build lsp-mode (and packages using it) with LSP_USE_PLISTS set. 163 | 164 | This may improve performance (see 165 | https://emacs-lsp.github.io/lsp-mode/page/performance/#use-plists-for-deserialization). 166 | ''; 167 | }; 168 | 169 | # Home Manager-specific options. 170 | provideEmacs = mkOption { 171 | type = types.bool; 172 | default = true; 173 | example = false; 174 | description = '' 175 | If enabled (the default), provide "emacs" (and "emacsclient", etc). 176 | If disabled, provide a "doom-emacs" binary. 177 | 178 | Disable this to install doom-emacs in parallel with vanilla Emacs. 179 | ''; 180 | }; 181 | 182 | # Hidden/internal options. 183 | finalEmacsPackage = mkOption { 184 | type = types.package; 185 | visible = false; 186 | readOnly = true; 187 | description = "The final Emacs-compatible package"; 188 | }; 189 | 190 | finalDoomPackage = mkOption { 191 | type = types.package; 192 | visible = false; 193 | readOnly = true; 194 | description = "The final doom-emacs package"; 195 | }; 196 | 197 | }; 198 | }; 199 | 200 | config = mkIf cfg.enable (mkMerge [ 201 | ( 202 | let 203 | doomPackages = doomFromPackages pkgs { 204 | inherit (cfg) 205 | doomDir 206 | doomLocalDir 207 | emacs 208 | profileName 209 | experimentalFetchTree 210 | extraPackages 211 | extraBinPackages 212 | tangleArgs 213 | emacsPackageOverrides 214 | lspUsePlists 215 | ; 216 | }; 217 | in 218 | { 219 | programs.doom-emacs.finalDoomPackage = doomPackages.doomEmacs; 220 | programs.doom-emacs.finalEmacsPackage = doomPackages.emacsWithDoom; 221 | } 222 | ) 223 | { 224 | home.packages = [ 225 | (if cfg.provideEmacs then cfg.finalEmacsPackage else cfg.finalDoomPackage) 226 | ]; 227 | } 228 | (mkIf (options.services ? emacs && cfg.provideEmacs) { 229 | services.emacs.package = cfg.finalEmacsPackage; 230 | }) 231 | ]); 232 | } 233 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /elisp-packages-late.nix: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Overrides applied after Doom's pins. 16 | 17 | { 18 | eself, 19 | esuper, 20 | git, 21 | lib, 22 | makeWrapper, 23 | stdenvNoCC, 24 | writableTmpDirAsHomeHook, 25 | }: 26 | let 27 | packages = { 28 | # Doom uses emacs-straight/auctex, which still contains parts of upstream's 29 | # build system but does not contain all .in files, resulting in a failed build 30 | # if we attempt to use upstream's configure. 31 | # 32 | # Use melpaBuild instead of trivialBuild because company-auctex installs as a 33 | # package (with a specified dependency) while trivialBuild does not include 34 | # the necessary package metadata to satisfy that dependency. 35 | auctex = esuper.melpaBuild { 36 | inherit (esuper.auctex) pname version src; 37 | meta = { 38 | description = "build auctex from emacs-straight for Doom"; 39 | }; 40 | # Most of auctex fails to byte-compile unless we do this. 41 | # TODO: figure out why this is necessary (there may be a better 42 | # solution). 43 | nativeBuildInputs = [ 44 | writableTmpDirAsHomeHook 45 | ]; 46 | }; 47 | # Doom lets Straight provide org-autoloads.el as an alternative for 48 | # org-loaddefs.el, and manually generates org-version.el. 49 | # 50 | # I currently run Org's build system to generate org-version.el. 51 | # But we need this org on load-path while byte-compiling packages that depend on it. 52 | # We accomplish that by using a melpaBuild for everything else. 53 | # 54 | # If we let org install itself, it ends up in an "org" subdir of site-lisp that is not on 55 | # load-path while byte-compiling dependent packages, causing those to pick up the built-in version 56 | # of org (causing problems at runtime). 57 | # 58 | # This ends up writing both org-autoloads.el and org-loaddefs.el, which combined provide the same 59 | # autoloads org-loaddefs.el has when using just Org's build system. I doubt all this is working as 60 | # intended, but the end result seems to work... 61 | org = esuper.org.overrideAttrs (old: { 62 | nativeBuildInputs = old.nativeBuildInputs ++ [ 63 | esuper.emacs 64 | makeWrapper 65 | ]; 66 | # Finding ORGVERSION is a hack (based on the one in Doom). 67 | # TODO: set GITVERSION? 68 | # datadir makes oc-csl find etc/csl and ox-odt find etc/styles. 69 | # org-odt-schema-dir stays nil because it looks for od-schema*.rnc which is not installed. 70 | # (Not sure if OpenDocument-schema-v1.3.rnc is misnamed or this file is not distributed...) 71 | configurePhase = '' 72 | echo "prefix = $out" > local.mk 73 | echo "datadir = $out/share/emacs/site-lisp/org/etc" >> local.mk 74 | echo "ORGVERSION = $(sed -ne 's/^;; Version: \([^\n-]\+\).*/\1/p' lisp/org.el)" >> local.mk 75 | make config 76 | ''; 77 | preBuild = (old.preBuild or "") + '' 78 | make autoloads 79 | ''; 80 | postInstall = (old.postInstall or "") + '' 81 | make install-etc install-info 82 | ''; 83 | }); 84 | org-contrib = esuper.org-contrib.overrideAttrs (old: { 85 | packageRequires = old.packageRequires ++ [ eself.org ]; 86 | }); 87 | sln-mode = esuper.sln-mode.overrideAttrs (old: { 88 | # Straight uses a recipe from el-get that specifies the font-lock-ext 89 | # dependency. 90 | buildInputs = (old.buildInputs or [ ]) ++ [ eself.font-lock-ext ]; 91 | }); 92 | # Straight checks for git's presence at import time. 93 | # We could probably get by with feeding it /bin/true or similar, 94 | # but it seems not worth the risk. 95 | straight = esuper.melpaBuild { 96 | inherit (esuper.straight) pname version src; 97 | meta = { 98 | description = "build straight with Git dependency added for Doom"; 99 | }; 100 | # Do not install files that shadow builtins and/or have undesirable side 101 | # effects if loaded. 102 | files = "(\"straight*.el\")"; 103 | nativeBuildInputs = [ git ]; 104 | }; 105 | # Nix uses a Melpa recipe that assumes the upstream CMake repo layout. 106 | # Doom uses emacsmirror and sets :files (:defaults "*"). 107 | cmake-mode = esuper.melpaBuild { 108 | inherit (esuper.cmake-mode) pname version src; 109 | meta = { 110 | description = "build cmake-mode from emacsmirror for Doom"; 111 | }; 112 | }; 113 | # Doom uses a recipe with :files (:defaults "*"), which MELPA's package-build 114 | # rejects because it includes dotfiles 115 | # (https://github.com/melpa/package-build/pull/67). 116 | # Use a melpaBuild here so the package ends up in its own directory: 117 | # it uses that directory as a snippets directory, and using site-lisp/ as that 118 | # might go wrong. 119 | doom-snippets = esuper.doom-snippets.overrideAttrs { 120 | # The directories we want to match must be mode names: assume those are 121 | # sensibly named (they currently are). 122 | files = ''(:defaults "*-mode")''; 123 | packageRequires = [ eself.yasnippet ]; 124 | # Stop all snippets from ending up on load-path via 125 | # normal-top-level-add-subdirs-to-load-path. 126 | # Avoids "default" in one of these from being mistaken for default.el. 127 | preBuild = '' 128 | for d in *-mode; do 129 | touch $d/.nosearch 130 | done 131 | ''; 132 | }; 133 | # Contains an extension containing debug.el that should not be on load-path. 134 | julia-snail = esuper.julia-snail.overrideAttrs (old: { 135 | preBuild = (old.preBuild or "") + '' 136 | for d in extensions/*; do 137 | touch $d/.nosearch 138 | done 139 | ''; 140 | }); 141 | # Rustic dropped its hard flycheck dependency upstream, but Doom is pinned to a revision 142 | # that still has it, causing errors at nativecomp time. 143 | # TODO: drop this once Doom catches up 144 | rustic = esuper.rustic.overrideAttrs (old: { 145 | packageRequires = old.packageRequires ++ [ eself.flycheck ]; 146 | }); 147 | # TODO: refactor our dependency-extraction so we can apply it selectively to packages we don't 148 | # generate the entire derivation for. 149 | # 150 | # nixpkgs deletes dapui.el because it is empty (only comments), triggering a compilation error. 151 | # Upstream deleted the file in 152 | # https://github.com/emacs-lsp/dap-mode/commit/438679755e880f2a662a63bc04da9e843257e248 153 | # 154 | # TODO: remove this if nixpkgs drops support for dap-mode that needed this. 155 | # 156 | # nixpkgs applies the fix conditionally, but since we set our version to "9999snapshot" we 157 | # unintentionally get counted as an old version. 158 | dap-mode = esuper.dap-mode.overrideAttrs (old: { 159 | preBuild = lib.replaceStrings [ "rm --verbose dapui.el" ] [ "" ] old.preBuild; 160 | }); 161 | # mu4e-compat depends on mu4e, which (if I understand correctly) cannot be on melpa because it is 162 | # bundled with mu, and therefore mu4e-compat cannot have the dependency in its package-requires. 163 | # But it does not byte-compile without mu4e present. Add the dependency. 164 | mu4e-compat = esuper.mu4e-compat.overrideAttrs { 165 | packageRequires = [ eself.mu4e ]; 166 | }; 167 | # TODO: attempt to fix sly-stepper properly. 168 | # sly-stepper `require`s `sly-stickers`. That lives in sly's contribs subdirectory: it looks like 169 | # that is normally added to the load path by `sly-setup`, which Doom runs from after-init-hook. 170 | # But at byte compile time that subdirectory is not on the load path, breaking byte compilation. 171 | sly-stepper = esuper.sly-stepper.overrideAttrs { 172 | packageRequires = [ eself.sly ]; 173 | ignoreCompilationError = true; 174 | }; 175 | org-noter = esuper.org-noter.overrideAttrs { 176 | # Nixpkgs conditionally patches an older version, which our "9999snapshot" version breaks. 177 | patches = [ ]; 178 | }; 179 | # Newer nixpkgs uses a melpa-build that requires a library with the same name of the package name. 180 | # Several wanderlust-related packages do not. emacsmirror fixed this: apply their fix. 181 | apel = esuper.apel.overrideAttrs (attrs: { 182 | patches = (attrs.patches or [ ]) ++ [ 183 | ./elisp-patches/apel-library.patch 184 | ]; 185 | }); 186 | flim = esuper.flim.overrideAttrs (attrs: { 187 | patches = (attrs.patches or [ ]) ++ [ 188 | ./elisp-patches/flim-library.patch 189 | ]; 190 | }); 191 | semi = esuper.semi.overrideAttrs (attrs: { 192 | patches = (attrs.patches or [ ]) ++ [ 193 | ./elisp-patches/semi-library.patch 194 | ]; 195 | }); 196 | wanderlust = esuper.wanderlust.overrideAttrs (attrs: { 197 | patches = (attrs.patches or [ ]) ++ [ 198 | ./elisp-patches/wanderlust-library.patch 199 | ]; 200 | }); 201 | # reveal.js is not actually an ELisp package. Doom gets straight.el to install it, 202 | # then makes org-re-reveal use it as data. 203 | revealjs = stdenvNoCC.mkDerivation { 204 | inherit (esuper.revealjs) pname version src; 205 | buildPhase = '' 206 | siteDir=$out/share/emacs/site-lisp/revealjs 207 | mkdir -p $siteDir 208 | cp -r css dist js plugin $siteDir/ 209 | ''; 210 | }; 211 | # Make it byte-compile properly. 212 | code-review = esuper.code-review.overrideAttrs (attrs: { 213 | nativeBuildInputs = (attrs.nativeBuildInputs or [ ]) ++ [ git ]; 214 | }); 215 | # Make it byte-compile (see auctex) 216 | company-auctex = esuper.company-auctex.overrideAttrs (attrs: { 217 | nativeBuildInputs = (attrs.nativeBuildInputs or [ ]) ++ [ 218 | writableTmpDirAsHomeHook 219 | ]; 220 | }); 221 | # This also needs a real $HOME. nixpkgs actually provides one but it's an Elpa package... 222 | auctex-cont-latexmk = esuper.auctex-cont-latexmk.overrideAttrs (old: { 223 | nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ 224 | writableTmpDirAsHomeHook 225 | ]; 226 | }); 227 | # Make it byte-compile. 228 | # 229 | # TODO ask upstream about missing evil dependency? 230 | # https://github.com/PythonNut/evil-easymotion/commit/fb7182625fcb1b1f7d43f69df620d98aa0f42a86 231 | # removed the dependency, I do not understand why. 232 | evil-easymotion = esuper.evil-easymotion.overrideAttrs (attrs: { 233 | buildInputs = attrs.buildInputs ++ [ eself.evil ]; 234 | }); 235 | 236 | janet-ts-mode = esuper.janet-ts-mode.overrideAttrs { 237 | # TODO: Attempts to use libtree-sitter-janet-simple at build time. 238 | # This is not sufficient to fix it: 239 | # packageRequires = [ 240 | # (esuper.treesit-grammars.with-grammars (p: [ p.tree-sitter-janet-simple ])) 241 | # ]; 242 | ignoreCompilationError = true; 243 | }; 244 | 245 | # Other files that fail to byte-compile: 246 | # - rustic-flycheck, no flycheck dependency. Seems undesirable to force. 247 | # - stylus-mode, missing dependency on sws-mode(?) 248 | # See also https://github.com/doomemacs/doomemacs/commit/f9feaec5bd75f4d997e0b07bc5c8b9177be20781 249 | # - xref-js2: upstream bug(?). 250 | # Error: `add-to-list' can't use lexical var `words'; use `push' or `cl-pushnew' 251 | # - several others, looks like mostly missing (frequently optional) deps. 252 | # 253 | # To check for these: `doom-emacs --script build-helpers/byte-compile-check.el` 254 | 255 | # TODO: clean up some more load-path clutter? 256 | # 257 | # The single biggest contributors are tree-sitter-langs (73) and ansible (36), 258 | # leaving 48 others. 259 | # 260 | # It looks like upstream has the same issue for both of these. 261 | # For tree-sitter-langs, we do use our own generated derivation (because elpa), 262 | # but the problematic directory is package-specific (queries/). 263 | # For ansible, we use upstream's derivation from melpaPackages. 264 | # 265 | # Since it looks like it's not breaking things and the number of entries added 266 | # to load-path is relatively modest I'm leaving this alone for now. 267 | }; 268 | in 269 | 270 | # Only apply changes to packages actually pinned. 271 | # 272 | # Although some of our changes are safe to apply unconditionally, not all of them are, and not all 273 | # of them get test coverage both ways: since breakage in the other direction seems less likely, 274 | # filter out unpinned packages here rather than trying to keep track of it per-package. 275 | 276 | lib.flip lib.mapAttrs packages ( 277 | name: package: 278 | let 279 | orig = esuper.${name}; 280 | in 281 | if (orig.passthru.doom-emacs-pinned or false) then package else orig 282 | ) 283 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nix-doom-emacs-unstraightened 2 | 3 | `nix-doom-emacs-unstraightened` (referred to as "Unstraightened" below) builds 4 | [Doom Emacs](https://github.com/doomemacs/doomemacs) using 5 | [Nix](https://nixos.org) bundling a user configuration directory and the 6 | dependencies specified by it. It is very similar to 7 | [nix-doom-emacs](https://github.com/nix-community/nix-doom-emacs), but is 8 | implemented differently. 9 | 10 | ## Status 11 | 12 | [![CI](https://github.com/marienz/nix-doom-emacs-unstraightened/actions/workflows/ci.yml/badge.svg)](https://github.com/marienz/nix-doom-emacs-unstraightened/actions/workflows/ci.yml) 13 | 14 | Tested and working on Linux, with emacs-overlay and Doom inputs updated 15 | automatically. If you're reading this on GitHub, there should be a CI status 16 | badge above: if CI is passing, Unstraightened installs an up-to-date version of 17 | Doom Emacs and (almost) all module dependencies. 18 | 19 | macOS is not covered by CI but was previously reported working. Please file an 20 | issue if it does not work for you (and please comment on 21 | https://github.com/marienz/nix-doom-emacs-unstraightened/issues/42 if you know 22 | why CI is not working). 23 | 24 | You may encounter "Cannot find Git revision" errors on Nix versions newer than 25 | 2.18.x (see [#14](https://github.com/marienz/nix-doom-emacs-unstraightened/issues/14)). 26 | Try enabling `experimentalFetchTree` to work around this (see below). 27 | 28 | Please report any issues. 29 | 30 | ## How to use 31 | 32 | ### Test run 33 | 34 | 1. Check out this repository 35 | 2. Optional: run `nix flake update nixpkgs` (Nix 2.19 or up) or `nix flake lock 36 | --update-input nixpkgs` (earlier versions). If `nixpkgs` is in the system 37 | registry (which it is by default on NixOS 24.05 and up) this will make 38 | Unstraightened reuse more dependencies already on your system. 39 | 40 | > [!NOTE] 41 | > Updating other inputs (with `nix flake update`) is not recommended. These 42 | > inputs are automatically updated daily as long as tests pass. Updating 43 | > manually may update to an incompatible version of Doom or Emacs packages. 44 | 45 | 3. `rm doomdir/*` (delete my example/test configuration) 46 | 4. Copy your Doom configuration into `doomdir` 47 | 5. Make sure all files are added to the Git index (`git add doomdir`) 48 | 6. Run `nix run .#doom-emacs`. 49 | 50 | If this does not work, the "with flakes" setup below is unlikely to work either. 51 | Please file an issue. 52 | 53 | ### With flakes 54 | 55 | Add this flake as an input in `flake.nix`: 56 | 57 | ``` nix 58 | inputs = { 59 | nix-doom-emacs-unstraightened.url = "github:marienz/nix-doom-emacs-unstraightened"; 60 | # Optional, to download less. Neither the module nor the overlay uses this input. 61 | nix-doom-emacs-unstraightened.inputs.nixpkgs.follows = ""; 62 | }; 63 | ``` 64 | 65 | If your Doom configuration lives in a different repository, add that as input 66 | too: 67 | 68 | ``` nix 69 | inputs = { 70 | doom-config.url = "..."; 71 | doom-config.flake = false; 72 | }; 73 | ``` 74 | 75 | #### Home Manager 76 | 77 | If you use [Home Manager](https://github.com/nix-community/home-manager), add 78 | Unstraightened's Home Manager module in `flake.nix`: 79 | 80 | ``` nix 81 | outputs = inputs @ { nixpkgs, home-manager, ... }: { 82 | homeConfigurations."username" = home-manager.lib.homeManagerConfiguration { 83 | modules = [ 84 | inputs.nix-doom-emacs-unstraightened.homeModule 85 | ./home.nix 86 | ]; 87 | extraSpecialArgs = { inherit inputs; }; 88 | }; 89 | }; 90 | ``` 91 | 92 | Configure it in `home.nix`: 93 | 94 | ``` nix 95 | programs.doom-emacs = { 96 | enable = true; 97 | doomDir = inputs.doom-config; # or e.g. `./doom.d` for a local configuration 98 | }; 99 | ``` 100 | 101 | There are a few other configurable options, see below. 102 | 103 | If you set `services.emacs.enable = true`, that will run Unstraightened as well 104 | (Unstraightened sets itself as `services.emacs.package`). Set 105 | `programs.doom-emacs.provideEmacs = false` or override `services.emacs.package` 106 | if you want a vanilla Emacs daemon instead. 107 | 108 | > [!WARNING] 109 | > Using the overlay described below with `programs.emacs.package` will not work 110 | > correctly (see [HACKING](./HACKING.md) for details). 111 | 112 | #### Overlay 113 | 114 | If you don't use Home Manager or prefer not to use Unstraightened's Home Manager 115 | module, add Unstraightened's overlay. Typically that means adding: 116 | 117 | ``` nix 118 | nixpkgs.overlays = [ inputs.nix-doom-emacs-unstraightened.overlays.default ]; 119 | ``` 120 | 121 | to a Home Manager or NixOS module. 122 | 123 | The overlay adds two packages: 124 | 125 | - To install Unstraightened in parallel with a normal Emacs, add: 126 | 127 | ``` nix 128 | (pkgs.doomEmacs { 129 | doomDir = inputs.doom-config; 130 | # If you stored your Doom configuration in the same flake, use 131 | # doomDir = ./path/to/doom/config; 132 | # instead. 133 | doomLocalDir = "~/.local/share/nix-doom"; 134 | }) 135 | ``` 136 | 137 | to your installed packages (see below for what `doomLocalDir` is for). This 138 | installs a binary named `doom-emacs`. 139 | 140 | - To install Unstraightened as your default Emacs, use `pkgs.emacsWithDoom` 141 | instead of `pkgs.doomEmacs`. This installs a binary named `emacs` as well as 142 | `emacsclient` and other helpers (similar to [`emacsWithPackages` in 143 | nixpkgs](https://nixos.org/manual/nixos/stable/#module-services-emacs-adding-packages)). 144 | 145 | ### Without flakes 146 | 147 | This is currently not explicitly supported, but should be possible (use 148 | `pkgs.callPackages ./nix-doom-emacs-unstraightened`). PRs extending this part of 149 | the documentation are welcome, as are (within reason) changes necessary to 150 | support use without flakes. 151 | 152 | ### Options 153 | 154 | `doomEmacs` and `emacsWithDoom` support the following options: 155 | 156 | - `doomDir`: your configuration directory (also known as DOOMDIR, Doom private 157 | directory / module). Required. 158 | 159 | - `doomLocalDir`: value Doom should use as `DOOMLOCALDIR`. Required, because by 160 | default Doom would use its source directory, which is read-only. 161 | 162 | > [!NOTE] 163 | > This supports `~` expansion but does **not** support shell variable expansion. 164 | > Using `$XDG_DATA_HOME` will not work. 165 | 166 | > [!NOTE] 167 | > Because Unstraightened uses Doom's profile system, using the same value you 168 | > used with vanilla Doom will not result in Unstraightened finding your files. 169 | > See below. 170 | 171 | - `emacs`: Emacs package to use. Defaults to `pkgs.emacs`. 172 | Use this to select different Emacs variants like `pkgs.emacs-pgtk`. 173 | 174 | - `doomSource`: Doom source tree. Defaults to a flake input: overriding that 175 | input is probably easier than passing this. 176 | 177 | - `extraBinPackages`: packages to add to `$PATH`. Defaults to Git, ripgrep and 178 | fd. 179 | 180 | - `extraPackages`: Specify extra Emacs packages from nixpkgs to be available to 181 | Doom Emacs. Defaults to this function `epkgs: [ ]` (no extra packages). 182 | For example to include Emacs package `treesit-grammars.with-all-grammars`: 183 | `extraPackages = epkgs: [ epkgs.treesit-grammars.with-all-grammars ];`. 184 | 185 | - `experimentalFetchTree`: fetch packages using `fetchTree`, which is more 186 | efficient but considered experimental in Nix (subject to changes which might 187 | break fetches). 188 | 189 | - `tangleArgs`: When set, Unstraightened runs `doom +org tangle` in `doomDir`. 190 | See `doom +org tangle --help` for the arguments you can use here, and see the 191 | [Org-mode manual](https://orgmode.org/manual/Extracting-Source-Code.html) for 192 | more information. 193 | 194 | - `emacsPackageOverrides`: Function passed to `(emacsPackagesFor 195 | emacs).overrideScope` (see [Nixpkgs 196 | manual](https://nixos.org/manual/nixpkgs/stable/#sec-emacs-config)). Runs 197 | after Unstraightened's own overrides have been applied. That means it can be 198 | used to fix problems with Unstraightened's automatically-generated 199 | derivations, by using `esuper..overrideAttrs`. 200 | 201 | > [!NOTE] 202 | > The `:config literate` module has no effect when using Unstraightened. 203 | > Use `tangleArgs = "--all config.org"` instead. 204 | 205 | > [!NOTE] 206 | > Currently, this feature uses the version of Org-mode that comes with Emacs 207 | > when tangling, not the version installed by Doom. This limitation could be 208 | > lifted: file an issue if this limitation is a problem for you. 209 | 210 | There are a few other settings but they are not typically useful. See the 211 | source. 212 | 213 | The Home Manager module supports the same options, as well as: 214 | 215 | - `provideEmacs`: disable this to only provide a `doom-emacs` binary, not an 216 | `emacs` binary (that is: it switches from `emacsWithDoom` to `doomEmacs`). Use 217 | this if you want to install vanilla Emacs in parallel. 218 | 219 | ## Comparison to "normal" Doom Emacs 220 | 221 | - Unstraightened updates Doom and its dependencies along with the rest of your 222 | Nix packages, removing the need to run `doom sync` and similar Doom-specific 223 | commands. 224 | 225 | - Doom pins its direct dependencies, but still pulls the live version of some 226 | packages from MELPA or other repositories. Its pins are also applied to build 227 | recipes whose source is not pinned. This makes Doom installs not fully 228 | reproducible and can cause intermittent breakage. 229 | 230 | Unstraightened pulls these dependencies from nixpkgs or 231 | [emacs-overlay](https://github.com/nix-community/emacs-overlay). Pinning 232 | emacs-overlay pins all build recipes and packages not already pinned by Doom. 233 | 234 | - Unstraightened stores your Doom configuration 235 | (`~/.doom.d`/`~/.config/doom`/`$DOOMDIR`) in the Nix store. This has 236 | advantages (the configuration's enabled modules always match available 237 | dependencies), but also some disadvantages (see known problems below). 238 | 239 | - Unstraightened uses Doom's 240 | [profiles](https://github.com/doomemacs/doomemacs/tree/master/profiles) under 241 | the hood. This affects where Doom stores local state: 242 | 243 | | Variable | Doom | Unstraightened | 244 | |-|-|-| 245 | | `doom-cache-dir` | `$DOOMLOCALDIR/cache` | `~/.cache/doom` | 246 | | `doom-data-dir` | `$DOOMLOCALDIR/etc` | `~/.local/share/doom` | 247 | | `doom-state-dir` | `$DOOMLOCALDIR/state` | `~/.local/state/doom` | 248 | 249 | (Doom also stores some things in per-profile subdirectories below the above 250 | directories: the default profile name used by Unstraightened is `nix`, 251 | resulting in paths like `~/.cache/doom/nix`. All of these also respect the usual 252 | `XDG_*_DIR` environment variables.) 253 | 254 | When migrating from "normal" Doom, you may need to move some files around. 255 | 256 | If this bothers you, you can try setting `profileName = ""`. This makes 257 | Unstraightened use the usual paths (relative to `doomLocalDir`), but uses 258 | somewhat of a hack to do so (please report any issues observed). 259 | 260 | ## Comparison to `nix-doom-emacs` 261 | 262 | - Unstraightened does not attempt to use straight.el at all. Instead, it uses 263 | Doom's CLI to make Doom export its dependencies, then uses Nix's 264 | `emacsWithPackages` to install them all, then configures Doom to use the 265 | "built-in" version for all its dependencies. This approach seems simpler to 266 | me, but time will have to tell how well it holds up. 267 | 268 | - Unstraightened respects Doom's pins. I believe this is necessary for a system 269 | like this to work: Doom really does frequently make local changes to adjust to 270 | changes or work around bugs in its dependencies. 271 | 272 | - Unstraightened is much younger. It is simpler in places because it assumes 273 | Emacs >=29. It probably still has some problems already solved by 274 | `nix-doom-emacs`, and it is too soon to tell how robust it is. 275 | 276 | ## Bugs 277 | 278 | *Do not report bugs upstream*. If you think it's a bug in Doom, reproduce it 279 | without Unstraightened first, or report it here first. 280 | 281 | There are a few known current bugs and likely future bugs in Unstraightened: 282 | 283 | ### Pins can break 284 | 285 | The way Unstraightened applies Doom's pins to Nix instead of straight.el build 286 | recipes is a hack. Although it seems to work fairly well (better than I 287 | expected), it will break at times. 288 | 289 | If it breaks, it should break at build time, but I do not know all failure modes 290 | to expect yet. 291 | 292 | One likely failure mode is an error about Git commits not being present in the 293 | upstream repository. To fix this, try building against a revision of the 294 | `emacs-overlay` flake that is closer to the age of `doomemacs`. This is a 295 | fundamental limitation: Doom assumes its pins are applied to `straight.el` build 296 | recipes, while we use nixpkgs / emacs-overlay. If these diverge, our build 297 | breaks. 298 | 299 | Another possible problem is a package failing to build or run because one of its 300 | dependencies is missing. Unstraightened currently uses dependencies from the 301 | original (emacs-overlay) package. This is largely a performance optimization, 302 | that can be revisited if it breaks too frequently. 303 | 304 | ### Saving Custom changes fails 305 | 306 | Saving changes through Custom will not work, because `custom-file` is read-only. 307 | I am open to suggestions for how this should work: 308 | 309 | - Currently, `DOOMDIR/custom.el` is loaded, but changes need to be applied 310 | manually. 311 | - If we set `custom-file` to a writable location, that fixes saving but breaks 312 | loading. If the user copies their custom-file out of their DOOMDIR to this 313 | location once, they are not alerted to changes they may want to copy back. 314 | - If we try to use Home Manager, I would expect to hit the same problems 315 | and/or collisions on activation, but I have not experimented with this. 316 | 317 | ### Flag-controlled packages may be broken 318 | 319 | Doom supports listing all packages (including ones pulled in by modules that are 320 | not currently enabled). Unstraightened uses this to build-test them. However, 321 | this does not include packages enabled through currently-disabled flags. 322 | 323 | This is tricky because Doom seems to not support accessing supported flags 324 | programmatically, and because some flags are mutually exclusive. 325 | 326 | I may end up approximating this by checking in a hardcoded `init.el` with all 327 | (or at least most) currently-available flags enabled. 328 | 329 | ### `doom doctor` fails with / complains about... 330 | 331 | #### "Checking for stale elc files... File is missing" 332 | 333 | ``` 334 | > Checking for stale elc files... 335 | x There was an unexpected runtime error 336 | Message: File is missing 337 | Details: ("Opening directory" "No such file or directory" "/home/marienz/.local/share/nix-doom/straight/build-30.1") 338 | 339 | ``` 340 | 341 | For now, just create the directory. 342 | 343 | I would like to fix this but have not thought of the least messy way yet. 344 | 345 | #### "Doom is installed in a non-standard location" 346 | 347 | Ignore it. 348 | 349 | Unstraightened uses `--init-directory`, as the doctor recommends. 350 | 351 | #### "Found another Emacs config:" 352 | 353 | Safe to ignore, for the same reason as the previous warning. 354 | 355 | ### tree-sitter grammars are not installed automatically 356 | 357 | Install them manually by setting 358 | 359 | ``` nix 360 | extraPackages = epkgs: [ epkgs.treesit-grammars.with-all-grammars ]; 361 | ``` 362 | 363 | (Or specify just the grammars you need by following the [example in 364 | nixpkgs](https://github.com/NixOS/nixpkgs/blob/46cef0d5c1df9604fd53eebdaf69f31b74c13419/pkgs/applications/editors/emacs/elisp-packages/manual-packages/treesit-grammars/package.nix#L15), 365 | but these are tiny so installing all of them is usually fine.) 366 | 367 | This may be automated in the future (see [this 368 | issue](https://github.com/marienz/nix-doom-emacs-unstraightened/issues/82)). 369 | 370 | ## Frequently Anticipated Questions 371 | 372 | ### How do I add more packages? 373 | 374 | Add `(package! foo)` to `packages.el`. 375 | 376 | Do not wrap emacsWithDoom in emacsWithPackages. See [HACKING](./HACKING.md) for 377 | why this will not work. 378 | 379 | The Home Manager option `extraPackages` is available to add extra Emacs packages from nixpkgs to Doom Emacs. 380 | If this is not sufficient, please file an issue. 381 | 382 | ### How do I add packages not in Emacs overlay? 383 | 384 | Add `(package! foo :recipe ...)` to `packages.el`. 385 | 386 | If there is a derivation in emacs-overlay or nixpkgs, it will use the same dependencies as the deriviation (which won't 387 | always work if you pin to a sufficiently different version). If not, the dependencies are autodetected them from headers 388 | in the package. 389 | 390 | If the detected dependencies aren't sufficient, you'll have to add the package in Nix directly, with `extraPackages`. 391 | 392 | ```nix 393 | extraPackages = ( 394 | epkgs: [ 395 | (epkgs.melpaBuild { 396 | pname = ; 397 | version = "9999snapshot1"; 398 | packageRequires = [ ]; 399 | src = fetchTree {...}; 400 | }) 401 | ] 402 | ); 403 | ``` 404 | 405 | See also [#70](https://github.com/marienz/nix-doom-emacs-unstraightened/issues/70). 406 | 407 | If this is not sufficient, file an issue explaining what you're trying to do. 408 | 409 | ### What's wrong with `straight.el`? 410 | 411 | `straight.el` is great, but its features are somewhat at odds with Nix: 412 | 413 | - `straight.el` can fetch package build recipes and packages. We cannot use this 414 | from within Nix's build sandbox: we would need to build a system similar to 415 | how emacs-overlay updates elpa / melpa and get `straight.el` to use it. 416 | - `straight.el` maintains a tree of mutable Git checkouts: you can edit these 417 | directly or use `straight.el` to maintain them. The Nix store is immutable so 418 | none of this will work. 419 | - `straight.el` can build packages, but so can nixpkgs / emacs-overlay. 420 | 421 | Doom heavily uses `straight.el` during `doom sync`, but it does not use it at 422 | all at startup and barely uses it after that. Since we're replacing `doom sync` 423 | in its entirety, bypassing `straight.el` seems simpler than trying to use it 424 | just for package builds. 425 | 426 | ### Unstraightened seems to use `package.el`. Isn't that bad? 427 | 428 | Doom's FAQ offers [several arguments against 429 | `package.el`](https://github.com/doomemacs/doomemacs/blob/master/docs/faq.org#why-does-doom-use-straightel-and-not-packageel). 430 | They boil down to two problems, neither of which applies to Unstraightened: 431 | 432 | - `package.el` always builds from head: no rollback, no pinning, no 433 | reproducibility, no way to override the package source used. Unstraightened 434 | does not use `package.el` to fetch packages: it leaves that to Nix. We can 435 | handle pinning there, and Nix flakes add further reproducibility and rollback 436 | beyond what Doom's pins offer. 437 | - `package.el` can be slow to initialize. Doom normally speeds up startup by 438 | combining autoloads from all installed packages into one file. Because 439 | `package.el` produces autoload files much like `straight.el` does, and we're 440 | loading everything from the immutable Nix store, we can apply exactly the same 441 | approach to `package.el`. Unstraightened startup performance should be about 442 | the same as vanilla Doom. 443 | 444 | ### It's so slow to build! 445 | 446 | Parallel builds should help (set Nix's `max-jobs` to something greater than 1), 447 | but it is a bit slow. 448 | 449 | There are a few issues: 450 | 451 | - Unstraightened uses 452 | [IFD](https://nixos.org/manual/nix/stable/language/import-from-derivation) to 453 | determine packages to install and to determine package dependencies for 454 | packages not in emacs-overlay. Especially the latter is slow. 455 | 456 | - Doom (currently) [does not native-compile ahead of 457 | time](https://github.com/doomemacs/doomemacs/issues/6811), but Unstraightened 458 | (or nixpkgs, really), does. 459 | 460 | - It should be possible to disable nativecomp and/or move it to runtime, but 461 | see the next point... 462 | 463 | - Unstraightened's packages should be cacheable, but I don't have that set up 464 | yet. 465 | 466 | - In particular, Unstraightened's generated derivations for elisp packages do 467 | not depend on the exact Doom source or configuration they're generated from. 468 | They depend on the pinned version and Emacs binary used to build them, but 469 | not much else. So it should be possible to build a Doom configuration with 470 | just a few modules enabled using commonly-used versions of Emacs from CI and 471 | push the results to a binary cache like https://cachix.org/. 472 | 473 | ### Required disclaimer 474 | 475 | This is not an officially supported Google product. It is a personal [side 476 | project](https://opensource.google/documentation/reference/releasing). 477 | -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | { 16 | # DOOMDIR / Doom private directory / module. 17 | doomDir, 18 | # Default DOOMLOCALDIR (use something like ~/.local/share/doom). 19 | doomLocalDir, 20 | # Doom source tree. 21 | doomSource, 22 | # Emacs package to build against. 23 | emacs, 24 | # Name of doom profile to use. Empty string to disable profile early in startup. 25 | profileName ? "nix", 26 | # Use fetchTree instead of fetchGit for package fetches. 27 | experimentalFetchTree ? false, 28 | # Extra emacs packages from nixpkgs 29 | extraPackages ? epkgs: [ ], 30 | # Extra packages to add to $PATH 31 | extraBinPackages ? [ 32 | git 33 | fd 34 | ripgrep 35 | ], 36 | # Args to pass to `doom +org tangle`. 37 | tangleArgs ? null, 38 | # Passed to overrideScope (see https://nixos.org/manual/nixpkgs/stable/#sec-emacs-config). 39 | emacsPackageOverrides ? (eself: esuper: { }), 40 | # True to build lsp-mode and dependant packages with LSP_USE_PLISTS set. 41 | lspUsePlists ? true, 42 | 43 | callPackage, 44 | callPackages, 45 | fd, 46 | git, 47 | ripgrep, 48 | emacsPackagesFor, 49 | lib, 50 | runCommandLocal, 51 | runtimeShell, 52 | makeBinaryWrapper, 53 | stdenv, 54 | stdenvNoCC, 55 | toInit, 56 | writeTextDir, 57 | }: 58 | let 59 | inherit (import ./fetch-overrides.nix) extraFiles extraPins extraUrls; 60 | 61 | nonEmptyProfileName = if profileName != "" then profileName else "nix"; 62 | 63 | tangleDoomDir = writeTextDir "init.el" ( 64 | toInit lib { 65 | lang.org = true; 66 | } 67 | ); 68 | 69 | # Preprocess DOOMDIR with `doom +org tangle` if requested. 70 | doomDir' = 71 | if tangleArgs != null then 72 | runCommandLocal "tangled-doomdir" 73 | { 74 | inherit 75 | tangleArgs 76 | doomDir 77 | doomSource 78 | runtimeShell 79 | ; 80 | EMACS = lib.getExe emacs; 81 | DOOMDIR = tangleDoomDir; 82 | # Enable this to troubleshoot failures at this step. 83 | #DEBUG = "1"; 84 | } 85 | '' 86 | mkdir $out doomlocaldir 87 | export DOOMLOCALDIR=$PWD/doomlocaldir 88 | cd $out 89 | ln -s $doomDir/* ./ 90 | $runtimeShell $doomSource/bin/doom +org tangle $tangleArgs 91 | '' 92 | else 93 | doomDir; 94 | 95 | # Step 1: determine which Emacs packages to pull in. 96 | # 97 | # Inputs: Doom, original DOOMDIR (only init.el and packages.el are used). 98 | # Outputs: 99 | # - Packages Doom normally loads using Straight (as json) 100 | # - modified packages.el that claims all packages are system-installed 101 | # 102 | # Uses Doom's CLI framework, which does not require anything else is installed 103 | # (not even straight). 104 | 105 | # Force local build in case the user init.el does something weird and to avoid a roundtrip. 106 | doomIntermediates = callPackage ./build-helpers/doomscript.nix { 107 | name = "doom-intermediates"; 108 | inherit doomSource emacs; 109 | extraArgs = { 110 | DOOMDIR = "${doomDir'}"; 111 | }; 112 | script = ./build-helpers/dump; 113 | scriptArgs = "-o $out"; 114 | }; 115 | 116 | # Ignore agda-input: nixpkgs installs this as part of agda2-mode. 117 | doomPackageSet = lib.filterAttrs (n: v: n != "agda-input") ( 118 | lib.importJSON "${doomIntermediates}/packages.json" 119 | ); 120 | 121 | # Step 2: override Emacs packages to respect Doom's pins (and add/fix packages). 122 | doomEmacsPackages = lib.foldl' (p: p.overrideScope) (emacsPackagesFor emacs) [ 123 | (eself: esuper: callPackages ./elisp-packages-early.nix { inherit esuper eself; }) 124 | ( 125 | eself: esuper: 126 | let 127 | # If multiple packages are built from the same repository, straight.el pins the repository 128 | # if only one of them is pinned. Doom relies on this behavior, so try to do the same. 129 | # 130 | # We need to do this for dependencies that are not in doomPackageSet. But we don't collect 131 | # all extra pins here, as that would involve pulling the repository from all packages in 132 | # esuper. Instead we map repositories to pins, and then do the rest of the work in 133 | # makePackage. 134 | 135 | # TODO: refactor url determination out of makePackage, use here? 136 | # Probably best done at the same time as the codeberg TODO in fetch-overrides.nix. 137 | repoToPin = 138 | let 139 | # Not unique, but that's ok as this is only used with genAttrs. 140 | packageNames = lib.attrNames doomPackageSet; 141 | packageToRepo = lib.genAttrs packageNames (name: esuper.${name}.src.gitRepoUrl or null); 142 | repoToPackages = lib.zipAttrs (lib.mapAttrsToList (name: repo: { ${repo} = name; }) packageToRepo); 143 | packageToPin = lib.mapAttrs (name: p: extraPins.${name} or p.pin or null) doomPackageSet; 144 | repoToPins = lib.mapAttrs ( 145 | name: packages: lib.unique (lib.filter (p: p != null) (map (p: packageToPin.${p}) packages)) 146 | ) repoToPackages; 147 | in 148 | lib.mapAttrs ( 149 | name: pins: 150 | assert lib.assertMsg ((lib.length pins) <= 1) '' 151 | ${name}: 152 | used by ${lib.concatStringsSep ", " repoToPackages.${name}} 153 | pinned to different versions ${lib.concatStringsSep ", " pins} 154 | 155 | nix-doom-emacs-unstraightened assumes these packages would use the same repo 156 | when Doom Emacs builds them using straight.el, meaning this would not work. 157 | 158 | If that assumption is correct, this is a bug in Doom Emacs. 159 | 160 | If that assumption is not correct, this is a bug in Unstraightened. 161 | 162 | If unsure, report this as a bug in Unstraightened.''; 163 | lib.findFirst (lib.const true) null pins 164 | ) repoToPins; 165 | 166 | # We want to override `version` along with `src` to avoid spurious 167 | # rebuilds on version bumps in emacs-overlay of packages Doom has 168 | # pinned. 169 | # 170 | # The elisp manual says we need a version `version-to-list` can parse, 171 | # which means it must start with a number and cannot contain the actual 172 | # commit ID. We start with a large integer in case package.el starts 173 | # version-checking dependencies (it currently does not but a comment in 174 | # the code says it should). Additionally, `(package-version-join 175 | # (version-to-list v))` must roundtrip to avoid elpa2nix failing with 176 | # "Package does not untar cleanly". 177 | # 178 | # Additionally, we currently need this version to be recognized by 179 | # https://github.com/NixOS/nixpkgs/blob/26b2bef8b3c73a0931af73d902af2d806588f6bb/pkgs/applications/editors/emacs/build-support/elpa2nix.el#L4-L12 180 | # That means we need a digit after "snapshot". 181 | snapshotVersion = "9999snapshot1"; 182 | 183 | makePackage = 184 | name: p: 185 | assert lib.asserts.assertEachOneOf "keys for ${name}" (lib.attrNames p) [ 186 | "modules" 187 | "recipe" 188 | "pin" 189 | "type" 190 | "env" # ignored. Used by doom for LSP_USE_PLISTS: revisit if its use spreads. 191 | ]; 192 | assert (p ? type) -> lib.asserts.assertOneOf "type of ${name}" p.type [ "core" ]; 193 | let 194 | # We're called for all attributes of esuper (to check if they're a package pinned via 195 | # repoToPin). Some of those attributes are null. So we cannot use `esuper.${name} or 196 | # null`, we need to explicitly check for presence. 197 | hasOrigEPkg = esuper ? ${name}; 198 | origEPkg = esuper.${name}; 199 | pin = 200 | extraPins.${name} or p.pin or ( 201 | # Don't use `url`: this needs to be in sync with repoToPin above. 202 | # (If we remap ELPA packages to emacs-straight here but not above, it breaks...) 203 | let 204 | repo = esuper.${name}.src.gitRepoUrl or null; 205 | in 206 | if repo != null then repoToPin.${repo} or null else null 207 | ); 208 | files = p.recipe.files or extraFiles.${name} or null; 209 | # We have to specialcase ELPA packages pinned by Doom: Straight mirrors / 210 | # repackages them. Doom's pins assume that mirror is used (so we have to 211 | # use it), and replacing the source in nixpkgs's derivation will not work 212 | # (it assumes it gets a tarball as input). 213 | 214 | # TODO: check notmuch works correctly without notmuch-version.el 215 | 216 | isElpa = 217 | hasOrigEPkg 218 | && ( 219 | origEPkg == esuper.elpaPackages.${name} or null || origEPkg == esuper.nongnuPackages.${name} or null 220 | ); 221 | epkg = 222 | if hasOrigEPkg && (pin != null -> !isElpa) then 223 | origEPkg 224 | else 225 | assert lib.assertMsg (isElpa || (p ? recipe) || extraUrls ? ${name}) '' 226 | nix-doom-emacs-unstraightened: ${name}: unable to derive repository URL: 227 | - no recipe provided 228 | - not an ELPA package 229 | - not in Unstraightened's fetch-overrides.nix 230 | 231 | If this is a custom `package!` entry in packages.el, add a `:recipe`. 232 | ''; 233 | # Assume we can safely ignore (pre-)build unless we're actually 234 | # building our own package. 235 | 236 | # HACK: ignore these checks for org, which elisp-packages-late fixes up. 237 | # (Generalize this if we ever need the same treatment for other packages.) 238 | assert lib.assertMsg (name != "org" -> !(p ? recipe.pre-build)) "${name}: pre-build not supported"; 239 | assert lib.assertMsg (name != "org" -> !(p ? recipe.build)) "${name}: build not supported"; 240 | assert lib.assertMsg (pin != null) '' 241 | nix-doom-emacs-unstraightened: ${name}: not in nixpkgs or emacs-overlay, not pinned. 242 | All packages must be pinned so they can be fetched deterministically. 243 | 244 | If this is a custom `package!` entry in your packages.el, add a `:pin`. 245 | If it is a `package!` in Doom, add an entry to Unstraightened's fetch-overrides.nix. 246 | ''; 247 | 248 | # This uses melpaBuild instead of trivialBuild to end up with 249 | # something package.el understands as satisfying dependencies. 250 | # This is necessary if we're replacing a pinned ELPA dependency 251 | # of an unpinned ELPA package. 252 | (esuper.melpaBuild { 253 | pname = name; 254 | # melpaBuild requires we set `version` and `commit` here 255 | # (leaving `version` unset until overrideAttrs below does not 256 | # work). 257 | version = snapshotVersion; 258 | meta = { 259 | description = "trivial build for doom-emacs"; 260 | }; 261 | inherit files; 262 | # TODO: refactor out the recursive call to makePackage. 263 | # (Currently needed for dependencies on packages not in epkgs or doom.) 264 | packageRequires = map (name: eself.${name} or (makePackage name { })) reqlist; 265 | }).overrideAttrs 266 | (prev: { 267 | # We only depend on this during evaluation. Force a dependency so it does not 268 | # get garbage-collected, which slows down subsequent evaluation. 269 | inherit reqfile; 270 | postInstall = (prev.postInstall or "") + '' 271 | mkdir -p $out/nix-support 272 | ln -s $reqfile $out/nix-support/unstraightened-dependencies.json 273 | ''; 274 | }); 275 | # nixpkgs uses fetchZip for these, so epkg.src.gitRepoUrl is unset. 276 | # Derive the repo URL from the archive name, which will look like 277 | # https://codeberg.org/rwv/android-mode/archive/67f7c0d7d37605efc7f055b76d731556861c3eb9.tar.gz 278 | codeberg = lib.strings.match "(https://codeberg.org/[^/]+/[^/]+)/.*" (epkg.src.url or ""); 279 | url = 280 | # Build a canonical fetch URL from the recipe host/repo when possible. 281 | # 282 | # - GitHub recipes use "owner/repo" (optionally with .git); we prefix with github host. 283 | # - SourceHut recipes use "~owner/repo"; we prefix with git.sr.ht host. 284 | if (p.recipe.host or "") == "github" && p ? recipe.repo then 285 | "https://github.com/${p.recipe.repo}" 286 | else if (p.recipe.host or "") == "sourcehut" && p ? recipe.repo then 287 | "https://git.sr.ht/~${p.recipe.repo}" 288 | else if (p.recipe.type or "git") == "git" && p ? recipe.repo && (p.recipe.host or null) == null then 289 | p.recipe.repo 290 | else 291 | extraUrls.${name} or epkg.src.gitRepoUrl or ( 292 | if isElpa then 293 | "https://github.com/emacs-straight/${name}" 294 | else if codeberg != null then 295 | (lib.head codeberg) + ".git" 296 | else 297 | ( 298 | let 299 | recipe = lib.generators.toPretty { } (p.recipe or "missing"); 300 | in 301 | throw "${name}: cannot derive url from recipe ${recipe}" 302 | ) 303 | ); 304 | # Use the fetchGit primop instead of nixpkgs's fetchFromGitHub because 305 | # fetchGit allows fetching a specific git commit without a hash. 306 | fetchGitArgs = { 307 | inherit url; 308 | rev = pin; 309 | allRefs = true; 310 | # Skip submodules by default because they seem to be hitting 311 | # https://github.com/NixOS/nix/issues/10773 (or a similar caching issue) and for 312 | # parity between fetchTree's github fetcher and fetchGit (GitHub's exports don't 313 | # seem to contain submodules). 314 | submodules = !(p.recipe.nonrecursive or true); 315 | # TODO: pull ref from derivation.src when not pulling it from p.recipe? 316 | # Note Doom does have packages with pin + branch (or nonrecursive) set, 317 | # expecting to inherit the rest of the recipe from Straight. 318 | 319 | # Always specify a ref to work around https://github.com/NixOS/nix/issues/10773 320 | ref = p.recipe.branch or "HEAD"; 321 | 322 | # TODO: remove if https://github.com/NixOS/nix/issues/11012 is fixed. 323 | shallow = false; 324 | }; 325 | src = 326 | if experimentalFetchTree then 327 | fetchTree ( 328 | if lib.hasPrefix "https://github.com/" url then 329 | let 330 | tail = lib.removePrefix "https://github.com/" url; 331 | split = lib.splitString "/" tail; 332 | owner = lib.head split; 333 | repo = lib.removeSuffix ".git" (lib.elemAt split 1); 334 | in 335 | { 336 | type = "github"; 337 | inherit owner repo; 338 | rev = pin; 339 | } 340 | else if lib.hasPrefix "https://git.sr.ht/" url then 341 | let 342 | tail = lib.removePrefix "https://git.sr.ht/" url; 343 | split = lib.splitString "/" tail; 344 | owner = lib.head split; 345 | # Mirror GitHub handling and strip optional ".git" suffix. 346 | repo = lib.removeSuffix ".git" (lib.elemAt split 1); 347 | in 348 | { 349 | type = "sourcehut"; 350 | inherit owner repo; 351 | rev = pin; 352 | } 353 | else 354 | ( 355 | { 356 | type = "git"; 357 | } 358 | // fetchGitArgs 359 | ) 360 | ) 361 | else 362 | fetchGit fetchGitArgs; 363 | # Run locally to avoid a network roundtrip. 364 | reqfile = runCommandLocal "${name}-deps" { 365 | inherit src name; 366 | emacs = lib.getExe emacs; 367 | printDeps = ./build-helpers/print-deps.el; 368 | } "$emacs -Q --batch --script $printDeps $src $name > $out"; 369 | reqjson = lib.importJSON reqfile; 370 | # json-encode encodes the empty list as null (nil), not []. 371 | reqlist = if reqjson == null then [ ] else reqjson; 372 | in 373 | if pin != null then 374 | epkg.overrideAttrs { 375 | inherit src; 376 | version = snapshotVersion; 377 | commit = pin; 378 | # elisp-packages-late checks for this. 379 | passthru.doom-emacs-pinned = true; 380 | } 381 | else 382 | epkg; 383 | # Hack: we call makePackage for everything (not just doomPackageSet), just to hit the 384 | # repoToPin check. We cannot easily call it just for transitive dependencies, because we 385 | # need makePackage to figure out what the dependencies (for packages not in esuper) are. 386 | # But we do need some filtering (currently just "emacs" itself) to avoid infinite recursion 387 | # while populating repoToPin. 388 | upstreamWithPins = lib.mapAttrs ( 389 | n: p: if (!lib.isDerivation p) || p == esuper.emacs then p else makePackage n { } 390 | ) esuper; 391 | doomPackages = lib.mapAttrs makePackage doomPackageSet; 392 | allPackages = upstreamWithPins // doomPackages; 393 | in 394 | allPackages 395 | ) 396 | (eself: esuper: callPackages ./elisp-packages-late.nix { inherit esuper eself; }) 397 | ( 398 | eself: esuper: 399 | let 400 | manglePackage = 401 | name: pkg: 402 | let 403 | isLspModeOrDependant = 404 | # This causes infinite recursion, and I have not wrapped my head around why yet: 405 | # (name == "lsp-mode") || lib.elem esuper.lsp-mode pkg.packageRequires; 406 | # So check by package name. 407 | (name == "lsp-mode") 408 | || (lib.elem "lsp-mode" (map (p: p.ename or "not-a-package") (pkg.packageRequires or [ ]))); 409 | in 410 | pkg.overrideAttrs ( 411 | lib.optionalAttrs isLspModeOrDependant { 412 | # TODO: simplify if https://github.com/NixOS/nixpkgs/pull/452898 is merged. 413 | preBuild = let 414 | origPreBuild = pkg.preBuild or ""; 415 | origPreBuildString = if origPreBuild == null then "" else origPreBuild; 416 | in 417 | origPreBuildString + '' 418 | export LSP_USE_PLISTS=1 419 | ''; 420 | } 421 | ); 422 | # TODO: very similar map to the upstreamWithPins one. Rework makePackage signature for reuse? 423 | result = lib.mapAttrs ( 424 | name: pkg: if (!lib.isDerivation pkg) || pkg == esuper.emacs then pkg else (manglePackage name pkg) 425 | ) esuper; 426 | in 427 | result 428 | ) 429 | emacsPackageOverrides 430 | ]; 431 | 432 | # Step 3: Build an emacsWithPackages, pulling all packages from step 1 from 433 | # the set from step 2. 434 | emacsWithPackages = doomEmacsPackages.emacsWithPackages ( 435 | epkgs: 436 | (map (p: epkgs.${p}) (lib.attrNames doomPackageSet)) ++ (extraPackages epkgs) ++ extraBinPackages 437 | ); 438 | 439 | # Step 4: build a DOOMDIR, Doom profile and profile loader using Emacs from 440 | # step 3 and packages.el from step 1. 441 | # 442 | # Do this all in one derivation because these refer back to each other: 443 | # - init.el in DOOMDIR refers to the straight.el build cache generated along 444 | # with the profile 445 | # - The path to the generated profile is included in the loader 446 | # - Generating the profile depends on the loader 447 | 448 | doomProfile = stdenvNoCC.mkDerivation { 449 | name = "doom-profile"; 450 | buildCommandPath = ./build-helpers/build-doom-profile.sh; 451 | 452 | inherit 453 | doomIntermediates 454 | doomSource 455 | runtimeShell 456 | ; 457 | doomDir = doomDir'; 458 | profileName = nonEmptyProfileName; 459 | noProfileHack = profileName == ""; 460 | buildProfileLoader = ./build-helpers/build-profile-loader; 461 | buildProfile = ./build-helpers/build-profile; 462 | initEl = ./init.el; 463 | EMACS = lib.getExe emacsWithPackages; 464 | inherit (emacsWithPackages) deps; 465 | # Enable this to troubleshoot failures at this step. 466 | #DEBUG = "1"; 467 | 468 | # Required to avoid Doom erroring out at startup. 469 | nativeBuildInputs = [ git ]; 470 | # Force local build in case the user init.el does something weird. 471 | preferLocalBuild = true; 472 | allowSubstitutes = false; 473 | }; 474 | 475 | # Step 5: write wrappers to start the whole thing. 476 | 477 | # makeBinaryWrapper pulls in a compiler, so don't force this one local. 478 | doomEmacs = stdenv.mkDerivation { 479 | name = "doom-emacs"; 480 | buildCommandPath = ./build-helpers/build-doom-emacs.sh; 481 | 482 | # emacsWithPackages also accessed externally (for pushing to Cachix). 483 | inherit 484 | doomProfile 485 | doomLocalDir 486 | doomSource 487 | emacsWithPackages 488 | lspUsePlists 489 | ; 490 | profileName = nonEmptyProfileName; 491 | 492 | nativeBuildInputs = [ makeBinaryWrapper ]; 493 | }; 494 | 495 | emacsWithDoom = stdenvNoCC.mkDerivation { 496 | inherit (lib.appendToName "with-doom" emacs) name; 497 | inherit (emacs) meta; 498 | inherit doomEmacs emacs; 499 | buildCommandPath = ./build-helpers/build-emacs-with-doom.sh; 500 | 501 | # Force local build as it's near-trivial. 502 | preferLocalBuild = true; 503 | allowSubstitutes = false; 504 | }; 505 | in 506 | { 507 | inherit doomEmacs emacsWithDoom; 508 | } 509 | --------------------------------------------------------------------------------