Files
doomemacs/lisp/lib/config.el
Henrik Lissner 15904349cf refactor!: module API
BREAKING CHANGE: This backports some architectural choices from v3.0.
This changes Doom's module API, renaming some functions and removing
others, in order to facilitate some new features, prepare to move Doom's
modules into separate repos, and make way for two, much larger breaking
commits coming in the next few days.

This commit won't break anything for users unless they're tinkering with
Doom's internals/using its `doom-module-*` API directly. I am avoiding
broader backwards incompatibilities until the 3.0 release.

What's new:

- Negated flags. (modulep! :editor evil -everywhere) will return non-nil
  if :editor evil is active without its +everywhere flag.
- `modulep!` now takes multiple flags to simplify AND checks. E.g.

    (and (modulep! +foo)
         (modulep! +bar)
         (not (modulep! +baz)))

  Can now be expressed with:

    (modulep! +foo +bar -baz)
- Adds pcase matchers for `doom-module-context` and `doom-module`
  structs, making the following destructuring binds possible:

    (pcase-dolist ((doom-module group name flags features)
                   (hash-table-values doom-modules))
      ...)

  This will be used more in v3.0.
- Adds file cookie support to module init.el and config.el files.

Here's a summary of breaking changes made in this commit:

- `doom-module-context` was changed from a vector to a struct (record).
- `doom-modules` is now a table of `doom-module` structs, rather than
  free-form plists.
- The following macros have been renamed:
  - `doom-context-with` -> `with-doom-context`
  - `doom-module-context-with` -> `with-doom-module`
- The followings functions have been replaced/removed:
  - `doom-module-context`+`doom-module-context-get` -> `doom-module`
  - `doom-module-set` -> `doom-module--put`
  - `doom-module-p` -> `doom-module-active-p`
  - `doom-module-context-key` (is now a getter with the same name)
  - `doom-module-put` (removed)
  - `doom-module--context-field` (removed)
- The signatures for these functions have changed:
  - `doom-module-get CATEGORY &optional MODULE PROP` ->
    `doom-module-get (GROUP . MODULE) &optional PROP`
  - `doom-module-locate-path CATEGORY &optional MODULE FILE` ->
    `doom-module-locate-path (GROUP . MODULE) &optional FILE`
  - `doom-module-expand-path CATEGORY MODULE &optional FILE` ->
    `doom-module-expand-path (GROUP . MODULE) &optional FILE`
- Adds the following functions
  - `doom-module-exists-p`
  - `doom-module-key`
  - `doom-module->context`
  - `doom-module<-context`
- Removes the following variables
  - `doom-module--empty-context`

This commit results in a little redundancy, which I will address in
parts 2/3 and/or v3.0.
2024-10-20 02:41:42 -04:00

171 lines
5.9 KiB
EmacsLisp

;;; lisp/lib/config.el -*- lexical-binding: t; -*-
;;;###autoload
(defvar doom-after-reload-hook nil
"A list of hooks to run after `doom/reload' has reloaded Doom.")
;;;###autoload
(defvar doom-before-reload-hook nil
"A list of hooks to run before `doom/reload' has reloaded Doom.")
;;;###autoload
(defun doom/open-private-config ()
"Browse your `doom-user-dir'."
(interactive)
(unless (file-directory-p doom-user-dir)
(make-directory doom-user-dir t))
(doom-project-browse doom-user-dir))
;;;###autoload
(defun doom/find-file-in-private-config ()
"Search for a file in `doom-user-dir'."
(interactive)
(doom-project-find-file doom-user-dir))
;;;###autoload
(defun doom/goto-private-init-file ()
"Open your private init.el file.
And jumps to your `doom!' block."
(interactive)
(find-file (expand-file-name doom-module-init-file doom-user-dir))
(goto-char
(or (save-excursion
(goto-char (point-min))
(search-forward "(doom!" nil t))
(point))))
;;;###autoload
(defun doom/goto-private-config-file ()
"Open your private config.el file."
(interactive)
(find-file (expand-file-name doom-module-config-file doom-user-dir)))
;;;###autoload
(defun doom/goto-private-packages-file ()
"Open your private packages.el file."
(interactive)
(find-file (expand-file-name doom-module-packages-file doom-user-dir)))
;;
;;; Managements
(defmacro doom--if-compile (command on-success &optional on-failure)
(declare (indent 2))
`(let ((doom-bin "doom")
(default-directory doom-emacs-dir)
(exec-path (cons doom-bin-dir exec-path)))
(when (and (featurep :system 'windows)
(string-match-p "cmdproxy.exe$" shell-file-name))
(unless (executable-find "pwsh")
(user-error "Powershell 3.0+ is required, but pwsh.exe was not found in your $PATH"))
(setq doom-bin "doom.ps1"))
;; Ensure the bin/doom operates with the same environment as this
;; running session.
(letenv! (("PATH" (string-join exec-path path-separator))
("EMACS" (doom-path invocation-directory invocation-name))
("EMACSDIR" doom-emacs-dir)
("DOOMDIR" doom-user-dir)
("DOOMLOCALDIR" doom-local-dir)
("DEBUG" (if doom-debug-mode (number-to-string doom-log-level) "")))
(with-current-buffer
(compile (format ,command (expand-file-name doom-bin doom-bin-dir)) t)
(let ((w (get-buffer-window (current-buffer))))
(select-window w)
(add-hook
'compilation-finish-functions
(lambda (_buf status)
(if (equal status "finished\n")
(progn
(delete-window w)
,on-success)
,on-failure))
nil 'local))))))
(defvar doom-reload-command
(format "%s sync -B -e"
;; /usr/bin/env doesn't exist on Android
(if (featurep :system 'android)
"sh %s"
"%s"))
"Command that `doom/reload' runs.")
;;;###autoload
(defun doom/reload ()
"Reloads your private config.
WARNING: This command is experimental! If you haven't configured your config to
be idempotent, then this could cause compounding slowness or errors.
This is experimental! It will try to do as `bin/doom sync' does, but from within
this Emacs session. i.e. it reload autoloads files (if necessary), reloads your
package list, and lastly, reloads your private config.el.
Runs `doom-after-reload-hook' afterwards."
(interactive)
(mapc #'require (cdr doom-incremental-packages))
(doom--if-compile doom-reload-command
(with-doom-context '(reload modules)
(doom-run-hooks 'doom-before-reload-hook)
(doom-load (file-name-concat doom-user-dir doom-module-init-file) t)
(with-demoted-errors "PRIVATE CONFIG ERROR: %s"
(general-auto-unbind-keys)
(unwind-protect
(startup--load-user-init-file nil)
(general-auto-unbind-keys t)))
(doom-run-hooks 'doom-after-reload-hook)
(message "Config successfully reloaded!"))
(user-error "Failed to reload your config")))
;;;###autoload
(defun doom/reload-autoloads ()
"Reload only the autoloads of the current profile.
This is much faster and safer than `doom/reload', but not as comprehensive. This
reloads your package and module visibility, but does not install new packages or
remove orphaned ones. It also doesn't reload your private config.
It is useful to only pull in changes performed by 'doom sync' on the command
line."
(interactive)
(require 'doom-profiles)
;; TODO: Make this more robust
(with-doom-context 'reload
(dolist (file (mapcar #'car doom-profile-generators))
(when (string-match-p "/[0-9]+-loaddefs[.-]" file)
(load (doom-path doom-profile-dir doom-profile-init-dir-name file)
'noerror)))))
;;;###autoload
(defun doom/reload-env ()
"Reloads your envvar file.
DOES NOT REGENERATE IT. You must run 'doom env' in your shell OUTSIDE of Emacs.
Doing so from within Emacs will taint your shell environment.
An envvar file contains a snapshot of your shell environment, which can be
imported into Emacs."
(interactive)
(with-doom-context 'reload
(let ((default-directory doom-emacs-dir))
(with-temp-buffer
(doom-load-envvars-file doom-env-file)
(message "Reloaded %S" (abbreviate-file-name doom-env-file))))))
(defvar doom-upgrade-command
(format "%s upgrade -B --force"
;; /usr/bin/env doesn't exist on Android
(if (featurep :system 'android)
"sh %s"
"%s"))
"Command that `doom/upgrade' runs.")
;;;###autoload
(defun doom/upgrade ()
"Run 'doom upgrade' then prompt to restart Emacs."
(interactive)
(doom--if-compile doom-upgrade-command
(when (y-or-n-p "You must restart Emacs for the upgrade to take effect.\n\nRestart Emacs?")
(doom/restart-and-restore))))
(provide 'doom-lib '(config))
;;; config.el ends here