├── .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 | [](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 |
--------------------------------------------------------------------------------